summaryrefslogtreecommitdiff
path: root/indra/newview/llpanelpeople.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llpanelpeople.cpp')
-rw-r--r--indra/newview/llpanelpeople.cpp1371
1 files changed, 1075 insertions, 296 deletions
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp
index 1d7a2748cc..d096b17145 100644
--- a/indra/newview/llpanelpeople.cpp
+++ b/indra/newview/llpanelpeople.cpp
@@ -2,31 +2,25 @@
* @file llpanelpeople.cpp
* @brief Side tray "People" panel
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * 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.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * 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.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * 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$
*/
@@ -35,54 +29,210 @@
// libs
#include "llfloaterreg.h"
#include "llmenugl.h"
-#include "llsearcheditor.h"
+#include "llnotificationsutil.h"
+#include "lleventtimer.h"
+#include "llfiltereditor.h"
#include "lltabcontainer.h"
#include "lluictrlfactory.h"
#include "llpanelpeople.h"
// newview
+#include "llaccordionctrl.h"
+#include "llaccordionctrltab.h"
#include "llagent.h"
+#include "llavataractions.h"
#include "llavatarlist.h"
+#include "llavatarlistitem.h"
#include "llcallingcard.h" // for LLAvatarTracker
#include "llfloateravatarpicker.h"
-#include "llfloaterminiinspector.h"
-#include "llfriendactions.h"
+//#include "llfloaterminiinspector.h"
+#include "llfriendcard.h"
#include "llgroupactions.h"
#include "llgrouplist.h"
+#include "llinventoryobserver.h"
+#include "llpanelpeoplemenus.h"
+#include "llsidetray.h"
+#include "llsidetraypanelcontainer.h"
#include "llrecentpeople.h"
#include "llviewercontrol.h" // for gSavedSettings
#include "llviewermenu.h" // for gMenuHolder
#include "llvoiceclient.h"
#include "llworld.h"
-
-using namespace LLOldEvents;
+#include "llspeakers.h"
#define FRIEND_LIST_UPDATE_TIMEOUT 0.5
#define NEARBY_LIST_UPDATE_INTERVAL 1
-#define RECENT_LIST_UPDATE_DELAY 1
+
+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 COLLAPSED_BY_USER = "collapsed_by_user";
+
+/** Comparator for comparing avatar items by last interaction date */
+class LLAvatarItemRecentComparator : public LLAvatarItemComparator
+{
+public:
+ LLAvatarItemRecentComparator() {};
+ virtual ~LLAvatarItemRecentComparator() {};
+
+protected:
+ virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
+ {
+ LLRecentPeople& people = LLRecentPeople::instance();
+ const LLDate& date1 = people.getDate(avatar_item1->getAvatarId());
+ const LLDate& date2 = people.getDate(avatar_item2->getAvatarId());
+
+ //older comes first
+ return date1 > date2;
+ }
+};
+
+/** Compares avatar items by online status, then by name */
+class LLAvatarItemStatusComparator : public LLAvatarItemComparator
+{
+public:
+ LLAvatarItemStatusComparator() {};
+
+protected:
+ /**
+ * @return true if item1 < item2, false otherwise
+ */
+ virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ bool online1 = at.isBuddyOnline(item1->getAvatarId());
+ bool online2 = at.isBuddyOnline(item2->getAvatarId());
+
+ if (online1 == online2)
+ {
+ std::string name1 = item1->getAvatarName();
+ std::string name2 = item2->getAvatarName();
+
+ LLStringUtil::toUpper(name1);
+ LLStringUtil::toUpper(name2);
+
+ return name1 < name2;
+ }
+
+ return online1 > online2;
+ }
+};
+
+/** Compares avatar items by distance between you and them */
+class LLAvatarItemDistanceComparator : public LLAvatarItemComparator
+{
+public:
+ typedef std::map < LLUUID, LLVector3d > id_to_pos_map_t;
+ LLAvatarItemDistanceComparator() {};
+
+ void updateAvatarsPositions(std::vector<LLVector3d>& positions, uuid_vec_t& uuids)
+ {
+ std::vector<LLVector3d>::const_iterator
+ pos_it = positions.begin(),
+ pos_end = positions.end();
+
+ uuid_vec_t::const_iterator
+ id_it = uuids.begin(),
+ id_end = uuids.end();
+
+ LLAvatarItemDistanceComparator::id_to_pos_map_t pos_map;
+
+ mAvatarsPositions.clear();
+
+ for (;pos_it != pos_end && id_it != id_end; ++pos_it, ++id_it )
+ {
+ mAvatarsPositions[*id_it] = *pos_it;
+ }
+ };
+
+protected:
+ virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
+ {
+ const LLVector3d& me_pos = gAgent.getPositionGlobal();
+ const LLVector3d& item1_pos = mAvatarsPositions.find(item1->getAvatarId())->second;
+ const LLVector3d& item2_pos = mAvatarsPositions.find(item2->getAvatarId())->second;
+ F32 dist1 = dist_vec(item1_pos, me_pos);
+ F32 dist2 = dist_vec(item2_pos, me_pos);
+ return dist1 < dist2;
+ }
+private:
+ id_to_pos_map_t mAvatarsPositions;
+};
+
+/** Comparator for comparing nearby avatar items by last spoken time */
+class LLAvatarItemRecentSpeakerComparator : public LLAvatarItemNameComparator
+{
+public:
+ LLAvatarItemRecentSpeakerComparator() {};
+ virtual ~LLAvatarItemRecentSpeakerComparator() {};
+
+protected:
+ virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
+ {
+ LLPointer<LLSpeaker> lhs = LLActiveSpeakerMgr::instance().findSpeaker(item1->getAvatarId());
+ LLPointer<LLSpeaker> rhs = LLActiveSpeakerMgr::instance().findSpeaker(item2->getAvatarId());
+ if ( lhs.notNull() && rhs.notNull() )
+ {
+ // Compare by last speaking time
+ if( lhs->mLastSpokeTime != rhs->mLastSpokeTime )
+ return ( lhs->mLastSpokeTime > rhs->mLastSpokeTime );
+ }
+ else if ( lhs.notNull() )
+ {
+ // True if only item1 speaker info available
+ return true;
+ }
+ else if ( rhs.notNull() )
+ {
+ // False if only item2 speaker info available
+ return false;
+ }
+ // By default compare by name.
+ return LLAvatarItemNameComparator::doCompare(item1, item2);
+ }
+};
+
+static const LLAvatarItemRecentComparator RECENT_COMPARATOR;
+static const LLAvatarItemStatusComparator STATUS_COMPARATOR;
+static LLAvatarItemDistanceComparator DISTANCE_COMPARATOR;
+static const LLAvatarItemRecentSpeakerComparator RECENT_SPEAKER_COMPARATOR;
static LLRegisterPanelClassWrapper<LLPanelPeople> t_people("panel_people");
//=============================================================================
+/**
+ * Updates given list either on regular basis or on external events (up to implementation).
+ */
class LLPanelPeople::Updater
{
public:
- typedef boost::function<bool(U32)> callback_t;
+ typedef boost::function<void()> callback_t;
Updater(callback_t cb)
: mCallback(cb)
{
}
+
virtual ~Updater()
{
}
+
+ /**
+ * Activate/deactivate updater.
+ *
+ * This may start/stop regular updates.
+ */
virtual void setActive(bool) {}
+
protected:
- bool updateList(U32 mask = 0)
+ void updateList()
{
- return mCallback(mask);
+ mCallback();
}
+
callback_t mCallback;
};
@@ -95,83 +245,180 @@ public:
{
mEventTimer.stop();
}
+
+ virtual BOOL tick() // from LLEventTimer
+ {
+ return FALSE;
+ }
};
/**
* Updates the friends list.
+ *
+ * Updates the list on external events which trigger the changed() method.
*/
class LLFriendListUpdater : public LLAvatarListUpdater, public LLFriendObserver
{
LOG_CLASS(LLFriendListUpdater);
+ class LLInventoryFriendCardObserver;
+
public:
+ friend class LLInventoryFriendCardObserver;
LLFriendListUpdater(callback_t cb)
: LLAvatarListUpdater(cb, FRIEND_LIST_UPDATE_TIMEOUT)
+ , mIsActive(false)
{
LLAvatarTracker::instance().addObserver(this);
+
// For notification when SIP online status changes.
LLVoiceClient::getInstance()->addObserver(this);
+ mInvObserver = new LLInventoryFriendCardObserver(this);
}
+
~LLFriendListUpdater()
{
+ // will be deleted by ~LLInventoryModel
+ //delete mInvObserver;
LLVoiceClient::getInstance()->removeObserver(this);
LLAvatarTracker::instance().removeObserver(this);
}
- /*virtual*/ void setActive(bool val)
- {
- if (!val)
- return;
- // Perform updates until all names are loaded.
- if (!updateList(LLFriendObserver::ADD))
- changed(LLFriendObserver::ADD);
- }
/*virtual*/ void changed(U32 mask)
{
- // events can arrive quickly in bulk - we need not process EVERY one of them -
- // so we wait a short while to let others pile-in, and process them in aggregate.
- mEventTimer.start();
+ if (mIsActive)
+ {
+ // events can arrive quickly in bulk - we need not process EVERY one of them -
+ // so we wait a short while to let others pile-in, and process them in aggregate.
+ mEventTimer.start();
+ }
// save-up all the mask-bits which have come-in
mMask |= mask;
}
+
+
/*virtual*/ BOOL tick()
{
- if (updateList(mMask))
+ if (!mIsActive) return FALSE;
+
+ if (mMask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
{
- // Got all names, stop updates.
- mEventTimer.stop();
- mMask = 0;
+ updateList();
}
+ // Stop updates.
+ mEventTimer.stop();
+ mMask = 0;
+
return FALSE;
}
+
+ // virtual
+ void setActive(bool active)
+ {
+ mIsActive = active;
+ if (active)
+ {
+ tick();
+ }
+ }
+
private:
U32 mMask;
+ LLInventoryFriendCardObserver* mInvObserver;
+ bool mIsActive;
+
+ /**
+ * This class is intended for updating Friend List when Inventory Friend Card is added/removed.
+ *
+ * The main usage is when Inventory Friends/All content is added while synchronizing with
+ * friends list on startup is performed. In this case Friend Panel should be updated when
+ * missing Inventory Friend Card is created.
+ * *NOTE: updating is fired when Inventory item is added into CallingCards/Friends subfolder.
+ * Otherwise LLFriendObserver functionality is enough to keep Friends Panel synchronized.
+ */
+ class LLInventoryFriendCardObserver : public LLInventoryObserver
+ {
+ LOG_CLASS(LLFriendListUpdater::LLInventoryFriendCardObserver);
+
+ friend class LLFriendListUpdater;
+
+ private:
+ LLInventoryFriendCardObserver(LLFriendListUpdater* updater) : mUpdater(updater)
+ {
+ gInventory.addObserver(this);
+ }
+ ~LLInventoryFriendCardObserver()
+ {
+ gInventory.removeObserver(this);
+ }
+ /*virtual*/ void changed(U32 mask)
+ {
+ lldebugs << "Inventory changed: " << mask << llendl;
+
+ // *NOTE: deleting of InventoryItem is performed via moving to Trash.
+ // That means LLInventoryObserver::STRUCTURE is present in MASK instead of LLInventoryObserver::REMOVE
+ if ((CALLINGCARD_ADDED & mask) == CALLINGCARD_ADDED)
+ {
+ lldebugs << "Calling card added: count: " << gInventory.getChangedIDs().size()
+ << ", first Inventory ID: "<< (*gInventory.getChangedIDs().begin())
+ << llendl;
+
+ bool friendFound = false;
+ std::set<LLUUID> changedIDs = gInventory.getChangedIDs();
+ for (std::set<LLUUID>::const_iterator it = changedIDs.begin(); it != changedIDs.end(); ++it)
+ {
+ if (isDescendentOfInventoryFriends(*it))
+ {
+ friendFound = true;
+ break;
+ }
+ }
+
+ if (friendFound)
+ {
+ lldebugs << "friend found, panel should be updated" << llendl;
+ mUpdater->changed(LLFriendObserver::ADD);
+ }
+ }
+ }
+
+ bool isDescendentOfInventoryFriends(const LLUUID& invItemID)
+ {
+ LLViewerInventoryItem * item = gInventory.getItem(invItemID);
+ if (NULL == item)
+ return false;
+
+ return LLFriendCardsManager::instance().isItemInAnyFriendsList(item);
+ }
+ LLFriendListUpdater* mUpdater;
+
+ static const U32 CALLINGCARD_ADDED = LLInventoryObserver::ADD | LLInventoryObserver::CALLING_CARD;
+ };
};
/**
* Periodically updates the nearby people list while the Nearby tab is active.
+ *
+ * The period is defined by NEARBY_LIST_UPDATE_INTERVAL constant.
*/
class LLNearbyListUpdater : public LLAvatarListUpdater
{
LOG_CLASS(LLNearbyListUpdater);
+
public:
LLNearbyListUpdater(callback_t cb)
: LLAvatarListUpdater(cb, NEARBY_LIST_UPDATE_INTERVAL)
{
setActive(false);
}
- /*virtual*/ BOOL tick()
- {
- updateList();
- return FALSE;
- }
+
/*virtual*/ void setActive(bool val)
{
if (val)
{
// update immediately and start regular updates
- tick();
+ updateList();
mEventTimer.start();
}
else
@@ -180,69 +427,27 @@ public:
mEventTimer.stop();
}
}
-private:
-};
-/**
- * Updates the recent people list (those the agent has recently interacted with).
- */
-class LLRecentListUpdater : public LLAvatarListUpdater
-{
- LOG_CLASS(LLRecentListUpdater);
-public:
- LLRecentListUpdater(callback_t cb)
- : LLAvatarListUpdater(cb, RECENT_LIST_UPDATE_DELAY)
- {
- LLRecentPeople::instance().setChangedCallback(boost::bind(&LLRecentListUpdater::onRecentPeopleChanged, this));
- }
-private:
/*virtual*/ BOOL tick()
{
- // Update the list until we get all the names.
- if (updateList())
- {
- // Got all names, stop updates.
- mEventTimer.stop();
- }
-
+ updateList();
return FALSE;
}
- void onRecentPeopleChanged()
- {
- if (!updateList())
- {
- // Some names are incomplete, schedule another update.
- mEventTimer.start();
- }
- }
+private:
};
/**
- * Updates the group list on events from LLAgent.
+ * Updates the recent people list (those the agent has recently interacted with).
*/
-class LLGroupListUpdater : public LLPanelPeople::Updater, public LLSimpleListener
+class LLRecentListUpdater : public LLAvatarListUpdater, public boost::signals2::trackable
{
- LOG_CLASS(LLGroupListUpdater);
+ LOG_CLASS(LLRecentListUpdater);
+
public:
- LLGroupListUpdater(callback_t cb)
- : LLPanelPeople::Updater(cb)
- {
- gAgent.addListener(this, "new group");
- }
- ~LLGroupListUpdater()
- {
- gAgent.removeListener(this);
- }
- /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ LLRecentListUpdater(callback_t cb)
+ : LLAvatarListUpdater(cb, 0)
{
- // Why is "new group" sufficient?
- if (event->desc() == "new group")
- {
- updateList();
- return true;
- }
-
- return false;
+ LLRecentPeople::instance().setChangedCallback(boost::bind(&LLRecentListUpdater::updateList, this));
}
};
@@ -251,16 +456,19 @@ public:
LLPanelPeople::LLPanelPeople()
: LLPanel(),
mFilterSubString(LLStringUtil::null),
- mSearchEditor(NULL),
+ mFilterSubStringOrig(LLStringUtil::null),
+ mFilterEditor(NULL),
mTabContainer(NULL),
- mFriendList(NULL),
+ mOnlineFriendList(NULL),
+ mAllFriendList(NULL),
mNearbyList(NULL),
- mRecentList(NULL)
+ mRecentList(NULL),
+ mGroupList(NULL)
{
- mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::onFriendListUpdate,this, _1));
+ mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this));
mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this));
mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this));
- mGroupListUpdater = new LLGroupListUpdater (boost::bind(&LLPanelPeople::updateGroupList, this));
+ mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this));
}
LLPanelPeople::~LLPanelPeople()
@@ -268,152 +476,292 @@ LLPanelPeople::~LLPanelPeople()
delete mNearbyListUpdater;
delete mFriendListUpdater;
delete mRecentListUpdater;
- delete mGroupListUpdater;
+
+ if(LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver(this);
+ }
LLView::deleteViewByHandle(mGroupPlusMenuHandle);
- LLView::deleteViewByHandle(mGroupMinusMenuHandle);
+ LLView::deleteViewByHandle(mNearbyViewSortMenuHandle);
+ LLView::deleteViewByHandle(mFriendsViewSortMenuHandle);
+ LLView::deleteViewByHandle(mGroupsViewSortMenuHandle);
+ LLView::deleteViewByHandle(mRecentViewSortMenuHandle);
+
+}
+
+void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list)
+{
+ if(!avatar_list)
+ {
+ llerrs << "Bad parameter" << llendl;
+ return;
+ }
+
+ bool expanded = param.asBoolean();
+
+ setAccordionCollapsedByUser(ctrl, !expanded);
+ if(!expanded)
+ {
+ avatar_list->resetSelection();
+ }
}
BOOL LLPanelPeople::postBuild()
{
- mSearchEditor = getChild<LLSearchEditor>("filter_input");
+ mFilterEditor = getChild<LLFilterEditor>("filter_input");
+ mFilterEditor->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
mTabContainer = getChild<LLTabContainer>("tabs");
mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2));
- mTabContainer->selectTabByName("friends_panel"); // must go after setting commit callback
- mFriendList = getChild<LLPanel>("friends_panel")->getChild<LLAvatarList>("avatar_list");
- mNearbyList = getChild<LLPanel>("nearby_panel")->getChild<LLAvatarList>("avatar_list");
- mRecentList = getChild<LLPanel>("recent_panel")->getChild<LLAvatarList>("avatar_list");
+ LLPanel* friends_tab = getChild<LLPanel>(FRIENDS_TAB_NAME);
+ // updater is active only if panel is visible to user.
+ friends_tab->setVisibleCallback(boost::bind(&Updater::setActive, mFriendListUpdater, _2));
+ mOnlineFriendList = friends_tab->getChild<LLAvatarList>("avatars_online");
+ mAllFriendList = friends_tab->getChild<LLAvatarList>("avatars_all");
+ mOnlineFriendList->setNoItemsCommentText(getString("no_friends_online"));
+ mOnlineFriendList->setShowIcons("FriendsListShowIcons");
+ mOnlineFriendList->showPermissions("FriendsListShowPermissions");
+ mAllFriendList->setNoItemsCommentText(getString("no_friends"));
+ mAllFriendList->setShowIcons("FriendsListShowIcons");
+ mAllFriendList->showPermissions("FriendsListShowPermissions");
+
+ LLPanel* nearby_tab = getChild<LLPanel>(NEARBY_TAB_NAME);
+ nearby_tab->setVisibleCallback(boost::bind(&Updater::setActive, mNearbyListUpdater, _2));
+ mNearbyList = nearby_tab->getChild<LLAvatarList>("avatar_list");
+ mNearbyList->setNoItemsCommentText(getString("no_one_near"));
+ mNearbyList->setNoItemsMsg(getString("no_one_near"));
+ mNearbyList->setNoFilteredItemsMsg(getString("no_one_filtered_near"));
+ mNearbyList->setShowIcons("NearbyListShowIcons");
+
+ mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarList>("avatar_list");
+ mRecentList->setNoItemsCommentText(getString("no_recent_people"));
+ mRecentList->setNoItemsMsg(getString("no_recent_people"));
+ mRecentList->setNoFilteredItemsMsg(getString("no_filtered_recent_people"));
+ mRecentList->setShowIcons("RecentListShowIcons");
+
mGroupList = getChild<LLGroupList>("group_list");
+ mGroupList->setNoItemsMsg(getString("no_groups_msg"));
+ mGroupList->setNoFilteredItemsMsg(getString("no_filtered_groups_msg"));
+
+ mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
+ mRecentList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
+ mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
+ mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gNearbyMenu);
- LLPanel* groups_panel = getChild<LLPanel>("groups_panel");
+ setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false);
+ setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false);
+ setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false);
+
+ LLPanel* groups_panel = getChild<LLPanel>(GROUP_TAB_NAME);
groups_panel->childSetAction("activate_btn", boost::bind(&LLPanelPeople::onActivateButtonClicked, this));
groups_panel->childSetAction("plus_btn", boost::bind(&LLPanelPeople::onGroupPlusButtonClicked, this));
- groups_panel->childSetAction("minus_btn", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this));
- LLPanel* friends_panel = getChild<LLPanel>("friends_panel");
+ LLPanel* friends_panel = getChild<LLPanel>(FRIENDS_TAB_NAME);
friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this));
friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this));
- mFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mFriendList));
- mNearbyList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mNearbyList));
- mRecentList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mRecentList));
- mFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mFriendList));
+ mOnlineFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
+ mAllFriendList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
+ mNearbyList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
+ mRecentList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
+
+ mOnlineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOnlineFriendList));
+ mAllFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mAllFriendList));
mNearbyList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mNearbyList));
mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mRecentList));
- mGroupList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this));
+ // Set openning IM as default on return action for avatar lists
+ mOnlineFriendList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+ mAllFriendList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+ mNearbyList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+ mRecentList->setReturnCallback(boost::bind(&LLPanelPeople::onImButtonClicked, this));
+
+ mGroupList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this));
mGroupList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this));
+ mGroupList->setReturnCallback(boost::bind(&LLPanelPeople::onChatButtonClicked, this));
+
+ LLAccordionCtrlTab* accordion_tab = getChild<LLAccordionCtrlTab>("tab_all");
+ accordion_tab->setDropDownStateChangedCallback(
+ boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mAllFriendList));
+
+ accordion_tab = getChild<LLAccordionCtrlTab>("tab_online");
+ accordion_tab->setDropDownStateChangedCallback(
+ boost::bind(&LLPanelPeople::onFriendsAccordionExpandedCollapsed, this, _1, _2, mOnlineFriendList));
buttonSetAction("view_profile_btn", boost::bind(&LLPanelPeople::onViewProfileButtonClicked, this));
- buttonSetAction("add_friend_btn", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this));
buttonSetAction("group_info_btn", boost::bind(&LLPanelPeople::onGroupInfoButtonClicked, this));
buttonSetAction("chat_btn", boost::bind(&LLPanelPeople::onChatButtonClicked, this));
buttonSetAction("im_btn", boost::bind(&LLPanelPeople::onImButtonClicked, this));
buttonSetAction("call_btn", boost::bind(&LLPanelPeople::onCallButtonClicked, this));
+ buttonSetAction("group_call_btn", boost::bind(&LLPanelPeople::onGroupCallButtonClicked, this));
buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this));
buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this));
- buttonSetAction("more_btn", boost::bind(&LLPanelPeople::onMoreButtonClicked, this));
+
+ getChild<LLPanel>(NEARBY_TAB_NAME)->childSetAction("nearby_view_sort_btn",boost::bind(&LLPanelPeople::onNearbyViewSortButtonClicked, this));
+ getChild<LLPanel>(RECENT_TAB_NAME)->childSetAction("recent_viewsort_btn",boost::bind(&LLPanelPeople::onRecentViewSortButtonClicked, this));
+ getChild<LLPanel>(FRIENDS_TAB_NAME)->childSetAction("friends_viewsort_btn",boost::bind(&LLPanelPeople::onFriendsViewSortButtonClicked, this));
+ getChild<LLPanel>(GROUP_TAB_NAME)->childSetAction("groups_viewsort_btn",boost::bind(&LLPanelPeople::onGroupsViewSortButtonClicked, this));
+
+ // Must go after setting commit callback and initializing all pointers to children.
+ mTabContainer->selectTabByName(NEARBY_TAB_NAME);
// Create menus.
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
+ LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+
registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2));
- LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder);
+ registrar.add("People.Group.Minus.Action", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this));
+ registrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2));
+ registrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2));
+ registrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2));
+ registrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2));
+
+ enable_registrar.add("People.Group.Minus.Enable", boost::bind(&LLPanelPeople::isRealGroup, this));
+ enable_registrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2));
+ enable_registrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2));
+ enable_registrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2));
+
+ LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
mGroupPlusMenuHandle = plus_menu->getHandle();
- registrar.add("People.Group.Minus.Action", boost::bind(&LLPanelPeople::onGroupMinusMenuItemClicked, this, _2));
- LLMenuGL* minus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_minus.xml", gMenuHolder);
- mGroupMinusMenuHandle = minus_menu->getHandle();
- // Perform initial update.
- mFriendListUpdater->setActive(true);
- updateGroupList();
- updateRecentList();
+ LLMenuGL* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if(nearby_view_sort)
+ mNearbyViewSortMenuHandle = nearby_view_sort->getHandle();
- return TRUE;
-}
+ LLMenuGL* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if(friend_view_sort)
+ mFriendsViewSortMenuHandle = friend_view_sort->getHandle();
-bool LLPanelPeople::refreshFriendNames(U32 changed_mask)
-{
- // get all buddies we know about
- LLAvatarTracker::buddy_map_t all_buddies;
- LLAvatarTracker::instance().copyBuddyList(all_buddies);
-
- bool have_names = true;
+ LLMenuGL* group_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_groups_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if(group_view_sort)
+ mGroupsViewSortMenuHandle = group_view_sort->getHandle();
- if (changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
- {
- // *TODO: it's suboptimal to rebuild the whole list on online status change.
+ LLMenuGL* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+ if(recent_view_sort)
+ mRecentViewSortMenuHandle = recent_view_sort->getHandle();
- // convert the buddy map to vector
- std::vector<LLUUID> avatar_ids;
- LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin();
- for (; buddy_it != all_buddies.end(); ++buddy_it)
- avatar_ids.push_back(buddy_it->first);
+ LLVoiceClient::getInstance()->addObserver(this);
- // do refresh the friend list
- if (avatar_ids.size() > 0)
- have_names = mFriendList->updateList(avatar_ids);
- else
- mFriendList->setCommentText(getString("no_friends"));
- }
+ // call this method in case some list is empty and buttons can be in inconsistent state
+ updateButtons();
- return have_names;
+ mOnlineFriendList->setRefreshCompleteCallback(boost::bind(&LLPanelPeople::onFriendListRefreshComplete, this, _1, _2));
+ mAllFriendList->setRefreshCompleteCallback(boost::bind(&LLPanelPeople::onFriendListRefreshComplete, this, _1, _2));
+
+ return TRUE;
}
-bool LLPanelPeople::updateFriendList(U32 changed_mask)
+// virtual
+void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI, bool proximal)
{
- // Refresh names.
- if (changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
+ if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL)
{
- return refreshFriendNames(changed_mask);
+ return;
}
- return true;
+ updateButtons();
}
-bool LLPanelPeople::updateNearbyList()
+void LLPanelPeople::updateFriendListHelpText()
{
- std::vector<LLUUID> avatar_ids;
+ // show special help text for just created account to help finding friends. EXT-4836
+ static LLTextBox* no_friends_text = getChild<LLTextBox>("no_friends_help_text");
+
+ // Seems sometimes all_friends can be empty because of issue with Inventory loading (clear cache, slow connection...)
+ // So, lets check all lists to avoid overlapping the text with online list. See EXT-6448.
+ bool any_friend_exists = mAllFriendList->filterHasMatches() || mOnlineFriendList->filterHasMatches();
+ no_friends_text->setVisible(!any_friend_exists);
+ if (no_friends_text->getVisible())
+ {
+ //update help text for empty lists
+ std::string message_name = mFilterSubString.empty() ? "no_friends_msg" : "no_filtered_friends_msg";
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(mFilterSubStringOrig);
+ no_friends_text->setText(getString(message_name, args));
+ }
+}
- LLWorld::getInstance()->getAvatars(&avatar_ids, NULL, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
+void LLPanelPeople::updateFriendList()
+{
+ if (!mOnlineFriendList || !mAllFriendList)
+ return;
- mNearbyList->updateList(avatar_ids);
+ // get all buddies we know about
+ const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ LLAvatarTracker::buddy_map_t all_buddies;
+ av_tracker.copyBuddyList(all_buddies);
- if (avatar_ids.size() == 0)
- mNearbyList->setCommentText(getString("no_one_near"));
+ // save them to the online and all friends vectors
+ uuid_vec_t& online_friendsp = mOnlineFriendList->getIDs();
+ uuid_vec_t& all_friendsp = mAllFriendList->getIDs();
- return true;
+ all_friendsp.clear();
+ online_friendsp.clear();
+
+ LLFriendCardsManager::folderid_buddies_map_t listMap;
+
+ // *NOTE: For now collectFriendsLists returns data only for Friends/All folder. EXT-694.
+ LLFriendCardsManager::instance().collectFriendsLists(listMap);
+ if (listMap.size() > 0)
+ {
+ lldebugs << "Friends Cards were found, count: " << listMap.begin()->second.size() << llendl;
+ all_friendsp = listMap.begin()->second;
+ }
+ else
+ {
+ lldebugs << "Friends Cards were not found" << llendl;
+ }
+
+ LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin();
+ for (; buddy_it != all_buddies.end(); ++buddy_it)
+ {
+ LLUUID buddy_id = buddy_it->first;
+ if (av_tracker.isBuddyOnline(buddy_id))
+ online_friendsp.push_back(buddy_id);
+ }
+
+ /*
+ * Avatarlists will be hidden by showFriendsAccordionsIfNeeded(), if they do not have items.
+ * But avatarlist can be updated only if it is visible @see LLAvatarList::draw();
+ * So we need to do force update of lists to avoid inconsistency of data and view of avatarlist.
+ */
+ mOnlineFriendList->setDirty(true, !mOnlineFriendList->filterHasMatches());// do force update if list do NOT have items
+ mAllFriendList->setDirty(true, !mAllFriendList->filterHasMatches());
+ //update trash and other buttons according to a selected item
+ updateButtons();
+ showFriendsAccordionsIfNeeded();
}
-bool LLPanelPeople::updateRecentList()
+void LLPanelPeople::updateNearbyList()
{
- std::vector<LLUUID> avatar_ids;
+ if (!mNearbyList)
+ return;
- LLRecentPeople::instance().get(avatar_ids);
-
- if (avatar_ids.size() > 0)
- return mRecentList->updateList(avatar_ids);
+ std::vector<LLVector3d> positions;
- mRecentList->setCommentText(getString("no_people"));
- return true;
+ LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
+ mNearbyList->setDirty();
+
+ DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs());
+ LLActiveSpeakerMgr::instance().update(TRUE);
}
-bool LLPanelPeople::updateGroupList()
+void LLPanelPeople::updateRecentList()
{
- return mGroupList->updateList();
+ if (!mRecentList)
+ return;
+
+ LLRecentPeople::instance().get(mRecentList->getIDs());
+ mRecentList->setDirty();
}
void LLPanelPeople::buttonSetVisible(std::string btn_name, BOOL visible)
{
- // Currently all bottom buttons are wrapped with layout panels.
- // Hiding a button has no effect: the panel still occupies its space.
- // So we have to hide the whole panel (along with its button)
- // to free some space up.
- LLButton* btn = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name);
- LLPanel* btn_parent = dynamic_cast<LLPanel*>(btn->getParent());
- if (btn_parent)
- btn_parent->setVisible(visible);
+ // To make sure we're referencing the right widget (a child of the button bar).
+ LLButton* button = getChild<LLView>("button_bar")->getChild<LLButton>(btn_name);
+ button->setVisible(visible);
}
void LLPanelPeople::buttonSetEnabled(const std::string& btn_name, bool enabled)
@@ -432,79 +780,128 @@ void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_si
void LLPanelPeople::updateButtons()
{
- std::string cur_tab = mTabContainer->getCurrentPanel()->getName();
- bool nearby_tab_active = (cur_tab == "nearby_panel");
- bool friends_tab_active = (cur_tab == "friends_panel");
- bool group_tab_active = (cur_tab == "groups_panel");
- bool recent_tab_active = (cur_tab == "recent_panel");
+ std::string cur_tab = getActiveTabName();
+ bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME);
+ bool friends_tab_active = (cur_tab == FRIENDS_TAB_NAME);
+ bool group_tab_active = (cur_tab == GROUP_TAB_NAME);
+ //bool recent_tab_active = (cur_tab == RECENT_TAB_NAME);
LLUUID selected_id;
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+ bool item_selected = (selected_uuids.size() == 1);
+ bool multiple_selected = (selected_uuids.size() >= 1);
+
buttonSetVisible("group_info_btn", group_tab_active);
buttonSetVisible("chat_btn", group_tab_active);
- buttonSetVisible("add_friend_btn", nearby_tab_active || recent_tab_active);
buttonSetVisible("view_profile_btn", !group_tab_active);
buttonSetVisible("im_btn", !group_tab_active);
- buttonSetVisible("teleport_btn", friends_tab_active || group_tab_active);
- buttonSetVisible("share_btn", !recent_tab_active && false); // not implemented yet
+ buttonSetVisible("call_btn", !group_tab_active);
+ buttonSetVisible("group_call_btn", group_tab_active);
+ buttonSetVisible("teleport_btn", friends_tab_active);
+ buttonSetVisible("share_btn", nearby_tab_active || friends_tab_active);
if (group_tab_active)
{
bool cur_group_active = true;
- selected_id = mGroupList->getCurrentID();
- if (selected_id.notNull())
+ if (item_selected)
+ {
+ selected_id = mGroupList->getSelectedUUID();
cur_group_active = (gAgent.getGroupID() == selected_id);
+ }
- bool item_selected = selected_id.notNull();
LLPanel* groups_panel = mTabContainer->getCurrentPanel();
- groups_panel->childSetEnabled("activate_btn", !item_selected || !cur_group_active); // "none" or a non-active group selected
- groups_panel->childSetEnabled("plus_btn", item_selected);
- groups_panel->childSetEnabled("minus_btn", item_selected);
+ groups_panel->getChildView("activate_btn")->setEnabled(item_selected && !cur_group_active); // "none" or a non-active group selected
+ groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull());
}
else
{
bool is_friend = true;
- LLAvatarList* list;
// Check whether selected avatar is our friend.
- if ((list = getActiveAvatarList()) && (selected_id = list->getCurrentID()).notNull())
+ if (item_selected)
{
+ selected_id = selected_uuids.front();
is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL;
}
- childSetEnabled("add_friend_btn", !is_friend);
+ LLPanel* cur_panel = mTabContainer->getCurrentPanel();
+ if (cur_panel)
+ {
+ cur_panel->getChildView("add_friend_btn")->setEnabled(!is_friend);
+ if (friends_tab_active)
+ {
+ cur_panel->getChildView("del_btn")->setEnabled(multiple_selected);
+ }
+ }
}
- bool item_selected = selected_id.notNull();
- buttonSetEnabled("teleport_btn", friends_tab_active && item_selected);
- buttonSetEnabled("view_profile_btn", item_selected);
- buttonSetEnabled("im_btn", item_selected);
- buttonSetEnabled("call_btn", item_selected && false); // not implemented yet
- buttonSetEnabled("share_btn", item_selected && false); // not implemented yet
- buttonSetEnabled("group_info_btn", item_selected);
- buttonSetEnabled("chat_btn", item_selected);
+ bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled();
+
+ buttonSetEnabled("view_profile_btn",item_selected);
+ buttonSetEnabled("share_btn", item_selected);
+ buttonSetEnabled("im_btn", multiple_selected); // allow starting the friends conference for multiple selection
+ buttonSetEnabled("call_btn", multiple_selected && enable_calls);
+ buttonSetEnabled("teleport_btn", multiple_selected && LLAvatarActions::canOfferTeleport(selected_uuids));
+
+ bool none_group_selected = item_selected && selected_id.isNull();
+ buttonSetEnabled("group_info_btn", !none_group_selected);
+ buttonSetEnabled("group_call_btn", !none_group_selected && enable_calls);
+ buttonSetEnabled("chat_btn", !none_group_selected);
+}
+
+std::string LLPanelPeople::getActiveTabName() const
+{
+ return mTabContainer->getCurrentPanel()->getName();
}
-LLAvatarList* LLPanelPeople::getActiveAvatarList() const
+LLUUID LLPanelPeople::getCurrentItemID() const
{
- std::string cur_tab = mTabContainer->getCurrentPanel()->getName();
+ std::string cur_tab = getActiveTabName();
- if (cur_tab == "friends_panel")
- return mFriendList;
- if (cur_tab == "nearby_panel")
- return mNearbyList;
- if (cur_tab == "recent_panel")
- return mRecentList;
+ if (cur_tab == FRIENDS_TAB_NAME) // this tab has two lists
+ {
+ LLUUID cur_online_friend;
- return NULL;
+ if ((cur_online_friend = mOnlineFriendList->getSelectedUUID()).notNull())
+ return cur_online_friend;
+
+ return mAllFriendList->getSelectedUUID();
+ }
+
+ if (cur_tab == NEARBY_TAB_NAME)
+ return mNearbyList->getSelectedUUID();
+
+ if (cur_tab == RECENT_TAB_NAME)
+ return mRecentList->getSelectedUUID();
+
+ if (cur_tab == GROUP_TAB_NAME)
+ return mGroupList->getSelectedUUID();
+
+ llassert(0 && "unknown tab selected");
+ return LLUUID::null;
}
-LLUUID LLPanelPeople::getCurrentItemID() const
+void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const
{
- LLAvatarList* alist = getActiveAvatarList();
- if (alist)
- return alist->getCurrentID();
- return mGroupList->getCurrentID();
+ std::string cur_tab = getActiveTabName();
+
+ if (cur_tab == FRIENDS_TAB_NAME)
+ {
+ // friends tab has two lists
+ mOnlineFriendList->getSelectedUUIDs(selected_uuids);
+ mAllFriendList->getSelectedUUIDs(selected_uuids);
+ }
+ else if (cur_tab == NEARBY_TAB_NAME)
+ mNearbyList->getSelectedUUIDs(selected_uuids);
+ else if (cur_tab == RECENT_TAB_NAME)
+ mRecentList->getSelectedUUIDs(selected_uuids);
+ else if (cur_tab == GROUP_TAB_NAME)
+ mGroupList->getSelectedUUIDs(selected_uuids);
+ else
+ llassert(0 && "unknown tab selected");
+
}
void LLPanelPeople::showGroupMenu(LLMenuGL* menu)
@@ -526,64 +923,144 @@ void LLPanelPeople::showGroupMenu(LLMenuGL* menu)
LLMenuGL::showPopup(parent_panel, menu, menu_x, menu_y);
}
-void LLPanelPeople::onVisibilityChange(BOOL new_visibility)
+void LLPanelPeople::setSortOrder(LLAvatarList* list, ESortOrder order, bool save)
{
- if (new_visibility == FALSE)
+ switch (order)
{
- // Don't update anything while we're invisible.
- mNearbyListUpdater->setActive(FALSE);
+ case E_SORT_BY_NAME:
+ list->sortByName();
+ break;
+ case E_SORT_BY_STATUS:
+ list->setComparator(&STATUS_COMPARATOR);
+ list->sort();
+ break;
+ case E_SORT_BY_MOST_RECENT:
+ list->setComparator(&RECENT_COMPARATOR);
+ list->sort();
+ break;
+ case E_SORT_BY_RECENT_SPEAKERS:
+ list->setComparator(&RECENT_SPEAKER_COMPARATOR);
+ list->sort();
+ break;
+ case E_SORT_BY_DISTANCE:
+ list->setComparator(&DISTANCE_COMPARATOR);
+ list->sort();
+ break;
+ default:
+ llwarns << "Unrecognized people sort order for " << list->getName() << llendl;
+ return;
}
- else
+
+ if (save)
{
- // Make the tab-container re-select current tab
- // for onTabSelected() callback to get called.
- // (currently this is needed to reactivate nearby list updates
- // when we get visible)
- mTabContainer->selectTab(mTabContainer->getCurrentPanelIndex());
+ std::string setting;
+
+ if (list == mAllFriendList || list == mOnlineFriendList)
+ setting = "FriendsSortOrder";
+ else if (list == mRecentList)
+ setting = "RecentPeopleSortOrder";
+ else if (list == mNearbyList)
+ setting = "NearbyPeopleSortOrder";
+
+ if (!setting.empty())
+ gSavedSettings.setU32(setting, order);
}
}
-void LLPanelPeople::onSearchEdit(const std::string& search_string)
+bool LLPanelPeople::isRealGroup()
{
- if (mFilterSubString == search_string)
+ return getCurrentItemID() != LLUUID::null;
+}
+
+void LLPanelPeople::onFilterEdit(const std::string& search_string)
+{
+ mFilterSubStringOrig = search_string;
+ LLStringUtil::trimHead(mFilterSubStringOrig);
+ // Searches are case-insensitive
+ std::string search_upper = mFilterSubStringOrig;
+ LLStringUtil::toUpper(search_upper);
+
+ if (mFilterSubString == search_upper)
return;
- mFilterSubString = search_string;
+ mFilterSubString = search_upper;
- LLStringUtil::toUpper(mFilterSubString);
- LLStringUtil::trimHead(mFilterSubString);
- mSearchEditor->setText(mFilterSubString);
+ //store accordion tabs state before any manipulation with accordion tabs
+ if(!mFilterSubString.empty())
+ {
+ notifyChildren(LLSD().with("action","store_state"));
+ }
+
+
+ // Apply new filter.
+ mNearbyList->setNameFilter(mFilterSubStringOrig);
+ mOnlineFriendList->setNameFilter(mFilterSubStringOrig);
+ mAllFriendList->setNameFilter(mFilterSubStringOrig);
+ mRecentList->setNameFilter(mFilterSubStringOrig);
+ mGroupList->setNameFilter(mFilterSubStringOrig);
+
+ setAccordionCollapsedByUser("tab_online", false);
+ setAccordionCollapsedByUser("tab_all", false);
+
+ showFriendsAccordionsIfNeeded();
+
+ //restore accordion tabs state _after_ all manipulations...
+ if(mFilterSubString.empty())
+ {
+ notifyChildren(LLSD().with("action","restore_state"));
+ }
}
void LLPanelPeople::onTabSelected(const LLSD& param)
{
std::string tab_name = getChild<LLPanel>(param.asString())->getName();
- mNearbyListUpdater->setActive(tab_name == "nearby_panel");
updateButtons();
+
+ showFriendsAccordionsIfNeeded();
+
+ if (GROUP_TAB_NAME == tab_name)
+ mFilterEditor->setLabel(getString("groups_filter_label"));
+ else
+ mFilterEditor->setLabel(getString("people_filter_label"));
}
-void LLPanelPeople::onAvatarListDoubleClicked(LLAvatarList* list)
+void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl)
{
- LLUUID clicked_id = list->getCurrentID();
-
- if (clicked_id.isNull())
+ LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl);
+ if(!item)
+ {
return;
+ }
- // Open mini-inspector for the avatar being clicked
- LLFloaterReg::showInstance("mini_inspector", clicked_id);
- // inspector will delete itself on close
+ LLUUID clicked_id = item->getAvatarId();
+
+#if 0 // SJB: Useful for testing, but not currently functional or to spec
+ LLAvatarActions::showProfile(clicked_id);
+#else // spec says open IM window
+ LLAvatarActions::startIM(clicked_id);
+#endif
}
void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list)
{
- (void) list;
+ // Make sure only one of the friends lists (online/all) has selection.
+ if (getActiveTabName() == FRIENDS_TAB_NAME)
+ {
+ if (list == mOnlineFriendList)
+ mAllFriendList->resetSelection(true);
+ else if (list == mAllFriendList)
+ mOnlineFriendList->resetSelection(true);
+ else
+ llassert(0 && "commit on unknown friends list");
+ }
+
updateButtons();
}
void LLPanelPeople::onViewProfileButtonClicked()
{
LLUUID id = getCurrentItemID();
- LLFriendActions::showProfile(id);
+ LLAvatarActions::showProfile(id);
}
void LLPanelPeople::onAddFriendButtonClicked()
@@ -591,16 +1068,32 @@ void LLPanelPeople::onAddFriendButtonClicked()
LLUUID id = getCurrentItemID();
if (id.notNull())
{
- std::string name;
- gCacheName->getFullName(id, name);
- LLFriendActions::requestFriendshipDialog(id, name);
+ LLAvatarActions::requestFriendshipDialog(id);
+ }
+}
+
+bool LLPanelPeople::isItemsFreeOfFriends(const uuid_vec_t& uuids)
+{
+ const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ for ( uuid_vec_t::const_iterator
+ id = uuids.begin(),
+ id_end = uuids.end();
+ id != id_end; ++id )
+ {
+ if (av_tracker.isBuddy (*id))
+ {
+ return false;
+ }
}
+ return true;
}
void LLPanelPeople::onAddFriendWizButtonClicked()
{
// Show add friend wizard.
- LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(onAvatarPicked, NULL, FALSE, TRUE);
+ LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelPeople::onAvatarPicked, _1, _2), FALSE, TRUE);
+ // Need to disable 'ok' button when friend occurs in selection
+ if (picker) picker->setOkBtnEnableCb(boost::bind(&LLPanelPeople::isItemsFreeOfFriends, this, _1));
LLFloater* root_floater = gFloaterView->getParentFloater(this);
if (root_floater)
{
@@ -610,60 +1103,69 @@ void LLPanelPeople::onAddFriendWizButtonClicked()
void LLPanelPeople::onDeleteFriendButtonClicked()
{
- LLFriendActions::removeFriendDialog(getCurrentItemID());
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+
+ if (selected_uuids.size() == 1)
+ {
+ LLAvatarActions::removeFriendDialog( selected_uuids.front() );
+ }
+ else if (selected_uuids.size() > 1)
+ {
+ LLAvatarActions::removeFriendsDialog( selected_uuids );
+ }
}
void LLPanelPeople::onGroupInfoButtonClicked()
{
- LLUUID group_id = getCurrentItemID();
- if (group_id.notNull())
- LLGroupActions::info(group_id);
+ LLGroupActions::show(getCurrentItemID());
}
void LLPanelPeople::onChatButtonClicked()
{
LLUUID group_id = getCurrentItemID();
if (group_id.notNull())
- LLGroupActions::startChat(group_id);
+ LLGroupActions::startIM(group_id);
}
void LLPanelPeople::onImButtonClicked()
{
- LLUUID id = getCurrentItemID();
- if (id.notNull())
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+ if ( selected_uuids.size() == 1 )
+ {
+ // if selected only one person then start up IM
+ LLAvatarActions::startIM(selected_uuids.at(0));
+ }
+ else if ( selected_uuids.size() > 1 )
{
- LLFriendActions::startIM(id);
+ // for multiple selection start up friends conference
+ LLAvatarActions::startConference(selected_uuids);
}
}
void LLPanelPeople::onActivateButtonClicked()
{
- LLGroupActions::activate(mGroupList->getCurrentID());
+ LLGroupActions::activate(mGroupList->getSelectedUUID());
}
// static
void LLPanelPeople::onAvatarPicked(
const std::vector<std::string>& names,
- const std::vector<LLUUID>& ids,
- void*)
+ const uuid_vec_t& ids)
{
if (!names.empty() && !ids.empty())
- LLFriendActions::requestFriendshipDialog(ids[0], names[0]);
-}
-
-bool LLPanelPeople::onFriendListUpdate(U32 changed_mask)
-{
- bool have_names = updateFriendList(changed_mask);
-
- // Update online status in the Recent tab.
- // *TODO: isn't it too much to update the whole list?
- updateRecentList();
-
- return have_names;
+ LLAvatarActions::requestFriendshipDialog(ids[0], names[0]);
}
void LLPanelPeople::onGroupPlusButtonClicked()
{
+ if (!gAgent.canJoinGroups())
+ {
+ LLNotificationsUtil::add("JoinedTooManyGroups");
+ return;
+ }
+
LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get();
if (!plus_menu)
return;
@@ -673,11 +1175,9 @@ void LLPanelPeople::onGroupPlusButtonClicked()
void LLPanelPeople::onGroupMinusButtonClicked()
{
- LLMenuGL* minus_menu = (LLMenuGL*)mGroupMinusMenuHandle.get();
- if (!minus_menu)
- return;
-
- showGroupMenu(minus_menu);
+ LLUUID group_id = getCurrentItemID();
+ if (group_id.notNull())
+ LLGroupActions::leave(group_id);
}
void LLPanelPeople::onGroupPlusMenuItemClicked(const LLSD& userdata)
@@ -687,35 +1187,159 @@ void LLPanelPeople::onGroupPlusMenuItemClicked(const LLSD& userdata)
if (chosen_item == "join_group")
LLGroupActions::search();
else if (chosen_item == "new_group")
- LLGroupActions::create();
+ LLGroupActions::createGroup();
}
-void LLPanelPeople::onGroupMinusMenuItemClicked(const LLSD& userdata)
+void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata)
{
std::string chosen_item = userdata.asString();
- LLUUID group_id = getCurrentItemID();
- if (chosen_item == "leave_group")
- LLGroupActions::leave(group_id);
- /*
- else if (chosen_item == "delete_group")
- ; // *TODO: how to delete a group?
- */
+ if (chosen_item == "sort_name")
+ {
+ setSortOrder(mAllFriendList, E_SORT_BY_NAME);
+ }
+ else if (chosen_item == "sort_status")
+ {
+ setSortOrder(mAllFriendList, E_SORT_BY_STATUS);
+ }
+ else if (chosen_item == "view_icons")
+ {
+ mAllFriendList->toggleIcons();
+ mOnlineFriendList->toggleIcons();
+ }
+ else if (chosen_item == "view_permissions")
+ {
+ bool show_permissions = !gSavedSettings.getBOOL("FriendsListShowPermissions");
+ gSavedSettings.setBOOL("FriendsListShowPermissions", show_permissions);
+
+ mAllFriendList->showPermissions(show_permissions);
+ mOnlineFriendList->showPermissions(show_permissions);
+ }
+}
+
+void LLPanelPeople::onGroupsViewSortMenuItemClicked(const LLSD& userdata)
+{
+ std::string chosen_item = userdata.asString();
+
+ if (chosen_item == "show_icons")
+ {
+ mGroupList->toggleIcons();
+ }
+}
+
+void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata)
+{
+ std::string chosen_item = userdata.asString();
+
+ if (chosen_item == "sort_by_recent_speakers")
+ {
+ setSortOrder(mNearbyList, E_SORT_BY_RECENT_SPEAKERS);
+ }
+ else if (chosen_item == "sort_name")
+ {
+ setSortOrder(mNearbyList, E_SORT_BY_NAME);
+ }
+ else if (chosen_item == "view_icons")
+ {
+ mNearbyList->toggleIcons();
+ }
+ else if (chosen_item == "sort_distance")
+ {
+ setSortOrder(mNearbyList, E_SORT_BY_DISTANCE);
+ }
+}
+
+bool LLPanelPeople::onNearbyViewSortMenuItemCheck(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ U32 sort_order = gSavedSettings.getU32("NearbyPeopleSortOrder");
+
+ if (item == "sort_by_recent_speakers")
+ return sort_order == E_SORT_BY_RECENT_SPEAKERS;
+ if (item == "sort_name")
+ return sort_order == E_SORT_BY_NAME;
+ if (item == "sort_distance")
+ return sort_order == E_SORT_BY_DISTANCE;
+
+ return false;
+}
+
+void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata)
+{
+ std::string chosen_item = userdata.asString();
+
+ if (chosen_item == "sort_recent")
+ {
+ setSortOrder(mRecentList, E_SORT_BY_MOST_RECENT);
+ }
+ else if (chosen_item == "sort_name")
+ {
+ setSortOrder(mRecentList, E_SORT_BY_NAME);
+ }
+ else if (chosen_item == "view_icons")
+ {
+ mRecentList->toggleIcons();
+ }
+}
+
+bool LLPanelPeople::onFriendsViewSortMenuItemCheck(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ U32 sort_order = gSavedSettings.getU32("FriendsSortOrder");
+
+ if (item == "sort_name")
+ return sort_order == E_SORT_BY_NAME;
+ if (item == "sort_status")
+ return sort_order == E_SORT_BY_STATUS;
+
+ return false;
+}
+
+bool LLPanelPeople::onRecentViewSortMenuItemCheck(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ U32 sort_order = gSavedSettings.getU32("RecentPeopleSortOrder");
+
+ if (item == "sort_recent")
+ return sort_order == E_SORT_BY_MOST_RECENT;
+ if (item == "sort_name")
+ return sort_order == E_SORT_BY_NAME;
+
+ return false;
}
void LLPanelPeople::onCallButtonClicked()
{
- // *TODO: not implemented yet
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+
+ if (selected_uuids.size() == 1)
+ {
+ // initiate a P2P voice chat with the selected user
+ LLAvatarActions::startCall(getCurrentItemID());
+ }
+ else if (selected_uuids.size() > 1)
+ {
+ // initiate an ad-hoc voice chat with multiple users
+ LLAvatarActions::startAdhocCall(selected_uuids);
+ }
+}
+
+void LLPanelPeople::onGroupCallButtonClicked()
+{
+ LLGroupActions::startCall(getCurrentItemID());
}
void LLPanelPeople::onTeleportButtonClicked()
{
- LLFriendActions::offerTeleport(getCurrentItemID());
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+ LLAvatarActions::offerTeleport(selected_uuids);
}
void LLPanelPeople::onShareButtonClicked()
{
- // *TODO: not implemented yet
+ LLAvatarActions::share(getCurrentItemID());
}
void LLPanelPeople::onMoreButtonClicked()
@@ -723,7 +1347,162 @@ void LLPanelPeople::onMoreButtonClicked()
// *TODO: not implemented yet
}
+void LLPanelPeople::onFriendsViewSortButtonClicked()
+{
+ LLMenuGL* menu = (LLMenuGL*)mFriendsViewSortMenuHandle.get();
+ if (!menu)
+ return;
+ showGroupMenu(menu);
+}
+
+void LLPanelPeople::onGroupsViewSortButtonClicked()
+{
+ LLMenuGL* menu = (LLMenuGL*)mGroupsViewSortMenuHandle.get();
+ if (!menu)
+ return;
+ showGroupMenu(menu);
+}
+
+void LLPanelPeople::onRecentViewSortButtonClicked()
+{
+ LLMenuGL* menu = (LLMenuGL*)mRecentViewSortMenuHandle.get();
+ if (!menu)
+ return;
+ showGroupMenu(menu);
+}
+
+void LLPanelPeople::onNearbyViewSortButtonClicked()
+{
+ LLMenuGL* menu = (LLMenuGL*)mNearbyViewSortMenuHandle.get();
+ if (!menu)
+ return;
+ showGroupMenu(menu);
+}
+
void LLPanelPeople::onOpen(const LLSD& key)
{
- mTabContainer->selectTab(key.asInteger());
+ std::string tab_name = key["people_panel_tab_name"];
+ mFilterEditor -> clear();
+ onFilterEdit("");
+
+ if (!tab_name.empty())
+ mTabContainer->selectTabByName(tab_name);
}
+
+bool LLPanelPeople::notifyChildren(const LLSD& info)
+{
+ if (info.has("task-panel-action") && info["task-panel-action"].asString() == "handle-tri-state")
+ {
+ LLSideTrayPanelContainer* container = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
+ if (!container)
+ {
+ llwarns << "Cannot find People panel container" << llendl;
+ return true;
+ }
+
+ if (container->getCurrentPanelIndex() > 0)
+ {
+ // if not on the default panel, switch to it
+ container->onOpen(LLSD().with(LLSideTrayPanelContainer::PARAM_SUB_PANEL_NAME, getName()));
+ }
+ else
+ LLSideTray::getInstance()->collapseSideBar();
+
+ return true; // this notification is only supposed to be handled by task panels
+ }
+
+ return LLPanel::notifyChildren(info);
+}
+
+void LLPanelPeople::showAccordion(const std::string name, bool show)
+{
+ if(name.empty())
+ {
+ llwarns << "No name provided" << llendl;
+ return;
+ }
+
+ LLAccordionCtrlTab* tab = getChild<LLAccordionCtrlTab>(name);
+ tab->setVisible(show);
+ if(show)
+ {
+ // don't expand accordion if it was collapsed by user
+ if(!isAccordionCollapsedByUser(tab))
+ {
+ // expand accordion
+ tab->changeOpenClose(false);
+ }
+ }
+}
+
+void LLPanelPeople::showFriendsAccordionsIfNeeded()
+{
+ if(FRIENDS_TAB_NAME == getActiveTabName())
+ {
+ // Expand and show accordions if needed, else - hide them
+ showAccordion("tab_online", mOnlineFriendList->filterHasMatches());
+ showAccordion("tab_all", mAllFriendList->filterHasMatches());
+
+ // Rearrange accordions
+ LLAccordionCtrl* accordion = getChild<LLAccordionCtrl>("friends_accordion");
+ accordion->arrange();
+
+ // *TODO: new no_matched_tabs_text attribute was implemented in accordion (EXT-7368).
+ // this code should be refactored to use it
+ // keep help text in a synchronization with accordions visibility.
+ updateFriendListHelpText();
+ }
+}
+
+void LLPanelPeople::onFriendListRefreshComplete(LLUICtrl*ctrl, const LLSD& param)
+{
+ if(ctrl == mOnlineFriendList)
+ {
+ showAccordion("tab_online", param.asInteger());
+ }
+ else if(ctrl == mAllFriendList)
+ {
+ showAccordion("tab_all", param.asInteger());
+ }
+}
+
+void LLPanelPeople::setAccordionCollapsedByUser(LLUICtrl* acc_tab, bool collapsed)
+{
+ if(!acc_tab)
+ {
+ llwarns << "Invalid parameter" << llendl;
+ return;
+ }
+
+ LLSD param = acc_tab->getValue();
+ param[COLLAPSED_BY_USER] = collapsed;
+ acc_tab->setValue(param);
+}
+
+void LLPanelPeople::setAccordionCollapsedByUser(const std::string& name, bool collapsed)
+{
+ setAccordionCollapsedByUser(getChild<LLUICtrl>(name), collapsed);
+}
+
+bool LLPanelPeople::isAccordionCollapsedByUser(LLUICtrl* acc_tab)
+{
+ if(!acc_tab)
+ {
+ llwarns << "Invalid parameter" << llendl;
+ return false;
+ }
+
+ LLSD param = acc_tab->getValue();
+ if(!param.has(COLLAPSED_BY_USER))
+ {
+ return false;
+ }
+ return param[COLLAPSED_BY_USER].asBoolean();
+}
+
+bool LLPanelPeople::isAccordionCollapsedByUser(const std::string& name)
+{
+ return isAccordionCollapsedByUser(getChild<LLUICtrl>(name));
+}
+
+// EOF