summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/CMakeLists.txt8
-rw-r--r--indra/newview/llappviewer.cpp43
-rw-r--r--indra/newview/llfloaterimcontainer.cpp7
-rw-r--r--indra/newview/llpanelpeople.cpp496
-rw-r--r--indra/newview/llpanelpeople.h38
-rw-r--r--indra/newview/llparticipantlist.cpp18
-rw-r--r--indra/newview/llparticipantlist.h1
-rw-r--r--indra/newview/llpersonfolderview.cpp114
-rw-r--r--indra/newview/llpersonfolderview.h64
-rw-r--r--indra/newview/llpersonmodelcommon.cpp281
-rw-r--r--indra/newview/llpersonmodelcommon.h244
-rw-r--r--indra/newview/llpersontabview.cpp398
-rw-r--r--indra/newview/llpersontabview.h146
-rw-r--r--indra/newview/llsociallist.cpp154
-rw-r--r--indra/newview/llsociallist.h102
-rw-r--r--indra/newview/llviewerregion.cpp2
-rw-r--r--indra/newview/skins/default/xui/en/menu_gear_fbc.xml44
-rw-r--r--indra/newview/skins/default/xui/en/menu_viewer.xml7
-rw-r--r--indra/newview/skins/default/xui/en/panel_people.xml195
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_tab_view.xml13
-rw-r--r--indra/newview/skins/default/xui/en/widgets/person_view.xml116
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&apos;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&apos;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>
+