diff options
Diffstat (limited to 'indra/newview')
21 files changed, 2456 insertions, 35 deletions
| diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4f7ce88165..6b7fa7d842 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -457,6 +457,9 @@ set(viewer_SOURCE_FILES      llpathfindingobjectlist.cpp      llpathfindingpathtool.cpp      llpersistentnotificationstorage.cpp +    llpersonfolderview.cpp +    llpersonmodelcommon.cpp +    llpersontabview.cpp      llphysicsmotion.cpp      llphysicsshapebuilderutil.cpp      llpipelinelistener.cpp @@ -499,6 +502,7 @@ set(viewer_SOURCE_FILES      llsidetraypanelcontainer.cpp      llsky.cpp      llslurl.cpp +    llsociallist.cpp      llspatialpartition.cpp      llspeakers.cpp      llspeakingindicatormanager.cpp @@ -1025,6 +1029,9 @@ set(viewer_HEADER_FILES      llpathfindingobjectlist.h      llpathfindingpathtool.h      llpersistentnotificationstorage.h +    llpersonfolderview.h +    llpersonmodelcommon.h +    llpersontabview.h      llphysicsmotion.h      llphysicsshapebuilderutil.h      llpipelinelistener.h @@ -1068,6 +1075,7 @@ set(viewer_HEADER_FILES      llsidetraypanelcontainer.h      llsky.h      llslurl.h +    llsociallist.h      llspatialpartition.h      llspeakers.h      llspeakingindicatormanager.h diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3adf956ae3..861717669f 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2598,20 +2598,38 @@ bool LLAppViewer::initConfiguration()      // What can happen is that someone can use IE (or potentially       // other browsers) and do the rough equivalent of command       // injection and steal passwords. Phoenix. SL-55321 + +	LLSLURL option_slurl; +      if(clp.hasOption("url"))      { -		LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); +		option_slurl = LLSLURL(clp.getOption("url")[0]); +		LLStartUp::setStartSLURL(option_slurl);  		if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION)   		{    			LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); -			 -		}   +		}      }      else if(clp.hasOption("slurl"))      { -		LLSLURL start_slurl(clp.getOption("slurl")[0]); -		LLStartUp::setStartSLURL(start_slurl); +		option_slurl = LLSLURL(clp.getOption("slurl")[0]); +		LLStartUp::setStartSLURL(option_slurl);      } +	 +	//RN: if we received a URL, hand it off to the existing instance. +	// don't call anotherInstanceRunning() when doing URL handoff, as +	// it relies on checking a marker file which will not work when running +	// out of different directories + +	if (option_slurl.isValid() && +		(gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) +	{ +		if (sendURLToOtherInstance(option_slurl.getSLURLString())) +		{ +			// successfully handed off URL to existing instance, exit +			return false; +		} +	}  	const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent");  	if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) @@ -2702,21 +2720,6 @@ bool LLAppViewer::initConfiguration()  #endif  	LLStringUtil::truncate(gWindowTitle, 255); -	//RN: if we received a URL, hand it off to the existing instance. -	// don't call anotherInstanceRunning() when doing URL handoff, as -	// it relies on checking a marker file which will not work when running -	// out of different directories - -	if (LLStartUp::getStartSLURL().isValid() && -		(gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) -	{ -		if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) -		{ -			// successfully handed off URL to existing instance, exit -			return false; -		} -	} -  	if (!gSavedSettings.getBOOL("AllowMultipleViewers"))  	{  	    // diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index b88888da3b..21ba3a444b 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -346,8 +346,11 @@ void LLFloaterIMContainer::onStubCollapseButtonClicked()  void LLFloaterIMContainer::onSpeakButtonClicked()  { -	LLAgent::toggleMicrophone("speak"); -	updateSpeakBtnState(); +	//LLAgent::toggleMicrophone("speak"); +	//updateSpeakBtnState(); + +	LLParticipantList* session_model = dynamic_cast<LLParticipantList*>(mConversationsItems[LLUUID(NULL)]); +	session_model->addTestAvatarAgents();  }  void LLFloaterIMContainer::onExpandCollapseButtonClicked()  { diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 4138558bad..03135ce580 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -28,6 +28,8 @@  // libs  #include "llavatarname.h" +#include "llconversationview.h" +#include "llfloaterimcontainer.h"  #include "llfloaterreg.h"  #include "llfloatersidepanelcontainer.h"  #include "llmenubutton.h" @@ -48,7 +50,10 @@  #include "llavataractions.h"  #include "llavatarlist.h"  #include "llavatarlistitem.h" +#include "llavatarnamecache.h"  #include "llcallingcard.h"			// for LLAvatarTracker +#include "llcallbacklist.h" +#include "llerror.h"  #include "llfloateravatarpicker.h"  //#include "llfloaterminiinspector.h"  #include "llfriendcard.h" @@ -57,25 +62,65 @@  #include "llinventoryobserver.h"  #include "llnetmap.h"  #include "llpanelpeoplemenus.h" +#include "llparticipantlist.h" +#include "llpersonfolderview.h" +#include "llpersonmodelcommon.h" +#include "llpersontabview.h"  #include "llsidetraypanelcontainer.h"  #include "llrecentpeople.h"  #include "llviewercontrol.h"		// for gSavedSettings  #include "llviewermenu.h"			// for gMenuHolder  #include "llvoiceclient.h"  #include "llworld.h" +#include "llsociallist.h"  #include "llspeakers.h" +#include "llfloaterwebcontent.h" +#include "llurlaction.h" +#include "llcommandhandler.h"  #define FRIEND_LIST_UPDATE_TIMEOUT	0.5  #define NEARBY_LIST_UPDATE_INTERVAL 1 +#define FBCTEST_LIST_UPDATE_INTERVAL 0.25  static const std::string NEARBY_TAB_NAME	= "nearby_panel";  static const std::string FRIENDS_TAB_NAME	= "friends_panel";  static const std::string GROUP_TAB_NAME		= "groups_panel";  static const std::string RECENT_TAB_NAME	= "recent_panel";  static const std::string BLOCKED_TAB_NAME	= "blocked_panel"; // blocked avatars - +static const std::string FBCTEST_TAB_NAME	= "fbctest_panel"; +static const std::string FBCTESTTWO_TAB_NAME	= "fbctesttwo_panel";  static const std::string COLLAPSED_BY_USER  = "collapsed_by_user"; +class LLFacebookConnectHandler : public LLCommandHandler +{ +public: +	LLFacebookConnectHandler() : LLCommandHandler("fbc", UNTRUSTED_THROTTLE), mPanelPeople(NULL) { } + +	LLPanelPeople* mPanelPeople; + +	bool handle(const LLSD& tokens, const LLSD& query_map, +				LLMediaCtrl* web) +	{ +		if (tokens.size() > 0) +		{ +			if (tokens[0].asString() == "connect") +			{ +				if (query_map.has("code")) +				{ +					if (mPanelPeople) +					{ +						mPanelPeople->connectToFacebook(query_map["code"]); +						mPanelPeople = NULL; +					} +				} +				return true; +			} +		} +		return false; +	} +}; +LLFacebookConnectHandler gFacebookConnectHandler; +  /** Comparator for comparing avatar items by last interaction date */  class LLAvatarItemRecentComparator : public LLAvatarItemComparator  { @@ -489,10 +534,52 @@ public:  	}  }; +/** + * Periodically updates the FBC test list after a login is initiated. + *  + * The period is defined by FBCTEST_LIST_UPDATE_INTERVAL constant. + */ +class LLFbcTestListUpdater : public LLAvatarListUpdater +{ +	LOG_CLASS(LLFbcTestListUpdater); + +public: +	LLFbcTestListUpdater(callback_t cb) +	:	LLAvatarListUpdater(cb, FBCTEST_LIST_UPDATE_INTERVAL) +	{ +		setActive(false); +	} + +	/*virtual*/ void setActive(bool val) +	{ +		if (val) +		{ +			// update immediately and start regular updates +			update(); +			mEventTimer.start();  +		} +		else +		{ +			// stop regular updates +			mEventTimer.stop(); +		} +	} + +	/*virtual*/ BOOL tick() +	{ +		update(); +		return FALSE; +	} +private: +}; +  //=============================================================================  LLPanelPeople::LLPanelPeople()  	:	LLPanel(), +		mConnectedToFbc(false), +		mPersonFolderView(NULL), +		mTryToConnectToFbc(true),  		mTabContainer(NULL),  		mOnlineFriendList(NULL),  		mAllFriendList(NULL), @@ -504,8 +591,15 @@ LLPanelPeople::LLPanelPeople()  	mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList,	this));  	mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList,	this));  	mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList,	this)); +	mFbcTestListUpdater = new LLFbcTestListUpdater(boost::bind(&LLPanelPeople::updateFbcTestList,	this));  	mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); +	mCommitCallbackRegistrar.add("People.loginFBC", boost::bind(&LLPanelPeople::onLoginFbcButtonClicked, this)); +	mCommitCallbackRegistrar.add("People.requestFBC", boost::bind(&LLPanelPeople::onFacebookAppRequestClicked, this)); +	mCommitCallbackRegistrar.add("People.sendFBC", boost::bind(&LLPanelPeople::onFacebookAppSendClicked, this)); +	mCommitCallbackRegistrar.add("People.testaddFBC", boost::bind(&LLPanelPeople::onFacebookTestAddClicked, this)); +	mCommitCallbackRegistrar.add("People.testaddFBCFolderView", boost::bind(&LLPanelPeople::addTestParticipant, this)); +  	mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this));  	mCommitCallbackRegistrar.add("People.AddFriendWizard",	boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked,	this));  	mCommitCallbackRegistrar.add("People.DelFriend",		boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked,	this)); @@ -532,11 +626,14 @@ LLPanelPeople::~LLPanelPeople()  	delete mNearbyListUpdater;  	delete mFriendListUpdater;  	delete mRecentListUpdater; +	delete mFbcTestListUpdater;  	if(LLVoiceClient::instanceExists())  	{  		LLVoiceClient::getInstance()->removeObserver(this);  	} + +	if (mFbcTestBrowserHandle.get()) mFbcTestBrowserHandle.get()->die();  }  void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) @@ -571,6 +668,7 @@ BOOL LLPanelPeople::postBuild()  	getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));  	getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));  	getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); +	getChild<LLFilterEditor>("fbc_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));  	mTabContainer = getChild<LLTabContainer>("tabs");  	mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); @@ -616,6 +714,74 @@ BOOL LLPanelPeople::postBuild()  	mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);  	mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); +	LLPanel * social_tab = getChild<LLPanel>(FBCTEST_TAB_NAME); +	mFacebookFriends = social_tab->getChild<LLSocialList>("facebook_friends"); +	social_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFbcTestListUpdater, _2)); + +	//===Test START======================================================================== + +	LLPanel * socialtwo_tab = getChild<LLPanel>(FBCTESTTWO_TAB_NAME); + +	//Create folder view +	LLPersonModelCommon* base_item = new LLPersonModelCommon(mPersonFolderViewModel); + +	LLPersonFolderView::Params folder_view_params(LLUICtrlFactory::getDefaultParams<LLPersonFolderView>()); +     +	folder_view_params.parent_panel = socialtwo_tab; +	folder_view_params.listener = base_item; +	folder_view_params.view_model = &mPersonFolderViewModel; +	folder_view_params.root = NULL; +	folder_view_params.use_ellipses = false; +	folder_view_params.options_menu = "menu_conversation.xml"; +	folder_view_params.name = "fbcfolderview"; +	mPersonFolderView = LLUICtrlFactory::create<LLPersonFolderView>(folder_view_params); + +	//Create scroller +	LLRect scroller_view_rect = socialtwo_tab->getRect(); +	scroller_view_rect.mTop -= 2+27; // 27 is the height of the top toolbar +	scroller_view_rect.mRight -= 4; +	scroller_view_rect.mLeft += 2; +	LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); +	scroller_params.rect(scroller_view_rect); + +	LLScrollContainer* scroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); +	socialtwo_tab->addChildInBack(scroller); +	scroller->addChild(mPersonFolderView); +	scroller->setFollowsAll(); +	mPersonFolderView->setScrollContainer(scroller); +	mPersonFolderView->setFollowsAll(); + +	//Create a person tab +	LLPersonTabModel* item = new LLPersonTabModel("Facebook Friends", mPersonFolderViewModel); +	LLPersonTabView::Params params; +	params.name = item->getDisplayName(); +	params.root = mPersonFolderView; +	params.listener = item; +	params.tool_tip = params.name; +	LLPersonTabView * widget = LLUICtrlFactory::create<LLPersonTabView>(params); +	widget->addToFolder(mPersonFolderView); + +	mPersonFolderView->mPersonFolderModelMap[item->getID()] = item; +	mPersonFolderView->mPersonFolderViewMap[item->getID()] = widget; + +	//Create a person tab +	item = new LLPersonTabModel("Facebook Friends Tab Two", mPersonFolderViewModel); +	params.name = item->getDisplayName(); +	params.root = mPersonFolderView; +	params.listener = item; +	params.tool_tip = params.name; +	widget = LLUICtrlFactory::create<LLPersonTabView>(params); +	widget->addToFolder(mPersonFolderView); + +	mPersonFolderView->mPersonFolderModelMap[item->getID()] = item; +	mPersonFolderView->mPersonFolderViewMap[item->getID()] = widget; +	 +	gIdleCallbacks.addFunction(idle, this); + +	//===Test END======================================================================== + + +  	setSortOrder(mRecentList,		(ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"),	false);  	setSortOrder(mAllFriendList,	(ESortOrder)gSavedSettings.getU32("FriendsSortOrder"),		false);  	setSortOrder(mNearbyList,		(ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"),	false); @@ -664,6 +830,15 @@ BOOL LLPanelPeople::postBuild()  	// Must go after setting commit callback and initializing all pointers to children.  	mTabContainer->selectTabByName(NEARBY_TAB_NAME); +	mFBCGearButton = getChild<LLMenuButton>("fbc_options_btn"); + +	LLToggleableMenu* fbc_menu  = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_gear_fbc.xml",  gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); +	if(fbc_menu) +	{ +		mFBCMenuHandle = fbc_menu->getHandle(); +		mFBCGearButton->setMenu(fbc_menu); +	} +  	LLVoiceClient::getInstance()->addObserver(this);  	// call this method in case some list is empty and buttons can be in inconsistent state @@ -686,6 +861,12 @@ void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI,  	updateButtons();  } +void LLPanelPeople::idle(void * user_data) +{ +	LLPanelPeople * self = static_cast<LLPanelPeople *>(user_data); +	self->mPersonFolderView->update(); +} +  void LLPanelPeople::updateFriendListHelpText()  {  	// show special help text for just created account to help finding friends. EXT-4836 @@ -786,6 +967,21 @@ void LLPanelPeople::updateRecentList()  	mRecentList->setDirty();  } +void LLPanelPeople::updateFbcTestList() +{ +	if (mTryToConnectToFbc) +	{	 +		// try to reconnect to facebook! +		tryToReconnectToFacebook(); + +		// don't try again +		mTryToConnectToFbc = false; +		 +		// stop updating +		mFbcTestListUpdater->setActive(false); +	} +} +  void LLPanelPeople::updateButtons()  {  	std::string cur_tab		= getActiveTabName(); @@ -870,6 +1066,13 @@ LLUUID LLPanelPeople::getCurrentItemID() const  	if (cur_tab == BLOCKED_TAB_NAME)  		return LLUUID::null; // FIXME? +	 +	if (cur_tab == FBCTEST_TAB_NAME) +		return LLUUID::null; + +	if (cur_tab == FBCTESTTWO_TAB_NAME) +		return LLUUID::null; +  	llassert(0 && "unknown tab selected");  	return LLUUID::null; @@ -893,6 +1096,10 @@ void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const  		mGroupList->getSelectedUUIDs(selected_uuids);  	else if (cur_tab == BLOCKED_TAB_NAME)  		selected_uuids.clear(); // FIXME? +	else if (cur_tab == FBCTEST_TAB_NAME) +		return; +	else if (cur_tab == FBCTESTTWO_TAB_NAME) +		return;  	else  		llassert(0 && "unknown tab selected"); @@ -989,23 +1196,23 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)  	{  		// store accordion tabs opened/closed state before any manipulation with accordion tabs  		if (!saved_filter.empty()) -	{ -		notifyChildren(LLSD().with("action","store_state")); -	} +        { +            notifyChildren(LLSD().with("action","store_state")); +        }  		mOnlineFriendList->setNameFilter(filter);  		mAllFriendList->setNameFilter(filter); -	setAccordionCollapsedByUser("tab_online", false); -	setAccordionCollapsedByUser("tab_all", false); -	showFriendsAccordionsIfNeeded(); +        setAccordionCollapsedByUser("tab_online", false); +        setAccordionCollapsedByUser("tab_all", false); +        showFriendsAccordionsIfNeeded();  		// restore accordion tabs state _after_ all manipulations  		if(saved_filter.empty()) -	{ -		notifyChildren(LLSD().with("action","restore_state")); -	} -} +        { +            notifyChildren(LLSD().with("action","restore_state")); +        } +    }  	else if (cur_tab == GROUP_TAB_NAME)  	{  		mGroupList->setNameFilter(filter); @@ -1014,6 +1221,10 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)  	{  		mRecentList->setNameFilter(filter);  	} +    else if (cur_tab == FBCTESTTWO_TAB_NAME) +    { +        mPersonFolderViewModel.getFilter().setFilterSubString(filter); +    }  }  void LLPanelPeople::onTabSelected(const LLSD& param) @@ -1225,7 +1436,7 @@ void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata)  		mAllFriendList->showPermissions(show_permissions);  		mOnlineFriendList->showPermissions(show_permissions);  	} -} +	}  void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata)  { @@ -1446,4 +1657,265 @@ bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name)  	return isAccordionCollapsedByUser(getChild<LLUICtrl>(name));  } +void LLPanelPeople::openFacebookWeb(std::string url) +{ +	gFacebookConnectHandler.mPanelPeople = this; +	LLUrlAction::openURLExternal(url); +} + +void LLPanelPeople::showFacebookFriends(const LLSD& friends) +{ +	mFacebookFriends->clear(); + +	for (LLSD::map_const_iterator i = friends.beginMap(); i != friends.endMap(); ++i) +	{ +		std::string name = i->second["name"].asString(); +		LLUUID agent_id = i->second.has("agent_id") ? i->second["agent_id"].asUUID() : LLUUID(NULL); +		 +		//add to avatar list +		mFacebookFriends->addNewItem(agent_id, name, false); + +		//Add to folder view +		LLPersonTabModel * session_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderView->mPersonFolderModelMap.begin()->second); +		if(session_model) +		{ +			addParticipantToModel(session_model, agent_id, name); +		} +	} +} + +void LLPanelPeople::addTestParticipant() +{ +    std::string suffix("Aa"); +    std::string prefix("Test Name"); +	for(int i = 0; i < 300; ++i) +	{ +		LLPersonTabModel * person_folder_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderView->mPersonFolderModelMap.begin()->second); +        std::string name = prefix + " " + suffix; +		addParticipantToModel(person_folder_model, gAgent.getID(), name); +        // Next suffix : Aa, Ab, Ac ... Az, Ba, Bb, Bc ... Bz, Ca, Cb ... +        suffix[1]+=1; +        if (suffix[1]=='{') +        { +            suffix[1]='a'; +            suffix[0]+=1; +            if (suffix[0]=='[') +                suffix[0]='A'; +        } +	} +} + +void LLPanelPeople::addParticipantToModel(LLPersonTabModel * person_folder_model, const LLUUID& agent_id, const std::string& name) +{ +	LLPersonModel* person_model = NULL; + +	LLAvatarName avatar_name; +	bool avatar_name_exists = LLAvatarNameCache::get(agent_id, &avatar_name); + +	std::string aggregated_name = avatar_name_exists ? name + " (" + avatar_name.getDisplayName() + ") " : name; + +	person_model = new LLPersonModel(agent_id, aggregated_name, mPersonFolderViewModel); +	person_folder_model->addParticipant(person_model); +} + +void LLPanelPeople::hideFacebookFriends() +{ +	mFacebookFriends->clear(); +} + +class FacebookConnectResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; + +	FacebookConnectResponder(LLPanelPeople * panel_people) : mPanelPeople(panel_people) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; +			 +			// grab some graph data now that we are connected +			mPanelPeople->mConnectedToFbc = true; +			mPanelPeople->loadFacebookFriends(); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; +		} +	} + +	/*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (status == 302) +		{ +			mPanelPeople->openFacebookWeb(content["location"]); +		} +	} +}; + +class FacebookDisconnectResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; + +	FacebookDisconnectResponder(LLPanelPeople * panel_people) : mPanelPeople(panel_people) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; +			 +			// hide all the facebook stuff +			mPanelPeople->mConnectedToFbc = false; +			mPanelPeople->hideFacebookFriends(); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; +		} +	} +}; + +class FacebookConnectedResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; +	bool mShowLoginIfNotConnected; + +	FacebookConnectedResponder(LLPanelPeople * panel_people, bool show_login_if_not_connected) : mPanelPeople(panel_people), mShowLoginIfNotConnected(show_login_if_not_connected) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; + +			// grab some graph data if already connected +			mPanelPeople->mConnectedToFbc = true; +			mPanelPeople->loadFacebookFriends(); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; + +			// show the facebook login page if not connected yet +			if (status == 404 && mShowLoginIfNotConnected) +			{ +				mPanelPeople->connectToFacebook(); +			} +		} +	} +}; + +class FacebookFriendsResponder : public LLHTTPClient::Responder +{ +public: + +	LLPanelPeople * mPanelPeople; + +	FacebookFriendsResponder(LLPanelPeople * panel_people) : mPanelPeople(panel_people) {} + +	/*virtual*/ void completed(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (isGoodStatus(status)) +		{ +			llinfos << content << llendl; + +			// display the list of friends +			mPanelPeople->showFacebookFriends(content); +		} +		else +		{ +			llinfos << "failed to get response. reason: " << reason << " status: " << status << llendl; +		} +	} + +	/*virtual*/ void completedHeader(U32 status, const std::string& reason, const LLSD& content) +	{ +		if (status == 302) +		{ +			mPanelPeople->openFacebookWeb(content["location"]); +		} +	} +}; + +void LLPanelPeople::loadFacebookFriends() +{ +	LLHTTPClient::get(getFacebookConnectURL("/friend"), new FacebookFriendsResponder(this)); +} + +void LLPanelPeople::tryToReconnectToFacebook() +{ +	if (!mConnectedToFbc) +	{ +		LLHTTPClient::get(getFacebookConnectURL("/connection"), new FacebookConnectedResponder(this, false)); +	} +} + +void LLPanelPeople::connectToFacebook(const std::string& auth_code) +{ +	LLSD body; +	if (!auth_code.empty()) +		body["code"] = auth_code; + +	LLHTTPClient::put(getFacebookConnectURL("/connection"), body, new FacebookConnectResponder(this)); +} + +void LLPanelPeople::disconnectFromFacebook() +{ +	LLHTTPClient::del(getFacebookConnectURL("/connection"), new FacebookDisconnectResponder(this)); +} + +std::string LLPanelPeople::getFacebookConnectURL(const std::string& route) +{ +	static std::string sFacebookConnectUrl = gAgent.getRegion()->getCapability("FacebookConnect"); +	std::string url = sFacebookConnectUrl + route; +	llinfos << url << llendl; +	return url; +} + +void LLPanelPeople::onLoginFbcButtonClicked() +{ +	if (mConnectedToFbc) +	{ +		disconnectFromFacebook(); +	} +	else +	{ +		LLHTTPClient::get(getFacebookConnectURL("/connection"), new FacebookConnectedResponder(this, true)); +	} +} + +void LLPanelPeople::onFacebookAppRequestClicked() +{ +} + +void LLPanelPeople::onFacebookAppSendClicked() +{ +} + +static LLFastTimer::DeclareTimer FTM_AVATAR_LIST_TEST("avatar list test"); + +void LLPanelPeople::onFacebookTestAddClicked() +{ +	LLFastTimer _(FTM_AVATAR_LIST_TEST); + +	mFacebookFriends->clear(); + +	LL_INFOS("LLPanelPeople") << "start adding 300 users" << LL_ENDL; + +	for(int i = 0; i < 300; ++i) +	{ +		mFacebookFriends->addNewItem(LLUUID(), "Test", false); +	} + +	LL_INFOS("LLPanelPeople") << "finished adding 300 users" << LL_ENDL; +} +  // EOF diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 4740964dee..943d84ac1d 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -22,7 +22,7 @@   *    * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA   * $/LicenseInfo$ - */ + */   #ifndef LL_LLPANELPEOPLE_H  #define LL_LLPANELPEOPLE_H @@ -30,12 +30,17 @@  #include <llpanel.h>  #include "llcallingcard.h" // for avatar tracker +#include "llpersonmodelcommon.h" +#include "llfloaterwebcontent.h"  #include "llvoiceclient.h"  class LLAvatarList; +class LLAvatarListSocial;  class LLAvatarName;  class LLFilterEditor;  class LLGroupList; +class LLPersonFolderView; +class LLSocialList;  class LLMenuButton;  class LLTabContainer; @@ -55,6 +60,23 @@ public:  	// when voice is available  	/*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); +	static void idle(void * user_data); + +	void openFacebookWeb(std::string url); +	void showFacebookFriends(const LLSD& friends); +	void addTestParticipant(); +	void addParticipantToModel(LLPersonTabModel * session_model, const LLUUID& agent_id, const std::string& name); +	void hideFacebookFriends(); +	void loadFacebookFriends(); +	void tryToReconnectToFacebook(); +	void connectToFacebook(const std::string& auth_code = ""); +	void disconnectFromFacebook(); +	 +	std::string getFacebookConnectURL(const std::string& route = ""); +	 +	bool mConnectedToFbc; +	bool mTryToConnectToFbc; +  	// internals  	class Updater; @@ -75,6 +97,7 @@ private:  	void					updateFriendList();  	void					updateNearbyList();  	void					updateRecentList(); +	void					updateFbcTestList();  	bool					isItemsFreeOfFriends(const uuid_vec_t& uuids); @@ -106,6 +129,11 @@ private:  	void					onGroupsViewSortMenuItemClicked(const LLSD& userdata);  	void					onRecentViewSortMenuItemClicked(const LLSD& userdata); +	void					onLoginFbcButtonClicked(); +	void					onFacebookAppRequestClicked(); +	void					onFacebookAppSendClicked(); +	void					onFacebookTestAddClicked(); +  	bool					onFriendsViewSortMenuItemCheck(const LLSD& userdata);  	bool					onRecentViewSortMenuItemCheck(const LLSD& userdata);  	bool					onNearbyViewSortMenuItemCheck(const LLSD& userdata); @@ -132,16 +160,24 @@ private:  	LLAvatarList*			mNearbyList;  	LLAvatarList*			mRecentList;  	LLGroupList*			mGroupList; +	LLSocialList*			mFacebookFriends;  	LLNetMap*				mMiniMap;  	std::vector<std::string> mSavedOriginalFilters;  	std::vector<std::string> mSavedFilters; +	LLHandle<LLView>		mFBCMenuHandle; +	LLHandle<LLFloater>		mFbcTestBrowserHandle;  	Updater*				mFriendListUpdater;  	Updater*				mNearbyListUpdater;  	Updater*				mRecentListUpdater; +	Updater*				mFbcTestListUpdater;  	Updater*				mButtonsUpdater; +	LLMenuButton*			mFBCGearButton;      LLHandle< LLFloater >	mPicker; + +	LLPersonFolderViewModel mPersonFolderViewModel; +	LLPersonFolderView* mPersonFolderView;  };  #endif //LL_LLPANELPEOPLE_H diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index c53760bca1..b5c9f4a310 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -27,6 +27,7 @@  #include "llviewerprecompiledheaders.h"  #include "llavatarnamecache.h" +#include "llerror.h"  #include "llimview.h"  #include "llfloaterimcontainer.h"  #include "llparticipantlist.h" @@ -401,6 +402,23 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)  	adjustParticipant(avatar_id);  } +static LLFastTimer::DeclareTimer FTM_FOLDERVIEW_TEST("add test avatar agents"); + + +void LLParticipantList::addTestAvatarAgents() +{ +	LLFastTimer _(FTM_FOLDERVIEW_TEST); + +	LL_INFOS("LLParticipantList") << "start adding 300 users" << LL_ENDL; + +	for(int i = 0; i < 300; ++i) +	{ +		addAvatarIDExceptAgent(LLUUID().generateNewID()); +	} + +	LL_INFOS("LLParticipantList") << "finished adding 300 users" << LL_ENDL; +} +  void LLParticipantList::adjustParticipant(const LLUUID& speaker_id)  {  	LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(speaker_id); diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 3a3ae76604..936e289c08 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -50,6 +50,7 @@ public:  	 * @param[in] avatar_id - Avatar UUID to be added into the list  	 */  	void addAvatarIDExceptAgent(const LLUUID& avatar_id); +	void addTestAvatarAgents();  	/**  	 * Refreshes the participant list. diff --git a/indra/newview/llpersonfolderview.cpp b/indra/newview/llpersonfolderview.cpp new file mode 100644 index 0000000000..ba1e9d20eb --- /dev/null +++ b/indra/newview/llpersonfolderview.cpp @@ -0,0 +1,114 @@ +/**  +* @file llpersonfolderview.cpp +* @brief Implementation of llpersonfolderview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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 "llpersonfolderview.h" + +#include "llpersontabview.h" +#include "llpersonmodelcommon.h" + + +LLPersonFolderView::LLPersonFolderView(const Params &p) :  +LLFolderView(p), +	mConversationsEventStream("ConversationsEventsTwo") +{ +	mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLPersonFolderView::onConversationModelEvent, this, _1)); +} + +LLPersonFolderView::~LLPersonFolderView() +{ +	mConversationsEventStream.stopListening("ConversationsRefresh"); +} + +BOOL LLPersonFolderView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ +	LLFolderViewItem * item = getCurSelectedItem(); + +	//Will disable highlight on tab +	if(item) +	{ +		LLPersonTabView * person_tab= dynamic_cast<LLPersonTabView *>(item); +		if(person_tab) +		{ +			person_tab->highlight = false; +		} +		else +		{ +			person_tab = dynamic_cast<LLPersonTabView *>(item->getParent()); +			person_tab->highlight = false; +		} +	} + +	mKeyboardSelection = FALSE;  +	mSearchString.clear(); + +	LLEditMenuHandler::gEditMenuHandler = this; + +	return LLView::handleMouseDown( x, y, mask ); +} + +bool LLPersonFolderView::onConversationModelEvent(const LLSD &event) +{ +	std::string type = event.get("type").asString(); +	LLUUID folder_id = event.get("folder_id").asUUID(); +	LLUUID person_id = event.get("person_id").asUUID(); + +	if(type == "add_participant") +	{ +		LLPersonTabModel * person_folder_model = dynamic_cast<LLPersonTabModel *>(mPersonFolderModelMap[folder_id]); +		LLPersonTabView * person_folder_view = dynamic_cast<LLPersonTabView *>(mPersonFolderViewMap[folder_id]); + +		if(person_folder_model) +		{ +			LLPersonModel * person_model = person_folder_model->findParticipant(person_id); + +			if(person_model) +			{ +				LLPersonView * participant_view = createConversationViewParticipant(person_model); +				participant_view->addToFolder(person_folder_view); +			} +		} +	} + +	return false; +} + +LLPersonView * LLPersonFolderView::createConversationViewParticipant(LLPersonModel * item) +{ +	LLPersonView::Params params; + +	params.name = item->getDisplayName(); +	params.root = this; +	params.listener = item; + +	//24 should be loaded from .xml somehow +	params.rect = LLRect (0, 24, getRect().getWidth(), 0); +	params.tool_tip = params.name; + +	return LLUICtrlFactory::create<LLPersonView>(params); +} diff --git a/indra/newview/llpersonfolderview.h b/indra/newview/llpersonfolderview.h new file mode 100644 index 0000000000..74e9142a7c --- /dev/null +++ b/indra/newview/llpersonfolderview.h @@ -0,0 +1,64 @@ +/**  +* @file   llpersonfolderview.h +* @brief  Header file for llpersonfolderview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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_LLPERSONFOLDERVIEW_H +#define LL_LLPERSONFOLDERVIEW_H + +class LLPersonTabModel; +class LLPersonTabView; +class LLPersonView; +class LLPersonModel; + +typedef std::map<LLUUID, LLPersonTabModel *> person_folder_model_map; +typedef std::map<LLUUID, LLPersonTabView *> person_folder_view_map; + +#include "llevents.h" +#include "llfolderview.h" + +class LLPersonFolderView : public LLFolderView +{ +public: +	struct Params : public LLInitParam::Block<Params, LLFolderView::Params> +	{ +		Params() +		{} +	}; + +	LLPersonFolderView(const Params &p); +	~LLPersonFolderView(); + +	bool onConversationModelEvent(const LLSD &event); +	LLPersonView * createConversationViewParticipant(LLPersonModel * item); + +	BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + +	person_folder_model_map mPersonFolderModelMap; +	person_folder_view_map mPersonFolderViewMap; +	LLEventStream mConversationsEventStream; +}; + +#endif // LL_LLPERSONFOLDERVIEW_H + diff --git a/indra/newview/llpersonmodelcommon.cpp b/indra/newview/llpersonmodelcommon.cpp new file mode 100644 index 0000000000..9660432b80 --- /dev/null +++ b/indra/newview/llpersonmodelcommon.cpp @@ -0,0 +1,281 @@ +/**  +* @file llavatarfolder.cpp +* @brief Implementation of llavatarfolder +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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 "llpersonmodelcommon.h" + +#include "llevents.h" +#include "llsdutil.h" +#include "llstring.h" + +// +// LLPersonModelCommon +//  + +LLPersonModelCommon::LLPersonModelCommon(std::string display_name, LLFolderViewModelInterface& root_view_model) : +    LLFolderViewModelItemCommon(root_view_model), +	mID(LLUUID().generateNewID()) +{ +    renameItem(display_name); +} + +LLPersonModelCommon::LLPersonModelCommon(LLFolderViewModelInterface& root_view_model) : +    LLFolderViewModelItemCommon(root_view_model), +	mName(""), +    mSearchableName(""), +	mID(LLUUID().generateNewID()) +{ +} + +LLPersonModelCommon::~LLPersonModelCommon() +{ + +} + +BOOL LLPersonModelCommon::renameItem(const std::string& new_name) +{ +    mName = new_name; +    mSearchableName = new_name; +    LLStringUtil::toUpper(mSearchableName); +    return TRUE; +} + +void LLPersonModelCommon::postEvent(const std::string& event_type, LLPersonTabModel* folder, LLPersonModel* person) +{ +	LLUUID folder_id = folder->getID(); +	LLUUID person_id = person->getID(); +	LLSD event(LLSDMap("type", event_type)("folder_id", folder_id)("person_id", person_id)); +	LLEventPumps::instance().obtain("ConversationsEventsTwo").post(event); +} + +// Virtual action callbacks +void LLPersonModelCommon::performAction(LLInventoryModel* model, std::string action) +{ +} + +void LLPersonModelCommon::openItem( void ) +{ +} + +void LLPersonModelCommon::closeItem( void ) +{ +} + +void LLPersonModelCommon::previewItem( void ) +{ +} + +void LLPersonModelCommon::showProperties(void) +{ +} + +bool LLPersonModelCommon::filter( LLFolderViewFilter& filter) +{ +    // See LLFolderViewModelItemInventory::filter() +/* +    if (!filter.isModified()) +    { +        llinfos << "Merov : LLPersonModelCommon::filter, exit, no modif" << llendl; +        return true; +    } +*/         +    if (!mChildren.empty()) +    { +        //llinfos << "Merov : LLPersonModelCommon::filter, filtering folder = " << getDisplayName() << llendl; +        setPassedFilter(1, -1, filter.getStringMatchOffset(this), filter.getFilterStringSize()); +        for (child_list_t::iterator iter = mChildren.begin(), end_iter = mChildren.end(); +            iter != end_iter; +            ++iter) +        { +            // LLFolderViewModelItem +            LLPersonModelCommon* item = dynamic_cast<LLPersonModelCommon*>(*iter); +            item->filter(filter); +        } +    } +    else +    { +        const bool passed_filter = filter.check(this); +        setPassedFilter(passed_filter, -1, filter.getStringMatchOffset(this), filter.getFilterStringSize()); +    } +     +    filter.clearModified(); +    return true; +} + +// +// LLPersonTabModel +//  + +LLPersonTabModel::LLPersonTabModel(std::string display_name, LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(display_name,root_view_model) +{ + +} + +LLPersonTabModel::LLPersonTabModel(LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(root_view_model) +{ + +} + +void LLPersonTabModel::addParticipant(LLPersonModel* participant) +{ +	addChild(participant); +	postEvent("add_participant", this, participant); +} + +void LLPersonTabModel::removeParticipant(LLPersonModel* participant) +{ +	removeChild(participant); +	postEvent("remove_participant", this, participant); +} + +void LLPersonTabModel::removeParticipant(const LLUUID& participant_id) +{ +	LLPersonModel* participant = findParticipant(participant_id); +	if (participant) +	{ +		removeParticipant(participant); +	} +} + +void LLPersonTabModel::clearParticipants() +{ +	clearChildren(); +} + +LLPersonModel* LLPersonTabModel::findParticipant(const LLUUID& person_id) +{ +	LLPersonModel * person_model = NULL; +	child_list_t::iterator iter; + +	for(iter = mChildren.begin(); iter != mChildren.end(); ++iter) +	{ +		person_model = static_cast<LLPersonModel *>(*iter); + +		if(person_model->getID() == person_id) +		{ +			break; +		} +	} + +	return iter == mChildren.end() ? NULL : person_model; +} + +// +// LLPersonModel +//  + +LLPersonModel::LLPersonModel(const LLUUID& agent_id, const std::string display_name, LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(display_name,root_view_model), +mAgentID(agent_id) +{ +} + +LLPersonModel::LLPersonModel(LLFolderViewModelInterface& root_view_model) : +LLPersonModelCommon(root_view_model), +mAgentID(LLUUID(NULL)) +{ +} + +LLUUID LLPersonModel::getAgentID() +{ +	return mAgentID; +} + +// +// LLPersonViewFilter +// + +LLPersonViewFilter::LLPersonViewFilter() : +    mEmptyLookupMessage(""), +    mFilterSubString(""), +    mName(""), +    mFilterModified(FILTER_NONE) +{ +} + +void LLPersonViewFilter::setFilterSubString(const std::string& string) +{ +	std::string filter_sub_string_new = string; +	LLStringUtil::trimHead(filter_sub_string_new); +	LLStringUtil::toUpper(filter_sub_string_new); +     +	if (mFilterSubString != filter_sub_string_new) +	{ +        // *TODO : Add logic to support more and less restrictive filtering +        setModified(FILTER_RESTART); +		mFilterSubString = filter_sub_string_new; +	} +} + +bool LLPersonViewFilter::showAllResults() const +{ +	return mFilterSubString.size() > 0; +} + +bool LLPersonViewFilter::check(const LLFolderViewModelItem* item) +{ +	return (mFilterSubString.size() ? (item->getSearchableName().find(mFilterSubString) != std::string::npos) : true); +} + +std::string::size_type LLPersonViewFilter::getStringMatchOffset(LLFolderViewModelItem* item) const +{ +	return mFilterSubString.size() ? item->getSearchableName().find(mFilterSubString) : std::string::npos; +} + +std::string::size_type LLPersonViewFilter::getFilterStringSize() const +{ +	return mFilterSubString.size(); +} + +bool LLPersonViewFilter::isActive() const +{ +	return mFilterSubString.size(); +} + +bool LLPersonViewFilter::isModified() const +{ +	return mFilterModified != FILTER_NONE; +} + +void LLPersonViewFilter::clearModified() +{ +    mFilterModified = FILTER_NONE; +} + +void LLPersonViewFilter::setEmptyLookupMessage(const std::string& message) +{ +	mEmptyLookupMessage = message; +} + +std::string LLPersonViewFilter::getEmptyLookupMessage() const +{ +	return mEmptyLookupMessage; +} + diff --git a/indra/newview/llpersonmodelcommon.h b/indra/newview/llpersonmodelcommon.h new file mode 100644 index 0000000000..ffd145b549 --- /dev/null +++ b/indra/newview/llpersonmodelcommon.h @@ -0,0 +1,244 @@ +/**  +* @file   llavatarfolder.h +* @brief  Header file for llavatarfolder +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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_LLPERSONMODELCOMMON_H +#define LL_LLPERSONMODELCOMMON_H + +#include "../llui/llfolderviewitem.h" +#include "../llui/llfolderviewmodel.h" + +class LLPersonTabModel; +class LLPersonModel; + +// Conversation items: we hold a list of those and create an LLFolderViewItem widget for each   +// that we tuck into the mConversationsListPanel.  +class LLPersonModelCommon : public LLFolderViewModelItemCommon +{ +public: + +	LLPersonModelCommon(std::string name, LLFolderViewModelInterface& root_view_model); +	LLPersonModelCommon(LLFolderViewModelInterface& root_view_model); +	virtual ~LLPersonModelCommon(); + +	// Stub those things we won't really be using in this conversation context +	virtual const std::string& getName() const { return mName; } +	virtual const std::string& getDisplayName() const { return mName; } +	virtual const std::string& getSearchableName() const { return mSearchableName; } + +	virtual LLPointer<LLUIImage> getIcon() const { return NULL; } +	virtual LLPointer<LLUIImage> getOpenIcon() const { return getIcon(); } +	virtual LLFontGL::StyleFlags getLabelStyle() const { return LLFontGL::NORMAL; } +	virtual std::string getLabelSuffix() const { return LLStringUtil::null; } +	virtual BOOL isItemRenameable() const { return TRUE; } +	virtual BOOL renameItem(const std::string& new_name); +	virtual BOOL isItemMovable( void ) const { return FALSE; } +	virtual BOOL isItemRemovable( void ) const { return FALSE; } +	virtual BOOL isItemInTrash( void) const { return FALSE; } +	virtual BOOL removeItem() { return FALSE; } +	virtual void removeBatch(std::vector<LLFolderViewModelItem*>& batch) { } +	virtual void move( LLFolderViewModelItem* parent_listener ) { } +	virtual BOOL isItemCopyable() const { return FALSE; } +	virtual BOOL copyToClipboard() const { return FALSE; } +	virtual BOOL cutToClipboard() const { return FALSE; } +	virtual BOOL isClipboardPasteable() const { return FALSE; } +	virtual void pasteFromClipboard() { } +	virtual void pasteLinkFromClipboard() { } +	virtual void buildContextMenu(LLMenuGL& menu, U32 flags) { } +	virtual BOOL isUpToDate() const { return TRUE; } +	virtual bool hasChildren() const { return FALSE; } + +	virtual bool potentiallyVisible() { return true; } +     +	virtual bool filter( LLFolderViewFilter& filter); + +	virtual bool descendantsPassedFilter(S32 filter_generation = -1) { return true; } +//	virtual void setPassedFilter(bool passed, S32 filter_generation, std::string::size_type string_offset = std::string::npos, std::string::size_type string_size = 0) { } +	virtual bool passedFilter(S32 filter_generation = -1) { return mPassedFilter; } + +	// The action callbacks +	virtual void performAction(LLInventoryModel* model, std::string action); +	virtual void openItem( void ); +	virtual void closeItem( void ); +	virtual void previewItem( void ); +	virtual void selectItem(void) { }  +	virtual void showProperties(void); + +	// This method will be called to determine if a drop can be +	// performed, and will set drop to TRUE if a drop is +	// requested.  +	// Returns TRUE if a drop is possible/happened, FALSE otherwise. +	virtual BOOL dragOrDrop(MASK mask, BOOL drop, +		EDragAndDropType cargo_type, +		void* cargo_data, +		std::string& tooltip_msg) { return FALSE; } + +	const LLUUID& getID() {return mID;} +	void postEvent(const std::string& event_type, LLPersonTabModel* session, LLPersonModel* participant); + +protected: + +	std::string mName;              // Name of the person +	std::string mSearchableName;	// Name used in string matching for this person +	LLUUID mID; +};	 + +class LLPersonTabModel : public LLPersonModelCommon +{ +public: +	LLPersonTabModel(std::string display_name, LLFolderViewModelInterface& root_view_model); +	LLPersonTabModel(LLFolderViewModelInterface& root_view_model); + +	LLPointer<LLUIImage> getIcon() const { return NULL; } +	void addParticipant(LLPersonModel* participant); +	void removeParticipant(LLPersonModel* participant); +	void removeParticipant(const LLUUID& participant_id); +	void clearParticipants(); +	LLPersonModel* findParticipant(const LLUUID& person_id); + +private: +}; + +class LLPersonModel : public LLPersonModelCommon +{ +public: +	LLPersonModel(const LLUUID& agent_id, const std::string display_name, LLFolderViewModelInterface& root_view_model); +	LLPersonModel(LLFolderViewModelInterface& root_view_model); + +	LLUUID getAgentID(); + +private: +	LLUUID mAgentID; +}; + +// Filtering functional object + +class LLPersonViewFilter : public LLFolderViewFilter +{ +public: + +	enum ESortOrderType +	{ +		SO_NAME = 0,						// Sort by name +		SO_ONLINE_STATUS = 0x1				// Sort by online status (i.e. online or not) +	}; +	// Default sort order is by name +	static const U32 SO_DEFAULT = SO_NAME; + +	LLPersonViewFilter(); +	~LLPersonViewFilter() {} + +	// +-------------------------------------------------------------------+ +	// + Execution And Results +	// +-------------------------------------------------------------------+ +	bool 				check(const LLFolderViewModelItem* item); +	bool				checkFolder(const LLFolderViewModelItem* folder) const { return true; } +     +	void 				setEmptyLookupMessage(const std::string& message); +	std::string			getEmptyLookupMessage() const; +     +	bool				showAllResults() const; +     +	std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const; +	std::string::size_type getFilterStringSize() const; + + 	// +-------------------------------------------------------------------+ +	// + Status +	// +-------------------------------------------------------------------+ + 	bool 				isActive() const; +	bool 				isModified() const; +	void 				clearModified(); +	const std::string& 	getName() const { return mName; } +	const std::string& 	getFilterText() { return mName; } +	void 				setModified(EFilterModified behavior = FILTER_RESTART) { mFilterModified = behavior; } +     +	// +-------------------------------------------------------------------+ +	// + Count +	// +-------------------------------------------------------------------+ +    // Note : we currently filter the whole person list at once, no need to count then. +	//void 				setFilterCount(S32 count) { } +	//S32 				getFilterCount() const { return 1; } +	//void 				decrementFilterCount() { } + +	// +-------------------------------------------------------------------+ +	// + Time +	// +-------------------------------------------------------------------+ +    // Note : we currently filter the whole person list at once, no need to timeout then. +	void 				resetTime(S32 timeout) { } +    bool                isTimedOut() { return false; } +     +	// +-------------------------------------------------------------------+ +	// + Default +	// +-------------------------------------------------------------------+ +    // Note : we don't support runtime default setting for person filter +	bool 				isDefault() const  { return !isActive(); } +	bool 				isNotDefault() const { return isActive(); } +	void 				markDefault() { } +	void 				resetDefault() { setModified(); } +     +	// +-------------------------------------------------------------------+ +	// + Generation +	// +-------------------------------------------------------------------+ +    // Note : unclear if we have to take tab on generation at that point +	S32 				getCurrentGeneration() const { return 0; } +	S32 				getFirstSuccessGeneration() const { return 0; } +	S32 				getFirstRequiredGeneration() const { return 0; } + +    // Non Virtual Methods (i.e. specific to this class) +	void 				setFilterSubString(const std::string& string); +    +private: +	std::string         mName; +	std::string         mEmptyLookupMessage; +	std::string			mFilterSubString; +	EFilterModified 	mFilterModified; +}; + +class LLPersonViewSort +{ +public: +	LLPersonViewSort(U32 order = LLPersonViewFilter::SO_DEFAULT) : mSortOrder(order) { } + +	bool operator()(const LLPersonModelCommon* const& a, const LLPersonModelCommon* const& b) const {return false;} +	operator U32() const { return mSortOrder; } +private: +	// Note: we're treating this value as a sort order bitmask as done in other places in the code (e.g. inventory) +	U32  mSortOrder; +}; + + +class LLPersonFolderViewModel +	: public  LLFolderViewModel<LLPersonViewSort, LLPersonModelCommon, LLPersonModelCommon, LLPersonViewFilter> +{ +public: +	typedef LLFolderViewModel<LLPersonViewSort, LLPersonModelCommon, LLPersonModelCommon, LLPersonViewFilter> base_t; + +	void sort(LLFolderViewFolder* folder) { base_t::sort(folder);} +	bool startDrag(std::vector<LLFolderViewModelItem*>& items) { return false; } // We do not allow drag of conversation items +}; + + +#endif // LL_LLPERSONMODELCOMMON_H + diff --git a/indra/newview/llpersontabview.cpp b/indra/newview/llpersontabview.cpp new file mode 100644 index 0000000000..fdc024beb8 --- /dev/null +++ b/indra/newview/llpersontabview.cpp @@ -0,0 +1,398 @@ +/**  +* @file llpersontabview.cpp +* @brief Implementation of llpersontabview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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 "llpersontabview.h" + +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llpersonmodelcommon.h" + +static LLDefaultChildRegistry::Register<LLPersonTabView> r_person_tab_view("person_tab_view"); + +const LLColor4U DEFAULT_WHITE(255, 255, 255); + +LLPersonTabView::Params::Params() +{} + +LLPersonTabView::LLPersonTabView(const LLPersonTabView::Params& p) : +LLFolderViewFolder(p), +highlight(false), +mImageHeader(LLUI::getUIImage("Accordion_Off")), +mImageHeaderOver(LLUI::getUIImage("Accordion_Over")), +mImageHeaderFocused(LLUI::getUIImage("Accordion_Selected")) +{ +} + +S32 LLPersonTabView::getLabelXPos() +{  +	return getIndentation() + mArrowSize + 15;//Should be a .xml variable but causes crash; +} + +LLPersonTabView::~LLPersonTabView() +{ + +} + +BOOL LLPersonTabView::handleMouseDown( S32 x, S32 y, MASK mask ) +{ +	bool selected_item = LLFolderViewFolder::handleMouseDown(x, y, mask); + +	if(selected_item) +	{ +		gFocusMgr.setKeyboardFocus( this ); +		highlight = true; +	} + +	return selected_item; +} + +void LLPersonTabView::draw() +{ +	static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); +	static const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLPersonTabView>(); + +	const LLFontGL * font = LLFontGL::getFontSansSerif(); +	F32 text_left = (F32)getLabelXPos(); +	F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; +	LLColor4 color = sFgColor; +	F32 right_x  = 0; + +	drawHighlight(); +	updateLabelRotation(); +	drawOpenFolderArrow(default_params, sFgColor); + +	drawLabel(font, text_left, y, color, right_x); + +	LLView::draw(); +} + +void LLPersonTabView::drawHighlight() +{ +	S32 width = getRect().getWidth(); +	S32 height = mItemHeight; +	S32 x = 1; +	S32 y = getRect().getHeight() - mItemHeight; + +	if(highlight) +	{ +		mImageHeaderFocused->draw(x,y,width,height); +	} +	else +	{ +		mImageHeader->draw(x,y,width,height); +	} + +	if(mIsMouseOverTitle) +	{ +		mImageHeaderOver->draw(x,y,width,height); +	} + +} + +// +// LLPersonView +//  + +static LLDefaultChildRegistry::Register<LLPersonView> r_person_view("person_view"); + +bool LLPersonView::sChildrenWidthsInitialized = false; +ChildWidthVec LLPersonView::mChildWidthVec; + +LLPersonView::Params::Params() : +avatar_icon("avatar_icon"), +last_interaction_time_textbox("last_interaction_time_textbox"), +permission_edit_theirs_icon("permission_edit_theirs_icon"), +permission_edit_mine_icon("permission_edit_mine_icon"), +permission_map_icon("permission_map_icon"), +permission_online_icon("permission_online_icon"), +info_btn("info_btn"), +profile_btn("profile_btn"), +output_monitor("output_monitor") +{} + +LLPersonView::LLPersonView(const LLPersonView::Params& p) : +LLFolderViewItem(p), +mImageOver(LLUI::getUIImage("ListItem_Over")), +mImageSelected(LLUI::getUIImage("ListItem_Select")), +mAvatarIcon(NULL), +mLastInteractionTimeTextbox(NULL), +mPermissionEditTheirsIcon(NULL), +mPermissionEditMineIcon(NULL), +mPermissionMapIcon(NULL), +mPermissionOnlineIcon(NULL), +mInfoBtn(NULL), +mProfileBtn(NULL), +mOutputMonitorCtrl(NULL) +{ +} + +S32 LLPersonView::getLabelXPos() +{ +	return getIndentation() + mAvatarIcon->getRect().getWidth() + mIconPad; +} + +void LLPersonView::addToFolder(LLFolderViewFolder * person_folder_view) +{ +	LLFolderViewItem::addToFolder(person_folder_view); +	//Added item to folder, could change folder's mHasVisibleChildren flag so call arrange +	person_folder_view->requestArrange(); +} + +LLPersonView::~LLPersonView() +{ + +} + +BOOL LLPersonView::postBuild() +{ +	if(!sChildrenWidthsInitialized) +	{ +		initChildrenWidthVec(this); +		sChildrenWidthsInitialized = true; +	} +	 +	initChildVec(); +	updateChildren(); + +	LLPersonModel * person_model = static_cast<LLPersonModel *>(getViewModelItem()); + +	mAvatarIcon->setValue(person_model->getAgentID()); +	mInfoBtn->setClickedCallback(boost::bind(&LLFloaterReg::showInstance, "inspect_avatar", LLSD().with("avatar_id", person_model->getAgentID()), FALSE)); +	mProfileBtn->setClickedCallback(boost::bind(&LLAvatarActions::showProfile, person_model->getAgentID())); + +	return LLFolderViewItem::postBuild(); +} + +void LLPersonView::onMouseEnter(S32 x, S32 y, MASK mask) +{ +	mInfoBtn->setVisible(TRUE); +	mProfileBtn->setVisible(TRUE); +	updateChildren(); +	LLFolderViewItem::onMouseEnter(x, y, mask); +} + +void LLPersonView::onMouseLeave(S32 x, S32 y, MASK mask) +{ +	mInfoBtn->setVisible(FALSE); +	mProfileBtn->setVisible(FALSE); +	updateChildren(); +	LLFolderViewItem::onMouseLeave(x, y, mask); +} + +BOOL LLPersonView::handleMouseDown( S32 x, S32 y, MASK mask) +{ +	if(!LLView::childrenHandleMouseDown(x, y, mask)) +	{ +		gFocusMgr.setMouseCapture( this ); +	} + +	if (!mIsSelected) +	{ +		if(mask & MASK_CONTROL) +		{ +			getRoot()->changeSelection(this, !mIsSelected); +		} +		else if (mask & MASK_SHIFT) +		{ +			getParentFolder()->extendSelectionTo(this); +		} +		else +		{ +			getRoot()->setSelection(this, FALSE); +		} +		make_ui_sound("UISndClick"); +	} +	else +	{ +		// If selected, we reserve the decision of deselecting/reselecting to the mouse up moment. +		// This is necessary so we maintain selection consistent when starting a drag. +		mSelectPending = TRUE; +	} + +	mDragStartX = x; +	mDragStartY = y; +	return TRUE; +} + +void LLPersonView::draw() +{ +	static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); +	static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); + +	const LLFontGL * font = LLFontGL::getFontSansSerifSmall(); +	F32 text_left = (F32)getLabelXPos(); +	F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; +	LLColor4 color = mIsSelected ? sHighlightFgColor : sFgColor; +	F32 right_x  = 0; + +	drawHighlight(); +	drawLabel(font, text_left, y, color, right_x); + +	LLView::draw(); +} + +void LLPersonView::drawHighlight() +{ +	static LLUIColor outline_color = LLUIColorTable::instance().getColor("EmphasisColor", DEFAULT_WHITE); + +	S32 width = getRect().getWidth(); +	S32 height = mItemHeight; +	S32 x = 1; +	S32 y = 0; + +	if(mIsSelected) +	{ +		mImageSelected->draw(x, y, width, height); +		//Draw outline +		gl_rect_2d(x,  +			height,  +			width, +			y, +			outline_color, FALSE); +	} + +	if(mIsMouseOverTitle) +	{ +		mImageOver->draw(x, y, width, height); +	} +} + +void LLPersonView::initFromParams(const LLPersonView::Params & params) +{ +	LLAvatarIconCtrl::Params avatar_icon_params(params.avatar_icon()); +	applyXUILayout(avatar_icon_params, this); +	mAvatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params); +	addChild(mAvatarIcon); +	 +	LLTextBox::Params last_interaction_time_textbox(params.last_interaction_time_textbox()); +	applyXUILayout(last_interaction_time_textbox, this); +	mLastInteractionTimeTextbox = LLUICtrlFactory::create<LLTextBox>(last_interaction_time_textbox); +	addChild(mLastInteractionTimeTextbox); + +	LLIconCtrl::Params permission_edit_theirs_icon(params.permission_edit_theirs_icon()); +	applyXUILayout(permission_edit_theirs_icon, this); +	mPermissionEditTheirsIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_edit_theirs_icon); +	addChild(mPermissionEditTheirsIcon); + +	LLIconCtrl::Params permission_edit_mine_icon(params.permission_edit_mine_icon()); +	applyXUILayout(permission_edit_mine_icon, this); +	mPermissionEditMineIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_edit_mine_icon); +	addChild(mPermissionEditMineIcon); + +	LLIconCtrl::Params permission_map_icon(params.permission_map_icon()); +	applyXUILayout(permission_map_icon, this); +	mPermissionMapIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_map_icon); +	addChild(mPermissionMapIcon); + +	LLIconCtrl::Params permission_online_icon(params.permission_online_icon()); +	applyXUILayout(permission_online_icon, this); +	mPermissionOnlineIcon = LLUICtrlFactory::create<LLIconCtrl>(permission_online_icon); +	addChild(mPermissionOnlineIcon); + +	LLButton::Params info_btn(params.info_btn()); +	applyXUILayout(info_btn, this); +	mInfoBtn = LLUICtrlFactory::create<LLButton>(info_btn); +	addChild(mInfoBtn); + +	LLButton::Params profile_btn(params.profile_btn()); +	applyXUILayout(profile_btn, this); +	mProfileBtn = LLUICtrlFactory::create<LLButton>(profile_btn); +	addChild(mProfileBtn); +	 +	LLOutputMonitorCtrl::Params output_monitor(params.output_monitor()); +	applyXUILayout(output_monitor, this); +	mOutputMonitorCtrl = LLUICtrlFactory::create<LLOutputMonitorCtrl>(output_monitor); +	addChild(mOutputMonitorCtrl); +} + +void LLPersonView::initChildrenWidthVec(LLPersonView* self) +{ +	S32 output_monitor_width = self->getRect().getWidth() - self->mOutputMonitorCtrl->getRect().mLeft; +	S32 profile_btn_width = self->mOutputMonitorCtrl->getRect().mLeft - self->mProfileBtn->getRect().mLeft; +	S32 info_btn_width = self->mProfileBtn->getRect().mLeft - self->mInfoBtn->getRect().mLeft; +	S32 permission_online_icon_width = self->mInfoBtn->getRect().mLeft - self->mPermissionOnlineIcon->getRect().mLeft; +	S32 permissions_map_icon_width = self->mPermissionOnlineIcon->getRect().mLeft - self->mPermissionMapIcon->getRect().mLeft; +	S32 permission_edit_mine_icon_width = self->mPermissionMapIcon->getRect().mLeft - self->mPermissionEditMineIcon->getRect().mLeft; +	S32 permission_edit_theirs_icon_width = self->mPermissionEditMineIcon->getRect().mLeft - self->mPermissionEditTheirsIcon->getRect().mLeft; +	S32 last_interaction_time_textbox_width = self->mPermissionEditTheirsIcon->getRect().mLeft - self->mLastInteractionTimeTextbox->getRect().mLeft; + +	self->mChildWidthVec.push_back(output_monitor_width); +	self->mChildWidthVec.push_back(profile_btn_width); +	self->mChildWidthVec.push_back(info_btn_width); +	self->mChildWidthVec.push_back(permission_online_icon_width); +	self->mChildWidthVec.push_back(permissions_map_icon_width); +	self->mChildWidthVec.push_back(permission_edit_mine_icon_width); +	self->mChildWidthVec.push_back(permission_edit_theirs_icon_width); +	self->mChildWidthVec.push_back(last_interaction_time_textbox_width); +} + +void LLPersonView::initChildVec() +{ +	mChildVec.push_back(mOutputMonitorCtrl); +	mChildVec.push_back(mProfileBtn); +	mChildVec.push_back(mInfoBtn); +	mChildVec.push_back(mPermissionOnlineIcon); +	mChildVec.push_back(mPermissionMapIcon); +	mChildVec.push_back(mPermissionEditMineIcon); +	mChildVec.push_back(mPermissionEditTheirsIcon); +	mChildVec.push_back(mLastInteractionTimeTextbox); +} + +void LLPersonView::updateChildren() +{ +	mLabelPaddingRight = 0; +	LLView * control; +	S32 control_width; +	LLRect control_rect; + +	llassert(mChildWidthVec.size() == mChildVec.size()); + +	for(S32 i = 0; i < mChildWidthVec.size(); ++i) +	{ +		control = mChildVec[i]; + +		if(!control->getVisible()) +		{ +			continue; +		} + +		control_width = mChildWidthVec[i]; +		mLabelPaddingRight += control_width; + +		control_rect = control->getRect(); +		control_rect.setLeftTopAndSize( +			getLocalRect().getWidth() - mLabelPaddingRight, +			control_rect.mTop, +			control_rect.getWidth(), +			control_rect.getHeight()); + +		control->setShape(control_rect); + +	} +} diff --git a/indra/newview/llpersontabview.h b/indra/newview/llpersontabview.h new file mode 100644 index 0000000000..9cce615167 --- /dev/null +++ b/indra/newview/llpersontabview.h @@ -0,0 +1,146 @@ +/**  +* @file   llpersontabview.h +* @brief  Header file for llpersontabview +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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_LLPERSONTABVIEW_H +#define LL_LLPERSONTABVIEW_H + +#include "llavatariconctrl.h" +#include "llbutton.h" +#include "llfolderviewitem.h" +#include "lloutputmonitorctrl.h" +#include "lltextbox.h" + +class LLPersonTabView : public LLFolderViewFolder +{ + +public: + +	struct Params : public LLInitParam::Block<Params, LLFolderViewFolder::Params> +	{ +		Params(); +	}; + +	LLPersonTabView(const LLPersonTabView::Params& p); +	virtual ~LLPersonTabView(); + +	S32 getLabelXPos(); +	bool highlight; + +	BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + +protected:	 +	 void draw(); +	 void drawHighlight(); + +private: + +	// Background images +	LLPointer<LLUIImage> mImageHeader; +	LLPointer<LLUIImage> mImageHeaderOver; +	LLPointer<LLUIImage> mImageHeaderFocused; + +}; + +typedef std::vector<S32> ChildWidthVec; +typedef std::vector<LLView *> ChildVec; + +class LLPersonView : public LLFolderViewItem +{ + +public: + +	struct Params : public LLInitParam::Block<Params, LLFolderViewItem::Params> +	{ +		Params(); +		Optional<LLAvatarIconCtrl::Params> avatar_icon; +		Optional<LLTextBox::Params> last_interaction_time_textbox; +		Optional<LLIconCtrl::Params> permission_edit_theirs_icon; +		Optional<LLIconCtrl::Params> permission_edit_mine_icon; +		Optional<LLIconCtrl::Params> permission_map_icon; +		Optional<LLIconCtrl::Params> permission_online_icon; +		Optional<LLButton::Params> info_btn; +		Optional<LLButton::Params> profile_btn; +		Optional<LLOutputMonitorCtrl::Params> output_monitor; +	}; + +	LLPersonView(const LLPersonView::Params& p); +	virtual ~LLPersonView(); + +	S32 getLabelXPos(); +	void addToFolder(LLFolderViewFolder * person_folder_view); +	void initFromParams(const LLPersonView::Params & params); +	BOOL postBuild(); +	void onMouseEnter(S32 x, S32 y, MASK mask); +	void onMouseLeave(S32 x, S32 y, MASK mask); +	BOOL handleMouseDown( S32 x, S32 y, MASK mask); + +protected:	 +	 +	void draw(); +	void drawHighlight(); + +private: + +	LLPointer<LLUIImage> mImageOver; +	LLPointer<LLUIImage> mImageSelected; + +	LLAvatarIconCtrl* mAvatarIcon; +	LLTextBox * mLastInteractionTimeTextbox; +	LLIconCtrl * mPermissionEditTheirsIcon; +	LLIconCtrl * mPermissionEditMineIcon; +	LLIconCtrl * mPermissionMapIcon; +	LLIconCtrl * mPermissionOnlineIcon; +	LLButton * mInfoBtn; +	LLButton * mProfileBtn; +	LLOutputMonitorCtrl * mOutputMonitorCtrl; + + + +	typedef enum e_avatar_item_child { +		ALIC_SPEAKER_INDICATOR, +		ALIC_PROFILE_BUTTON, +		ALIC_INFO_BUTTON, +		ALIC_PERMISSION_ONLINE, +		ALIC_PERMISSION_MAP, +		ALIC_PERMISSION_EDIT_MINE, +		ALIC_PERMISSION_EDIT_THEIRS, +		ALIC_INTERACTION_TIME, +		ALIC_COUNT, +	} EAvatarListItemChildIndex; + +	//Widths of controls are same for every instance so can be static +	static ChildWidthVec mChildWidthVec; +	//Control pointers are different for each instance so non-static +	ChildVec mChildVec; + +	static bool	sChildrenWidthsInitialized; +	static void initChildrenWidthVec(LLPersonView* self); +	void initChildVec(); +	void updateChildren(); +}; + +#endif // LL_LLPERSONTABVIEW_H + diff --git a/indra/newview/llsociallist.cpp b/indra/newview/llsociallist.cpp new file mode 100644 index 0000000000..9f827cf04f --- /dev/null +++ b/indra/newview/llsociallist.cpp @@ -0,0 +1,154 @@ +/**  +* @file llsociallist.cpp +* @brief Implementation of llsociallist +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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 "llsociallist.h" + +#include "llavataractions.h" +#include "llfloaterreg.h" +#include "llavatariconctrl.h" +#include "llavatarnamecache.h" +#include "lloutputmonitorctrl.h" +#include "lltextutil.h" + +static LLDefaultChildRegistry::Register<LLSocialList> r("social_list"); + +LLSocialList::LLSocialList(const Params&p) : LLFlatListViewEx(p) +{ + +} + +LLSocialList::~LLSocialList() +{ + +} + +void LLSocialList::draw() +{ +	LLFlatListView::draw(); +} + +void LLSocialList::refresh() +{ + +} + +void LLSocialList::addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos) +{ +	LLSocialListItem * item = new LLSocialListItem(); +	LLAvatarName avatar_name; +	bool has_avatar_name = id.notNull() && LLAvatarNameCache::get(id, &avatar_name); + +	item->mAvatarId = id; +	if(id.notNull()) +	{ +		item->mIcon->setValue(id); +	} + +	item->setName(has_avatar_name ? name + " (" + avatar_name.getDisplayName() + ")" : name, mNameFilter); +	addItem(item, id, pos); +} + +LLSocialListItem::LLSocialListItem() +{ +	buildFromFile("panel_avatar_list_item.xml"); +} + +LLSocialListItem::~LLSocialListItem() +{ + +} + +BOOL LLSocialListItem::postBuild() +{ +	mIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); +	mLabelTextBox = getChild<LLTextBox>("avatar_name"); + +	mLastInteractionTime = getChild<LLTextBox>("last_interaction"); +	mIconPermissionOnline = getChild<LLIconCtrl>("permission_online_icon"); +	mIconPermissionMap = getChild<LLIconCtrl>("permission_map_icon"); +	mIconPermissionEditMine = getChild<LLIconCtrl>("permission_edit_mine_icon"); +	mIconPermissionEditTheirs = getChild<LLIconCtrl>("permission_edit_theirs_icon"); +	mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); +	mInfoBtn = getChild<LLButton>("info_btn"); +	mProfileBtn = getChild<LLButton>("profile_btn"); + +	mLastInteractionTime->setVisible(false); +	mIconPermissionOnline->setVisible(false); +	mIconPermissionMap->setVisible(false); +	mIconPermissionEditMine->setVisible(false); +	mIconPermissionEditTheirs->setVisible(false); +	mSpeakingIndicator->setVisible(false); +	mInfoBtn->setVisible(false); +	mProfileBtn->setVisible(false); + +	mInfoBtn->setClickedCallback(boost::bind(&LLSocialListItem::onInfoBtnClick, this)); +	mProfileBtn->setClickedCallback(boost::bind(&LLSocialListItem::onProfileBtnClick, this)); + +	return TRUE; +} + +void LLSocialListItem::setName(const std::string& name, const std::string& highlight) +{ +	mLabel = name; +	LLTextUtil::textboxSetHighlightedVal(mLabelTextBox, mLabelTextBoxStyle, name, highlight); +} + +void LLSocialListItem::setValue(const LLSD& value) +{ +	getChildView("selected_icon")->setVisible( value["selected"]); +} + +void LLSocialListItem::onMouseEnter(S32 x, S32 y, MASK mask) +{ +	getChildView("hovered_icon")->setVisible( true); +	mInfoBtn->setVisible(true); +	mProfileBtn->setVisible(true); + +	LLPanel::onMouseEnter(x, y, mask); +} + +void LLSocialListItem::onMouseLeave(S32 x, S32 y, MASK mask) +{ +	getChildView("hovered_icon")->setVisible( false); +	mInfoBtn->setVisible(false); +	mProfileBtn->setVisible(false); + +	LLPanel::onMouseLeave(x, y, mask); +} + +void LLSocialListItem::onInfoBtnClick() +{ +	LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mAvatarId)); +} + +void LLSocialListItem::onProfileBtnClick() +{ +	LLAvatarActions::showProfile(mAvatarId); +} diff --git a/indra/newview/llsociallist.h b/indra/newview/llsociallist.h new file mode 100644 index 0000000000..bc667fc400 --- /dev/null +++ b/indra/newview/llsociallist.h @@ -0,0 +1,102 @@ +/**  +* @file   llsociallist.h +* @brief  Header file for llsociallist +* @author Gilbert@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 2013, 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_LLSOCIALLIST_H +#define LL_LLSOCIALLIST_H + +#include "llflatlistview.h" +#include "llstyle.h" + + +/** + * Generic list of avatars. + *  + * Updates itself when it's dirty, using optional name filter. + * To initiate update, modify the UUID list and call setDirty(). + *  + * @see getIDs() + * @see setDirty() + * @see setNameFilter() + */ + +class LLAvatarIconCtrl; +class LLIconCtrl; +class LLOutputMonitorCtrl; + +class LLSocialList : public LLFlatListViewEx +{ +public: + +	struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params> +	{ +	}; + +	LLSocialList(const Params&p); +	virtual	~LLSocialList(); + +	virtual void draw(); +	void refresh(); +	void addNewItem(const LLUUID& id, const std::string& name, BOOL is_online, EAddPosition pos = ADD_BOTTOM); + + + +	std::string mNameFilter; +}; + +class LLSocialListItem : public LLPanel +{ +	public: +	LLSocialListItem(); +	~LLSocialListItem(); + +	BOOL postBuild(); +	void setName(const std::string& name, const std::string& highlight = LLStringUtil::null); +	void setValue(const LLSD& value); +	void onMouseEnter(S32 x, S32 y, MASK mask); +	void onMouseLeave(S32 x, S32 y, MASK mask); +	void onInfoBtnClick(); +	void onProfileBtnClick(); + +	LLUUID mAvatarId; + +	LLTextBox * mLabelTextBox; +	std::string mLabel; +	LLStyle::Params mLabelTextBoxStyle; + + +	LLAvatarIconCtrl * mIcon; +	LLTextBox * mLastInteractionTime; +	LLIconCtrl * mIconPermissionOnline; +	LLIconCtrl * mIconPermissionMap; +	LLIconCtrl * mIconPermissionEditMine; +	LLIconCtrl * mIconPermissionEditTheirs; +	LLOutputMonitorCtrl * mSpeakingIndicator; +	LLButton * mInfoBtn; +	LLButton * mProfileBtn; +};	 + + +#endif // LL_LLSOCIALLIST_H diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b8b53aa6e4..fba835f642 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1585,6 +1585,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)  	capabilityNames.append("EnvironmentSettings");  	capabilityNames.append("EstateChangeInfo");  	capabilityNames.append("EventQueueGet"); +	capabilityNames.append("FacebookConnect"); +	//capabilityNames.append("FacebookRedirect");  	if (gSavedSettings.getBOOL("UseHTTPInventory"))  	{	 diff --git a/indra/newview/skins/default/xui/en/menu_gear_fbc.xml b/indra/newview/skins/default/xui/en/menu_gear_fbc.xml new file mode 100644 index 0000000000..d73cee344b --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_gear_fbc.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu  +     name="menu_group_plus" +     left="0" bottom="0" visible="false" +     mouse_opaque="false"> +  <menu_item_check +   label="Facebook App Settings" +   layout="topleft" +   name="Facebook App Settings"> +      <menu_item_check.on_click +       function="Advanced.WebContentTest" +       parameter="http://www.facebook.com/settings?tab=applications" /> +  </menu_item_check> +  <menu_item_check +   label="Facebook App Request" +   layout="topleft" +   name="Facebook App Request"> +    <menu_item_check.on_click +     function="People.requestFBC" +     parameter="http://www.facebook.com/settings?tab=applications" /> +  </menu_item_check> +  <menu_item_check +   label="Facebook App Send" +   layout="topleft" +   name="Facebook App Send"> +    <menu_item_check.on_click +     function="People.sendFBC" +     parameter="http://www.facebook.com/settings?tab=applications" /> +  </menu_item_check> +  <menu_item_check +   label="Facebook Add 300 test users to AvatarList" +   layout="topleft" +   name="Facebook App Add"> +    <menu_item_check.on_click +     function="People.testaddFBC"/> +  </menu_item_check> +  <menu_item_check +   label="Facebook Add 300 test users to FolderView" +   layout="topleft" +   name="Facebook App Add"> +    <menu_item_check.on_click +     function="People.testaddFBCFolderView"/> +  </menu_item_check>   +</toggleable_menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index a11cd13fdb..39e777b246 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -3018,6 +3018,13 @@               parameter="http://google.com"/>            </menu_item_call>            <menu_item_call +           label="FB Connect Test" +           name="FB Connect Test"> +            <menu_item_call.on_click +             function="Advanced.WebContentTest" +             parameter="https://cryptic-ridge-1632.herokuapp.com/"/> +          </menu_item_call> +          <menu_item_call               label="Dump SelectMgr"               name="Dump SelectMgr">                  <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index 7ce2627be9..9bab2ccb0b 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -633,5 +633,200 @@ Looking for people to hang out with? Try the [secondlife:///app/worldmap World M             top="0"             right="-1" />          </panel> +         +<!-- ================================= FBC TEST tab (Temporary) ========================== --> +         +        <panel +           background_opaque="true" +           background_visible="true" +           bg_alpha_color="DkGray" +           bg_opaque_color="DkGray" +           follows="all" +           height="383" +           label="FBC TEST" +           layout="topleft" +           left="0" +           help_topic="people_fbctest_tab" +           name="fbctest_panel" +           top="0" +           width="313"> +          <accordion +                    background_visible="true" +                    bg_alpha_color="DkGray2" +                    bg_opaque_color="DkGray2" +                      follows="all" +                      height="356" +                      layout="topleft" +                      left="3" +                      name="friends_accordion" +                      top="0" +                      width="307"> +            <accordion_tab +             layout="topleft" +             height="172" +             min_height="150" +             name="tab_facebook" +             title="Facebook Friends"> +              <social_list +               allow_select="true" +               follows="all" +               height="172" +               layout="topleft" +               left="0" +               multi_select="true" +               name="facebook_friends" +               show_permissions_granted="true" +               top="0" +               width="307" /> +            </accordion_tab> +          </accordion> +          <panel +           background_visible="true" +           follows="left|right|bottom" +           height="27" +           label="bottom_panel" +           layout="topleft" +           left="3" +           name="bottom_panel" +           top_pad="0" +           width="313"> +            <menu_button +              follows="bottom|left" +              tool_tip="Options" +              height="25" +              image_hover_unselected="Toolbar_Left_Over" +              image_overlay="OptionsMenu_Off" +              image_selected="Toolbar_Left_Selected" +              image_unselected="Toolbar_Left_Off" +              layout="topleft" +              name="fbc_options_btn" +              top="1" +              width="31" /> +            <button +               follows="bottom|left" +               height="25" +               image_hover_unselected="Toolbar_Middle_Over" +               image_overlay="AddItem_Off" +               image_selected="Toolbar_Middle_Selected" +               image_unselected="Toolbar_Middle_Off" +               layout="topleft" +               left_pad="1" +               name="fbc_login_btn" +               tool_tip="Log in to FBC" +               width="31"> +              <commit_callback +                 function="People.loginFBC" /> +            </button> +            <icon +             follows="bottom|left|right" +             height="25" +             image_name="Toolbar_Right_Off" +             layout="topleft" +             left_pad="1" +             name="dummy_icon" +             width="244" +             /> +          </panel> +        </panel> +         +<!-- ================================= FBC TEST TWO tab (Final, to be renamed) ========================== --> +         +        <panel +           background_opaque="true" +           background_visible="true" +           bg_alpha_color="DkGray" +           bg_opaque_color="DkGray" +           follows="all" +           height="383" +           label="FBC TEST TWO" +           layout="topleft" +           left="0" +           help_topic="people_fbctest_tab" +           name="fbctesttwo_panel" +           top="0"> +            <panel +                follows="left|top|right" +                height="27" +                label="bottom_panel" +                layout="topleft" +                left="0" +                name="fbc_buttons_panel" +                right="-1" +                top="0"> +                <filter_editor +                follows="left|top|right" +                height="23" +                layout="topleft" +                left="6" +                label="Filter People" +                max_length_chars="300" +                name="fbc_filter_input" +                text_color="Black" +                text_pad_left="10" +                top="4" +                width="177" /> +                <button +                commit_callback.function="People.Gear" +                follows="right" +                height="25" +                image_hover_unselected="Toolbar_Middle_Over" +                image_overlay="OptionsMenu_Off" +                image_selected="Toolbar_Middle_Selected" +                image_unselected="Toolbar_Middle_Off" +                layout="topleft" +                left_pad="8" +                name="gear_btn" +                tool_tip="Actions on selected person" +                top="3" +                width="31" /> +                <menu_button +                follows="right" +                height="25" +                image_hover_unselected="Toolbar_Middle_Over" +                image_overlay="Conv_toolbar_sort" +                image_selected="Toolbar_Middle_Selected" +                image_unselected="Toolbar_Middle_Off" +                layout="topleft" +                left_pad="2" +                menu_filename="menu_people_friends_view.xml" +                menu_position="bottomleft" +                name="fbc_view_btn" +                tool_tip="View/sort options" +                top_delta="0" +                width="31" /> +                <button +                    follows="right" +                    height="25" +                    image_hover_unselected="Toolbar_Middle_Over" +                    image_overlay="AddItem_Off" +                    image_selected="Toolbar_Middle_Selected" +                    image_unselected="Toolbar_Middle_Off" +                    layout="topleft" +                    left_pad="2" +                    name="fbc_add_btn" +                    tool_tip="Offer friendship to a resident" +                    top_delta="0" +                    width="31"> +                    <commit_callback +                    function="People.AddFriendWizard" /> +                </button> +                <dnd_button +                    follows="right" +                    height="25" +                    image_hover_unselected="Toolbar_Middle_Over" +                    image_overlay="TrashItem_Off" +                    image_selected="Toolbar_Middle_Selected" +                    image_unselected="Toolbar_Middle_Off" +                    left_pad="2" +                    layout="topleft" +                    name="fbc_del_btn" +                    tool_tip="Remove selected person as a friend" +                    top_delta="0" +                    width="31"> +                    <commit_callback +                    function="People.DelFriend" /> +                </dnd_button> +            </panel> +        </panel>      </tab_container>  </panel> diff --git a/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml new file mode 100644 index 0000000000..af5aec2c34 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/person_tab_view.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<person_tab_view +  folder_arrow_image="Folder_Arrow" +  folder_indentation="5" +  item_height="24"  +  item_top_pad="3" +  mouse_opaque="true" +  follows="left|top|right" +  text_pad="6" +  text_pad_left="4" +  text_pad_right="4" +  arrow_size="10" +  max_folder_item_overlap="2"/> diff --git a/indra/newview/skins/default/xui/en/widgets/person_view.xml b/indra/newview/skins/default/xui/en/widgets/person_view.xml new file mode 100644 index 0000000000..4a39109f36 --- /dev/null +++ b/indra/newview/skins/default/xui/en/widgets/person_view.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<person_view +  folder_arrow_image="Folder_Arrow" +  folder_indentation="5" +  item_height="24"  +  item_top_pad="3" +  mouse_opaque="true" +  follows="left|top|right" +  icon_pad="4" +  icon_width="20" +  text_pad="6" +  text_pad_left="4" +  text_pad_right="4" +  arrow_size="10" +  max_folder_item_overlap="2"> +    <avatar_icon +      follows="left" +      layout="topleft" +      height="20" +      default_icon_name="Generic_Person" +      left="5" +      top="2" +      width="20" /> +    <last_interaction_time_textbox +      layout="topleft" +      follows="right" +      font="SansSerifSmall" +      height="15" +      left_pad="5" +      right="-164" +      name="last_interaction_time_textbox" +      text_color="LtGray_50" +      value="0s" +      visible="false"  +      width="35" /> +    <permission_edit_theirs_icon +      layout="topleft" +      height="16" +      follows="right" +      image_name="Permission_Edit_Objects_Theirs" +      left_pad="3" +      right="-129" +      name="permission_edit_theirs_icon" +      tool_tip="You can edit this friend's objects" +      top="4" +      visible="false"  +      width="16" /> +    <permission_edit_mine_icon +      layout="topleft" +      height="16" +      follows="right" +      image_name="Permission_Edit_Objects_Mine" +      left_pad="3" +      right="-110" +      name="permission_edit_mine_icon" +      tool_tip="This friend can edit, delete or take your objects" +      top="4" +      visible="false"  +      width="16" /> +    <permission_map_icon +      height="16" +      follows="right" +      image_name="Permission_Visible_Map" +      left_pad="3" +      tool_tip="This friend can locate you on the map" +      right="-91" +      name="permission_map_icon" +      visible="false"  +      width="16" /> +    <permission_online_icon +      height="16" +      follows="right" +      image_name="Permission_Visible_Online" +      left_pad="3" +      right="-72" +      name="permission_online_icon" +      tool_tip="This friend can see when you're online" +      visible="false"  +      width="16" /> +    <info_btn +      follows="right" +      height="16" +      image_pressed="Info_Press" +      image_unselected="Info_Over" +      left_pad="3" +      right="-53" +      name="info_btn" +      tool_tip="More info" +      tab_stop="false" +      visible="false"       +      width="16" /> +    <profile_btn +      layout="topleft" +      follows="right" +      height="20" +      image_overlay="Web_Profile_Off" +      left_pad="5" +      right="-28" +      name="profile_btn" +      tab_stop="false" +      tool_tip="View profile" +      top="2" +      visible="false" +      width="20" /> +    <output_monitor +      auto_update="true" +      follows="right" +      draw_border="false" +      height="16" +      right="-3" +      mouse_opaque="true" +      name="speaking_indicator" +      visible="false" +      width="20" /> + </person_view> + | 
