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/llfloaterimcontainer.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/llfloaterimcontainer.cpp')
| -rw-r--r-- | indra/newview/llfloaterimcontainer.cpp | 5000 |
1 files changed, 2500 insertions, 2500 deletions
diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 13fe7e16a9..2f8ada98cb 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -1,2500 +1,2500 @@ -/** - * @file llfloaterimcontainer.cpp - * @brief Multifloater containing active IM sessions in separate tab container tabs - * - * $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" - -#include "llfloaterimsession.h" -#include "llfloaterimcontainer.h" - -#include "llfloaterreg.h" -#include "lllayoutstack.h" -#include "llfloaterimnearbychat.h" - -#include "llagent.h" -#include "llavataractions.h" -#include "llavatariconctrl.h" -#include "llavatarnamecache.h" -#include "llcallbacklist.h" -#include "lldonotdisturbnotificationstorage.h" -#include "llgroupactions.h" -#include "llgroupiconctrl.h" -#include "llflashtimer.h" -#include "llfloateravatarpicker.h" -#include "llfloaterpreference.h" -#include "llfloaterreporter.h" -#include "llimview.h" -#include "llnotificationsutil.h" -#include "lltoolbarview.h" -#include "lltransientfloatermgr.h" -#include "llviewercontrol.h" -#include "llconversationview.h" -#include "llcallbacklist.h" -#include "llworld.h" -#include "llsdserialize.h" -#include "llviewermenu.h" // is_agent_mappable -#include "llviewerobjectlist.h" - - -const S32 EVENTS_PER_IDLE_LOOP_CURRENT_SESSION = 80; -const S32 EVENTS_PER_IDLE_LOOP_BACKGROUND = 40; -const F32 EVENTS_PER_IDLE_LOOP_MIN_PERCENTAGE = 0.01f; // process a minimum of 1% of total events per frame - -// -// LLFloaterIMContainer -// -LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& params /*= getDefaultParams()*/) -: LLMultiFloater(seed, params), - mExpandCollapseBtn(NULL), - mConversationsRoot(NULL), - mConversationsEventStream("ConversationsEvents"), - mInitialized(false), - mIsFirstLaunch(true), - mConversationEventQueue() -{ - mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2)); - mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2)); - - mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMContainer::checkContextMenuItem, this, _2)); - mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMContainer::enableContextMenuItem, this, _2)); - mEnableCallbackRegistrar.add("Avatar.VisibleItem", boost::bind(&LLFloaterIMContainer::visibleContextMenuItem, this, _2)); - mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelected, this, _2)); - - mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelectedGroup, this, _2)); - - // Firstly add our self to IMSession observers, so we catch session events - LLIMMgr::getInstance()->addSessionObserver(this); - - mAutoResize = false; - LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this); -} - -LLFloaterIMContainer::~LLFloaterIMContainer() -{ - mConversationsEventStream.stopListening("ConversationsRefresh"); - gIdleCallbacks.deleteFunction(idle, this); - mNewMessageConnection.disconnect(); - LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this); - - if (mMicroChangedSignal.connected()) - { - mMicroChangedSignal.disconnect(); - } - - gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed()); - gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed()); - gSavedPerAccountSettings.setBOOL("ConversationsParticipantListCollapsed", !isParticipantListExpanded()); - - if (LLIMMgr::instanceExists()) - { - LLIMMgr::getInstance()->removeSessionObserver(this); - } -} - -void LLFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg) -{ - addConversationListItem(session_id); - LLFloaterIMSessionTab::addToHost(session_id); -} - -void LLFloaterIMContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id) -{ - if(!isInVisibleChain()) - { - setVisibleAndFrontmost(false); - } - selectConversationPair(session_id, true); - collapseMessagesPane(false); -} - -void LLFloaterIMContainer::sessionVoiceOrIMStarted(const LLUUID& session_id) -{ - addConversationListItem(session_id); - LLFloaterIMSessionTab::addToHost(session_id); -} - -void LLFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id) -{ - // The general strategy when a session id is modified is to delete all related objects and create them anew. - - // Note however that the LLFloaterIMSession has its session id updated through a call to sessionInitReplyReceived() - // and do not need to be deleted and recreated (trying this creates loads of problems). We do need however to suppress - // its related mSessions record as it's indexed with the wrong id. - // Grabbing the updated LLFloaterIMSession and readding it in mSessions will eventually be done by addConversationListItem(). - mSessions.erase(old_session_id); - - // Delete the model and participants related to the old session - bool change_focus = removeConversationListItem(old_session_id); - - // Create a new conversation with the new id - addConversationListItem(new_session_id, change_focus); - LLFloaterIMSessionTab::addToHost(new_session_id); -} - - -LLConversationItem* LLFloaterIMContainer::getSessionModel(const LLUUID& session_id) -{ - conversations_items_map::iterator iter = mConversationsItems.find(session_id); - if (iter == mConversationsItems.end()) - { - return NULL; - } - else - { - return iter->second.get(); - } -} - -void LLFloaterIMContainer::sessionRemoved(const LLUUID& session_id) -{ - removeConversationListItem(session_id); -} - -// static -void LLFloaterIMContainer::onCurrentChannelChanged(const LLUUID& session_id) -{ - if (session_id != LLUUID::null) - { - LLFloaterIMContainer::getInstance()->showConversation(session_id); - } -} - -bool LLFloaterIMContainer::postBuild() -{ - mOrigMinWidth = getMinWidth(); - mOrigMinHeight = getMinHeight(); - - mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLFloaterIMContainer::onNewMessageReceived, this, _1)); - // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button - // mTabContainer will be initialized in LLMultiFloater::addChild() - - setTabContainer(getChild<LLTabContainer>("im_box_tab_container")); - mStubPanel = getChild<LLPanel>("stub_panel"); - mStubTextBox = getChild<LLTextBox>("stub_textbox"); - mStubTextBox->setURLClickedCallback(boost::bind(&LLFloaterIMContainer::returnFloaterToHost, this)); - - mConversationsStack = getChild<LLLayoutStack>("conversations_stack"); - mConversationsPane = getChild<LLLayoutPanel>("conversations_layout_panel"); - mMessagesPane = getChild<LLLayoutPanel>("messages_layout_panel"); - - mConversationsListPanel = getChild<LLPanel>("conversations_list_panel"); - - // Open IM session with selected participant on double click event - mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLFloaterIMContainer::doToSelected, this, LLSD("im"))); - - // The resize limits for LLFloaterIMContainer should be updated, based on current values of width of conversation and message panels - mConversationsPane->getResizeBar()->setResizeListener(boost::bind(&LLFloaterIMContainer::assignResizeLimits, this)); - - // Create the root model and view for all conversation sessions - LLConversationItem* base_item = new LLConversationItem(getRootViewModel()); - - LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>()); - p.name = getName(); - p.title = getLabel(); - p.rect = LLRect(0, 0, getRect().getWidth(), 0); - p.parent_panel = mConversationsListPanel; - p.tool_tip = p.name; - p.listener = base_item; - p.view_model = &mConversationViewModel; - p.root = NULL; - p.use_ellipses = true; - p.options_menu = "menu_conversation.xml"; - mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p); - mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar); - mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar); - - // Add listener to conversation model events - mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLFloaterIMContainer::onConversationModelEvent, this, _1)); - - // a scroller for folder view - LLRect scroller_view_rect = mConversationsListPanel->getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - scroller_view_rect.mBottom += getChild<LLLayoutStack>("conversations_pane_buttons_stack")->getRect().getHeight(); - LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>()); - scroller_params.rect(scroller_view_rect); - - LLScrollContainer* scroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params); - scroller->setFollowsAll(); - mConversationsListPanel->addChild(scroller); - scroller->addChild(mConversationsRoot); - mConversationsRoot->setScrollContainer(scroller); - mConversationsRoot->setFollowsAll(); - mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox); - - addConversationListItem(LLUUID()); // manually add nearby chat - - mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn"); - mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this)); - mStubCollapseBtn = getChild<LLButton>("stub_collapse_btn"); - mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this)); - mSpeakBtn = getChild<LLButton>("speak_btn"); - - mSpeakBtn->setMouseDownCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonPressed, this)); - mSpeakBtn->setMouseUpCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonReleased, this)); - - childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this)); - - collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); - collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed"), false); - LLAvatarNameCache::getInstance()->addUseDisplayNamesCallback(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false)); - mMicroChangedSignal = LLVoiceClient::getInstance()->MicroChangedCallback(boost::bind(&LLFloaterIMContainer::updateSpeakBtnState, this)); - - if (! mMessagesPane->isCollapsed() && ! mConversationsPane->isCollapsed()) - { - S32 conversations_panel_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth"); - LLRect conversations_panel_rect = mConversationsPane->getRect(); - conversations_panel_rect.mRight = conversations_panel_rect.mLeft + conversations_panel_width; - mConversationsPane->handleReshape(conversations_panel_rect, true); - } - - // Init the sort order now that the root had been created - setSortOrder(LLConversationSort(gSavedSettings.getU32("ConversationSortOrder"))); - - //We should expand nearby chat participants list for the new user - if(gAgent.isFirstLogin() || !gSavedPerAccountSettings.getBOOL("ConversationsParticipantListCollapsed")) - { - expandConversation(); - } - // Keep the xml set title around for when we have to overwrite it - mGeneralTitle = getTitle(); - - mInitialized = true; - - mIsFirstOpen = true; - - // Add callbacks: - // We'll take care of view updates on idle - gIdleCallbacks.addFunction(idle, this); - // When display name option change, we need to reload all participant names - LLAvatarNameCache::getInstance()->addUseDisplayNamesCallback(boost::bind(&LLFloaterIMContainer::processParticipantsStyleUpdate, this)); - - mParticipantRefreshTimer.setTimerExpirySec(0); - mParticipantRefreshTimer.start(); - - return true; -} - -void LLFloaterIMContainer::onOpen(const LLSD& key) -{ - LLMultiFloater::onOpen(key); - reSelectConversation(); - assignResizeLimits(); - - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession); - session_floater->onOpen(key); -} - -// virtual -void LLFloaterIMContainer::addFloater(LLFloater* floaterp, - bool select_added_floater, - LLTabContainer::eInsertionPoint insertion_point) -{ - if(!floaterp) return; - - // already here - if (floaterp->getHost() == this) - { - openFloater(floaterp->getKey()); - return; - } - - LLUUID session_id = floaterp->getKey(); - - // Add the floater - LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point); - - - - LLIconCtrl* icon = 0; - bool is_in_group = gAgent.isInGroup(session_id, true); - LLUUID icon_id; - - if (is_in_group) - { - LLGroupIconCtrl::Params icon_params; - icon_params.group_id = session_id; - icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params); - icon_id = session_id; - - mSessions[session_id] = floaterp; - floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id)); - } - else - { LLUUID avatar_id = session_id.notNull()? - LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID(); - - LLAvatarIconCtrl::Params icon_params; - icon_params.avatar_id = avatar_id; - icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params); - icon_id = avatar_id; - - mSessions[session_id] = floaterp; - floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id)); - } - - LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id); - if (floater) - { - floater->updateChatIcon(icon_id); - } - - // forced resize of the floater - LLRect wrapper_rect = this->mTabContainer->getLocalRect(); - floaterp->setRect(wrapper_rect); - - mTabContainer->setTabImage(floaterp, icon); -} - - -void LLFloaterIMContainer::onCloseFloater(LLUUID& id) -{ - mSessions.erase(id); - setFocus(true); -} - -void LLFloaterIMContainer::onNewMessageReceived(const LLSD& data) -{ - LLUUID session_id = data["session_id"].asUUID(); - LLFloater* floaterp = get_ptr_in_map(mSessions, session_id); - LLFloater* current_floater = LLMultiFloater::getActiveFloater(); - - if(floaterp && current_floater && floaterp != current_floater) - { - if(LLMultiFloater::isFloaterFlashing(floaterp)) - LLMultiFloater::setFloaterFlashing(floaterp, false); - LLMultiFloater::setFloaterFlashing(floaterp, true); - } -} - -void LLFloaterIMContainer::onStubCollapseButtonClicked() -{ - collapseMessagesPane(true); -} - -void LLFloaterIMContainer::onSpeakButtonPressed() -{ - LLVoiceClient::getInstance()->inputUserControlState(true); - updateSpeakBtnState(); -} - -void LLFloaterIMContainer::onSpeakButtonReleased() -{ - LLVoiceClient::getInstance()->inputUserControlState(false); - updateSpeakBtnState(); -} - -void LLFloaterIMContainer::onExpandCollapseButtonClicked() -{ - if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed() - && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst")) - { - // Expand the messages pane from ultra minimized state - // if it was collapsed last in order. - collapseMessagesPane(false); - } - else - { - collapseConversationsPane(!mConversationsPane->isCollapsed()); - } - reSelectConversation(); -} - -LLFloaterIMContainer* LLFloaterIMContainer::findInstance() -{ - return LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container"); -} - -LLFloaterIMContainer* LLFloaterIMContainer::getInstance() -{ - return LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); -} - -// Update all participants in the conversation lists -void LLFloaterIMContainer::processParticipantsStyleUpdate() -{ - // On each session in mConversationsItems - for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) - { - // Get the current session descriptors - LLConversationItem* session_model = it_session->second; - // Iterate through each model participant child - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd(); - while (current_participant_model != end_participant_model) - { - LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model); - if (participant_model) - { - // Get the avatar name for this participant id from the cache and update the model - participant_model->updateName(); - } - // Next participant - current_participant_model++; - } - } -} - -// static -void LLFloaterIMContainer::idle(void* user_data) -{ - LLFloaterIMContainer* self = static_cast<LLFloaterIMContainer*>(user_data); - - self->idleProcessEvents(); - - if (!self->getVisible() || self->isMinimized()) - { - return; - } - self->idleUpdate(); -} - -void LLFloaterIMContainer::idleUpdate() -{ - if (mTabContainer->getTabCount() == 0) - { - // Do not close the container when every conversation is torn off because the user - // still needs the conversation list. Simply collapse the message pane in that case. - collapseMessagesPane(true); - } - - U32 sort_order = mConversationViewModel.getSorter().getSortOrderParticipants(); - - if (mParticipantRefreshTimer.hasExpired()) - { - const LLConversationItem *current_session = getCurSelectedViewModelItem(); - if (current_session) - { - if (current_session->getType() == LLConversationItem::CONV_SESSION_GROUP) - { - // Update moderator options visibility - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = current_session->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = current_session->getChildrenEnd(); - bool is_moderator = isGroupModerator(); - bool can_ban = haveAbilityToBan(); - while (current_participant_model != end_participant_model) - { - LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model); - if (participant_model) - { - participant_model->setModeratorOptionsVisible(is_moderator); - participant_model->setGroupBanVisible(can_ban && participant_model->getUUID() != gAgentID); - } - - current_participant_model++; - } - } - - // Update floater's title as required by the currently selected session or use the default title - LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(current_session->getUUID()); - setTitle(conversation_floaterp && conversation_floaterp->needsTitleOverwrite() ? conversation_floaterp->getTitle() : mGeneralTitle); - } - - mParticipantRefreshTimer.setTimerExpirySec(1.0f); - } - - // Update the distance to agent in the nearby chat session if required - // Note: it makes no sense of course to update the distance in other session - if (sort_order == LLConversationFilter::SO_DISTANCE) - { - // almost real-time updates - setNearbyDistances(); //calls arrange all - } - mConversationsRoot->update(); //arranges, resizes, heavy - - // "Manually" resize of mConversationsPane: same as temporarity cancellation of the flag "auto_resize=false" for it - if (!mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed()) - { - LLRect stack_rect = mConversationsStack->getRect(); - mConversationsPane->reshape(stack_rect.getWidth(), stack_rect.getHeight(), true); - } -} - -void LLFloaterIMContainer::idleProcessEvents() -{ - LLUUID current_session_id = getSelectedSession(); - conversations_items_deque::iterator iter = mConversationEventQueue.begin(); - conversations_items_deque::iterator end = mConversationEventQueue.end(); - while (iter != end) - { - std::deque<LLSD> &events = iter->second; - if (!events.empty()) - { - S32 events_to_handle; - S32 query_size = (S32)events.size(); - if (current_session_id == iter->first) - { - events_to_handle = EVENTS_PER_IDLE_LOOP_CURRENT_SESSION; - } - else - { - events_to_handle = EVENTS_PER_IDLE_LOOP_BACKGROUND; - } - - if (events_to_handle <= query_size) - { - // Some groups can be very large and can generate huge amount of updates, scale processing up to keep up - events_to_handle = llmax(events_to_handle, (S32)(query_size * EVENTS_PER_IDLE_LOOP_MIN_PERCENTAGE)); - } - else - { - events_to_handle = query_size; - } - - for (S32 i = 0; i < events_to_handle; i++) - { - handleConversationModelEvent(events.back()); - events.pop_back(); - } - } - iter++; - } -} - -bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event) -{ - LLUUID id = event.get("session_uuid").asUUID(); - mConversationEventQueue[id].push_front(event); - return true; -} - - -void LLFloaterIMContainer::handleConversationModelEvent(const LLSD& event) -{ - - // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that - // the model could change substantially and the view could echo only a portion of this model (though currently the - // conversation view does echo the conversation model 1 to 1). - // Consequently, the participant views need to be created either by the session view or by the container panel. - // For the moment, we create them here, at the container level, to conform to the pattern implemented in llinventorypanel.cpp - // (see LLInventoryPanel::buildNewViews()). - - std::string type = event.get("type").asString(); - LLUUID session_id = event.get("session_uuid").asUUID(); - LLUUID participant_id = event.get("participant_uuid").asUUID(); - - LLConversationViewSession* session_view = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,session_id)); - if (!session_view) - { - // We skip events that are not associated with a session - return; - } - LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id); - LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? - (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) - : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id))); - - if (type == "remove_participant") - { - // Remove a participant view from the hierarchical conversation list - if (participant_view) - { - session_view->extractItem(participant_view); - delete participant_view; - session_view->refresh(); - mConversationsRoot->arrangeAll(); - } - // Remove a participant view from the conversation floater - if (conversation_floater) - { - conversation_floater->removeConversationViewParticipant(participant_id); - } - } - else if (type == "add_participant") - { - LLConversationItem* item = getSessionModel(session_id); - LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(item); - LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL); - LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id); - if (!participant_view && session_model && participant_model) - { - if (session_id.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType())) - { - participant_view = createConversationViewParticipant(participant_model); - participant_view->addToFolder(session_view); - participant_view->setVisible(true); - } - } - // Add a participant view to the conversation floater - if (conversation_floater && participant_model) - { - bool skip_updating = im_sessionp && im_sessionp->isGroupChat(); - conversation_floater->addConversationViewParticipant(participant_model, !skip_updating); - } - } - else if (type == "update_participant") - { - // Update the participant view in the hierarchical conversation list - if (participant_view) - { - participant_view->refresh(); - } - // Update the participant view in the conversation floater - if (conversation_floater) - { - conversation_floater->updateConversationViewParticipant(participant_id); - } - } - else if (type == "update_session") - { - session_view->refresh(); - } - - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); -} - -void LLFloaterIMContainer::draw() -{ - LLFloater::draw(); -} - -void LLFloaterIMContainer::tabClose() -{ - if (mTabContainer->getTabCount() == 0) - { - // Do not close the container when every conversation is torn off because the user - // still needs the conversation list. Simply collapse the message pane in that case. - collapseMessagesPane(true); - } -} - -//Shows/hides the stub panel when a conversation floater is torn off -void LLFloaterIMContainer::showStub(bool stub_is_visible) -{ - S32 tabCount = 0; - LLPanel * tabPanel = NULL; - - if(stub_is_visible) - { - tabCount = mTabContainer->getTabCount(); - - //Hide all tabs even stub - for(S32 i = 0; i < tabCount; ++i) - { - tabPanel = mTabContainer->getPanelByIndex(i); - - if(tabPanel) - { - tabPanel->setVisible(false); - } - } - - //Set the index to the stub panel since we will be showing the stub - mTabContainer->setCurrentPanelIndex(0); - } - - //Now show/hide the stub - mStubPanel->setVisible(stub_is_visible); -} - -// listener for click on mStubTextBox2 -void LLFloaterIMContainer::returnFloaterToHost() -{ - LLUUID session_id = this->getSelectedSession(); - LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id); - floater->onTearOffClicked(); -} - -void LLFloaterIMContainer::setMinimized(bool b) -{ - bool was_minimized = isMinimized(); - LLMultiFloater::setMinimized(b); - - //Switching from minimized to un-minimized - if(was_minimized && !b) - { - gToolBarView->flashCommand(LLCommandId("chat"), false); - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); - - if(session_floater && !session_floater->isTornOff()) - { - //When in DND mode, remove stored IM notifications - //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal - if(gAgent.isDoNotDisturb() && mSelectedSession.notNull()) - { - LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession); - } - } - } -} - -void LLFloaterIMContainer::setVisible(bool visible) -{ - LLFloaterIMNearbyChat* nearby_chat; - if (visible) - { - // Make sure we have the Nearby Chat present when showing the conversation container - nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); - if ((nearby_chat == NULL) || mIsFirstOpen) - { - mIsFirstOpen = false; - // If not found, force the creation of the nearby chat conversation panel - // *TODO: find a way to move this to XML as a default panel or something like that - LLSD name("nearby_chat"); - LLFloaterReg::toggleInstanceOrBringToFront(name); - selectConversationPair(LLUUID(NULL), false, false); - } - - flashConversationItemWidget(mSelectedSession,false); - - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession); - if(session_floater && !session_floater->isMinimized()) - { - //When in DND mode, remove stored IM notifications - //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal - if(gAgent.isDoNotDisturb() && mSelectedSession.notNull()) - { - LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession); - } - } - } - - nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); - if (nearby_chat) - { - LLFloaterIMSessionTab::addToHost(LLUUID()); - } - - if (!LLFloater::isQuitRequested()) - { - // We need to show/hide all the associated conversations that have been torn off - // (and therefore, are not longer managed by the multifloater), - // so that they show/hide with the conversations manager. - conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - for (; widget_it != mConversationsWidgets.end(); ++widget_it) - { - LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); - if (widget) - { - LLFloater* session_floater = widget->getSessionFloater(); - if (session_floater != nearby_chat) - { - widget->setVisibleIfDetached(visible); - } - } - } - } - - // Now, do the normal multifloater show/hide - LLMultiFloater::setVisible(visible); -} - -void LLFloaterIMContainer::getDetachedConversationFloaters(floater_list_t& floaters) -{ - LLFloaterIMNearbyChat *nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); - - for (const auto& [key, fvi] : mConversationsWidgets) - { - LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(fvi); - if (widget) - { - LLFloater* session_floater = widget->getSessionFloater(); - - // Exclude nearby chat from output, as it should be handled separately - if (session_floater && session_floater->isDetachedAndNotMinimized() - && session_floater != nearby_chat) - { - floaters.push_back(session_floater); - } - } - } -} - -void LLFloaterIMContainer::setVisibleAndFrontmost(bool take_focus, const LLSD& key) -{ - LLMultiFloater::setVisibleAndFrontmost(take_focus, key); - // Do not select "Nearby Chat" conversation, since it will bring its window to front - // Only select other sessions - if (!getSelectedSession().isNull()) - { - selectConversationPair(getSelectedSession(), false, take_focus); - } - if (mInitialized && mIsFirstLaunch) - { - collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed")); - mIsFirstLaunch = false; -} -} - -void LLFloaterIMContainer::updateResizeLimits() -{ - LLMultiFloater::updateResizeLimits(); - assignResizeLimits(); -} - -bool LLFloaterIMContainer::isMessagesPaneCollapsed() -{ - return mMessagesPane->isCollapsed(); -} - -bool LLFloaterIMContainer::isConversationsPaneCollapsed() -{ - return mConversationsPane->isCollapsed(); -} - -void LLFloaterIMContainer::collapseMessagesPane(bool collapse) -{ - if (mMessagesPane->isCollapsed() == collapse) - { - return; - } - - // Save current width of panels before collapsing/expanding right pane. - S32 conv_pane_width = mConversationsPane->getRect().getWidth(); - S32 msg_pane_width = mMessagesPane->getRect().getWidth(); - - if (collapse) - { - // Save the messages pane width before collapsing it. - gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", msg_pane_width); - - // Save the order in which the panels are closed to reverse user's last action. - gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed()); - } - - mConversationsPane->setIgnoreReshape(collapse); - - // Show/hide the messages pane. - mConversationsStack->collapsePanel(mMessagesPane, collapse); - - // Make sure layout is updated before resizing conversation pane. - mConversationsStack->updateLayout(); - - reshapeFloaterAndSetResizeLimits(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth")); - - if (!collapse) - { - // Restore conversation's pane previous width after expanding messages pane. - mConversationsPane->setTargetDim(conv_pane_width); - } -} - -void LLFloaterIMContainer::collapseConversationsPane(bool collapse, bool save_is_allowed /*=true*/) -{ - if (mConversationsPane->isCollapsed() == collapse) - { - return; - } - - LLView* button_panel = getChild<LLView>("conversations_pane_buttons_expanded"); - button_panel->setVisible(!collapse); - mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon")); - - // Save current width of Conversation panel before collapsing/expanding right pane. - S32 conv_pane_width = mConversationsPane->getRect().getWidth(); - - if (collapse && save_is_allowed) - { - // Save the conversations pane width before collapsing it. - gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", conv_pane_width); - - // Save the order in which the panels are closed to reverse user's last action. - gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed()); - } - - mConversationsStack->collapsePanel(mConversationsPane, collapse); - if (!collapse) - { - // Make sure layout is updated before resizing conversation pane. - mConversationsStack->updateLayout(); - // Restore conversation's pane previous width. - mConversationsPane->setTargetDim(gSavedPerAccountSettings.getS32("ConversationsListPaneWidth")); - } - - S32 delta_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth") - - mConversationsPane->getMinDim() - mConversationsStack->getPanelSpacing() + 1; - - reshapeFloaterAndSetResizeLimits(collapse, delta_width); - - for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - widget_it != mConversationsWidgets.end(); ++widget_it) - { - LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); - if (widget) - { - widget->toggleCollapsedMode(collapse); - - // force closing all open conversations when collapsing to minimized state - if (collapse) - { - widget->setOpen(false); - } - widget->requestArrange(); - } - } -} - -void LLFloaterIMContainer::reshapeFloaterAndSetResizeLimits(bool collapse, S32 delta_width) -{ - LLRect floater_rect = getRect(); - floater_rect.mRight += ((collapse ? -1 : 1) * delta_width); - - // Set by_user = true so that reshaped rect is saved in user_settings. - setShape(floater_rect, true); - updateResizeLimits(); - - bool at_least_one_panel_is_expanded = - ! (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed()); - - setCanResize(at_least_one_panel_is_expanded); - setCanMinimize(at_least_one_panel_is_expanded); - - assignResizeLimits(); -} - -void LLFloaterIMContainer::assignResizeLimits() -{ - bool is_conv_pane_expanded = !mConversationsPane->isCollapsed(); - bool is_msg_pane_expanded = !mMessagesPane->isCollapsed(); - - S32 summary_width_of_visible_borders = (is_msg_pane_expanded ? mConversationsStack->getPanelSpacing() : 0) + 1; - - S32 conv_pane_target_width = is_conv_pane_expanded - ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() ) - : mConversationsPane->getMinDim(); - - S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0; - S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders; - - setResizeLimits(new_min_width, getMinHeight()); - - mConversationsStack->updateLayout(); -} - -void LLFloaterIMContainer::onAddButtonClicked() -{ - LLView * button = findChild<LLView>("conversations_pane_buttons_expanded")->findChild<LLButton>("add_btn"); - LLFloater* root_floater = gFloaterView->getParentFloater(this); - LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMContainer::onAvatarPicked, this, _1), true, true, true, root_floater->getName(), button); - - if (picker && root_floater) - { - root_floater->addDependentFloater(picker); - } -} - -void LLFloaterIMContainer::onAvatarPicked(const uuid_vec_t& ids) -{ - if (ids.size() == 1) - { - LLAvatarActions::startIM(ids.back()); - } - else - { - LLAvatarActions::startConference(ids); - } -} - -void LLFloaterIMContainer::onCustomAction(const LLSD& userdata) -{ - std::string command = userdata.asString(); - - if ("sort_sessions_by_type" == command) - { - setSortOrderSessions(LLConversationFilter::SO_SESSION_TYPE); - } - if ("sort_sessions_by_name" == command) - { - setSortOrderSessions(LLConversationFilter::SO_NAME); - } - if ("sort_sessions_by_recent" == command) - { - setSortOrderSessions(LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_name" == command) - { - setSortOrderParticipants(LLConversationFilter::SO_NAME); - } - if ("sort_participants_by_recent" == command) - { - setSortOrderParticipants(LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_distance" == command) - { - setSortOrderParticipants(LLConversationFilter::SO_DISTANCE); - } - if ("chat_preferences" == command) - { - LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences"); - if (floater_prefp) - { - floater_prefp->selectChatPanel(); - } - } - if ("privacy_preferences" == command) - { - LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences"); - if (floater_prefp) - { - floater_prefp->selectPrivacyPanel(); - } - } - if ("Translating.Toggle" == command) - { - gSavedSettings.setBOOL("TranslateChat", !gSavedSettings.getBOOL("TranslateChat")); - } -} - -bool LLFloaterIMContainer::isActionChecked(const LLSD& userdata) -{ - LLConversationSort order = mConversationViewModel.getSorter(); - std::string command = userdata.asString(); - if ("sort_sessions_by_type" == command) - { - return (order.getSortOrderSessions() == LLConversationFilter::SO_SESSION_TYPE); - } - if ("sort_sessions_by_name" == command) - { - return (order.getSortOrderSessions() == LLConversationFilter::SO_NAME); - } - if ("sort_sessions_by_recent" == command) - { - return (order.getSortOrderSessions() == LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_name" == command) - { - return (order.getSortOrderParticipants() == LLConversationFilter::SO_NAME); - } - if ("sort_participants_by_recent" == command) - { - return (order.getSortOrderParticipants() == LLConversationFilter::SO_DATE); - } - if ("sort_participants_by_distance" == command) - { - return (order.getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE); - } - if ("Translating.Enabled" == command) - { - return gSavedPerAccountSettings.getBOOL("TranslatingEnabled"); - } - if ("Translating.On" == command) - { - return gSavedSettings.getBOOL("TranslateChat"); - } - return false; -} - -void LLFloaterIMContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order) -{ - LLConversationSort old_order = mConversationViewModel.getSorter(); - if (order != old_order.getSortOrderSessions()) - { - old_order.setSortOrderSessions(order); - setSortOrder(old_order); - } -} - -void LLFloaterIMContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order) -{ - LLConversationSort old_order = mConversationViewModel.getSorter(); - if (order != old_order.getSortOrderParticipants()) - { - old_order.setSortOrderParticipants(order); - setSortOrder(old_order); - } -} - -void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order) -{ - mConversationViewModel.setSorter(order); - mConversationsRoot->arrangeAll(); - // try to keep selection onscreen, even if it wasn't to start with - mConversationsRoot->scrollToShowSelection(); - - // Notify all conversation (torn off or not) of the change to the sort order - // Note: For the moment, the sort order is *unique* across all conversations. That might change in the future. - for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) - { - LLUUID session_id = it_session->first; - LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id))); - if (conversation_floater) - { - conversation_floater->setSortOrder(order); - } - } - - gSavedSettings.setU32("ConversationSortOrder", (U32)order); -} - -void LLFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids, bool participant_uuids/* = true*/) -{ - const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList(); - - std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); - const std::set<LLFolderViewItem*>::const_iterator it_end = selectedItems.end(); - LLConversationItem * conversationItem; - - for (; it != it_end; ++it) - { - conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem()); - - //When a one-on-one conversation exists, retrieve the participant id from the conversation floater - if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1 && participant_uuids) - { - LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(conversationItem->getUUID()); - LLUUID participant_id = conversation_floaterp->getOtherParticipantUUID(); - selected_uuids.push_back(participant_id); - } - else - { - selected_uuids.push_back(conversationItem->getUUID()); - } - } -} - -const LLConversationItem * LLFloaterIMContainer::getCurSelectedViewModelItem() -{ - LLConversationItem * conversation_item = NULL; - - if(mConversationsRoot && - mConversationsRoot->getCurSelectedItem() && - mConversationsRoot->getCurSelectedItem()->getViewModelItem()) - { - LLFloaterIMSessionTab *selected_session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession); - if (selected_session_floater && !selected_session_floater->getHost() && selected_session_floater->getCurSelectedViewModelItem()) - { - conversation_item = selected_session_floater->getCurSelectedViewModelItem(); - } - else - { - conversation_item = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem()); - } - } - - return conversation_item; -} - -void LLFloaterIMContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids) -{ - //Find the conversation floater associated with the selected id - const LLConversationItem * conversation_item = getCurSelectedViewModelItem(); - - if (NULL == conversation_item) - { - return; - } - - getSelectedUUIDs(selected_uuids); -} - -void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS) -{ - if (selectedIDS.size() == 1) - { - const LLUUID& userID = selectedIDS.front(); - if ("view_profile" == command) - { - LLAvatarActions::showProfile(userID); - } - else if ("im" == command) - { - if (gAgent.getID() != userID) - { - LLAvatarActions::startIM(userID); - } - } - else if ("offer_teleport" == command) - { - LLAvatarActions::offerTeleport(selectedIDS); - } - else if ("request_teleport" == command) - { - LLAvatarActions::teleportRequest(selectedIDS.front()); - } - else if ("voice_call" == command) - { - LLAvatarActions::startCall(userID); - } - else if ("chat_history" == command) - { - LLAvatarActions::viewChatHistory(userID); - } - else if ("add_friend" == command) - { - LLAvatarActions::requestFriendshipDialog(userID); - } - else if ("remove_friend" == command) - { - LLAvatarActions::removeFriendDialog(userID); - } - else if ("invite_to_group" == command) - { - LLAvatarActions::inviteToGroup(userID); - } - else if ("zoom_in" == command) - { - handle_zoom_to_object(userID); - } - else if ("map" == command) - { - LLAvatarActions::showOnMap(userID); - } - else if ("share" == command) - { - LLAvatarActions::share(userID); - } - else if ("pay" == command) - { - LLAvatarActions::pay(userID); - } - else if ("report_abuse" == command) - { - LLAvatarName av_name; - if (LLAvatarNameCache::get(userID, &av_name)) - { - LLFloaterReporter::showFromAvatar(userID, av_name.getCompleteName()); - } - else - { - LLFloaterReporter::showFromAvatar(userID, "not avaliable"); - } - } - else if ("block_unblock" == command) - { - LLAvatarActions::toggleMute(userID, LLMute::flagVoiceChat); - } - else if ("mute_unmute" == command) - { - LLAvatarActions::toggleMute(userID, LLMute::flagTextChat); - } - else if ("selected" == command || "mute_all" == command || "unmute_all" == command) - { - moderateVoice(command, userID); - } - else if ("toggle_allow_text_chat" == command) - { - toggleAllowTextChat(userID); - } - else if ("ban_member" == command) - { - banSelectedMember(userID); - } - } - else if (selectedIDS.size() > 1) - { - if ("im" == command) - { - LLAvatarActions::startConference(selectedIDS); - } - else if ("offer_teleport" == command) - { - LLAvatarActions::offerTeleport(selectedIDS); - } - else if ("voice_call" == command) - { - LLAvatarActions::startAdhocCall(selectedIDS); - } - else if ("remove_friend" == command) - { - LLAvatarActions::removeFriendsDialog(selectedIDS); - } - } -} - -void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS) -{ - //Find the conversation floater associated with the selected id - const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); - LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(conversationItem->getUUID()); - - if(conversationFloater) - { - //Close the selected conversation - if("close_conversation" == command) - { - LLFloater::onClickClose(conversationFloater); - } - else if("close_selected_conversations" == command) - { - getSelectedUUIDs(selectedIDS,false); - closeSelectedConversations(selectedIDS); - } - else if("open_voice_conversation" == command) - { - gIMMgr->startCall(conversationItem->getUUID()); - } - else if("disconnect_from_voice" == command) - { - gIMMgr->endCall(conversationItem->getUUID()); - } - else if("chat_history" == command) - { - if (selectedIDS.size() > 0) - { - if(conversationItem->getType() == LLConversationItem::CONV_SESSION_GROUP) - { - LLFloaterReg::showInstance("preview_conversation", conversationItem->getUUID(), true); - } - else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_AD_HOC) - { - LLConversation* conv = LLConversationLog::instance().findConversation(LLIMModel::getInstance()->findIMSession(conversationItem->getUUID())); - if(conv) - { - LLFloaterReg::showInstance("preview_conversation", conv->getSessionID(), true); - } - } - else - { - LLAvatarActions::viewChatHistory(selectedIDS.front()); - } - } - } - else - { - if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1) - { - doToParticipants(command, selectedIDS); - } - } - } - //if there is no LLFloaterIMSession* instance for selected conversation it might be Nearby chat - else - { - if(conversationItem->getType() == LLConversationItem::CONV_SESSION_NEARBY) - { - if("chat_history" == command) - { - LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true); - } -} - } -} - -void LLFloaterIMContainer::doToSelected(const LLSD& userdata) -{ - std::string command = userdata.asString(); - const LLConversationItem * conversationItem = getCurSelectedViewModelItem(); - uuid_vec_t selected_uuids; - - if(conversationItem != NULL) - { - getParticipantUUIDs(selected_uuids); - - if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT) - { - doToParticipants(command, selected_uuids); - } - else - { - doToSelectedConversation(command, selected_uuids); - } - } -} - -void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata) -{ - std::string action = userdata.asString(); - - if (action == "group_profile") - { - LLGroupActions::show(mSelectedSession); - } - else if (action == "activate_group") - { - LLGroupActions::activate(mSelectedSession); - } - else if (action == "leave_group") - { - LLGroupActions::leave(mSelectedSession); - } -} - -bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata) -{ - const std::string& item = userdata.asString(); - uuid_vec_t uuids; - getParticipantUUIDs(uuids); - - - //If there is group or ad-hoc chat in multiselection, everything needs to be disabled - if(uuids.size() > 1) - { - const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList(); - LLConversationItem * conversationItem; - for(std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) - { - conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem()); - if((conversationItem->getType() == LLConversationItem::CONV_SESSION_GROUP) || (conversationItem->getType() == LLConversationItem::CONV_SESSION_AD_HOC)) - { - return false; - } - } - } - - if ("conversation_log" == item) - { - return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0; - } - - //Enable Chat history item for ad-hoc and group conversations - if ("can_chat_history" == item && uuids.size() > 0) - { - //Disable menu item if selected participant is user agent - if(uuids.front() != gAgentID) - { - if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_NEARBY) - { - return LLLogChat::isNearbyTranscriptExist(); - } - else if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_AD_HOC) - { - const LLConversation* conv = LLConversationLog::instance().findConversation(LLIMModel::getInstance()->findIMSession(uuids.front())); - if(conv) - { - return LLLogChat::isAdHocTranscriptExist(conv->getHistoryFileName()); - } - return false; - } - else - { - bool is_group = (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP); - return LLLogChat::isTranscriptExist(uuids.front(),is_group); - } - } - } - - // If nothing is selected(and selected item is not group chat), everything needs to be disabled - if (uuids.size() <= 0) - { - if(getCurSelectedViewModelItem()) - { - return getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP; - } - return false; - } - - if("can_activate_group" == item) - { - LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID(); - return gAgent.getGroupID() != selected_group_id; - } - - return enableContextMenuItem(item, uuids); -} - -bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_vec_t& uuids) -{ - // Extract the single select info - bool is_single_select = (uuids.size() == 1); - const LLUUID& single_id = uuids.front(); - - if ("can_chat_history" == item && is_single_select) - { - return LLLogChat::isTranscriptExist(uuids.front(),false); - } - - // Handle options that are applicable to all including the user agent - if ("can_view_profile" == item) - { - return is_single_select; - } - - bool is_moderator_option = ("can_moderate_voice" == item) || ("can_allow_text_chat" == item) || ("can_mute" == item) || ("can_unmute" == item); - - // Beyond that point, if only the user agent is selected, everything is disabled - if (is_single_select && (single_id == gAgentID)) - { - if (is_moderator_option) - { - return enableModerateContextMenuItem(item, true); - } - else - { - return false; - } - } - - // If the user agent is selected with others, everything is disabled - for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id) - { - if (gAgent.getID() == *id) - { - return false; - } - } - - // Handle all other options - if (("can_invite" == item) - || ("can_chat_history" == item) - || ("can_share" == item) - || ("can_pay" == item) - || ("report_abuse" == item)) - { - // Those menu items are enable only if a single avatar is selected - return is_single_select; - } - else if ("can_block" == item) - { - return (is_single_select ? LLAvatarActions::canBlock(single_id) : false); - } - else if ("can_add" == item) - { - // We can add friends if: - // - there is only 1 selected avatar (EXT-7389) - // - this avatar is not already a friend - return (is_single_select ? !LLAvatarActions::isFriend(single_id) : false); - } - else if ("can_delete" == item) - { - // We can remove friends if there are only friends among the selection - bool result = true; - for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id) - { - result &= LLAvatarActions::isFriend(*id); - } - return result; - } - else if ("can_call" == item) - { - return LLAvatarActions::canCall(); - } - else if ("can_open_voice_conversation" == item) - { - return is_single_select && LLAvatarActions::canCall(); - } - else if ("can_open_voice_conversation" == item) - { - return is_single_select && LLAvatarActions::canCall(); - } - else if ("can_zoom_in" == item) - { - return is_single_select && gObjectList.findObject(single_id); - } - else if ("can_show_on_map" == item) - { - return (is_single_select ? (LLAvatarTracker::instance().isBuddyOnline(single_id) && is_agent_mappable(single_id)) || gAgent.isGodlike() : false); - } - else if ("can_offer_teleport" == item) - { - return LLAvatarActions::canOfferTeleport(uuids); - } - else if ("can_ban_member" == item) - { - return canBanSelectedMember(single_id); - } - else if (is_moderator_option) - { - // *TODO : get that out of here... - return enableModerateContextMenuItem(item); - } - - // By default, options that not explicitely disabled are enabled - return true; -} - -bool LLFloaterIMContainer::checkContextMenuItem(const LLSD& userdata) -{ - std::string item = userdata.asString(); - uuid_vec_t uuids; - getParticipantUUIDs(uuids); - - return checkContextMenuItem(item, uuids); -} - -bool LLFloaterIMContainer::checkContextMenuItem(const std::string& item, uuid_vec_t& uuids) -{ - if (uuids.size() == 1) - { - if ("is_blocked" == item) - { - return LLMuteList::getInstance()->isMuted(uuids.front(), LLMute::flagVoiceChat); - } - else if (item == "is_muted") - { - return LLMuteList::getInstance()->isMuted(uuids.front(), LLMute::flagTextChat); - } - else if ("is_allowed_text_chat" == item) - { - const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); - - if (NULL != speakerp) - { - return !speakerp->mModeratorMutedText; - } - } - } - - return false; -} - -bool LLFloaterIMContainer::visibleContextMenuItem(const LLSD& userdata) -{ - const LLConversationItem *conversation_item = getCurSelectedViewModelItem(); - if(!conversation_item) - { - return false; - } - - const std::string& item = userdata.asString(); - - if ("show_mute" == item) - { - return !isMuted(conversation_item->getUUID()); - } - else if ("show_unmute" == item) - { - return isMuted(conversation_item->getUUID()); - } - - return true; -} - -void LLFloaterIMContainer::showConversation(const LLUUID& session_id) -{ - setVisibleAndFrontmost(false); - selectConversationPair(session_id, true); - - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id); - if (session_floater) - { - session_floater->restoreFloater(); - } -} - -void LLFloaterIMContainer::clearAllFlashStates() -{ - conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin(); - for (;widget_it != mConversationsWidgets.end(); ++widget_it) - { - LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second); - if (widget) - { - widget->setFlashState(false); - } - } -} - -void LLFloaterIMContainer::selectConversation(const LLUUID& session_id) -{ - selectConversationPair(session_id, true); -} - -// Select the conversation *after* (or before if none after) the passed uuid conversation -// Used to change the selection on key hits -void LLFloaterIMContainer::selectNextConversationByID(const LLUUID& uuid) -{ - bool new_selection = false; - selectConversation(uuid); - new_selection = selectNextorPreviousConversation(true); - if (!new_selection) - { - selectNextorPreviousConversation(false); - } -} - -// Synchronous select the conversation item and the conversation floater -bool LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool select_widget, bool focus_floater/*=true*/) -{ - bool handled = true; - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id); - - /* widget processing */ - if (select_widget && mConversationsRoot->getSelectedCount() <= 1) - { - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,session_id); - if (widget && widget->getParentFolder()) - { - widget->getParentFolder()->setSelection(widget, false, false); - mConversationsRoot->scrollToShowSelection(); - } - } - - /* floater processing */ - - if (NULL != session_floater && !session_floater->isDead()) - { - if (session_id != getSelectedSession()) - { - // Store the active session - setSelectedSession(session_id); - - - - if (session_floater->getHost()) - { - // Always expand the message pane if the panel is hosted by the container - collapseMessagesPane(false); - // Switch to the conversation floater that is being selected - selectFloater(session_floater); - } - else - { - showStub(true); - } - - //When in DND mode, remove stored IM notifications - //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal - if(gAgent.isDoNotDisturb() && session_id.notNull()) - { - LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, session_id); - } - } - - // Set the focus on the selected floater - if (!session_floater->hasFocus() && !session_floater->isMinimized()) - { - session_floater->setFocus(focus_floater); - } - } - flashConversationItemWidget(session_id,false); - return handled; -} - -void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id) -{ - LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(getSessionModel(session_id)); - if (item) - { - item->setTimeNow(participant_id); - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - } -} - -void LLFloaterIMContainer::setNearbyDistances() -{ - // Get the nearby chat session: that's the one with uuid nul - LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(getSessionModel(LLUUID())); - if (item) - { - // Get the positions of the nearby avatars and their ids - std::vector<LLVector3d> positions; - uuid_vec_t avatar_ids; - LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange")); - // Get the position of the agent - const LLVector3d& me_pos = gAgent.getPositionGlobal(); - // For each nearby avatar, compute and update the distance - int avatar_count = positions.size(); - for (int i = 0; i < avatar_count; i++) - { - F64 dist = dist_vec_squared(positions[i], me_pos); - item->setDistance(avatar_ids[i],dist); - } - // Also does it for the agent itself - item->setDistance(gAgent.getID(),0.0f); - // Request resort - mConversationViewModel.requestSortAll(); - mConversationsRoot->arrangeAll(); - } -} - -LLConversationItem* LLFloaterIMContainer::addConversationListItem(const LLUUID& uuid, bool isWidgetSelected /*= false*/) -{ - bool is_nearby_chat = uuid.isNull(); - - // Stores the display name for the conversation line item - std::string display_name = is_nearby_chat ? LLTrans::getString("NearbyChatLabel") : LLIMModel::instance().getName(uuid); - - // Check if the item is not already in the list, exit (nothing to do) - // Note: this happens often, when reattaching a torn off conversation for instance - conversations_items_map::iterator item_it = mConversationsItems.find(uuid); - if (item_it != mConversationsItems.end()) - { - return item_it->second; - } - - // Create a conversation session model - LLConversationItemSession* item = NULL; - LLSpeakerMgr* speaker_manager = (is_nearby_chat ? (LLSpeakerMgr*)(LLLocalSpeakerMgr::getInstance()) : LLIMModel::getInstance()->getSpeakerManager(uuid)); - if (speaker_manager) - { - item = new LLParticipantList(speaker_manager, getRootViewModel()); - } - if (!item) - { - LL_WARNS() << "Couldn't create conversation session item : " << display_name << LL_ENDL; - return NULL; - } - item->renameItem(display_name); - item->updateName(NULL); - - mConversationsItems[uuid] = item; - - // Create a widget from it - LLConversationViewSession* widget = createConversationItemWidget(item); - mConversationsWidgets[uuid] = widget; - - // Add a new conversation widget to the root folder of the folder view - widget->addToFolder(mConversationsRoot); - widget->requestArrange(); - - LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(uuid); - - // Create the participants widgets now - // Note: usually, we do not get an updated avatar list at that point - if (uuid.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType())) - { - LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin(); - LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd(); - while (current_participant_model != end_participant_model) - { - LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model); - LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model); - participant_view->addToFolder(widget); - current_participant_model++; - } - } - - if (uuid.notNull() && im_sessionp->isP2PSessionType()) - { - item->fetchAvatarName(false); - } - - // Do that too for the conversation dialog - LLFloaterIMSessionTab *conversation_floater = (uuid.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(uuid))); - if (conversation_floater) - { - conversation_floater->buildConversationViewParticipant(); - } - - // set the widget to minimized mode if conversations pane is collapsed - widget->toggleCollapsedMode(mConversationsPane->isCollapsed()); - - if (isWidgetSelected || 0 == mConversationsRoot->getSelectedCount()) - { - selectConversationPair(uuid, true); - widget->requestArrange(); - - // scroll to newly added item - mConversationsRoot->scrollToShowSelection(); - } - - return item; -} - -bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus) -{ - // Delete the widget and the associated conversation item - // Note : since the mConversationsItems is also the listener to the widget, deleting - // the widget will also delete its listener - bool is_widget_selected = false; - LLFolderViewItem* new_selection = NULL; - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid); - if (widget) - { - is_widget_selected = widget->isSelected(); - if (mConversationsRoot) - { - new_selection = mConversationsRoot->getNextFromChild(widget, false); - if (!new_selection) - { - new_selection = mConversationsRoot->getPreviousFromChild(widget, false); - } - } - - // Will destroy views and delete models that are not assigned to any views - widget->destroyView(); - } - - // Suppress the conversation items and widgets from their respective maps - mConversationsItems.erase(uuid); - mConversationsWidgets.erase(uuid); - // Clear event query (otherwise reopening session in some way can bombard session with stale data) - mConversationEventQueue.erase(uuid); - - // Don't let the focus fall IW, select and refocus on the first conversation in the list - if (change_focus) - { - setFocus(true); - if (new_selection) - { - if (mConversationsWidgets.size() == 1) - { - // If only one widget is left, it has to be the Nearby Chat. Select it directly. - selectConversationPair(LLUUID(NULL), true); - } - else - { - LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem()); - if (vmi) - { - selectConversationPair(vmi->getUUID(), true); - } - } - } - } - return is_widget_selected; -} - -LLConversationViewSession* LLFloaterIMContainer::createConversationItemWidget(LLConversationItem* item) -{ - LLConversationViewSession::Params params; - - params.name = item->getDisplayName(); - params.root = mConversationsRoot; - params.listener = item; - params.tool_tip = params.name; - params.container = this; - - //Indentation for aligning the p2p converstation image with the nearby chat arrow - if(item->getType() == LLConversationItem::CONV_SESSION_1_ON_1) - { - params.folder_indentation = 3; - } - - return LLUICtrlFactory::create<LLConversationViewSession>(params); -} - -LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParticipant(LLConversationItem* item) -{ - LLConversationViewParticipant::Params params; - LLRect panel_rect = mConversationsListPanel->getRect(); - - params.name = item->getDisplayName(); - params.root = mConversationsRoot; - params.listener = item; - - //24 is the the current hight of an item (itemHeight) loaded from conversation_view_participant.xml. - params.rect = LLRect (0, 24, panel_rect.getWidth(), 0); - params.tool_tip = params.name; - params.participant_id = item->getUUID(); - params.folder_indentation = 27; - - return LLUICtrlFactory::create<LLConversationViewParticipant>(params); -} - -bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata, bool is_self) -{ - // only group moderators can perform actions related to this "enable callback" - if (!isGroupModerator()) - { - return false; - } - - LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); - if (NULL == speakerp) - { - return false; - } - - bool voice_channel = speakerp->isInVoiceChannel(); - - if ("can_moderate_voice" == userdata) - { - return voice_channel; - } - else if (("can_mute" == userdata) && !is_self) - { - return voice_channel && !isMuted(getCurSelectedViewModelItem()->getUUID()); - } - else if ("can_unmute" == userdata) - { - return voice_channel && isMuted(getCurSelectedViewModelItem()->getUUID()); - } - - // The last invoke is used to check whether the "can_allow_text_chat" will enabled - return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()) && !is_self; -} - -bool LLFloaterIMContainer::isGroupModerator() -{ - LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); - if (NULL == speaker_manager) - { - LL_WARNS() << "Speaker manager is missing" << LL_ENDL; - return false; - } - - // Is session a group call/chat? - if(gAgent.isInGroup(speaker_manager->getSessionID())) - { - LLSpeaker * speaker = speaker_manager->findSpeaker(gAgentID).get(); - - // Is agent a moderator? - return speaker && speaker->mIsModerator; - } - - return false; -} - -bool LLFloaterIMContainer::haveAbilityToBan() -{ - LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); - if (NULL == speaker_manager) - { - LL_WARNS() << "Speaker manager is missing" << LL_ENDL; - return false; - } - LLUUID group_uuid = speaker_manager->getSessionID(); - - return gAgent.isInGroup(group_uuid) && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS); -} - -bool LLFloaterIMContainer::canBanSelectedMember(const LLUUID& participant_uuid) -{ - LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); - if (NULL == speaker_manager) - { - LL_WARNS() << "Speaker manager is missing" << LL_ENDL; - return false; - } - LLUUID group_uuid = speaker_manager->getSessionID(); - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); - if(!gdatap) - { - LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL; - return false; - } - - if (gdatap->mPendingBanRequest) - { - return false; - } - - if (gdatap->isRoleMemberDataComplete()) - { - if (gdatap->mMembers.size()) - { - LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find((participant_uuid)); - if (mi != gdatap->mMembers.end()) - { - LLGroupMemberData* member_data = (*mi).second; - // Is the member an owner? - if (member_data && member_data->isInRole(gdatap->mOwnerRole)) - { - return false; - } - } - } - } - - if( gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) && - gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS) ) - { - return true; - } - - return false; -} - -void LLFloaterIMContainer::banSelectedMember(const LLUUID& participant_uuid) -{ - LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant(); - if (NULL == speaker_manager) - { - LL_WARNS() << "Speaker manager is missing" << LL_ENDL; - return; - } - - LLUUID group_uuid = speaker_manager->getSessionID(); - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid); - if(!gdatap) - { - LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL; - return; - } - - gdatap->banMemberById(participant_uuid); - -} - -void LLFloaterIMContainer::moderateVoice(const std::string& command, const LLUUID& userID) -{ - if (!gAgent.getRegion()) return; - - if (command.compare("selected")) - { - moderateVoiceAllParticipants(command.compare("mute_all")); - } - else - { - moderateVoiceParticipant(userID, isMuted(userID)); - } -} - -bool LLFloaterIMContainer::isMuted(const LLUUID& avatar_id) -{ - const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant()); - return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED; -} - -void LLFloaterIMContainer::moderateVoiceAllParticipants(bool unmute) -{ - LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant()); - - if (NULL != speaker_managerp) - { - if (!unmute) - { - LLSD payload; - payload["session_id"] = speaker_managerp->getSessionID(); - LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback); - return; - } - - speaker_managerp->moderateVoiceAllParticipants(unmute); - } -} - -// static -void LLFloaterIMContainer::confirmMuteAllCallback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - // if Cancel pressed - if (option == 1) - { - return; - } - - const LLSD& payload = notification["payload"]; - const LLUUID& session_id = payload["session_id"]; - - LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> ( - LLIMModel::getInstance()->getSpeakerManager(session_id)); - if (speaker_manager) - { - speaker_manager->moderateVoiceAllParticipants(false); - } - - return; -} - -void LLFloaterIMContainer::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) -{ - LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr *>(getSpeakerMgrForSelectedParticipant()); - - if (NULL != speaker_managerp) - { - speaker_managerp->moderateVoiceParticipant(avatar_id, unmute); - } -} - -LLSpeakerMgr * LLFloaterIMContainer::getSpeakerMgrForSelectedParticipant() -{ - LLFolderViewItem *selectedItem = mConversationsRoot->getCurSelectedItem(); - if (NULL == selectedItem) - { - LL_WARNS() << "Current selected item is null" << LL_ENDL; - return NULL; - } - - conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin(); - conversations_widgets_map::const_iterator end = mConversationsWidgets.end(); - const LLUUID * conversation_uuidp = NULL; - while(iter != end) - { - if (iter->second == selectedItem || iter->second == selectedItem->getParentFolder()) - { - conversation_uuidp = &iter->first; - break; - } - ++iter; - } - if (NULL == conversation_uuidp) - { - LL_WARNS() << "Cannot find conversation item widget" << LL_ENDL; - return NULL; - } - - return conversation_uuidp->isNull() ? (LLSpeakerMgr *)LLLocalSpeakerMgr::getInstance() - : LLIMModel::getInstance()->getSpeakerManager(*conversation_uuidp); -} - -LLSpeaker * LLFloaterIMContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp) -{ - if (NULL == speaker_managerp) - { - LL_WARNS() << "Speaker manager is missing" << LL_ENDL; - return NULL; - } - - const LLConversationItem * participant_itemp = getCurSelectedViewModelItem(); - if (NULL == participant_itemp) - { - LL_WARNS() << "Cannot evaluate current selected view model item" << LL_ENDL; - return NULL; - } - - return speaker_managerp->findSpeaker(participant_itemp->getUUID()); -} - -void LLFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid) -{ - LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant()); - if (NULL != speaker_managerp) - { - speaker_managerp->toggleAllowTextChat(participant_uuid); - } -} - -void LLFloaterIMContainer::openNearbyChat() -{ - // If there's only one conversation in the container and that conversation is the nearby chat - //(which it should be...), open it so to make the list of participants visible. This happens to be the most common case when opening the Chat floater. - if((mConversationsItems.size() == 1)&&(!mConversationsPane->isCollapsed())) - { - LLConversationViewSession* nearby_chat = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,LLUUID())); - if (nearby_chat) - { - reSelectConversation(); - nearby_chat->setOpen(true); - } - } -} - -void LLFloaterIMContainer::reSelectConversation() -{ - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession); - if (session_floater->getHost()) - { - selectFloater(session_floater); - } -} - -void LLFloaterIMContainer::updateSpeakBtnState() -{ - mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState()); - mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak")); -} - -bool LLFloaterIMContainer::isConversationLoggingAllowed() -{ - return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0; -} - -void LLFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id, bool is_flashes) -{ - //Finds the conversation line item to flash using the session_id - LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id)); - - if (widget) - { - widget->setFlashState(is_flashes); - } -} - -void LLFloaterIMContainer::highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted) -{ - //Finds the conversation line item to highlight using the session_id - LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id)); - - if (widget) - { - widget->setHighlightState(is_highlighted); - } -} - -bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget) -{ - llassert(conversation_item_widget != NULL); - - // make sure the widget is actually in the right spot first - mConversationsRoot->arrange(NULL, NULL); - - // check whether the widget is in the visible portion of the scroll container - LLRect widget_rect; - conversation_item_widget->localRectToOtherView(conversation_item_widget->getLocalRect(), &widget_rect, mConversationsRoot); - return !mConversationsRoot->getVisibleRect().overlaps(widget_rect); -} - -bool LLFloaterIMContainer::handleKeyHere(KEY key, MASK mask ) -{ - bool handled = false; - - if(mask == MASK_ALT) - { - if (KEY_RETURN == key ) - { - expandConversation(); - handled = true; - } - - if ((KEY_DOWN == key ) || (KEY_RIGHT == key)) - { - selectNextorPreviousConversation(true); - handled = true; - } - if ((KEY_UP == key) || (KEY_LEFT == key)) - { - selectNextorPreviousConversation(false); - handled = true; - } - } - return handled; -} - -bool LLFloaterIMContainer::selectAdjacentConversation(bool focus_selected) -{ - bool selectedAdjacentConversation = selectNextorPreviousConversation(true, focus_selected); - - if(!selectedAdjacentConversation) - { - selectedAdjacentConversation = selectNextorPreviousConversation(false, focus_selected); - } - - return selectedAdjacentConversation; -} - -bool LLFloaterIMContainer::selectNextorPreviousConversation(bool select_next, bool focus_selected) -{ - if (mConversationsWidgets.size() > 1) - { - LLFolderViewItem* new_selection = NULL; - LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,getSelectedSession()); - if (widget) - { - if(select_next) - { - new_selection = mConversationsRoot->getNextFromChild(widget, false); - } - else - { - new_selection = mConversationsRoot->getPreviousFromChild(widget, false); - } - if (new_selection) - { - LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem()); - if (vmi) - { - selectConversationPair(vmi->getUUID(), true, focus_selected); - return true; - } - } - } - } - return false; -} - -void LLFloaterIMContainer::expandConversation() -{ - if(!mConversationsPane->isCollapsed()) - { - LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,getSelectedSession())); - if (widget) - { - widget->setOpen(!widget->isOpen()); - } - } -} -bool LLFloaterIMContainer::isParticipantListExpanded() -{ - bool is_expanded = false; - if(!mConversationsPane->isCollapsed()) - { - LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,getSelectedSession())); - if (widget) - { - is_expanded = widget->isOpen(); - } - } - return is_expanded; -} - -// By default, if torn off session is currently frontmost, LLFloater::isFrontmost() will return false, which can lead to some bugs -// So LLFloater::isFrontmost() is overriden here to check both selected session and the IM floater itself -// Exclude "Nearby Chat" session from the check, as "Nearby Chat" window and "Conversations" floater can be brought -// to front independently -/*virtual*/ -bool LLFloaterIMContainer::isFrontmost() -{ - LLFloaterIMSessionTab* selected_session = LLFloaterIMSessionTab::getConversation(mSelectedSession); - LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"); - return (selected_session && selected_session->isFrontmost() && (selected_session != nearby_chat)) - || LLFloater::isFrontmost(); -} - -// For conversations, closeFloater() (linked to Ctrl-W) does not actually close the floater but the active conversation. -// This is intentional so it doesn't confuse the user. onClickCloseBtn() closes the whole floater. -void LLFloaterIMContainer::onClickCloseBtn(bool app_quitting/* = false*/) -{ - gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth()); - LLMultiFloater::closeFloater(app_quitting); -} - -void LLFloaterIMContainer::closeHostedFloater() -{ - onClickCloseBtn(); -} - -void LLFloaterIMContainer::closeAllConversations() -{ - std::vector<LLUUID> ids; - for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++) - { - LLUUID session_id = it_session->first; - if (session_id != LLUUID()) - { - ids.push_back(session_id); - } - } - - for (std::vector<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it) - { - LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(*it); - LLFloater::onClickClose(conversationFloater); - } -} - -void LLFloaterIMContainer::closeSelectedConversations(const uuid_vec_t& ids) -{ - for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) - { - //We don't need to close Nearby chat, so skip it - if (*it != LLUUID()) - { - LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(*it); - if(conversationFloater) - { - LLFloater::onClickClose(conversationFloater); - } - } - } -} -void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/) -{ - if(app_quitting) - { - closeAllConversations(); - onClickCloseBtn(app_quitting); - } - else - { - // Check for currently active session - LLUUID session_id = getSelectedSession(); - // If current session is Nearby Chat or there is only one session remaining, close the floater - if (mConversationsItems.size() == 1 || session_id == LLUUID() || app_quitting) - { - onClickCloseBtn(); - } - else - { - // Otherwise, close current conversation - LLFloaterIMSessionTab* active_conversation = LLFloaterIMSessionTab::getConversation(session_id); - if (active_conversation) - { - active_conversation->closeFloater(); - } - } - } -} - -void LLFloaterIMContainer::handleReshape(const LLRect& rect, bool by_user) -{ - LLMultiFloater::handleReshape(rect, by_user); - storeRectControl(); -} - -// EOF +/**
+ * @file llfloaterimcontainer.cpp
+ * @brief Multifloater containing active IM sessions in separate tab container tabs
+ *
+ * $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"
+
+#include "llfloaterimsession.h"
+#include "llfloaterimcontainer.h"
+
+#include "llfloaterreg.h"
+#include "lllayoutstack.h"
+#include "llfloaterimnearbychat.h"
+
+#include "llagent.h"
+#include "llavataractions.h"
+#include "llavatariconctrl.h"
+#include "llavatarnamecache.h"
+#include "llcallbacklist.h"
+#include "lldonotdisturbnotificationstorage.h"
+#include "llgroupactions.h"
+#include "llgroupiconctrl.h"
+#include "llflashtimer.h"
+#include "llfloateravatarpicker.h"
+#include "llfloaterpreference.h"
+#include "llfloaterreporter.h"
+#include "llimview.h"
+#include "llnotificationsutil.h"
+#include "lltoolbarview.h"
+#include "lltransientfloatermgr.h"
+#include "llviewercontrol.h"
+#include "llconversationview.h"
+#include "llcallbacklist.h"
+#include "llworld.h"
+#include "llsdserialize.h"
+#include "llviewermenu.h" // is_agent_mappable
+#include "llviewerobjectlist.h"
+
+
+const S32 EVENTS_PER_IDLE_LOOP_CURRENT_SESSION = 80;
+const S32 EVENTS_PER_IDLE_LOOP_BACKGROUND = 40;
+const F32 EVENTS_PER_IDLE_LOOP_MIN_PERCENTAGE = 0.01f; // process a minimum of 1% of total events per frame
+
+//
+// LLFloaterIMContainer
+//
+LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& params /*= getDefaultParams()*/)
+: LLMultiFloater(seed, params),
+ mExpandCollapseBtn(NULL),
+ mConversationsRoot(NULL),
+ mConversationsEventStream("ConversationsEvents"),
+ mInitialized(false),
+ mIsFirstLaunch(true),
+ mConversationEventQueue()
+{
+ mEnableCallbackRegistrar.add("IMFloaterContainer.Check", boost::bind(&LLFloaterIMContainer::isActionChecked, this, _2));
+ mCommitCallbackRegistrar.add("IMFloaterContainer.Action", boost::bind(&LLFloaterIMContainer::onCustomAction, this, _2));
+
+ mEnableCallbackRegistrar.add("Avatar.CheckItem", boost::bind(&LLFloaterIMContainer::checkContextMenuItem, this, _2));
+ mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMContainer::enableContextMenuItem, this, _2));
+ mEnableCallbackRegistrar.add("Avatar.VisibleItem", boost::bind(&LLFloaterIMContainer::visibleContextMenuItem, this, _2));
+ mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelected, this, _2));
+
+ mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&LLFloaterIMContainer::doToSelectedGroup, this, _2));
+
+ // Firstly add our self to IMSession observers, so we catch session events
+ LLIMMgr::getInstance()->addSessionObserver(this);
+
+ mAutoResize = false;
+ LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
+}
+
+LLFloaterIMContainer::~LLFloaterIMContainer()
+{
+ mConversationsEventStream.stopListening("ConversationsRefresh");
+ gIdleCallbacks.deleteFunction(idle, this);
+ mNewMessageConnection.disconnect();
+ LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
+
+ if (mMicroChangedSignal.connected())
+ {
+ mMicroChangedSignal.disconnect();
+ }
+
+ gSavedPerAccountSettings.setBOOL("ConversationsListPaneCollapsed", mConversationsPane->isCollapsed());
+ gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed());
+ gSavedPerAccountSettings.setBOOL("ConversationsParticipantListCollapsed", !isParticipantListExpanded());
+
+ if (LLIMMgr::instanceExists())
+ {
+ LLIMMgr::getInstance()->removeSessionObserver(this);
+ }
+}
+
+void LLFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, bool has_offline_msg)
+{
+ addConversationListItem(session_id);
+ LLFloaterIMSessionTab::addToHost(session_id);
+}
+
+void LLFloaterIMContainer::sessionActivated(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
+{
+ if(!isInVisibleChain())
+ {
+ setVisibleAndFrontmost(false);
+ }
+ selectConversationPair(session_id, true);
+ collapseMessagesPane(false);
+}
+
+void LLFloaterIMContainer::sessionVoiceOrIMStarted(const LLUUID& session_id)
+{
+ addConversationListItem(session_id);
+ LLFloaterIMSessionTab::addToHost(session_id);
+}
+
+void LLFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id)
+{
+ // The general strategy when a session id is modified is to delete all related objects and create them anew.
+
+ // Note however that the LLFloaterIMSession has its session id updated through a call to sessionInitReplyReceived()
+ // and do not need to be deleted and recreated (trying this creates loads of problems). We do need however to suppress
+ // its related mSessions record as it's indexed with the wrong id.
+ // Grabbing the updated LLFloaterIMSession and readding it in mSessions will eventually be done by addConversationListItem().
+ mSessions.erase(old_session_id);
+
+ // Delete the model and participants related to the old session
+ bool change_focus = removeConversationListItem(old_session_id);
+
+ // Create a new conversation with the new id
+ addConversationListItem(new_session_id, change_focus);
+ LLFloaterIMSessionTab::addToHost(new_session_id);
+}
+
+
+LLConversationItem* LLFloaterIMContainer::getSessionModel(const LLUUID& session_id)
+{
+ conversations_items_map::iterator iter = mConversationsItems.find(session_id);
+ if (iter == mConversationsItems.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second.get();
+ }
+}
+
+void LLFloaterIMContainer::sessionRemoved(const LLUUID& session_id)
+{
+ removeConversationListItem(session_id);
+}
+
+// static
+void LLFloaterIMContainer::onCurrentChannelChanged(const LLUUID& session_id)
+{
+ if (session_id != LLUUID::null)
+ {
+ LLFloaterIMContainer::getInstance()->showConversation(session_id);
+ }
+}
+
+bool LLFloaterIMContainer::postBuild()
+{
+ mOrigMinWidth = getMinWidth();
+ mOrigMinHeight = getMinHeight();
+
+ mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&LLFloaterIMContainer::onNewMessageReceived, this, _1));
+ // Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button
+ // mTabContainer will be initialized in LLMultiFloater::addChild()
+
+ setTabContainer(getChild<LLTabContainer>("im_box_tab_container"));
+ mStubPanel = getChild<LLPanel>("stub_panel");
+ mStubTextBox = getChild<LLTextBox>("stub_textbox");
+ mStubTextBox->setURLClickedCallback(boost::bind(&LLFloaterIMContainer::returnFloaterToHost, this));
+
+ mConversationsStack = getChild<LLLayoutStack>("conversations_stack");
+ mConversationsPane = getChild<LLLayoutPanel>("conversations_layout_panel");
+ mMessagesPane = getChild<LLLayoutPanel>("messages_layout_panel");
+
+ mConversationsListPanel = getChild<LLPanel>("conversations_list_panel");
+
+ // Open IM session with selected participant on double click event
+ mConversationsListPanel->setDoubleClickCallback(boost::bind(&LLFloaterIMContainer::doToSelected, this, LLSD("im")));
+
+ // The resize limits for LLFloaterIMContainer should be updated, based on current values of width of conversation and message panels
+ mConversationsPane->getResizeBar()->setResizeListener(boost::bind(&LLFloaterIMContainer::assignResizeLimits, this));
+
+ // Create the root model and view for all conversation sessions
+ LLConversationItem* base_item = new LLConversationItem(getRootViewModel());
+
+ LLFolderView::Params p(LLUICtrlFactory::getDefaultParams<LLFolderView>());
+ p.name = getName();
+ p.title = getLabel();
+ p.rect = LLRect(0, 0, getRect().getWidth(), 0);
+ p.parent_panel = mConversationsListPanel;
+ p.tool_tip = p.name;
+ p.listener = base_item;
+ p.view_model = &mConversationViewModel;
+ p.root = NULL;
+ p.use_ellipses = true;
+ p.options_menu = "menu_conversation.xml";
+ mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
+ mConversationsRoot->setCallbackRegistrar(&mCommitCallbackRegistrar);
+ mConversationsRoot->setEnableRegistrar(&mEnableCallbackRegistrar);
+
+ // Add listener to conversation model events
+ mConversationsEventStream.listen("ConversationsRefresh", boost::bind(&LLFloaterIMContainer::onConversationModelEvent, this, _1));
+
+ // a scroller for folder view
+ LLRect scroller_view_rect = mConversationsListPanel->getRect();
+ scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom);
+ scroller_view_rect.mBottom += getChild<LLLayoutStack>("conversations_pane_buttons_stack")->getRect().getHeight();
+ LLScrollContainer::Params scroller_params(LLUICtrlFactory::getDefaultParams<LLFolderViewScrollContainer>());
+ scroller_params.rect(scroller_view_rect);
+
+ LLScrollContainer* scroller = LLUICtrlFactory::create<LLFolderViewScrollContainer>(scroller_params);
+ scroller->setFollowsAll();
+ mConversationsListPanel->addChild(scroller);
+ scroller->addChild(mConversationsRoot);
+ mConversationsRoot->setScrollContainer(scroller);
+ mConversationsRoot->setFollowsAll();
+ mConversationsRoot->addChild(mConversationsRoot->mStatusTextBox);
+
+ addConversationListItem(LLUUID()); // manually add nearby chat
+
+ mExpandCollapseBtn = getChild<LLButton>("expand_collapse_btn");
+ mExpandCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onExpandCollapseButtonClicked, this));
+ mStubCollapseBtn = getChild<LLButton>("stub_collapse_btn");
+ mStubCollapseBtn->setClickedCallback(boost::bind(&LLFloaterIMContainer::onStubCollapseButtonClicked, this));
+ mSpeakBtn = getChild<LLButton>("speak_btn");
+
+ mSpeakBtn->setMouseDownCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonPressed, this));
+ mSpeakBtn->setMouseUpCallback(boost::bind(&LLFloaterIMContainer::onSpeakButtonReleased, this));
+
+ childSetAction("add_btn", boost::bind(&LLFloaterIMContainer::onAddButtonClicked, this));
+
+ collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed"));
+ collapseConversationsPane(gSavedPerAccountSettings.getBOOL("ConversationsListPaneCollapsed"), false);
+ LLAvatarNameCache::getInstance()->addUseDisplayNamesCallback(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false));
+ mMicroChangedSignal = LLVoiceClient::getInstance()->MicroChangedCallback(boost::bind(&LLFloaterIMContainer::updateSpeakBtnState, this));
+
+ if (! mMessagesPane->isCollapsed() && ! mConversationsPane->isCollapsed())
+ {
+ S32 conversations_panel_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth");
+ LLRect conversations_panel_rect = mConversationsPane->getRect();
+ conversations_panel_rect.mRight = conversations_panel_rect.mLeft + conversations_panel_width;
+ mConversationsPane->handleReshape(conversations_panel_rect, true);
+ }
+
+ // Init the sort order now that the root had been created
+ setSortOrder(LLConversationSort(gSavedSettings.getU32("ConversationSortOrder")));
+
+ //We should expand nearby chat participants list for the new user
+ if(gAgent.isFirstLogin() || !gSavedPerAccountSettings.getBOOL("ConversationsParticipantListCollapsed"))
+ {
+ expandConversation();
+ }
+ // Keep the xml set title around for when we have to overwrite it
+ mGeneralTitle = getTitle();
+
+ mInitialized = true;
+
+ mIsFirstOpen = true;
+
+ // Add callbacks:
+ // We'll take care of view updates on idle
+ gIdleCallbacks.addFunction(idle, this);
+ // When display name option change, we need to reload all participant names
+ LLAvatarNameCache::getInstance()->addUseDisplayNamesCallback(boost::bind(&LLFloaterIMContainer::processParticipantsStyleUpdate, this));
+
+ mParticipantRefreshTimer.setTimerExpirySec(0);
+ mParticipantRefreshTimer.start();
+
+ return true;
+}
+
+void LLFloaterIMContainer::onOpen(const LLSD& key)
+{
+ LLMultiFloater::onOpen(key);
+ reSelectConversation();
+ assignResizeLimits();
+
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession);
+ session_floater->onOpen(key);
+}
+
+// virtual
+void LLFloaterIMContainer::addFloater(LLFloater* floaterp,
+ bool select_added_floater,
+ LLTabContainer::eInsertionPoint insertion_point)
+{
+ if(!floaterp) return;
+
+ // already here
+ if (floaterp->getHost() == this)
+ {
+ openFloater(floaterp->getKey());
+ return;
+ }
+
+ LLUUID session_id = floaterp->getKey();
+
+ // Add the floater
+ LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+
+
+
+ LLIconCtrl* icon = 0;
+ bool is_in_group = gAgent.isInGroup(session_id, true);
+ LLUUID icon_id;
+
+ if (is_in_group)
+ {
+ LLGroupIconCtrl::Params icon_params;
+ icon_params.group_id = session_id;
+ icon = LLUICtrlFactory::instance().create<LLGroupIconCtrl>(icon_params);
+ icon_id = session_id;
+
+ mSessions[session_id] = floaterp;
+ floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id));
+ }
+ else
+ { LLUUID avatar_id = session_id.notNull()?
+ LLIMModel::getInstance()->getOtherParticipantID(session_id) : LLUUID();
+
+ LLAvatarIconCtrl::Params icon_params;
+ icon_params.avatar_id = avatar_id;
+ icon = LLUICtrlFactory::instance().create<LLAvatarIconCtrl>(icon_params);
+ icon_id = avatar_id;
+
+ mSessions[session_id] = floaterp;
+ floaterp->mCloseSignal.connect(boost::bind(&LLFloaterIMContainer::onCloseFloater, this, session_id));
+ }
+
+ LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id);
+ if (floater)
+ {
+ floater->updateChatIcon(icon_id);
+ }
+
+ // forced resize of the floater
+ LLRect wrapper_rect = this->mTabContainer->getLocalRect();
+ floaterp->setRect(wrapper_rect);
+
+ mTabContainer->setTabImage(floaterp, icon);
+}
+
+
+void LLFloaterIMContainer::onCloseFloater(LLUUID& id)
+{
+ mSessions.erase(id);
+ setFocus(true);
+}
+
+void LLFloaterIMContainer::onNewMessageReceived(const LLSD& data)
+{
+ LLUUID session_id = data["session_id"].asUUID();
+ LLFloater* floaterp = get_ptr_in_map(mSessions, session_id);
+ LLFloater* current_floater = LLMultiFloater::getActiveFloater();
+
+ if(floaterp && current_floater && floaterp != current_floater)
+ {
+ if(LLMultiFloater::isFloaterFlashing(floaterp))
+ LLMultiFloater::setFloaterFlashing(floaterp, false);
+ LLMultiFloater::setFloaterFlashing(floaterp, true);
+ }
+}
+
+void LLFloaterIMContainer::onStubCollapseButtonClicked()
+{
+ collapseMessagesPane(true);
+}
+
+void LLFloaterIMContainer::onSpeakButtonPressed()
+{
+ LLVoiceClient::getInstance()->inputUserControlState(true);
+ updateSpeakBtnState();
+}
+
+void LLFloaterIMContainer::onSpeakButtonReleased()
+{
+ LLVoiceClient::getInstance()->inputUserControlState(false);
+ updateSpeakBtnState();
+}
+
+void LLFloaterIMContainer::onExpandCollapseButtonClicked()
+{
+ if (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed()
+ && gSavedPerAccountSettings.getBOOL("ConversationsExpandMessagePaneFirst"))
+ {
+ // Expand the messages pane from ultra minimized state
+ // if it was collapsed last in order.
+ collapseMessagesPane(false);
+ }
+ else
+ {
+ collapseConversationsPane(!mConversationsPane->isCollapsed());
+ }
+ reSelectConversation();
+}
+
+LLFloaterIMContainer* LLFloaterIMContainer::findInstance()
+{
+ return LLFloaterReg::findTypedInstance<LLFloaterIMContainer>("im_container");
+}
+
+LLFloaterIMContainer* LLFloaterIMContainer::getInstance()
+{
+ return LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+}
+
+// Update all participants in the conversation lists
+void LLFloaterIMContainer::processParticipantsStyleUpdate()
+{
+ // On each session in mConversationsItems
+ for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++)
+ {
+ // Get the current session descriptors
+ LLConversationItem* session_model = it_session->second;
+ // Iterate through each model participant child
+ LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = session_model->getChildrenBegin();
+ LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = session_model->getChildrenEnd();
+ while (current_participant_model != end_participant_model)
+ {
+ LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model);
+ if (participant_model)
+ {
+ // Get the avatar name for this participant id from the cache and update the model
+ participant_model->updateName();
+ }
+ // Next participant
+ current_participant_model++;
+ }
+ }
+}
+
+// static
+void LLFloaterIMContainer::idle(void* user_data)
+{
+ LLFloaterIMContainer* self = static_cast<LLFloaterIMContainer*>(user_data);
+
+ self->idleProcessEvents();
+
+ if (!self->getVisible() || self->isMinimized())
+ {
+ return;
+ }
+ self->idleUpdate();
+}
+
+void LLFloaterIMContainer::idleUpdate()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ // Do not close the container when every conversation is torn off because the user
+ // still needs the conversation list. Simply collapse the message pane in that case.
+ collapseMessagesPane(true);
+ }
+
+ U32 sort_order = mConversationViewModel.getSorter().getSortOrderParticipants();
+
+ if (mParticipantRefreshTimer.hasExpired())
+ {
+ const LLConversationItem *current_session = getCurSelectedViewModelItem();
+ if (current_session)
+ {
+ if (current_session->getType() == LLConversationItem::CONV_SESSION_GROUP)
+ {
+ // Update moderator options visibility
+ LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = current_session->getChildrenBegin();
+ LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = current_session->getChildrenEnd();
+ bool is_moderator = isGroupModerator();
+ bool can_ban = haveAbilityToBan();
+ while (current_participant_model != end_participant_model)
+ {
+ LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(*current_participant_model);
+ if (participant_model)
+ {
+ participant_model->setModeratorOptionsVisible(is_moderator);
+ participant_model->setGroupBanVisible(can_ban && participant_model->getUUID() != gAgentID);
+ }
+
+ current_participant_model++;
+ }
+ }
+
+ // Update floater's title as required by the currently selected session or use the default title
+ LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(current_session->getUUID());
+ setTitle(conversation_floaterp && conversation_floaterp->needsTitleOverwrite() ? conversation_floaterp->getTitle() : mGeneralTitle);
+ }
+
+ mParticipantRefreshTimer.setTimerExpirySec(1.0f);
+ }
+
+ // Update the distance to agent in the nearby chat session if required
+ // Note: it makes no sense of course to update the distance in other session
+ if (sort_order == LLConversationFilter::SO_DISTANCE)
+ {
+ // almost real-time updates
+ setNearbyDistances(); //calls arrange all
+ }
+ mConversationsRoot->update(); //arranges, resizes, heavy
+
+ // "Manually" resize of mConversationsPane: same as temporarity cancellation of the flag "auto_resize=false" for it
+ if (!mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed())
+ {
+ LLRect stack_rect = mConversationsStack->getRect();
+ mConversationsPane->reshape(stack_rect.getWidth(), stack_rect.getHeight(), true);
+ }
+}
+
+void LLFloaterIMContainer::idleProcessEvents()
+{
+ LLUUID current_session_id = getSelectedSession();
+ conversations_items_deque::iterator iter = mConversationEventQueue.begin();
+ conversations_items_deque::iterator end = mConversationEventQueue.end();
+ while (iter != end)
+ {
+ std::deque<LLSD> &events = iter->second;
+ if (!events.empty())
+ {
+ S32 events_to_handle;
+ S32 query_size = (S32)events.size();
+ if (current_session_id == iter->first)
+ {
+ events_to_handle = EVENTS_PER_IDLE_LOOP_CURRENT_SESSION;
+ }
+ else
+ {
+ events_to_handle = EVENTS_PER_IDLE_LOOP_BACKGROUND;
+ }
+
+ if (events_to_handle <= query_size)
+ {
+ // Some groups can be very large and can generate huge amount of updates, scale processing up to keep up
+ events_to_handle = llmax(events_to_handle, (S32)(query_size * EVENTS_PER_IDLE_LOOP_MIN_PERCENTAGE));
+ }
+ else
+ {
+ events_to_handle = query_size;
+ }
+
+ for (S32 i = 0; i < events_to_handle; i++)
+ {
+ handleConversationModelEvent(events.back());
+ events.pop_back();
+ }
+ }
+ iter++;
+ }
+}
+
+bool LLFloaterIMContainer::onConversationModelEvent(const LLSD& event)
+{
+ LLUUID id = event.get("session_uuid").asUUID();
+ mConversationEventQueue[id].push_front(event);
+ return true;
+}
+
+
+void LLFloaterIMContainer::handleConversationModelEvent(const LLSD& event)
+{
+
+ // Note: In conversations, the model is not responsible for creating the view, which is a good thing. This means that
+ // the model could change substantially and the view could echo only a portion of this model (though currently the
+ // conversation view does echo the conversation model 1 to 1).
+ // Consequently, the participant views need to be created either by the session view or by the container panel.
+ // For the moment, we create them here, at the container level, to conform to the pattern implemented in llinventorypanel.cpp
+ // (see LLInventoryPanel::buildNewViews()).
+
+ std::string type = event.get("type").asString();
+ LLUUID session_id = event.get("session_uuid").asUUID();
+ LLUUID participant_id = event.get("participant_uuid").asUUID();
+
+ LLConversationViewSession* session_view = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,session_id));
+ if (!session_view)
+ {
+ // We skip events that are not associated with a session
+ return;
+ }
+ LLConversationViewParticipant* participant_view = session_view->findParticipant(participant_id);
+ LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ?
+ (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat"))
+ : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id)));
+
+ if (type == "remove_participant")
+ {
+ // Remove a participant view from the hierarchical conversation list
+ if (participant_view)
+ {
+ session_view->extractItem(participant_view);
+ delete participant_view;
+ session_view->refresh();
+ mConversationsRoot->arrangeAll();
+ }
+ // Remove a participant view from the conversation floater
+ if (conversation_floater)
+ {
+ conversation_floater->removeConversationViewParticipant(participant_id);
+ }
+ }
+ else if (type == "add_participant")
+ {
+ LLConversationItem* item = getSessionModel(session_id);
+ LLConversationItemSession* session_model = dynamic_cast<LLConversationItemSession*>(item);
+ LLConversationItemParticipant* participant_model = (session_model ? session_model->findParticipant(participant_id) : NULL);
+ LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(session_id);
+ if (!participant_view && session_model && participant_model)
+ {
+ if (session_id.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType()))
+ {
+ participant_view = createConversationViewParticipant(participant_model);
+ participant_view->addToFolder(session_view);
+ participant_view->setVisible(true);
+ }
+ }
+ // Add a participant view to the conversation floater
+ if (conversation_floater && participant_model)
+ {
+ bool skip_updating = im_sessionp && im_sessionp->isGroupChat();
+ conversation_floater->addConversationViewParticipant(participant_model, !skip_updating);
+ }
+ }
+ else if (type == "update_participant")
+ {
+ // Update the participant view in the hierarchical conversation list
+ if (participant_view)
+ {
+ participant_view->refresh();
+ }
+ // Update the participant view in the conversation floater
+ if (conversation_floater)
+ {
+ conversation_floater->updateConversationViewParticipant(participant_id);
+ }
+ }
+ else if (type == "update_session")
+ {
+ session_view->refresh();
+ }
+
+ mConversationViewModel.requestSortAll();
+ mConversationsRoot->arrangeAll();
+}
+
+void LLFloaterIMContainer::draw()
+{
+ LLFloater::draw();
+}
+
+void LLFloaterIMContainer::tabClose()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ // Do not close the container when every conversation is torn off because the user
+ // still needs the conversation list. Simply collapse the message pane in that case.
+ collapseMessagesPane(true);
+ }
+}
+
+//Shows/hides the stub panel when a conversation floater is torn off
+void LLFloaterIMContainer::showStub(bool stub_is_visible)
+{
+ S32 tabCount = 0;
+ LLPanel * tabPanel = NULL;
+
+ if(stub_is_visible)
+ {
+ tabCount = mTabContainer->getTabCount();
+
+ //Hide all tabs even stub
+ for(S32 i = 0; i < tabCount; ++i)
+ {
+ tabPanel = mTabContainer->getPanelByIndex(i);
+
+ if(tabPanel)
+ {
+ tabPanel->setVisible(false);
+ }
+ }
+
+ //Set the index to the stub panel since we will be showing the stub
+ mTabContainer->setCurrentPanelIndex(0);
+ }
+
+ //Now show/hide the stub
+ mStubPanel->setVisible(stub_is_visible);
+}
+
+// listener for click on mStubTextBox2
+void LLFloaterIMContainer::returnFloaterToHost()
+{
+ LLUUID session_id = this->getSelectedSession();
+ LLFloaterIMSessionTab* floater = LLFloaterIMSessionTab::getConversation(session_id);
+ floater->onTearOffClicked();
+}
+
+void LLFloaterIMContainer::setMinimized(bool b)
+{
+ bool was_minimized = isMinimized();
+ LLMultiFloater::setMinimized(b);
+
+ //Switching from minimized to un-minimized
+ if(was_minimized && !b)
+ {
+ gToolBarView->flashCommand(LLCommandId("chat"), false);
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession);
+
+ if(session_floater && !session_floater->isTornOff())
+ {
+ //When in DND mode, remove stored IM notifications
+ //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal
+ if(gAgent.isDoNotDisturb() && mSelectedSession.notNull())
+ {
+ LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession);
+ }
+ }
+ }
+}
+
+void LLFloaterIMContainer::setVisible(bool visible)
+{
+ LLFloaterIMNearbyChat* nearby_chat;
+ if (visible)
+ {
+ // Make sure we have the Nearby Chat present when showing the conversation container
+ nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if ((nearby_chat == NULL) || mIsFirstOpen)
+ {
+ mIsFirstOpen = false;
+ // If not found, force the creation of the nearby chat conversation panel
+ // *TODO: find a way to move this to XML as a default panel or something like that
+ LLSD name("nearby_chat");
+ LLFloaterReg::toggleInstanceOrBringToFront(name);
+ selectConversationPair(LLUUID(NULL), false, false);
+ }
+
+ flashConversationItemWidget(mSelectedSession,false);
+
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(mSelectedSession);
+ if(session_floater && !session_floater->isMinimized())
+ {
+ //When in DND mode, remove stored IM notifications
+ //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal
+ if(gAgent.isDoNotDisturb() && mSelectedSession.notNull())
+ {
+ LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, mSelectedSession);
+ }
+ }
+ }
+
+ nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ if (nearby_chat)
+ {
+ LLFloaterIMSessionTab::addToHost(LLUUID());
+ }
+
+ if (!LLFloater::isQuitRequested())
+ {
+ // We need to show/hide all the associated conversations that have been torn off
+ // (and therefore, are not longer managed by the multifloater),
+ // so that they show/hide with the conversations manager.
+ conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
+ for (; widget_it != mConversationsWidgets.end(); ++widget_it)
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second);
+ if (widget)
+ {
+ LLFloater* session_floater = widget->getSessionFloater();
+ if (session_floater != nearby_chat)
+ {
+ widget->setVisibleIfDetached(visible);
+ }
+ }
+ }
+ }
+
+ // Now, do the normal multifloater show/hide
+ LLMultiFloater::setVisible(visible);
+}
+
+void LLFloaterIMContainer::getDetachedConversationFloaters(floater_list_t& floaters)
+{
+ LLFloaterIMNearbyChat *nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+
+ for (const auto& [key, fvi] : mConversationsWidgets)
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(fvi);
+ if (widget)
+ {
+ LLFloater* session_floater = widget->getSessionFloater();
+
+ // Exclude nearby chat from output, as it should be handled separately
+ if (session_floater && session_floater->isDetachedAndNotMinimized()
+ && session_floater != nearby_chat)
+ {
+ floaters.push_back(session_floater);
+ }
+ }
+ }
+}
+
+void LLFloaterIMContainer::setVisibleAndFrontmost(bool take_focus, const LLSD& key)
+{
+ LLMultiFloater::setVisibleAndFrontmost(take_focus, key);
+ // Do not select "Nearby Chat" conversation, since it will bring its window to front
+ // Only select other sessions
+ if (!getSelectedSession().isNull())
+ {
+ selectConversationPair(getSelectedSession(), false, take_focus);
+ }
+ if (mInitialized && mIsFirstLaunch)
+ {
+ collapseMessagesPane(gSavedPerAccountSettings.getBOOL("ConversationsMessagePaneCollapsed"));
+ mIsFirstLaunch = false;
+}
+}
+
+void LLFloaterIMContainer::updateResizeLimits()
+{
+ LLMultiFloater::updateResizeLimits();
+ assignResizeLimits();
+}
+
+bool LLFloaterIMContainer::isMessagesPaneCollapsed()
+{
+ return mMessagesPane->isCollapsed();
+}
+
+bool LLFloaterIMContainer::isConversationsPaneCollapsed()
+{
+ return mConversationsPane->isCollapsed();
+}
+
+void LLFloaterIMContainer::collapseMessagesPane(bool collapse)
+{
+ if (mMessagesPane->isCollapsed() == collapse)
+ {
+ return;
+ }
+
+ // Save current width of panels before collapsing/expanding right pane.
+ S32 conv_pane_width = mConversationsPane->getRect().getWidth();
+ S32 msg_pane_width = mMessagesPane->getRect().getWidth();
+
+ if (collapse)
+ {
+ // Save the messages pane width before collapsing it.
+ gSavedPerAccountSettings.setS32("ConversationsMessagePaneWidth", msg_pane_width);
+
+ // Save the order in which the panels are closed to reverse user's last action.
+ gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", mConversationsPane->isCollapsed());
+ }
+
+ mConversationsPane->setIgnoreReshape(collapse);
+
+ // Show/hide the messages pane.
+ mConversationsStack->collapsePanel(mMessagesPane, collapse);
+
+ // Make sure layout is updated before resizing conversation pane.
+ mConversationsStack->updateLayout();
+
+ reshapeFloaterAndSetResizeLimits(collapse, gSavedPerAccountSettings.getS32("ConversationsMessagePaneWidth"));
+
+ if (!collapse)
+ {
+ // Restore conversation's pane previous width after expanding messages pane.
+ mConversationsPane->setTargetDim(conv_pane_width);
+ }
+}
+
+void LLFloaterIMContainer::collapseConversationsPane(bool collapse, bool save_is_allowed /*=true*/)
+{
+ if (mConversationsPane->isCollapsed() == collapse)
+ {
+ return;
+ }
+
+ LLView* button_panel = getChild<LLView>("conversations_pane_buttons_expanded");
+ button_panel->setVisible(!collapse);
+ mExpandCollapseBtn->setImageOverlay(getString(collapse ? "expand_icon" : "collapse_icon"));
+
+ // Save current width of Conversation panel before collapsing/expanding right pane.
+ S32 conv_pane_width = mConversationsPane->getRect().getWidth();
+
+ if (collapse && save_is_allowed)
+ {
+ // Save the conversations pane width before collapsing it.
+ gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", conv_pane_width);
+
+ // Save the order in which the panels are closed to reverse user's last action.
+ gSavedPerAccountSettings.setBOOL("ConversationsExpandMessagePaneFirst", !mMessagesPane->isCollapsed());
+ }
+
+ mConversationsStack->collapsePanel(mConversationsPane, collapse);
+ if (!collapse)
+ {
+ // Make sure layout is updated before resizing conversation pane.
+ mConversationsStack->updateLayout();
+ // Restore conversation's pane previous width.
+ mConversationsPane->setTargetDim(gSavedPerAccountSettings.getS32("ConversationsListPaneWidth"));
+ }
+
+ S32 delta_width = gSavedPerAccountSettings.getS32("ConversationsListPaneWidth")
+ - mConversationsPane->getMinDim() - mConversationsStack->getPanelSpacing() + 1;
+
+ reshapeFloaterAndSetResizeLimits(collapse, delta_width);
+
+ for (conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
+ widget_it != mConversationsWidgets.end(); ++widget_it)
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second);
+ if (widget)
+ {
+ widget->toggleCollapsedMode(collapse);
+
+ // force closing all open conversations when collapsing to minimized state
+ if (collapse)
+ {
+ widget->setOpen(false);
+ }
+ widget->requestArrange();
+ }
+ }
+}
+
+void LLFloaterIMContainer::reshapeFloaterAndSetResizeLimits(bool collapse, S32 delta_width)
+{
+ LLRect floater_rect = getRect();
+ floater_rect.mRight += ((collapse ? -1 : 1) * delta_width);
+
+ // Set by_user = true so that reshaped rect is saved in user_settings.
+ setShape(floater_rect, true);
+ updateResizeLimits();
+
+ bool at_least_one_panel_is_expanded =
+ ! (mConversationsPane->isCollapsed() && mMessagesPane->isCollapsed());
+
+ setCanResize(at_least_one_panel_is_expanded);
+ setCanMinimize(at_least_one_panel_is_expanded);
+
+ assignResizeLimits();
+}
+
+void LLFloaterIMContainer::assignResizeLimits()
+{
+ bool is_conv_pane_expanded = !mConversationsPane->isCollapsed();
+ bool is_msg_pane_expanded = !mMessagesPane->isCollapsed();
+
+ S32 summary_width_of_visible_borders = (is_msg_pane_expanded ? mConversationsStack->getPanelSpacing() : 0) + 1;
+
+ S32 conv_pane_target_width = is_conv_pane_expanded
+ ? ( is_msg_pane_expanded?mConversationsPane->getRect().getWidth():mConversationsPane->getExpandedMinDim() )
+ : mConversationsPane->getMinDim();
+
+ S32 msg_pane_min_width = is_msg_pane_expanded ? mMessagesPane->getExpandedMinDim() : 0;
+ S32 new_min_width = conv_pane_target_width + msg_pane_min_width + summary_width_of_visible_borders;
+
+ setResizeLimits(new_min_width, getMinHeight());
+
+ mConversationsStack->updateLayout();
+}
+
+void LLFloaterIMContainer::onAddButtonClicked()
+{
+ LLView * button = findChild<LLView>("conversations_pane_buttons_expanded")->findChild<LLButton>("add_btn");
+ LLFloater* root_floater = gFloaterView->getParentFloater(this);
+ LLFloaterAvatarPicker* picker = LLFloaterAvatarPicker::show(boost::bind(&LLFloaterIMContainer::onAvatarPicked, this, _1), true, true, true, root_floater->getName(), button);
+
+ if (picker && root_floater)
+ {
+ root_floater->addDependentFloater(picker);
+ }
+}
+
+void LLFloaterIMContainer::onAvatarPicked(const uuid_vec_t& ids)
+{
+ if (ids.size() == 1)
+ {
+ LLAvatarActions::startIM(ids.back());
+ }
+ else
+ {
+ LLAvatarActions::startConference(ids);
+ }
+}
+
+void LLFloaterIMContainer::onCustomAction(const LLSD& userdata)
+{
+ std::string command = userdata.asString();
+
+ if ("sort_sessions_by_type" == command)
+ {
+ setSortOrderSessions(LLConversationFilter::SO_SESSION_TYPE);
+ }
+ if ("sort_sessions_by_name" == command)
+ {
+ setSortOrderSessions(LLConversationFilter::SO_NAME);
+ }
+ if ("sort_sessions_by_recent" == command)
+ {
+ setSortOrderSessions(LLConversationFilter::SO_DATE);
+ }
+ if ("sort_participants_by_name" == command)
+ {
+ setSortOrderParticipants(LLConversationFilter::SO_NAME);
+ }
+ if ("sort_participants_by_recent" == command)
+ {
+ setSortOrderParticipants(LLConversationFilter::SO_DATE);
+ }
+ if ("sort_participants_by_distance" == command)
+ {
+ setSortOrderParticipants(LLConversationFilter::SO_DISTANCE);
+ }
+ if ("chat_preferences" == command)
+ {
+ LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences");
+ if (floater_prefp)
+ {
+ floater_prefp->selectChatPanel();
+ }
+ }
+ if ("privacy_preferences" == command)
+ {
+ LLFloaterPreference * floater_prefp = LLFloaterReg::showTypedInstance<LLFloaterPreference>("preferences");
+ if (floater_prefp)
+ {
+ floater_prefp->selectPrivacyPanel();
+ }
+ }
+ if ("Translating.Toggle" == command)
+ {
+ gSavedSettings.setBOOL("TranslateChat", !gSavedSettings.getBOOL("TranslateChat"));
+ }
+}
+
+bool LLFloaterIMContainer::isActionChecked(const LLSD& userdata)
+{
+ LLConversationSort order = mConversationViewModel.getSorter();
+ std::string command = userdata.asString();
+ if ("sort_sessions_by_type" == command)
+ {
+ return (order.getSortOrderSessions() == LLConversationFilter::SO_SESSION_TYPE);
+ }
+ if ("sort_sessions_by_name" == command)
+ {
+ return (order.getSortOrderSessions() == LLConversationFilter::SO_NAME);
+ }
+ if ("sort_sessions_by_recent" == command)
+ {
+ return (order.getSortOrderSessions() == LLConversationFilter::SO_DATE);
+ }
+ if ("sort_participants_by_name" == command)
+ {
+ return (order.getSortOrderParticipants() == LLConversationFilter::SO_NAME);
+ }
+ if ("sort_participants_by_recent" == command)
+ {
+ return (order.getSortOrderParticipants() == LLConversationFilter::SO_DATE);
+ }
+ if ("sort_participants_by_distance" == command)
+ {
+ return (order.getSortOrderParticipants() == LLConversationFilter::SO_DISTANCE);
+ }
+ if ("Translating.Enabled" == command)
+ {
+ return gSavedPerAccountSettings.getBOOL("TranslatingEnabled");
+ }
+ if ("Translating.On" == command)
+ {
+ return gSavedSettings.getBOOL("TranslateChat");
+ }
+ return false;
+}
+
+void LLFloaterIMContainer::setSortOrderSessions(const LLConversationFilter::ESortOrderType order)
+{
+ LLConversationSort old_order = mConversationViewModel.getSorter();
+ if (order != old_order.getSortOrderSessions())
+ {
+ old_order.setSortOrderSessions(order);
+ setSortOrder(old_order);
+ }
+}
+
+void LLFloaterIMContainer::setSortOrderParticipants(const LLConversationFilter::ESortOrderType order)
+{
+ LLConversationSort old_order = mConversationViewModel.getSorter();
+ if (order != old_order.getSortOrderParticipants())
+ {
+ old_order.setSortOrderParticipants(order);
+ setSortOrder(old_order);
+ }
+}
+
+void LLFloaterIMContainer::setSortOrder(const LLConversationSort& order)
+{
+ mConversationViewModel.setSorter(order);
+ mConversationsRoot->arrangeAll();
+ // try to keep selection onscreen, even if it wasn't to start with
+ mConversationsRoot->scrollToShowSelection();
+
+ // Notify all conversation (torn off or not) of the change to the sort order
+ // Note: For the moment, the sort order is *unique* across all conversations. That might change in the future.
+ for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++)
+ {
+ LLUUID session_id = it_session->first;
+ LLFloaterIMSessionTab *conversation_floater = (session_id.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(session_id)));
+ if (conversation_floater)
+ {
+ conversation_floater->setSortOrder(order);
+ }
+ }
+
+ gSavedSettings.setU32("ConversationSortOrder", (U32)order);
+}
+
+void LLFloaterIMContainer::getSelectedUUIDs(uuid_vec_t& selected_uuids, bool participant_uuids/* = true*/)
+{
+ const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList();
+
+ std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin();
+ const std::set<LLFolderViewItem*>::const_iterator it_end = selectedItems.end();
+ LLConversationItem * conversationItem;
+
+ for (; it != it_end; ++it)
+ {
+ conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem());
+
+ //When a one-on-one conversation exists, retrieve the participant id from the conversation floater
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1 && participant_uuids)
+ {
+ LLFloaterIMSession * conversation_floaterp = LLFloaterIMSession::findInstance(conversationItem->getUUID());
+ LLUUID participant_id = conversation_floaterp->getOtherParticipantUUID();
+ selected_uuids.push_back(participant_id);
+ }
+ else
+ {
+ selected_uuids.push_back(conversationItem->getUUID());
+ }
+ }
+}
+
+const LLConversationItem * LLFloaterIMContainer::getCurSelectedViewModelItem()
+{
+ LLConversationItem * conversation_item = NULL;
+
+ if(mConversationsRoot &&
+ mConversationsRoot->getCurSelectedItem() &&
+ mConversationsRoot->getCurSelectedItem()->getViewModelItem())
+ {
+ LLFloaterIMSessionTab *selected_session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession);
+ if (selected_session_floater && !selected_session_floater->getHost() && selected_session_floater->getCurSelectedViewModelItem())
+ {
+ conversation_item = selected_session_floater->getCurSelectedViewModelItem();
+ }
+ else
+ {
+ conversation_item = static_cast<LLConversationItem *>(mConversationsRoot->getCurSelectedItem()->getViewModelItem());
+ }
+ }
+
+ return conversation_item;
+}
+
+void LLFloaterIMContainer::getParticipantUUIDs(uuid_vec_t& selected_uuids)
+{
+ //Find the conversation floater associated with the selected id
+ const LLConversationItem * conversation_item = getCurSelectedViewModelItem();
+
+ if (NULL == conversation_item)
+ {
+ return;
+ }
+
+ getSelectedUUIDs(selected_uuids);
+}
+
+void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec_t& selectedIDS)
+{
+ if (selectedIDS.size() == 1)
+ {
+ const LLUUID& userID = selectedIDS.front();
+ if ("view_profile" == command)
+ {
+ LLAvatarActions::showProfile(userID);
+ }
+ else if ("im" == command)
+ {
+ if (gAgent.getID() != userID)
+ {
+ LLAvatarActions::startIM(userID);
+ }
+ }
+ else if ("offer_teleport" == command)
+ {
+ LLAvatarActions::offerTeleport(selectedIDS);
+ }
+ else if ("request_teleport" == command)
+ {
+ LLAvatarActions::teleportRequest(selectedIDS.front());
+ }
+ else if ("voice_call" == command)
+ {
+ LLAvatarActions::startCall(userID);
+ }
+ else if ("chat_history" == command)
+ {
+ LLAvatarActions::viewChatHistory(userID);
+ }
+ else if ("add_friend" == command)
+ {
+ LLAvatarActions::requestFriendshipDialog(userID);
+ }
+ else if ("remove_friend" == command)
+ {
+ LLAvatarActions::removeFriendDialog(userID);
+ }
+ else if ("invite_to_group" == command)
+ {
+ LLAvatarActions::inviteToGroup(userID);
+ }
+ else if ("zoom_in" == command)
+ {
+ handle_zoom_to_object(userID);
+ }
+ else if ("map" == command)
+ {
+ LLAvatarActions::showOnMap(userID);
+ }
+ else if ("share" == command)
+ {
+ LLAvatarActions::share(userID);
+ }
+ else if ("pay" == command)
+ {
+ LLAvatarActions::pay(userID);
+ }
+ else if ("report_abuse" == command)
+ {
+ LLAvatarName av_name;
+ if (LLAvatarNameCache::get(userID, &av_name))
+ {
+ LLFloaterReporter::showFromAvatar(userID, av_name.getCompleteName());
+ }
+ else
+ {
+ LLFloaterReporter::showFromAvatar(userID, "not avaliable");
+ }
+ }
+ else if ("block_unblock" == command)
+ {
+ LLAvatarActions::toggleMute(userID, LLMute::flagVoiceChat);
+ }
+ else if ("mute_unmute" == command)
+ {
+ LLAvatarActions::toggleMute(userID, LLMute::flagTextChat);
+ }
+ else if ("selected" == command || "mute_all" == command || "unmute_all" == command)
+ {
+ moderateVoice(command, userID);
+ }
+ else if ("toggle_allow_text_chat" == command)
+ {
+ toggleAllowTextChat(userID);
+ }
+ else if ("ban_member" == command)
+ {
+ banSelectedMember(userID);
+ }
+ }
+ else if (selectedIDS.size() > 1)
+ {
+ if ("im" == command)
+ {
+ LLAvatarActions::startConference(selectedIDS);
+ }
+ else if ("offer_teleport" == command)
+ {
+ LLAvatarActions::offerTeleport(selectedIDS);
+ }
+ else if ("voice_call" == command)
+ {
+ LLAvatarActions::startAdhocCall(selectedIDS);
+ }
+ else if ("remove_friend" == command)
+ {
+ LLAvatarActions::removeFriendsDialog(selectedIDS);
+ }
+ }
+}
+
+void LLFloaterIMContainer::doToSelectedConversation(const std::string& command, uuid_vec_t& selectedIDS)
+{
+ //Find the conversation floater associated with the selected id
+ const LLConversationItem * conversationItem = getCurSelectedViewModelItem();
+ LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(conversationItem->getUUID());
+
+ if(conversationFloater)
+ {
+ //Close the selected conversation
+ if("close_conversation" == command)
+ {
+ LLFloater::onClickClose(conversationFloater);
+ }
+ else if("close_selected_conversations" == command)
+ {
+ getSelectedUUIDs(selectedIDS,false);
+ closeSelectedConversations(selectedIDS);
+ }
+ else if("open_voice_conversation" == command)
+ {
+ gIMMgr->startCall(conversationItem->getUUID());
+ }
+ else if("disconnect_from_voice" == command)
+ {
+ gIMMgr->endCall(conversationItem->getUUID());
+ }
+ else if("chat_history" == command)
+ {
+ if (selectedIDS.size() > 0)
+ {
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_GROUP)
+ {
+ LLFloaterReg::showInstance("preview_conversation", conversationItem->getUUID(), true);
+ }
+ else if(conversationItem->getType() == LLConversationItem::CONV_SESSION_AD_HOC)
+ {
+ LLConversation* conv = LLConversationLog::instance().findConversation(LLIMModel::getInstance()->findIMSession(conversationItem->getUUID()));
+ if(conv)
+ {
+ LLFloaterReg::showInstance("preview_conversation", conv->getSessionID(), true);
+ }
+ }
+ else
+ {
+ LLAvatarActions::viewChatHistory(selectedIDS.front());
+ }
+ }
+ }
+ else
+ {
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ doToParticipants(command, selectedIDS);
+ }
+ }
+ }
+ //if there is no LLFloaterIMSession* instance for selected conversation it might be Nearby chat
+ else
+ {
+ if(conversationItem->getType() == LLConversationItem::CONV_SESSION_NEARBY)
+ {
+ if("chat_history" == command)
+ {
+ LLFloaterReg::showInstance("preview_conversation", LLSD(LLUUID::null), true);
+ }
+}
+ }
+}
+
+void LLFloaterIMContainer::doToSelected(const LLSD& userdata)
+{
+ std::string command = userdata.asString();
+ const LLConversationItem * conversationItem = getCurSelectedViewModelItem();
+ uuid_vec_t selected_uuids;
+
+ if(conversationItem != NULL)
+ {
+ getParticipantUUIDs(selected_uuids);
+
+ if(conversationItem->getType() == LLConversationItem::CONV_PARTICIPANT)
+ {
+ doToParticipants(command, selected_uuids);
+ }
+ else
+ {
+ doToSelectedConversation(command, selected_uuids);
+ }
+ }
+}
+
+void LLFloaterIMContainer::doToSelectedGroup(const LLSD& userdata)
+{
+ std::string action = userdata.asString();
+
+ if (action == "group_profile")
+ {
+ LLGroupActions::show(mSelectedSession);
+ }
+ else if (action == "activate_group")
+ {
+ LLGroupActions::activate(mSelectedSession);
+ }
+ else if (action == "leave_group")
+ {
+ LLGroupActions::leave(mSelectedSession);
+ }
+}
+
+bool LLFloaterIMContainer::enableContextMenuItem(const LLSD& userdata)
+{
+ const std::string& item = userdata.asString();
+ uuid_vec_t uuids;
+ getParticipantUUIDs(uuids);
+
+
+ //If there is group or ad-hoc chat in multiselection, everything needs to be disabled
+ if(uuids.size() > 1)
+ {
+ const std::set<LLFolderViewItem*> selectedItems = mConversationsRoot->getSelectionList();
+ LLConversationItem * conversationItem;
+ for(std::set<LLFolderViewItem*>::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it)
+ {
+ conversationItem = static_cast<LLConversationItem *>((*it)->getViewModelItem());
+ if((conversationItem->getType() == LLConversationItem::CONV_SESSION_GROUP) || (conversationItem->getType() == LLConversationItem::CONV_SESSION_AD_HOC))
+ {
+ return false;
+ }
+ }
+ }
+
+ if ("conversation_log" == item)
+ {
+ return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0;
+ }
+
+ //Enable Chat history item for ad-hoc and group conversations
+ if ("can_chat_history" == item && uuids.size() > 0)
+ {
+ //Disable menu item if selected participant is user agent
+ if(uuids.front() != gAgentID)
+ {
+ if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_NEARBY)
+ {
+ return LLLogChat::isNearbyTranscriptExist();
+ }
+ else if (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_AD_HOC)
+ {
+ const LLConversation* conv = LLConversationLog::instance().findConversation(LLIMModel::getInstance()->findIMSession(uuids.front()));
+ if(conv)
+ {
+ return LLLogChat::isAdHocTranscriptExist(conv->getHistoryFileName());
+ }
+ return false;
+ }
+ else
+ {
+ bool is_group = (getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP);
+ return LLLogChat::isTranscriptExist(uuids.front(),is_group);
+ }
+ }
+ }
+
+ // If nothing is selected(and selected item is not group chat), everything needs to be disabled
+ if (uuids.size() <= 0)
+ {
+ if(getCurSelectedViewModelItem())
+ {
+ return getCurSelectedViewModelItem()->getType() == LLConversationItem::CONV_SESSION_GROUP;
+ }
+ return false;
+ }
+
+ if("can_activate_group" == item)
+ {
+ LLUUID selected_group_id = getCurSelectedViewModelItem()->getUUID();
+ return gAgent.getGroupID() != selected_group_id;
+ }
+
+ return enableContextMenuItem(item, uuids);
+}
+
+bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_vec_t& uuids)
+{
+ // Extract the single select info
+ bool is_single_select = (uuids.size() == 1);
+ const LLUUID& single_id = uuids.front();
+
+ if ("can_chat_history" == item && is_single_select)
+ {
+ return LLLogChat::isTranscriptExist(uuids.front(),false);
+ }
+
+ // Handle options that are applicable to all including the user agent
+ if ("can_view_profile" == item)
+ {
+ return is_single_select;
+ }
+
+ bool is_moderator_option = ("can_moderate_voice" == item) || ("can_allow_text_chat" == item) || ("can_mute" == item) || ("can_unmute" == item);
+
+ // Beyond that point, if only the user agent is selected, everything is disabled
+ if (is_single_select && (single_id == gAgentID))
+ {
+ if (is_moderator_option)
+ {
+ return enableModerateContextMenuItem(item, true);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // If the user agent is selected with others, everything is disabled
+ for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id)
+ {
+ if (gAgent.getID() == *id)
+ {
+ return false;
+ }
+ }
+
+ // Handle all other options
+ if (("can_invite" == item)
+ || ("can_chat_history" == item)
+ || ("can_share" == item)
+ || ("can_pay" == item)
+ || ("report_abuse" == item))
+ {
+ // Those menu items are enable only if a single avatar is selected
+ return is_single_select;
+ }
+ else if ("can_block" == item)
+ {
+ return (is_single_select ? LLAvatarActions::canBlock(single_id) : false);
+ }
+ else if ("can_add" == item)
+ {
+ // We can add friends if:
+ // - there is only 1 selected avatar (EXT-7389)
+ // - this avatar is not already a friend
+ return (is_single_select ? !LLAvatarActions::isFriend(single_id) : false);
+ }
+ else if ("can_delete" == item)
+ {
+ // We can remove friends if there are only friends among the selection
+ bool result = true;
+ for (uuid_vec_t::const_iterator id = uuids.begin(); id != uuids.end(); ++id)
+ {
+ result &= LLAvatarActions::isFriend(*id);
+ }
+ return result;
+ }
+ else if ("can_call" == item)
+ {
+ return LLAvatarActions::canCall();
+ }
+ else if ("can_open_voice_conversation" == item)
+ {
+ return is_single_select && LLAvatarActions::canCall();
+ }
+ else if ("can_open_voice_conversation" == item)
+ {
+ return is_single_select && LLAvatarActions::canCall();
+ }
+ else if ("can_zoom_in" == item)
+ {
+ return is_single_select && gObjectList.findObject(single_id);
+ }
+ else if ("can_show_on_map" == item)
+ {
+ return (is_single_select ? (LLAvatarTracker::instance().isBuddyOnline(single_id) && is_agent_mappable(single_id)) || gAgent.isGodlike() : false);
+ }
+ else if ("can_offer_teleport" == item)
+ {
+ return LLAvatarActions::canOfferTeleport(uuids);
+ }
+ else if ("can_ban_member" == item)
+ {
+ return canBanSelectedMember(single_id);
+ }
+ else if (is_moderator_option)
+ {
+ // *TODO : get that out of here...
+ return enableModerateContextMenuItem(item);
+ }
+
+ // By default, options that not explicitely disabled are enabled
+ return true;
+}
+
+bool LLFloaterIMContainer::checkContextMenuItem(const LLSD& userdata)
+{
+ std::string item = userdata.asString();
+ uuid_vec_t uuids;
+ getParticipantUUIDs(uuids);
+
+ return checkContextMenuItem(item, uuids);
+}
+
+bool LLFloaterIMContainer::checkContextMenuItem(const std::string& item, uuid_vec_t& uuids)
+{
+ if (uuids.size() == 1)
+ {
+ if ("is_blocked" == item)
+ {
+ return LLMuteList::getInstance()->isMuted(uuids.front(), LLMute::flagVoiceChat);
+ }
+ else if (item == "is_muted")
+ {
+ return LLMuteList::getInstance()->isMuted(uuids.front(), LLMute::flagTextChat);
+ }
+ else if ("is_allowed_text_chat" == item)
+ {
+ const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant());
+
+ if (NULL != speakerp)
+ {
+ return !speakerp->mModeratorMutedText;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool LLFloaterIMContainer::visibleContextMenuItem(const LLSD& userdata)
+{
+ const LLConversationItem *conversation_item = getCurSelectedViewModelItem();
+ if(!conversation_item)
+ {
+ return false;
+ }
+
+ const std::string& item = userdata.asString();
+
+ if ("show_mute" == item)
+ {
+ return !isMuted(conversation_item->getUUID());
+ }
+ else if ("show_unmute" == item)
+ {
+ return isMuted(conversation_item->getUUID());
+ }
+
+ return true;
+}
+
+void LLFloaterIMContainer::showConversation(const LLUUID& session_id)
+{
+ setVisibleAndFrontmost(false);
+ selectConversationPair(session_id, true);
+
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+ if (session_floater)
+ {
+ session_floater->restoreFloater();
+ }
+}
+
+void LLFloaterIMContainer::clearAllFlashStates()
+{
+ conversations_widgets_map::iterator widget_it = mConversationsWidgets.begin();
+ for (;widget_it != mConversationsWidgets.end(); ++widget_it)
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(widget_it->second);
+ if (widget)
+ {
+ widget->setFlashState(false);
+ }
+ }
+}
+
+void LLFloaterIMContainer::selectConversation(const LLUUID& session_id)
+{
+ selectConversationPair(session_id, true);
+}
+
+// Select the conversation *after* (or before if none after) the passed uuid conversation
+// Used to change the selection on key hits
+void LLFloaterIMContainer::selectNextConversationByID(const LLUUID& uuid)
+{
+ bool new_selection = false;
+ selectConversation(uuid);
+ new_selection = selectNextorPreviousConversation(true);
+ if (!new_selection)
+ {
+ selectNextorPreviousConversation(false);
+ }
+}
+
+// Synchronous select the conversation item and the conversation floater
+bool LLFloaterIMContainer::selectConversationPair(const LLUUID& session_id, bool select_widget, bool focus_floater/*=true*/)
+{
+ bool handled = true;
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+
+ /* widget processing */
+ if (select_widget && mConversationsRoot->getSelectedCount() <= 1)
+ {
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,session_id);
+ if (widget && widget->getParentFolder())
+ {
+ widget->getParentFolder()->setSelection(widget, false, false);
+ mConversationsRoot->scrollToShowSelection();
+ }
+ }
+
+ /* floater processing */
+
+ if (NULL != session_floater && !session_floater->isDead())
+ {
+ if (session_id != getSelectedSession())
+ {
+ // Store the active session
+ setSelectedSession(session_id);
+
+
+
+ if (session_floater->getHost())
+ {
+ // Always expand the message pane if the panel is hosted by the container
+ collapseMessagesPane(false);
+ // Switch to the conversation floater that is being selected
+ selectFloater(session_floater);
+ }
+ else
+ {
+ showStub(true);
+ }
+
+ //When in DND mode, remove stored IM notifications
+ //Nearby chat (Null) IMs are not stored while in DND mode, so can ignore removal
+ if(gAgent.isDoNotDisturb() && session_id.notNull())
+ {
+ LLDoNotDisturbNotificationStorage::getInstance()->removeNotification(LLDoNotDisturbNotificationStorage::toastName, session_id);
+ }
+ }
+
+ // Set the focus on the selected floater
+ if (!session_floater->hasFocus() && !session_floater->isMinimized())
+ {
+ session_floater->setFocus(focus_floater);
+ }
+ }
+ flashConversationItemWidget(session_id,false);
+ return handled;
+}
+
+void LLFloaterIMContainer::setTimeNow(const LLUUID& session_id, const LLUUID& participant_id)
+{
+ LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(getSessionModel(session_id));
+ if (item)
+ {
+ item->setTimeNow(participant_id);
+ mConversationViewModel.requestSortAll();
+ mConversationsRoot->arrangeAll();
+ }
+}
+
+void LLFloaterIMContainer::setNearbyDistances()
+{
+ // Get the nearby chat session: that's the one with uuid nul
+ LLConversationItemSession* item = dynamic_cast<LLConversationItemSession*>(getSessionModel(LLUUID()));
+ if (item)
+ {
+ // Get the positions of the nearby avatars and their ids
+ std::vector<LLVector3d> positions;
+ uuid_vec_t avatar_ids;
+ LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), gSavedSettings.getF32("NearMeRange"));
+ // Get the position of the agent
+ const LLVector3d& me_pos = gAgent.getPositionGlobal();
+ // For each nearby avatar, compute and update the distance
+ int avatar_count = positions.size();
+ for (int i = 0; i < avatar_count; i++)
+ {
+ F64 dist = dist_vec_squared(positions[i], me_pos);
+ item->setDistance(avatar_ids[i],dist);
+ }
+ // Also does it for the agent itself
+ item->setDistance(gAgent.getID(),0.0f);
+ // Request resort
+ mConversationViewModel.requestSortAll();
+ mConversationsRoot->arrangeAll();
+ }
+}
+
+LLConversationItem* LLFloaterIMContainer::addConversationListItem(const LLUUID& uuid, bool isWidgetSelected /*= false*/)
+{
+ bool is_nearby_chat = uuid.isNull();
+
+ // Stores the display name for the conversation line item
+ std::string display_name = is_nearby_chat ? LLTrans::getString("NearbyChatLabel") : LLIMModel::instance().getName(uuid);
+
+ // Check if the item is not already in the list, exit (nothing to do)
+ // Note: this happens often, when reattaching a torn off conversation for instance
+ conversations_items_map::iterator item_it = mConversationsItems.find(uuid);
+ if (item_it != mConversationsItems.end())
+ {
+ return item_it->second;
+ }
+
+ // Create a conversation session model
+ LLConversationItemSession* item = NULL;
+ LLSpeakerMgr* speaker_manager = (is_nearby_chat ? (LLSpeakerMgr*)(LLLocalSpeakerMgr::getInstance()) : LLIMModel::getInstance()->getSpeakerManager(uuid));
+ if (speaker_manager)
+ {
+ item = new LLParticipantList(speaker_manager, getRootViewModel());
+ }
+ if (!item)
+ {
+ LL_WARNS() << "Couldn't create conversation session item : " << display_name << LL_ENDL;
+ return NULL;
+ }
+ item->renameItem(display_name);
+ item->updateName(NULL);
+
+ mConversationsItems[uuid] = item;
+
+ // Create a widget from it
+ LLConversationViewSession* widget = createConversationItemWidget(item);
+ mConversationsWidgets[uuid] = widget;
+
+ // Add a new conversation widget to the root folder of the folder view
+ widget->addToFolder(mConversationsRoot);
+ widget->requestArrange();
+
+ LLIMModel::LLIMSession * im_sessionp = LLIMModel::getInstance()->findIMSession(uuid);
+
+ // Create the participants widgets now
+ // Note: usually, we do not get an updated avatar list at that point
+ if (uuid.isNull() || (im_sessionp && !im_sessionp->isP2PSessionType()))
+ {
+ LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = item->getChildrenBegin();
+ LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = item->getChildrenEnd();
+ while (current_participant_model != end_participant_model)
+ {
+ LLConversationItem* participant_model = dynamic_cast<LLConversationItem*>(*current_participant_model);
+ LLConversationViewParticipant* participant_view = createConversationViewParticipant(participant_model);
+ participant_view->addToFolder(widget);
+ current_participant_model++;
+ }
+ }
+
+ if (uuid.notNull() && im_sessionp->isP2PSessionType())
+ {
+ item->fetchAvatarName(false);
+ }
+
+ // Do that too for the conversation dialog
+ LLFloaterIMSessionTab *conversation_floater = (uuid.isNull() ? (LLFloaterIMSessionTab*)(LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat")) : (LLFloaterIMSessionTab*)(LLFloaterIMSession::findInstance(uuid)));
+ if (conversation_floater)
+ {
+ conversation_floater->buildConversationViewParticipant();
+ }
+
+ // set the widget to minimized mode if conversations pane is collapsed
+ widget->toggleCollapsedMode(mConversationsPane->isCollapsed());
+
+ if (isWidgetSelected || 0 == mConversationsRoot->getSelectedCount())
+ {
+ selectConversationPair(uuid, true);
+ widget->requestArrange();
+
+ // scroll to newly added item
+ mConversationsRoot->scrollToShowSelection();
+ }
+
+ return item;
+}
+
+bool LLFloaterIMContainer::removeConversationListItem(const LLUUID& uuid, bool change_focus)
+{
+ // Delete the widget and the associated conversation item
+ // Note : since the mConversationsItems is also the listener to the widget, deleting
+ // the widget will also delete its listener
+ bool is_widget_selected = false;
+ LLFolderViewItem* new_selection = NULL;
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,uuid);
+ if (widget)
+ {
+ is_widget_selected = widget->isSelected();
+ if (mConversationsRoot)
+ {
+ new_selection = mConversationsRoot->getNextFromChild(widget, false);
+ if (!new_selection)
+ {
+ new_selection = mConversationsRoot->getPreviousFromChild(widget, false);
+ }
+ }
+
+ // Will destroy views and delete models that are not assigned to any views
+ widget->destroyView();
+ }
+
+ // Suppress the conversation items and widgets from their respective maps
+ mConversationsItems.erase(uuid);
+ mConversationsWidgets.erase(uuid);
+ // Clear event query (otherwise reopening session in some way can bombard session with stale data)
+ mConversationEventQueue.erase(uuid);
+
+ // Don't let the focus fall IW, select and refocus on the first conversation in the list
+ if (change_focus)
+ {
+ setFocus(true);
+ if (new_selection)
+ {
+ if (mConversationsWidgets.size() == 1)
+ {
+ // If only one widget is left, it has to be the Nearby Chat. Select it directly.
+ selectConversationPair(LLUUID(NULL), true);
+ }
+ else
+ {
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem());
+ if (vmi)
+ {
+ selectConversationPair(vmi->getUUID(), true);
+ }
+ }
+ }
+ }
+ return is_widget_selected;
+}
+
+LLConversationViewSession* LLFloaterIMContainer::createConversationItemWidget(LLConversationItem* item)
+{
+ LLConversationViewSession::Params params;
+
+ params.name = item->getDisplayName();
+ params.root = mConversationsRoot;
+ params.listener = item;
+ params.tool_tip = params.name;
+ params.container = this;
+
+ //Indentation for aligning the p2p converstation image with the nearby chat arrow
+ if(item->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ params.folder_indentation = 3;
+ }
+
+ return LLUICtrlFactory::create<LLConversationViewSession>(params);
+}
+
+LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParticipant(LLConversationItem* item)
+{
+ LLConversationViewParticipant::Params params;
+ LLRect panel_rect = mConversationsListPanel->getRect();
+
+ params.name = item->getDisplayName();
+ params.root = mConversationsRoot;
+ params.listener = item;
+
+ //24 is the the current hight of an item (itemHeight) loaded from conversation_view_participant.xml.
+ params.rect = LLRect (0, 24, panel_rect.getWidth(), 0);
+ params.tool_tip = params.name;
+ params.participant_id = item->getUUID();
+ params.folder_indentation = 27;
+
+ return LLUICtrlFactory::create<LLConversationViewParticipant>(params);
+}
+
+bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata, bool is_self)
+{
+ // only group moderators can perform actions related to this "enable callback"
+ if (!isGroupModerator())
+ {
+ return false;
+ }
+
+ LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant());
+ if (NULL == speakerp)
+ {
+ return false;
+ }
+
+ bool voice_channel = speakerp->isInVoiceChannel();
+
+ if ("can_moderate_voice" == userdata)
+ {
+ return voice_channel;
+ }
+ else if (("can_mute" == userdata) && !is_self)
+ {
+ return voice_channel && !isMuted(getCurSelectedViewModelItem()->getUUID());
+ }
+ else if ("can_unmute" == userdata)
+ {
+ return voice_channel && isMuted(getCurSelectedViewModelItem()->getUUID());
+ }
+
+ // The last invoke is used to check whether the "can_allow_text_chat" will enabled
+ return LLVoiceClient::getInstance()->isParticipantAvatar(getCurSelectedViewModelItem()->getUUID()) && !is_self;
+}
+
+bool LLFloaterIMContainer::isGroupModerator()
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return false;
+ }
+
+ // Is session a group call/chat?
+ if(gAgent.isInGroup(speaker_manager->getSessionID()))
+ {
+ LLSpeaker * speaker = speaker_manager->findSpeaker(gAgentID).get();
+
+ // Is agent a moderator?
+ return speaker && speaker->mIsModerator;
+ }
+
+ return false;
+}
+
+bool LLFloaterIMContainer::haveAbilityToBan()
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return false;
+ }
+ LLUUID group_uuid = speaker_manager->getSessionID();
+
+ return gAgent.isInGroup(group_uuid) && gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS);
+}
+
+bool LLFloaterIMContainer::canBanSelectedMember(const LLUUID& participant_uuid)
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return false;
+ }
+ LLUUID group_uuid = speaker_manager->getSessionID();
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL;
+ return false;
+ }
+
+ if (gdatap->mPendingBanRequest)
+ {
+ return false;
+ }
+
+ if (gdatap->isRoleMemberDataComplete())
+ {
+ if (gdatap->mMembers.size())
+ {
+ LLGroupMgrGroupData::member_list_t::iterator mi = gdatap->mMembers.find((participant_uuid));
+ if (mi != gdatap->mMembers.end())
+ {
+ LLGroupMemberData* member_data = (*mi).second;
+ // Is the member an owner?
+ if (member_data && member_data->isInRole(gdatap->mOwnerRole))
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ if( gAgent.hasPowerInGroup(group_uuid, GP_ROLE_REMOVE_MEMBER) &&
+ gAgent.hasPowerInGroup(group_uuid, GP_GROUP_BAN_ACCESS) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void LLFloaterIMContainer::banSelectedMember(const LLUUID& participant_uuid)
+{
+ LLSpeakerMgr * speaker_manager = getSpeakerMgrForSelectedParticipant();
+ if (NULL == speaker_manager)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return;
+ }
+
+ LLUUID group_uuid = speaker_manager->getSessionID();
+ LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_uuid);
+ if(!gdatap)
+ {
+ LL_WARNS("Groups") << "Unable to get group data for group " << group_uuid << LL_ENDL;
+ return;
+ }
+
+ gdatap->banMemberById(participant_uuid);
+
+}
+
+void LLFloaterIMContainer::moderateVoice(const std::string& command, const LLUUID& userID)
+{
+ if (!gAgent.getRegion()) return;
+
+ if (command.compare("selected"))
+ {
+ moderateVoiceAllParticipants(command.compare("mute_all"));
+ }
+ else
+ {
+ moderateVoiceParticipant(userID, isMuted(userID));
+ }
+}
+
+bool LLFloaterIMContainer::isMuted(const LLUUID& avatar_id)
+{
+ const LLSpeaker * speakerp = getSpeakerOfSelectedParticipant(getSpeakerMgrForSelectedParticipant());
+ return NULL == speakerp ? true : speakerp->mStatus == LLSpeaker::STATUS_MUTED;
+}
+
+void LLFloaterIMContainer::moderateVoiceAllParticipants(bool unmute)
+{
+ LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant());
+
+ if (NULL != speaker_managerp)
+ {
+ if (!unmute)
+ {
+ LLSD payload;
+ payload["session_id"] = speaker_managerp->getSessionID();
+ LLNotificationsUtil::add("ConfirmMuteAll", LLSD(), payload, confirmMuteAllCallback);
+ return;
+ }
+
+ speaker_managerp->moderateVoiceAllParticipants(unmute);
+ }
+}
+
+// static
+void LLFloaterIMContainer::confirmMuteAllCallback(const LLSD& notification, const LLSD& response)
+{
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ // if Cancel pressed
+ if (option == 1)
+ {
+ return;
+ }
+
+ const LLSD& payload = notification["payload"];
+ const LLUUID& session_id = payload["session_id"];
+
+ LLIMSpeakerMgr * speaker_manager = dynamic_cast<LLIMSpeakerMgr*> (
+ LLIMModel::getInstance()->getSpeakerManager(session_id));
+ if (speaker_manager)
+ {
+ speaker_manager->moderateVoiceAllParticipants(false);
+ }
+
+ return;
+}
+
+void LLFloaterIMContainer::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute)
+{
+ LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr *>(getSpeakerMgrForSelectedParticipant());
+
+ if (NULL != speaker_managerp)
+ {
+ speaker_managerp->moderateVoiceParticipant(avatar_id, unmute);
+ }
+}
+
+LLSpeakerMgr * LLFloaterIMContainer::getSpeakerMgrForSelectedParticipant()
+{
+ LLFolderViewItem *selectedItem = mConversationsRoot->getCurSelectedItem();
+ if (NULL == selectedItem)
+ {
+ LL_WARNS() << "Current selected item is null" << LL_ENDL;
+ return NULL;
+ }
+
+ conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin();
+ conversations_widgets_map::const_iterator end = mConversationsWidgets.end();
+ const LLUUID * conversation_uuidp = NULL;
+ while(iter != end)
+ {
+ if (iter->second == selectedItem || iter->second == selectedItem->getParentFolder())
+ {
+ conversation_uuidp = &iter->first;
+ break;
+ }
+ ++iter;
+ }
+ if (NULL == conversation_uuidp)
+ {
+ LL_WARNS() << "Cannot find conversation item widget" << LL_ENDL;
+ return NULL;
+ }
+
+ return conversation_uuidp->isNull() ? (LLSpeakerMgr *)LLLocalSpeakerMgr::getInstance()
+ : LLIMModel::getInstance()->getSpeakerManager(*conversation_uuidp);
+}
+
+LLSpeaker * LLFloaterIMContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr * speaker_managerp)
+{
+ if (NULL == speaker_managerp)
+ {
+ LL_WARNS() << "Speaker manager is missing" << LL_ENDL;
+ return NULL;
+ }
+
+ const LLConversationItem * participant_itemp = getCurSelectedViewModelItem();
+ if (NULL == participant_itemp)
+ {
+ LL_WARNS() << "Cannot evaluate current selected view model item" << LL_ENDL;
+ return NULL;
+ }
+
+ return speaker_managerp->findSpeaker(participant_itemp->getUUID());
+}
+
+void LLFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid)
+{
+ LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant());
+ if (NULL != speaker_managerp)
+ {
+ speaker_managerp->toggleAllowTextChat(participant_uuid);
+ }
+}
+
+void LLFloaterIMContainer::openNearbyChat()
+{
+ // If there's only one conversation in the container and that conversation is the nearby chat
+ //(which it should be...), open it so to make the list of participants visible. This happens to be the most common case when opening the Chat floater.
+ if((mConversationsItems.size() == 1)&&(!mConversationsPane->isCollapsed()))
+ {
+ LLConversationViewSession* nearby_chat = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,LLUUID()));
+ if (nearby_chat)
+ {
+ reSelectConversation();
+ nearby_chat->setOpen(true);
+ }
+ }
+}
+
+void LLFloaterIMContainer::reSelectConversation()
+{
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession);
+ if (session_floater->getHost())
+ {
+ selectFloater(session_floater);
+ }
+}
+
+void LLFloaterIMContainer::updateSpeakBtnState()
+{
+ mSpeakBtn->setToggleState(LLVoiceClient::getInstance()->getUserPTTState());
+ mSpeakBtn->setEnabled(LLAgent::isActionAllowed("speak"));
+}
+
+bool LLFloaterIMContainer::isConversationLoggingAllowed()
+{
+ return gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0;
+}
+
+void LLFloaterIMContainer::flashConversationItemWidget(const LLUUID& session_id, bool is_flashes)
+{
+ //Finds the conversation line item to flash using the session_id
+ LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id));
+
+ if (widget)
+ {
+ widget->setFlashState(is_flashes);
+ }
+}
+
+void LLFloaterIMContainer::highlightConversationItemWidget(const LLUUID& session_id, bool is_highlighted)
+{
+ //Finds the conversation line item to highlight using the session_id
+ LLConversationViewSession * widget = dynamic_cast<LLConversationViewSession *>(get_ptr_in_map(mConversationsWidgets,session_id));
+
+ if (widget)
+ {
+ widget->setHighlightState(is_highlighted);
+ }
+}
+
+bool LLFloaterIMContainer::isScrolledOutOfSight(LLConversationViewSession* conversation_item_widget)
+{
+ llassert(conversation_item_widget != NULL);
+
+ // make sure the widget is actually in the right spot first
+ mConversationsRoot->arrange(NULL, NULL);
+
+ // check whether the widget is in the visible portion of the scroll container
+ LLRect widget_rect;
+ conversation_item_widget->localRectToOtherView(conversation_item_widget->getLocalRect(), &widget_rect, mConversationsRoot);
+ return !mConversationsRoot->getVisibleRect().overlaps(widget_rect);
+}
+
+bool LLFloaterIMContainer::handleKeyHere(KEY key, MASK mask )
+{
+ bool handled = false;
+
+ if(mask == MASK_ALT)
+ {
+ if (KEY_RETURN == key )
+ {
+ expandConversation();
+ handled = true;
+ }
+
+ if ((KEY_DOWN == key ) || (KEY_RIGHT == key))
+ {
+ selectNextorPreviousConversation(true);
+ handled = true;
+ }
+ if ((KEY_UP == key) || (KEY_LEFT == key))
+ {
+ selectNextorPreviousConversation(false);
+ handled = true;
+ }
+ }
+ return handled;
+}
+
+bool LLFloaterIMContainer::selectAdjacentConversation(bool focus_selected)
+{
+ bool selectedAdjacentConversation = selectNextorPreviousConversation(true, focus_selected);
+
+ if(!selectedAdjacentConversation)
+ {
+ selectedAdjacentConversation = selectNextorPreviousConversation(false, focus_selected);
+ }
+
+ return selectedAdjacentConversation;
+}
+
+bool LLFloaterIMContainer::selectNextorPreviousConversation(bool select_next, bool focus_selected)
+{
+ if (mConversationsWidgets.size() > 1)
+ {
+ LLFolderViewItem* new_selection = NULL;
+ LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,getSelectedSession());
+ if (widget)
+ {
+ if(select_next)
+ {
+ new_selection = mConversationsRoot->getNextFromChild(widget, false);
+ }
+ else
+ {
+ new_selection = mConversationsRoot->getPreviousFromChild(widget, false);
+ }
+ if (new_selection)
+ {
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(new_selection->getViewModelItem());
+ if (vmi)
+ {
+ selectConversationPair(vmi->getUUID(), true, focus_selected);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void LLFloaterIMContainer::expandConversation()
+{
+ if(!mConversationsPane->isCollapsed())
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,getSelectedSession()));
+ if (widget)
+ {
+ widget->setOpen(!widget->isOpen());
+ }
+ }
+}
+bool LLFloaterIMContainer::isParticipantListExpanded()
+{
+ bool is_expanded = false;
+ if(!mConversationsPane->isCollapsed())
+ {
+ LLConversationViewSession* widget = dynamic_cast<LLConversationViewSession*>(get_ptr_in_map(mConversationsWidgets,getSelectedSession()));
+ if (widget)
+ {
+ is_expanded = widget->isOpen();
+ }
+ }
+ return is_expanded;
+}
+
+// By default, if torn off session is currently frontmost, LLFloater::isFrontmost() will return false, which can lead to some bugs
+// So LLFloater::isFrontmost() is overriden here to check both selected session and the IM floater itself
+// Exclude "Nearby Chat" session from the check, as "Nearby Chat" window and "Conversations" floater can be brought
+// to front independently
+/*virtual*/
+bool LLFloaterIMContainer::isFrontmost()
+{
+ LLFloaterIMSessionTab* selected_session = LLFloaterIMSessionTab::getConversation(mSelectedSession);
+ LLFloaterIMNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<LLFloaterIMNearbyChat>("nearby_chat");
+ return (selected_session && selected_session->isFrontmost() && (selected_session != nearby_chat))
+ || LLFloater::isFrontmost();
+}
+
+// For conversations, closeFloater() (linked to Ctrl-W) does not actually close the floater but the active conversation.
+// This is intentional so it doesn't confuse the user. onClickCloseBtn() closes the whole floater.
+void LLFloaterIMContainer::onClickCloseBtn(bool app_quitting/* = false*/)
+{
+ gSavedPerAccountSettings.setS32("ConversationsListPaneWidth", mConversationsPane->getRect().getWidth());
+ LLMultiFloater::closeFloater(app_quitting);
+}
+
+void LLFloaterIMContainer::closeHostedFloater()
+{
+ onClickCloseBtn();
+}
+
+void LLFloaterIMContainer::closeAllConversations()
+{
+ std::vector<LLUUID> ids;
+ for (conversations_items_map::iterator it_session = mConversationsItems.begin(); it_session != mConversationsItems.end(); it_session++)
+ {
+ LLUUID session_id = it_session->first;
+ if (session_id != LLUUID())
+ {
+ ids.push_back(session_id);
+ }
+ }
+
+ for (std::vector<LLUUID>::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(*it);
+ LLFloater::onClickClose(conversationFloater);
+ }
+}
+
+void LLFloaterIMContainer::closeSelectedConversations(const uuid_vec_t& ids)
+{
+ for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ //We don't need to close Nearby chat, so skip it
+ if (*it != LLUUID())
+ {
+ LLFloaterIMSession *conversationFloater = LLFloaterIMSession::findInstance(*it);
+ if(conversationFloater)
+ {
+ LLFloater::onClickClose(conversationFloater);
+ }
+ }
+ }
+}
+void LLFloaterIMContainer::closeFloater(bool app_quitting/* = false*/)
+{
+ if(app_quitting)
+ {
+ closeAllConversations();
+ onClickCloseBtn(app_quitting);
+ }
+ else
+ {
+ // Check for currently active session
+ LLUUID session_id = getSelectedSession();
+ // If current session is Nearby Chat or there is only one session remaining, close the floater
+ if (mConversationsItems.size() == 1 || session_id == LLUUID() || app_quitting)
+ {
+ onClickCloseBtn();
+ }
+ else
+ {
+ // Otherwise, close current conversation
+ LLFloaterIMSessionTab* active_conversation = LLFloaterIMSessionTab::getConversation(session_id);
+ if (active_conversation)
+ {
+ active_conversation->closeFloater();
+ }
+ }
+ }
+}
+
+void LLFloaterIMContainer::handleReshape(const LLRect& rect, bool by_user)
+{
+ LLMultiFloater::handleReshape(rect, by_user);
+ storeRectControl();
+}
+
+// EOF
|
