diff options
| author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
|---|---|---|
| committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
| commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
| tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llpanelpeople.cpp | |
| parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
| parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) | |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
Diffstat (limited to 'indra/newview/llpanelpeople.cpp')
| -rw-r--r-- | indra/newview/llpanelpeople.cpp | 3138 |
1 files changed, 1569 insertions, 1569 deletions
diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index da2206aefb..68464d59bd 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -1,1569 +1,1569 @@ -/** - * @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 "llconversationview.h" -#include "llfloaterimcontainer.h" -#include "llfloaterreg.h" -#include "llfloatersidepanelcontainer.h" -#include "llmenubutton.h" -#include "llmenugl.h" -#include "llnotificationsutil.h" -#include "lleventtimer.h" -#include "llfiltereditor.h" -#include "lltabcontainer.h" -#include "lltoggleablemenu.h" -#include "lluictrlfactory.h" - -#include "llpanelpeople.h" - -// newview -#include "llaccordionctrl.h" -#include "llaccordionctrltab.h" -#include "llagent.h" -#include "llagentbenefits.h" -#include "llavataractions.h" -#include "llavatarlist.h" -#include "llavatarlistitem.h" -#include "llavatarnamecache.h" -#include "llcallingcard.h" // for LLAvatarTracker -#include "llcallbacklist.h" -#include "llerror.h" -#include "llfloateravatarpicker.h" -#include "llfriendcard.h" -#include "llgroupactions.h" -#include "llgrouplist.h" -#include "llinventoryobserver.h" -#include "llnetmap.h" -#include "llpanelpeoplemenus.h" -#include "llparticipantlist.h" -#include "llsidetraypanelcontainer.h" -#include "llrecentpeople.h" -#include "llviewercontrol.h" // for gSavedSettings -#include "llviewermenu.h" // for gMenuHolder -#include "llviewerregion.h" -#include "llvoiceclient.h" -#include "llworld.h" -#include "llspeakers.h" -#include "llfloaterwebcontent.h" - -#include "llagentui.h" -#include "llslurl.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 BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars -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; - - return dist_vec_squared(item1_pos, me_pos) < dist_vec_squared(item2_pos, me_pos); - } -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); - } -}; - -class LLAvatarItemRecentArrivalComparator : public LLAvatarItemNameComparator -{ -public: - LLAvatarItemRecentArrivalComparator() {}; - virtual ~LLAvatarItemRecentArrivalComparator() {}; - -protected: - virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const - { - - F32 arr_time1 = LLRecentPeople::instance().getArrivalTimeByID(item1->getAvatarId()); - F32 arr_time2 = LLRecentPeople::instance().getArrivalTimeByID(item2->getAvatarId()); - - if (arr_time1 == arr_time2) - { - std::string name1 = item1->getAvatarName(); - std::string name2 = item2->getAvatarName(); - - LLStringUtil::toUpper(name1); - LLStringUtil::toUpper(name2); - - return name1 < name2; - } - - return arr_time1 > arr_time2; - } -}; - -static const LLAvatarItemRecentComparator RECENT_COMPARATOR; -static const LLAvatarItemStatusComparator STATUS_COMPARATOR; -static LLAvatarItemDistanceComparator DISTANCE_COMPARATOR; -static const LLAvatarItemRecentSpeakerComparator RECENT_SPEAKER_COMPARATOR; -static LLAvatarItemRecentArrivalComparator RECENT_ARRIVAL_COMPARATOR; - -static LLPanelInjector<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 update() - { - mCallback(); - } - - callback_t mCallback; -}; - -/** - * Update buttons on changes in our friend relations (STORM-557). - */ -class LLButtonsUpdater : public LLPanelPeople::Updater, public LLFriendObserver -{ -public: - LLButtonsUpdater(callback_t cb) - : LLPanelPeople::Updater(cb) - { - LLAvatarTracker::instance().addObserver(this); - } - - ~LLButtonsUpdater() - { - LLAvatarTracker::instance().removeObserver(this); - } - - /*virtual*/ void changed(U32 mask) - { - (void) mask; - update(); - } -}; - -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; - if (LLVoiceClient::instanceExists()) - { - 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)) - { - update(); - } - - // 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) - { - LL_DEBUGS() << "Inventory changed: " << mask << LL_ENDL; - - static bool synchronize_friends_folders = true; - if (synchronize_friends_folders) - { - // Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" folder, - // fetches their contents if needed and synchronizes it with buddies list. - // If the folders are not found they are created. - LLFriendCardsManager::instance().syncFriendCardsFolders(); - synchronize_friends_folders = false; - } - - // *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) - { - LL_DEBUGS() << "Calling card added: count: " << gInventory.getChangedIDs().size() - << ", first Inventory ID: "<< (*gInventory.getChangedIDs().begin()) - << LL_ENDL; - - 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) - { - LL_DEBUGS() << "friend found, panel should be updated" << LL_ENDL; - 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 - update(); - mEventTimer.start(); - } - else - { - // stop regular updates - mEventTimer.stop(); - } - } - - /*virtual*/ bool tick() - { - update(); - 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::update, this)); - } -}; - -//============================================================================= - -LLPanelPeople::LLPanelPeople() - : LLPanel(), - mTabContainer(NULL), - mOnlineFriendList(NULL), - mAllFriendList(NULL), - mNearbyList(NULL), - mRecentList(NULL), - mGroupList(NULL), - mMiniMap(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)); - mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this)); - - mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this)); - mCommitCallbackRegistrar.add("People.AddFriendWizard", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); - mCommitCallbackRegistrar.add("People.DelFriend", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); - mCommitCallbackRegistrar.add("People.Group.Minus", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this)); - mCommitCallbackRegistrar.add("People.Chat", boost::bind(&LLPanelPeople::onChatButtonClicked, this)); - mCommitCallbackRegistrar.add("People.Gear", boost::bind(&LLPanelPeople::onGearButtonClicked, this, _1)); - - mCommitCallbackRegistrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); - mCommitCallbackRegistrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); - mCommitCallbackRegistrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); - mCommitCallbackRegistrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2)); - mCommitCallbackRegistrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); - - mEnableCallbackRegistrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2)); - mEnableCallbackRegistrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2)); - mEnableCallbackRegistrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2)); - - mEnableCallbackRegistrar.add("People.Group.Plus.Validate", boost::bind(&LLPanelPeople::onGroupPlusButtonValidate, this)); - - doPeriodically(boost::bind(&LLPanelPeople::updateNearbyArrivalTime, this), 2.0); -} - -LLPanelPeople::~LLPanelPeople() -{ - delete mButtonsUpdater; - delete mNearbyListUpdater; - delete mFriendListUpdater; - delete mRecentListUpdater; - - mNearbyFilterCommitConnection.disconnect(); - mFriedsFilterCommitConnection.disconnect(); - mGroupsFilterCommitConnection.disconnect(); - mRecentFilterCommitConnection.disconnect(); - - if(LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(this); - } -} - -void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list) -{ - if(!avatar_list) - { - LL_ERRS() << "Bad parameter" << LL_ENDL; - return; - } - - bool expanded = param.asBoolean(); - - setAccordionCollapsedByUser(ctrl, !expanded); - if(!expanded) - { - avatar_list->resetSelection(); - } -} - - -void LLPanelPeople::removePicker() -{ - if(mPicker.get()) - { - mPicker.get()->closeFloater(); - } -} - -bool LLPanelPeople::postBuild() -{ - S32 max_premium = LLAgentBenefitsMgr::get("Premium").getGroupMembershipLimit(); - - mNearbyFilterCommitConnection = getChild<LLFilterEditor>("nearby_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); - mFriedsFilterCommitConnection = getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); - mGroupsFilterCommitConnection = getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); - mRecentFilterCommitConnection = getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2)); - - if(LLAgentBenefitsMgr::current().getGroupMembershipLimit() < max_premium) - { - getChild<LLTextBox>("groupcount")->setText(getString("GroupCountWithInfo")); - getChild<LLTextBox>("groupcount")->setURLClickedCallback(boost::bind(&LLPanelPeople::onGroupLimitInfo, this)); - } - - mTabContainer = getChild<LLTabContainer>("tabs"); - mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); - mSavedFilters.resize(mTabContainer->getTabCount()); - mSavedOriginalFilters.resize(mTabContainer->getTabCount()); - - 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)); - friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::removePicker, this)); - - 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"); - mOnlineFriendList->setShowCompleteName(!gSavedSettings.getBOOL("FriendsListHideUsernames")); - mAllFriendList->setNoItemsCommentText(getString("no_friends")); - mAllFriendList->setShowIcons("FriendsListShowIcons"); - mAllFriendList->showPermissions("FriendsListShowPermissions"); - mAllFriendList->setShowCompleteName(!gSavedSettings.getBOOL("FriendsListHideUsernames")); - - 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"); - mNearbyList->setShowCompleteName(!gSavedSettings.getBOOL("NearbyListHideUsernames")); - mMiniMap = (LLNetMap*)getChildView("Net Map",true); - mMiniMap->setToolTipMsg(gSavedSettings.getBOOL("DoubleClickTeleport") ? - getString("AltMiniMapToolTipMsg") : getString("MiniMapToolTipMsg")); - - 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->setNoItemsCommentText(getString("no_groups_msg")); - mGroupList->setNoItemsMsg(getString("no_groups_msg")); - mGroupList->setNoFilteredItemsMsg(getString("no_filtered_groups_msg")); - - mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyPeopleContextMenu); - mRecentList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); - mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); - mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu); - - setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false); - setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false); - setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false); - - 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)); - - LLMenuButton* groups_gear_btn = getChild<LLMenuButton>("groups_gear_btn"); - - // Use the context menu of the Groups list for the Groups tab gear menu. - LLToggleableMenu* groups_gear_menu = mGroupList->getContextMenu(); - if (groups_gear_menu) - { - groups_gear_btn->setMenu(groups_gear_menu, LLMenuButton::MP_BOTTOM_LEFT); - } - else - { - LL_WARNS() << "People->Groups list menu not found" << LL_ENDL; - } - - 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)); - - // Must go after setting commit callback and initializing all pointers to children. - mTabContainer->selectTabByName(NEARBY_TAB_NAME); - - 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 - const std::string& filter = mSavedOriginalFilters[mTabContainer->getCurrentPanelIndex()]; - - std::string message_name = filter.empty() ? "no_friends_msg" : "no_filtered_friends_msg"; - LLStringUtil::format_map_t args; - args["[SEARCH_TERM]"] = LLURI::escape(filter); - 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(); - - uuid_vec_t buddies_uuids; - LLAvatarTracker::buddy_map_t::const_iterator buddies_iter; - - // Fill the avatar list with friends UUIDs - for (buddies_iter = all_buddies.begin(); buddies_iter != all_buddies.end(); ++buddies_iter) - { - buddies_uuids.push_back(buddies_iter->first); - } - - if (buddies_uuids.size() > 0) - { - LL_DEBUGS() << "Friends added to the list: " << buddies_uuids.size() << LL_ENDL; - all_friendsp = buddies_uuids; - } - else - { - LL_DEBUGS() << "No friends found" << LL_ENDL; - } - - 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::updateButtons() -{ - std::string cur_tab = getActiveTabName(); - 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); - - if (group_tab_active) - { - if (item_selected) - { - selected_id = mGroupList->getSelectedUUID(); - } - - LLPanel* groups_panel = mTabContainer->getCurrentPanel(); - groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected - - U32 groups_count = gAgent.mGroups.size(); - S32 max_groups = LLAgentBenefitsMgr::current().getGroupMembershipLimit(); - U32 groups_remaining = max_groups > groups_count ? max_groups - groups_count : 0; - groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d", groups_count)); - groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[REMAINING]", llformat("%d", groups_remaining)); - } - else - { - bool is_friend = true; - bool is_self = false; - // Check whether selected avatar is our friend. - if (item_selected) - { - selected_id = selected_uuids.front(); - is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL; - is_self = gAgent.getID() == selected_id; - } - - LLPanel* cur_panel = mTabContainer->getCurrentPanel(); - if (cur_panel) - { - if (cur_panel->hasChild("add_friend_btn", true)) - cur_panel->getChildView("add_friend_btn")->setEnabled(item_selected && !is_friend && !is_self); - - if (friends_tab_active) - { - cur_panel->getChildView("friends_del_btn")->setEnabled(multiple_selected); - } - - if (!group_tab_active) - { - cur_panel->getChildView("gear_btn")->setEnabled(multiple_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(); - - if (cur_tab == BLOCKED_TAB_NAME) - return LLUUID::null; // FIXME? - - 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 if (cur_tab == BLOCKED_TAB_NAME) - selected_uuids.clear(); // FIXME? - else - llassert(0 && "unknown tab selected"); - -} - -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; - case E_SORT_BY_RECENT_ARRIVAL: - list->setComparator(&RECENT_ARRIVAL_COMPARATOR); - list->sort(); - break; - default: - LL_WARNS() << "Unrecognized people sort order for " << list->getName() << LL_ENDL; - 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); - } -} - -void LLPanelPeople::onFilterEdit(const std::string& search_string) -{ - const S32 cur_tab_idx = mTabContainer->getCurrentPanelIndex(); - std::string& filter = mSavedOriginalFilters[cur_tab_idx]; - std::string& saved_filter = mSavedFilters[cur_tab_idx]; - - filter = search_string; - LLStringUtil::trimHead(filter); - - // Searches are case-insensitive - std::string search_upper = filter; - LLStringUtil::toUpper(search_upper); - - if (saved_filter == search_upper) - return; - - saved_filter = search_upper; - - // Apply new filter to the current tab. - const std::string cur_tab = getActiveTabName(); - if (cur_tab == NEARBY_TAB_NAME) - { - mNearbyList->setNameFilter(filter); - } - else if (cur_tab == FRIENDS_TAB_NAME) - { - // store accordion tabs opened/closed state before any manipulation with accordion tabs - if (!saved_filter.empty()) - { - notifyChildren(LLSD().with("action","store_state")); - } - - mOnlineFriendList->setNameFilter(filter); - mAllFriendList->setNameFilter(filter); - - setAccordionCollapsedByUser("tab_online", false); - setAccordionCollapsedByUser("tab_all", false); - showFriendsAccordionsIfNeeded(); - - // restore accordion tabs state _after_ all manipulations - if(saved_filter.empty()) - { - notifyChildren(LLSD().with("action","restore_state")); - } - } - else if (cur_tab == GROUP_TAB_NAME) - { - mGroupList->setNameFilter(filter); - } - else if (cur_tab == RECENT_TAB_NAME) - { - mRecentList->setNameFilter(filter); - } -} - -void LLPanelPeople::onGroupLimitInfo() -{ - LLSD args; - - S32 max_basic = LLAgentBenefitsMgr::get("Base").getGroupMembershipLimit(); - S32 max_premium = LLAgentBenefitsMgr::get("Premium").getGroupMembershipLimit(); - - args["MAX_BASIC"] = max_basic; - args["MAX_PREMIUM"] = max_premium; - - if (LLAgentBenefitsMgr::has("Premium_Plus")) - { - S32 max_premium_plus = LLAgentBenefitsMgr::get("Premium_Plus").getGroupMembershipLimit(); - args["MAX_PREMIUM_PLUS"] = max_premium_plus; - LLNotificationsUtil::add("GroupLimitInfoPlus", args); - } - else - { - LLNotificationsUtil::add("GroupLimitInfo", args); - } -} - -void LLPanelPeople::onTabSelected(const LLSD& param) -{ - std::string tab_name = getChild<LLPanel>(param.asString())->getName(); - updateButtons(); - - showFriendsAccordionsIfNeeded(); -} - -void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl) -{ - LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl); - if(!item) - { - return; - } - - LLUUID clicked_id = item->getAvatarId(); - if(gAgent.getID() == clicked_id) - { - return; - } - -#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) -{ - if (getActiveTabName() == NEARBY_TAB_NAME) - { - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - mMiniMap->setSelected(selected_uuids); - } else - // 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::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() -{ - LLPanel* cur_panel = mTabContainer->getCurrentPanel(); - LLView * button = cur_panel->findChild<LLButton>("friends_add_btn", true); - - // Show add friend wizard. - LLFloater* root_floater = gFloaterView->getParentFloater(this); - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelPeople::onAvatarPicked, _1, _2), false, true, false, root_floater->getName(), button); - if (!picker) - { - return; - } - - // Need to disable 'ok' button when friend occurs in selection - picker->setOkBtnEnableCb(boost::bind(&LLPanelPeople::isItemsFreeOfFriends, this, _1)); - - if (root_floater) - { - root_floater->addDependentFloater(picker); - } - - mPicker = picker->getHandle(); -} - -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::onChatButtonClicked() -{ - LLUUID group_id = getCurrentItemID(); - if (group_id.notNull()) - LLGroupActions::startIM(group_id); -} - -void LLPanelPeople::onGearButtonClicked(LLUICtrl* btn) -{ - uuid_vec_t selected_uuids; - getCurrentItemIDs(selected_uuids); - // Spawn at bottom left corner of the button. - if (getActiveTabName() == NEARBY_TAB_NAME) - LLPanelPeopleMenus::gNearbyPeopleContextMenu.show(btn, selected_uuids, 0, 0); - else - LLPanelPeopleMenus::gPeopleContextMenu.show(btn, selected_uuids, 0, 0); -} - -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); - } -} - -// 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()); -} - -bool LLPanelPeople::onGroupPlusButtonValidate() -{ - if (!gAgent.canJoinGroups()) - { - LLNotificationsUtil::add("JoinedTooManyGroups"); - return false; - } - - return true; -} - -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); - } - else if (chosen_item == "view_usernames") - { - bool hide_usernames = !gSavedSettings.getBOOL("FriendsListHideUsernames"); - gSavedSettings.setBOOL("FriendsListHideUsernames", hide_usernames); - - mAllFriendList->setShowCompleteName(!hide_usernames); - mAllFriendList->handleDisplayNamesOptionChanged(); - mOnlineFriendList->setShowCompleteName(!hide_usernames); - mOnlineFriendList->handleDisplayNamesOptionChanged(); - } - } - -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); - } - else if (chosen_item == "sort_arrival") - { - setSortOrder(mNearbyList, E_SORT_BY_RECENT_ARRIVAL); - } - else if (chosen_item == "view_usernames") - { - bool hide_usernames = !gSavedSettings.getBOOL("NearbyListHideUsernames"); - gSavedSettings.setBOOL("NearbyListHideUsernames", hide_usernames); - - mNearbyList->setShowCompleteName(!hide_usernames); - mNearbyList->handleDisplayNamesOptionChanged(); - } -} - -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; - if (item == "sort_arrival") - return sort_order == E_SORT_BY_RECENT_ARRIVAL; - - 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::onMoreButtonClicked() -{ - // *TODO: not implemented yet -} - -void LLPanelPeople::onOpen(const LLSD& key) -{ - std::string tab_name = key["people_panel_tab_name"]; - if (!tab_name.empty()) - { - mTabContainer->selectTabByName(tab_name); - if(tab_name == BLOCKED_TAB_NAME) - { - LLPanel* blocked_tab = mTabContainer->getCurrentPanel()->findChild<LLPanel>("panel_block_list_sidetray"); - if(blocked_tab) - { - blocked_tab->onOpen(key); - } - } - } -} - -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) - { - LL_WARNS() << "Cannot find People panel container" << LL_ENDL; - 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 - LLFloaterReg::hideInstance("people"); - - 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()) - { - LL_WARNS() << "No name provided" << LL_ENDL; - 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) - { - LL_WARNS() << "Invalid parameter" << LL_ENDL; - 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) - { - LL_WARNS() << "Invalid parameter" << LL_ENDL; - 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)); -} - -bool LLPanelPeople::updateNearbyArrivalTime() -{ - std::vector<LLVector3d> positions; - std::vector<LLUUID> uuids; - static LLCachedControl<F32> range(gSavedSettings, "NearMeRange"); - LLWorld::getInstance()->getAvatars(&uuids, &positions, gAgent.getPositionGlobal(), range); - LLRecentPeople::instance().updateAvatarsArrivalTime(uuids); - return LLApp::isExiting(); -} - - -// EOF +/**
+ * @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 "llconversationview.h"
+#include "llfloaterimcontainer.h"
+#include "llfloaterreg.h"
+#include "llfloatersidepanelcontainer.h"
+#include "llmenubutton.h"
+#include "llmenugl.h"
+#include "llnotificationsutil.h"
+#include "lleventtimer.h"
+#include "llfiltereditor.h"
+#include "lltabcontainer.h"
+#include "lltoggleablemenu.h"
+#include "lluictrlfactory.h"
+
+#include "llpanelpeople.h"
+
+// newview
+#include "llaccordionctrl.h"
+#include "llaccordionctrltab.h"
+#include "llagent.h"
+#include "llagentbenefits.h"
+#include "llavataractions.h"
+#include "llavatarlist.h"
+#include "llavatarlistitem.h"
+#include "llavatarnamecache.h"
+#include "llcallingcard.h" // for LLAvatarTracker
+#include "llcallbacklist.h"
+#include "llerror.h"
+#include "llfloateravatarpicker.h"
+#include "llfriendcard.h"
+#include "llgroupactions.h"
+#include "llgrouplist.h"
+#include "llinventoryobserver.h"
+#include "llnetmap.h"
+#include "llpanelpeoplemenus.h"
+#include "llparticipantlist.h"
+#include "llsidetraypanelcontainer.h"
+#include "llrecentpeople.h"
+#include "llviewercontrol.h" // for gSavedSettings
+#include "llviewermenu.h" // for gMenuHolder
+#include "llviewerregion.h"
+#include "llvoiceclient.h"
+#include "llworld.h"
+#include "llspeakers.h"
+#include "llfloaterwebcontent.h"
+
+#include "llagentui.h"
+#include "llslurl.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 BLOCKED_TAB_NAME = "blocked_panel"; // blocked avatars
+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;
+
+ return dist_vec_squared(item1_pos, me_pos) < dist_vec_squared(item2_pos, me_pos);
+ }
+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);
+ }
+};
+
+class LLAvatarItemRecentArrivalComparator : public LLAvatarItemNameComparator
+{
+public:
+ LLAvatarItemRecentArrivalComparator() {};
+ virtual ~LLAvatarItemRecentArrivalComparator() {};
+
+protected:
+ virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
+ {
+
+ F32 arr_time1 = LLRecentPeople::instance().getArrivalTimeByID(item1->getAvatarId());
+ F32 arr_time2 = LLRecentPeople::instance().getArrivalTimeByID(item2->getAvatarId());
+
+ if (arr_time1 == arr_time2)
+ {
+ std::string name1 = item1->getAvatarName();
+ std::string name2 = item2->getAvatarName();
+
+ LLStringUtil::toUpper(name1);
+ LLStringUtil::toUpper(name2);
+
+ return name1 < name2;
+ }
+
+ return arr_time1 > arr_time2;
+ }
+};
+
+static const LLAvatarItemRecentComparator RECENT_COMPARATOR;
+static const LLAvatarItemStatusComparator STATUS_COMPARATOR;
+static LLAvatarItemDistanceComparator DISTANCE_COMPARATOR;
+static const LLAvatarItemRecentSpeakerComparator RECENT_SPEAKER_COMPARATOR;
+static LLAvatarItemRecentArrivalComparator RECENT_ARRIVAL_COMPARATOR;
+
+static LLPanelInjector<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 update()
+ {
+ mCallback();
+ }
+
+ callback_t mCallback;
+};
+
+/**
+ * Update buttons on changes in our friend relations (STORM-557).
+ */
+class LLButtonsUpdater : public LLPanelPeople::Updater, public LLFriendObserver
+{
+public:
+ LLButtonsUpdater(callback_t cb)
+ : LLPanelPeople::Updater(cb)
+ {
+ LLAvatarTracker::instance().addObserver(this);
+ }
+
+ ~LLButtonsUpdater()
+ {
+ LLAvatarTracker::instance().removeObserver(this);
+ }
+
+ /*virtual*/ void changed(U32 mask)
+ {
+ (void) mask;
+ update();
+ }
+};
+
+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;
+ if (LLVoiceClient::instanceExists())
+ {
+ 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))
+ {
+ update();
+ }
+
+ // 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)
+ {
+ LL_DEBUGS() << "Inventory changed: " << mask << LL_ENDL;
+
+ static bool synchronize_friends_folders = true;
+ if (synchronize_friends_folders)
+ {
+ // Checks whether "Friends" and "Friends/All" folders exist in "Calling Cards" folder,
+ // fetches their contents if needed and synchronizes it with buddies list.
+ // If the folders are not found they are created.
+ LLFriendCardsManager::instance().syncFriendCardsFolders();
+ synchronize_friends_folders = false;
+ }
+
+ // *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)
+ {
+ LL_DEBUGS() << "Calling card added: count: " << gInventory.getChangedIDs().size()
+ << ", first Inventory ID: "<< (*gInventory.getChangedIDs().begin())
+ << LL_ENDL;
+
+ 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)
+ {
+ LL_DEBUGS() << "friend found, panel should be updated" << LL_ENDL;
+ 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
+ update();
+ mEventTimer.start();
+ }
+ else
+ {
+ // stop regular updates
+ mEventTimer.stop();
+ }
+ }
+
+ /*virtual*/ bool tick()
+ {
+ update();
+ 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::update, this));
+ }
+};
+
+//=============================================================================
+
+LLPanelPeople::LLPanelPeople()
+ : LLPanel(),
+ mTabContainer(NULL),
+ mOnlineFriendList(NULL),
+ mAllFriendList(NULL),
+ mNearbyList(NULL),
+ mRecentList(NULL),
+ mGroupList(NULL),
+ mMiniMap(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));
+ mButtonsUpdater = new LLButtonsUpdater(boost::bind(&LLPanelPeople::updateButtons, this));
+
+ mCommitCallbackRegistrar.add("People.AddFriend", boost::bind(&LLPanelPeople::onAddFriendButtonClicked, this));
+ mCommitCallbackRegistrar.add("People.AddFriendWizard", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this));
+ mCommitCallbackRegistrar.add("People.DelFriend", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this));
+ mCommitCallbackRegistrar.add("People.Group.Minus", boost::bind(&LLPanelPeople::onGroupMinusButtonClicked, this));
+ mCommitCallbackRegistrar.add("People.Chat", boost::bind(&LLPanelPeople::onChatButtonClicked, this));
+ mCommitCallbackRegistrar.add("People.Gear", boost::bind(&LLPanelPeople::onGearButtonClicked, this, _1));
+
+ mCommitCallbackRegistrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2));
+ mCommitCallbackRegistrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2));
+ mCommitCallbackRegistrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2));
+ mCommitCallbackRegistrar.add("People.Groups.ViewSort.Action", boost::bind(&LLPanelPeople::onGroupsViewSortMenuItemClicked, this, _2));
+ mCommitCallbackRegistrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2));
+
+ mEnableCallbackRegistrar.add("People.Friends.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemCheck, this, _2));
+ mEnableCallbackRegistrar.add("People.Recent.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemCheck, this, _2));
+ mEnableCallbackRegistrar.add("People.Nearby.ViewSort.CheckItem", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemCheck, this, _2));
+
+ mEnableCallbackRegistrar.add("People.Group.Plus.Validate", boost::bind(&LLPanelPeople::onGroupPlusButtonValidate, this));
+
+ doPeriodically(boost::bind(&LLPanelPeople::updateNearbyArrivalTime, this), 2.0);
+}
+
+LLPanelPeople::~LLPanelPeople()
+{
+ delete mButtonsUpdater;
+ delete mNearbyListUpdater;
+ delete mFriendListUpdater;
+ delete mRecentListUpdater;
+
+ mNearbyFilterCommitConnection.disconnect();
+ mFriedsFilterCommitConnection.disconnect();
+ mGroupsFilterCommitConnection.disconnect();
+ mRecentFilterCommitConnection.disconnect();
+
+ if(LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver(this);
+ }
+}
+
+void LLPanelPeople::onFriendsAccordionExpandedCollapsed(LLUICtrl* ctrl, const LLSD& param, LLAvatarList* avatar_list)
+{
+ if(!avatar_list)
+ {
+ LL_ERRS() << "Bad parameter" << LL_ENDL;
+ return;
+ }
+
+ bool expanded = param.asBoolean();
+
+ setAccordionCollapsedByUser(ctrl, !expanded);
+ if(!expanded)
+ {
+ avatar_list->resetSelection();
+ }
+}
+
+
+void LLPanelPeople::removePicker()
+{
+ if(mPicker.get())
+ {
+ mPicker.get()->closeFloater();
+ }
+}
+
+bool LLPanelPeople::postBuild()
+{
+ S32 max_premium = LLAgentBenefitsMgr::get("Premium").getGroupMembershipLimit();
+
+ mNearbyFilterCommitConnection = getChild<LLFilterEditor>("nearby_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
+ mFriedsFilterCommitConnection = getChild<LLFilterEditor>("friends_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
+ mGroupsFilterCommitConnection = getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
+ mRecentFilterCommitConnection = getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
+
+ if(LLAgentBenefitsMgr::current().getGroupMembershipLimit() < max_premium)
+ {
+ getChild<LLTextBox>("groupcount")->setText(getString("GroupCountWithInfo"));
+ getChild<LLTextBox>("groupcount")->setURLClickedCallback(boost::bind(&LLPanelPeople::onGroupLimitInfo, this));
+ }
+
+ mTabContainer = getChild<LLTabContainer>("tabs");
+ mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2));
+ mSavedFilters.resize(mTabContainer->getTabCount());
+ mSavedOriginalFilters.resize(mTabContainer->getTabCount());
+
+ 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));
+ friends_tab->setVisibleCallback(boost::bind(&LLPanelPeople::removePicker, this));
+
+ 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");
+ mOnlineFriendList->setShowCompleteName(!gSavedSettings.getBOOL("FriendsListHideUsernames"));
+ mAllFriendList->setNoItemsCommentText(getString("no_friends"));
+ mAllFriendList->setShowIcons("FriendsListShowIcons");
+ mAllFriendList->showPermissions("FriendsListShowPermissions");
+ mAllFriendList->setShowCompleteName(!gSavedSettings.getBOOL("FriendsListHideUsernames"));
+
+ 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");
+ mNearbyList->setShowCompleteName(!gSavedSettings.getBOOL("NearbyListHideUsernames"));
+ mMiniMap = (LLNetMap*)getChildView("Net Map",true);
+ mMiniMap->setToolTipMsg(gSavedSettings.getBOOL("DoubleClickTeleport") ?
+ getString("AltMiniMapToolTipMsg") : getString("MiniMapToolTipMsg"));
+
+ 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->setNoItemsCommentText(getString("no_groups_msg"));
+ mGroupList->setNoItemsMsg(getString("no_groups_msg"));
+ mGroupList->setNoFilteredItemsMsg(getString("no_filtered_groups_msg"));
+
+ mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyPeopleContextMenu);
+ mRecentList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+ mAllFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+ mOnlineFriendList->setContextMenu(&LLPanelPeopleMenus::gPeopleContextMenu);
+
+ setSortOrder(mRecentList, (ESortOrder)gSavedSettings.getU32("RecentPeopleSortOrder"), false);
+ setSortOrder(mAllFriendList, (ESortOrder)gSavedSettings.getU32("FriendsSortOrder"), false);
+ setSortOrder(mNearbyList, (ESortOrder)gSavedSettings.getU32("NearbyPeopleSortOrder"), false);
+
+ 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));
+
+ LLMenuButton* groups_gear_btn = getChild<LLMenuButton>("groups_gear_btn");
+
+ // Use the context menu of the Groups list for the Groups tab gear menu.
+ LLToggleableMenu* groups_gear_menu = mGroupList->getContextMenu();
+ if (groups_gear_menu)
+ {
+ groups_gear_btn->setMenu(groups_gear_menu, LLMenuButton::MP_BOTTOM_LEFT);
+ }
+ else
+ {
+ LL_WARNS() << "People->Groups list menu not found" << LL_ENDL;
+ }
+
+ 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));
+
+ // Must go after setting commit callback and initializing all pointers to children.
+ mTabContainer->selectTabByName(NEARBY_TAB_NAME);
+
+ 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
+ const std::string& filter = mSavedOriginalFilters[mTabContainer->getCurrentPanelIndex()];
+
+ std::string message_name = filter.empty() ? "no_friends_msg" : "no_filtered_friends_msg";
+ LLStringUtil::format_map_t args;
+ args["[SEARCH_TERM]"] = LLURI::escape(filter);
+ 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();
+
+ uuid_vec_t buddies_uuids;
+ LLAvatarTracker::buddy_map_t::const_iterator buddies_iter;
+
+ // Fill the avatar list with friends UUIDs
+ for (buddies_iter = all_buddies.begin(); buddies_iter != all_buddies.end(); ++buddies_iter)
+ {
+ buddies_uuids.push_back(buddies_iter->first);
+ }
+
+ if (buddies_uuids.size() > 0)
+ {
+ LL_DEBUGS() << "Friends added to the list: " << buddies_uuids.size() << LL_ENDL;
+ all_friendsp = buddies_uuids;
+ }
+ else
+ {
+ LL_DEBUGS() << "No friends found" << LL_ENDL;
+ }
+
+ 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::updateButtons()
+{
+ std::string cur_tab = getActiveTabName();
+ 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);
+
+ if (group_tab_active)
+ {
+ if (item_selected)
+ {
+ selected_id = mGroupList->getSelectedUUID();
+ }
+
+ LLPanel* groups_panel = mTabContainer->getCurrentPanel();
+ groups_panel->getChildView("minus_btn")->setEnabled(item_selected && selected_id.notNull()); // a real group selected
+
+ U32 groups_count = gAgent.mGroups.size();
+ S32 max_groups = LLAgentBenefitsMgr::current().getGroupMembershipLimit();
+ U32 groups_remaining = max_groups > groups_count ? max_groups - groups_count : 0;
+ groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[COUNT]", llformat("%d", groups_count));
+ groups_panel->getChild<LLUICtrl>("groupcount")->setTextArg("[REMAINING]", llformat("%d", groups_remaining));
+ }
+ else
+ {
+ bool is_friend = true;
+ bool is_self = false;
+ // Check whether selected avatar is our friend.
+ if (item_selected)
+ {
+ selected_id = selected_uuids.front();
+ is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL;
+ is_self = gAgent.getID() == selected_id;
+ }
+
+ LLPanel* cur_panel = mTabContainer->getCurrentPanel();
+ if (cur_panel)
+ {
+ if (cur_panel->hasChild("add_friend_btn", true))
+ cur_panel->getChildView("add_friend_btn")->setEnabled(item_selected && !is_friend && !is_self);
+
+ if (friends_tab_active)
+ {
+ cur_panel->getChildView("friends_del_btn")->setEnabled(multiple_selected);
+ }
+
+ if (!group_tab_active)
+ {
+ cur_panel->getChildView("gear_btn")->setEnabled(multiple_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();
+
+ if (cur_tab == BLOCKED_TAB_NAME)
+ return LLUUID::null; // FIXME?
+
+ 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 if (cur_tab == BLOCKED_TAB_NAME)
+ selected_uuids.clear(); // FIXME?
+ else
+ llassert(0 && "unknown tab selected");
+
+}
+
+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;
+ case E_SORT_BY_RECENT_ARRIVAL:
+ list->setComparator(&RECENT_ARRIVAL_COMPARATOR);
+ list->sort();
+ break;
+ default:
+ LL_WARNS() << "Unrecognized people sort order for " << list->getName() << LL_ENDL;
+ 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);
+ }
+}
+
+void LLPanelPeople::onFilterEdit(const std::string& search_string)
+{
+ const S32 cur_tab_idx = mTabContainer->getCurrentPanelIndex();
+ std::string& filter = mSavedOriginalFilters[cur_tab_idx];
+ std::string& saved_filter = mSavedFilters[cur_tab_idx];
+
+ filter = search_string;
+ LLStringUtil::trimHead(filter);
+
+ // Searches are case-insensitive
+ std::string search_upper = filter;
+ LLStringUtil::toUpper(search_upper);
+
+ if (saved_filter == search_upper)
+ return;
+
+ saved_filter = search_upper;
+
+ // Apply new filter to the current tab.
+ const std::string cur_tab = getActiveTabName();
+ if (cur_tab == NEARBY_TAB_NAME)
+ {
+ mNearbyList->setNameFilter(filter);
+ }
+ else if (cur_tab == FRIENDS_TAB_NAME)
+ {
+ // store accordion tabs opened/closed state before any manipulation with accordion tabs
+ if (!saved_filter.empty())
+ {
+ notifyChildren(LLSD().with("action","store_state"));
+ }
+
+ mOnlineFriendList->setNameFilter(filter);
+ mAllFriendList->setNameFilter(filter);
+
+ setAccordionCollapsedByUser("tab_online", false);
+ setAccordionCollapsedByUser("tab_all", false);
+ showFriendsAccordionsIfNeeded();
+
+ // restore accordion tabs state _after_ all manipulations
+ if(saved_filter.empty())
+ {
+ notifyChildren(LLSD().with("action","restore_state"));
+ }
+ }
+ else if (cur_tab == GROUP_TAB_NAME)
+ {
+ mGroupList->setNameFilter(filter);
+ }
+ else if (cur_tab == RECENT_TAB_NAME)
+ {
+ mRecentList->setNameFilter(filter);
+ }
+}
+
+void LLPanelPeople::onGroupLimitInfo()
+{
+ LLSD args;
+
+ S32 max_basic = LLAgentBenefitsMgr::get("Base").getGroupMembershipLimit();
+ S32 max_premium = LLAgentBenefitsMgr::get("Premium").getGroupMembershipLimit();
+
+ args["MAX_BASIC"] = max_basic;
+ args["MAX_PREMIUM"] = max_premium;
+
+ if (LLAgentBenefitsMgr::has("Premium_Plus"))
+ {
+ S32 max_premium_plus = LLAgentBenefitsMgr::get("Premium_Plus").getGroupMembershipLimit();
+ args["MAX_PREMIUM_PLUS"] = max_premium_plus;
+ LLNotificationsUtil::add("GroupLimitInfoPlus", args);
+ }
+ else
+ {
+ LLNotificationsUtil::add("GroupLimitInfo", args);
+ }
+}
+
+void LLPanelPeople::onTabSelected(const LLSD& param)
+{
+ std::string tab_name = getChild<LLPanel>(param.asString())->getName();
+ updateButtons();
+
+ showFriendsAccordionsIfNeeded();
+}
+
+void LLPanelPeople::onAvatarListDoubleClicked(LLUICtrl* ctrl)
+{
+ LLAvatarListItem* item = dynamic_cast<LLAvatarListItem*>(ctrl);
+ if(!item)
+ {
+ return;
+ }
+
+ LLUUID clicked_id = item->getAvatarId();
+ if(gAgent.getID() == clicked_id)
+ {
+ return;
+ }
+
+#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)
+{
+ if (getActiveTabName() == NEARBY_TAB_NAME)
+ {
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+ mMiniMap->setSelected(selected_uuids);
+ } else
+ // 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::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()
+{
+ LLPanel* cur_panel = mTabContainer->getCurrentPanel();
+ LLView * button = cur_panel->findChild<LLButton>("friends_add_btn", true);
+
+ // Show add friend wizard.
+ LLFloater* root_floater = gFloaterView->getParentFloater(this);
+ LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLPanelPeople::onAvatarPicked, _1, _2), false, true, false, root_floater->getName(), button);
+ if (!picker)
+ {
+ return;
+ }
+
+ // Need to disable 'ok' button when friend occurs in selection
+ picker->setOkBtnEnableCb(boost::bind(&LLPanelPeople::isItemsFreeOfFriends, this, _1));
+
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
+
+ mPicker = picker->getHandle();
+}
+
+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::onChatButtonClicked()
+{
+ LLUUID group_id = getCurrentItemID();
+ if (group_id.notNull())
+ LLGroupActions::startIM(group_id);
+}
+
+void LLPanelPeople::onGearButtonClicked(LLUICtrl* btn)
+{
+ uuid_vec_t selected_uuids;
+ getCurrentItemIDs(selected_uuids);
+ // Spawn at bottom left corner of the button.
+ if (getActiveTabName() == NEARBY_TAB_NAME)
+ LLPanelPeopleMenus::gNearbyPeopleContextMenu.show(btn, selected_uuids, 0, 0);
+ else
+ LLPanelPeopleMenus::gPeopleContextMenu.show(btn, selected_uuids, 0, 0);
+}
+
+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);
+ }
+}
+
+// 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());
+}
+
+bool LLPanelPeople::onGroupPlusButtonValidate()
+{
+ if (!gAgent.canJoinGroups())
+ {
+ LLNotificationsUtil::add("JoinedTooManyGroups");
+ return false;
+ }
+
+ return true;
+}
+
+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);
+ }
+ else if (chosen_item == "view_usernames")
+ {
+ bool hide_usernames = !gSavedSettings.getBOOL("FriendsListHideUsernames");
+ gSavedSettings.setBOOL("FriendsListHideUsernames", hide_usernames);
+
+ mAllFriendList->setShowCompleteName(!hide_usernames);
+ mAllFriendList->handleDisplayNamesOptionChanged();
+ mOnlineFriendList->setShowCompleteName(!hide_usernames);
+ mOnlineFriendList->handleDisplayNamesOptionChanged();
+ }
+ }
+
+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);
+ }
+ else if (chosen_item == "sort_arrival")
+ {
+ setSortOrder(mNearbyList, E_SORT_BY_RECENT_ARRIVAL);
+ }
+ else if (chosen_item == "view_usernames")
+ {
+ bool hide_usernames = !gSavedSettings.getBOOL("NearbyListHideUsernames");
+ gSavedSettings.setBOOL("NearbyListHideUsernames", hide_usernames);
+
+ mNearbyList->setShowCompleteName(!hide_usernames);
+ mNearbyList->handleDisplayNamesOptionChanged();
+ }
+}
+
+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;
+ if (item == "sort_arrival")
+ return sort_order == E_SORT_BY_RECENT_ARRIVAL;
+
+ 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::onMoreButtonClicked()
+{
+ // *TODO: not implemented yet
+}
+
+void LLPanelPeople::onOpen(const LLSD& key)
+{
+ std::string tab_name = key["people_panel_tab_name"];
+ if (!tab_name.empty())
+ {
+ mTabContainer->selectTabByName(tab_name);
+ if(tab_name == BLOCKED_TAB_NAME)
+ {
+ LLPanel* blocked_tab = mTabContainer->getCurrentPanel()->findChild<LLPanel>("panel_block_list_sidetray");
+ if(blocked_tab)
+ {
+ blocked_tab->onOpen(key);
+ }
+ }
+ }
+}
+
+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)
+ {
+ LL_WARNS() << "Cannot find People panel container" << LL_ENDL;
+ 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
+ LLFloaterReg::hideInstance("people");
+
+ 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())
+ {
+ LL_WARNS() << "No name provided" << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "Invalid parameter" << LL_ENDL;
+ 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)
+ {
+ LL_WARNS() << "Invalid parameter" << LL_ENDL;
+ 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));
+}
+
+bool LLPanelPeople::updateNearbyArrivalTime()
+{
+ std::vector<LLVector3d> positions;
+ std::vector<LLUUID> uuids;
+ static LLCachedControl<F32> range(gSavedSettings, "NearMeRange");
+ LLWorld::getInstance()->getAvatars(&uuids, &positions, gAgent.getPositionGlobal(), range);
+ LLRecentPeople::instance().updateAvatarsArrivalTime(uuids);
+ return LLApp::isExiting();
+}
+
+
+// EOF
|
