diff options
Diffstat (limited to 'indra/newview/llpanelpeople.cpp')
-rw-r--r-- | indra/newview/llpanelpeople.cpp | 1507 |
1 files changed, 1507 insertions, 0 deletions
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp new file mode 100644 index 0000000000..040b5319b9 --- /dev/null +++ b/indra/newview/llpanelpeople.cpp @@ -0,0 +1,1507 @@ +/** + * @file llpanelpeople.cpp + * @brief Side tray "People" panel + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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" + +// libs +#include "llavatarname.h" +#include "llfloaterreg.h" +#include "llmenugl.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 "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" +#include "llspeakers.h" + +#define FRIEND_LIST_UPDATE_TIMEOUT 0.5 +#define NEARBY_LIST_UPDATE_INTERVAL 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<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: + void updateList() + { + mCallback(); + } + + callback_t mCallback; +}; + +class LLAvatarListUpdater : public LLPanelPeople::Updater, public LLEventTimer +{ +public: + LLAvatarListUpdater(callback_t cb, F32 period) + : LLEventTimer(period), + LLPanelPeople::Updater(cb) + { + 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 changed(U32 mask) + { + 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 (!mIsActive) return FALSE; + + if (mMask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE)) + { + 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*/ void setActive(bool val) + { + if (val) + { + // update immediately and start regular updates + updateList(); + mEventTimer.start(); + } + else + { + // stop regular updates + mEventTimer.stop(); + } + } + + /*virtual*/ BOOL tick() + { + updateList(); + return FALSE; + } +private: +}; + +/** + * Updates the recent people list (those the agent has recently interacted with). + */ +class LLRecentListUpdater : public LLAvatarListUpdater, public boost::signals2::trackable +{ + LOG_CLASS(LLRecentListUpdater); + +public: + LLRecentListUpdater(callback_t cb) + : LLAvatarListUpdater(cb, 0) + { + LLRecentPeople::instance().setChangedCallback(boost::bind(&LLRecentListUpdater::updateList, this)); + } +}; + +//============================================================================= + +LLPanelPeople::LLPanelPeople() + : LLPanel(), + mFilterSubString(LLStringUtil::null), + mFilterSubStringOrig(LLStringUtil::null), + mFilterEditor(NULL), + mTabContainer(NULL), + mOnlineFriendList(NULL), + mAllFriendList(NULL), + mNearbyList(NULL), + mRecentList(NULL), + mGroupList(NULL) +{ + mFriendListUpdater = new LLFriendListUpdater(boost::bind(&LLPanelPeople::updateFriendList, this)); + mNearbyListUpdater = new LLNearbyListUpdater(boost::bind(&LLPanelPeople::updateNearbyList, this)); + mRecentListUpdater = new LLRecentListUpdater(boost::bind(&LLPanelPeople::updateRecentList, this)); + mCommitCallbackRegistrar.add("People.addFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); +} + +LLPanelPeople::~LLPanelPeople() +{ + delete mNearbyListUpdater; + delete mFriendListUpdater; + delete mRecentListUpdater; + + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver(this); + } + + LLView::deleteViewByHandle(mGroupPlusMenuHandle); + 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() +{ + 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)); + + 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); + + 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)); + + 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)); + + 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)); + + // 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("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)); + + 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)); + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + LLVoiceClient::getInstance()->addObserver(this); + + // call this method in case some list is empty and buttons can be in inconsistent state + updateButtons(); + + mOnlineFriendList->setRefreshCompleteCallback(boost::bind(&LLPanelPeople::onFriendListRefreshComplete, this, _1, _2)); + mAllFriendList->setRefreshCompleteCallback(boost::bind(&LLPanelPeople::onFriendListRefreshComplete, this, _1, _2)); + + return TRUE; +} + +// virtual +void LLPanelPeople::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) + { + return; + } + + updateButtons(); +} + +void LLPanelPeople::updateFriendListHelpText() +{ + // 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)); + } +} + +void LLPanelPeople::updateFriendList() +{ + if (!mOnlineFriendList || !mAllFriendList) + return; + + // get all buddies we know about + const LLAvatarTracker& av_tracker = LLAvatarTracker::instance(); + LLAvatarTracker::buddy_map_t all_buddies; + av_tracker.copyBuddyList(all_buddies); + + // save them to the online and all friends vectors + uuid_vec_t& online_friendsp = mOnlineFriendList->getIDs(); + uuid_vec_t& all_friendsp = mAllFriendList->getIDs(); + + 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(); +} + +void LLPanelPeople::updateNearbyList() +{ + if (!mNearbyList) + return; + + std::vector<LLVector3d> positions; + + LLWorld::getInstance()->getAvatars(&mNearbyList->getIDs(), &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); + mNearbyList->setDirty(); + + DISTANCE_COMPARATOR.updateAvatarsPositions(positions, mNearbyList->getIDs()); + LLActiveSpeakerMgr::instance().update(TRUE); +} + +void LLPanelPeople::updateRecentList() +{ + if (!mRecentList) + return; + + LLRecentPeople::instance().get(mRecentList->getIDs()); + mRecentList->setDirty(); +} + +void LLPanelPeople::buttonSetVisible(std::string btn_name, BOOL 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) +{ + // 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->setEnabled(enabled); +} + +void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_signal_t::slot_type& cb) +{ + // 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->setClickedCallback(cb); +} + +void LLPanelPeople::updateButtons() +{ + 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("view_profile_btn", !group_tab_active); + buttonSetVisible("im_btn", !group_tab_active); + 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; + + if (item_selected) + { + selected_id = mGroupList->getSelectedUUID(); + cur_group_active = (gAgent.getGroupID() == selected_id); + } + + LLPanel* groups_panel = mTabContainer->getCurrentPanel(); + 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; + + // Check whether selected avatar is our friend. + if (item_selected) + { + selected_id = selected_uuids.front(); + is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL; + } + + 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 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(); +} + +LLUUID LLPanelPeople::getCurrentItemID() const +{ + std::string cur_tab = getActiveTabName(); + + if (cur_tab == FRIENDS_TAB_NAME) // this tab has two lists + { + LLUUID cur_online_friend; + + 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; +} + +void LLPanelPeople::getCurrentItemIDs(uuid_vec_t& selected_uuids) const +{ + 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) +{ + // Shows the menu at the top of the button bar. + + // Calculate its coordinates. + // (assumes that groups panel is the current tab) + LLPanel* bottom_panel = mTabContainer->getCurrentPanel()->getChild<LLPanel>("bottom_panel"); + LLPanel* parent_panel = mTabContainer->getCurrentPanel(); + menu->arrangeAndClear(); + S32 menu_height = menu->getRect().getHeight(); + S32 menu_x = -2; // *HACK: compensates HPAD in showPopup() + S32 menu_y = bottom_panel->getRect().mTop + menu_height; + + // Actually show the menu. + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(parent_panel, menu, menu_x, menu_y); +} + +void LLPanelPeople::setSortOrder(LLAvatarList* list, ESortOrder order, bool save) +{ + switch (order) + { + 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; + } + + if (save) + { + 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); + } +} + +bool LLPanelPeople::isRealGroup() +{ + 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_upper; + + //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(); + updateButtons(); + + showFriendsAccordionsIfNeeded(); + + if (GROUP_TAB_NAME == tab_name) + mFilterEditor->setLabel(getString("groups_filter_label")); + else + mFilterEditor->setLabel(getString("people_filter_label")); +} + +void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) +{ + LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl); + if(!item) + { + return; + } + + 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) +{ + // 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(); + LLAvatarActions::showProfile(id); +} + +void LLPanelPeople::onAddFriendButtonClicked() +{ + LLUUID id = getCurrentItemID(); + if (id.notNull()) + { + 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(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) + { + root_floater->addDependentFloater(picker); + } +} + +void LLPanelPeople::onDeleteFriendButtonClicked() +{ + 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() +{ + LLGroupActions::show(getCurrentItemID()); +} + +void LLPanelPeople::onChatButtonClicked() +{ + LLUUID group_id = getCurrentItemID(); + if (group_id.notNull()) + LLGroupActions::startIM(group_id); +} + +void LLPanelPeople::onImButtonClicked() +{ + 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 ) + { + // for multiple selection start up friends conference + LLAvatarActions::startConference(selected_uuids); + } +} + +void LLPanelPeople::onActivateButtonClicked() +{ + LLGroupActions::activate(mGroupList->getSelectedUUID()); +} + +// static +void LLPanelPeople::onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName> names) +{ + if (!names.empty() && !ids.empty()) + LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); +} + +void LLPanelPeople::onGroupPlusButtonClicked() +{ + if (!gAgent.canJoinGroups()) + { + LLNotificationsUtil::add("JoinedTooManyGroups"); + return; + } + + LLMenuGL* plus_menu = (LLMenuGL*)mGroupPlusMenuHandle.get(); + if (!plus_menu) + return; + + showGroupMenu(plus_menu); +} + +void LLPanelPeople::onGroupMinusButtonClicked() +{ + LLUUID group_id = getCurrentItemID(); + if (group_id.notNull()) + LLGroupActions::leave(group_id); +} + +void LLPanelPeople::onGroupPlusMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + if (chosen_item == "join_group") + LLGroupActions::search(); + else if (chosen_item == "new_group") + LLGroupActions::createGroup(); +} + +void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); + + 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() +{ + 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() +{ + uuid_vec_t selected_uuids; + getCurrentItemIDs(selected_uuids); + LLAvatarActions::offerTeleport(selected_uuids); +} + +void LLPanelPeople::onShareButtonClicked() +{ + LLAvatarActions::share(getCurrentItemID()); +} + +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) +{ + 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 |