diff options
| author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 20:51:58 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-22 20:51:58 +0300 |
| commit | 6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a (patch) | |
| tree | fab23811a5cedc1ebf01479c852ee92ff62b636c /indra/newview/llconversationview.cpp | |
| parent | ef8f4819822288e044ea719feb6af7a1f4df4c4e (diff) | |
| parent | 7bb5afc11ee5a6af78302a8d76a9a619e2baaab2 (diff) | |
Merge pull request #1545 from Ansariel/DRTVWR-600-maint-A
Merge main into DRTVWR-600-maint-a
Diffstat (limited to 'indra/newview/llconversationview.cpp')
| -rw-r--r-- | indra/newview/llconversationview.cpp | 1770 |
1 files changed, 886 insertions, 884 deletions
diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 7e592ab468..0242e4d9eb 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -1,884 +1,886 @@ -/** - * @file llconversationview.cpp - * @brief Implementation of conversations list widgets and views - * - * $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 "llconversationview.h" - -#include <boost/bind.hpp> -#include "llagentdata.h" -#include "llavataractions.h" -#include "llconversationmodel.h" -#include "llfloaterimsession.h" -#include "llfloaterimnearbychat.h" -#include "llfloaterimsessiontab.h" -#include "llfloaterimcontainer.h" -#include "llfloaterreg.h" -#include "llgroupiconctrl.h" -#include "lluictrlfactory.h" -#include "lltoolbarview.h" - -// -// Implementation of conversations list session widgets -// -static LLDefaultChildRegistry::Register<LLConversationViewSession> r_conversation_view_session("conversation_view_session"); - -const LLColor4U DEFAULT_WHITE(255, 255, 255); - -class LLNearbyVoiceClientStatusObserver : public LLVoiceClientStatusObserver -{ -public: - - LLNearbyVoiceClientStatusObserver(LLConversationViewSession* conv) - : conversation(conv) - {} - - virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal) - { - conversation->showVoiceIndicator(conversation - && status != STATUS_JOINING - && status != STATUS_LEFT_CHANNEL - && LLVoiceClient::getInstance()->voiceEnabled() - && LLVoiceClient::getInstance()->isVoiceWorking()); - } - -private: - LLConversationViewSession* conversation; -}; - -LLConversationViewSession::Params::Params() : - container() -{} - -LLConversationViewSession::LLConversationViewSession(const LLConversationViewSession::Params& p): - LLFolderViewFolder(p), - mContainer(p.container), - mItemPanel(NULL), - mCallIconLayoutPanel(NULL), - mSessionTitle(NULL), - mSpeakingIndicator(NULL), - mVoiceClientObserver(NULL), - mCollapsedMode(false), - mHasArrow(true), - mIsInActiveVoiceChannel(false), - mFlashStateOn(false), - mFlashStarted(false) -{ - mFlashTimer = new LLFlashTimer(); - mAreChildrenInited = true; // inventory only -} - -LLConversationViewSession::~LLConversationViewSession() -{ - mActiveVoiceChannelConnection.disconnect(); - - if (mVoiceClientObserver) - { - if (LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver); - } - delete mVoiceClientObserver; - } - - mFlashTimer->unset(); -} - -void LLConversationViewSession::destroyView() -{ - // Chat can create and parent models(listeners) to session's model before creating - // coresponding views, such participant's models normally will wait for idle cycles - // but since we are deleting session and won't be processing any more events, make - // sure unowned LLConversationItemParticipant models are removed as well. - - LLConversationItemSession* vmi = dynamic_cast<LLConversationItemSession*>(getViewModelItem()); - - // CONV_SESSION_1_ON_1 stores participants as two models that belong to views independent - // from session (nasty! These views are widgets in LLFloaterIMSessionTab, see buildConversationViewParticipant) - if (vmi && vmi->getType() != LLConversationItem::CONV_SESSION_1_ON_1) - { - // Destroy existing views - while (!mItems.empty()) - { - LLFolderViewItem *itemp = mItems.back(); - mItems.pop_back(); - - LLFolderViewModelItem* item_vmi = itemp->getViewModelItem(); - if (item_vmi) // supposed to exist - { - // unparent to remove from child list - vmi->removeChild(item_vmi); - } - itemp->destroyView(); - } - - // Not needed in scope of sessions, but just in case - while (!mFolders.empty()) - { - LLFolderViewFolder *folderp = mFolders.back(); - mFolders.pop_back(); - - LLFolderViewModelItem* folder_vmi = folderp->getViewModelItem(); - if (folder_vmi) - { - vmi->removeChild(folder_vmi); - } - folderp->destroyView(); - } - - // Now everything that is left in model(listener) is not owned by views, - // only by sessions, deparent so it won't point to soon to be dead model - vmi->clearAndDeparentModels(); - } - - LLFolderViewFolder::destroyView(); -} - -void LLConversationViewSession::setFlashState(bool flash_state) -{ - if (flash_state && !mFlashStateOn) - { - // flash chat toolbar button if scrolled out of sight (because flashing will not be visible) - if (mContainer->isScrolledOutOfSight(this)) - { - gToolBarView->flashCommand(LLCommandId("chat"), true); - } - } - - mFlashStateOn = flash_state; - mFlashStarted = false; - mFlashTimer->stopFlashing(); -} - -void LLConversationViewSession::setHighlightState(bool hihglight_state) -{ - mFlashStateOn = hihglight_state; - mFlashStarted = true; - mFlashTimer->stopFlashing(); -} - -void LLConversationViewSession::startFlashing() -{ - // Need to start flashing only when "Conversations" is opened or brought on top - if (isInVisibleChain() - && mFlashStateOn - && !mFlashStarted - && ! LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")->isMinimized() ) - { - mFlashStarted = true; - mFlashTimer->startFlashing(); - } -} - -bool LLConversationViewSession::isHighlightAllowed() -{ - return mFlashStateOn || mIsSelected; -} - -bool LLConversationViewSession::isHighlightActive() -{ - return (mFlashStateOn ? (mFlashTimer->isFlashingInProgress() ? mFlashTimer->isCurrentlyHighlighted() : true) : mIsCurSelection); -} - -bool LLConversationViewSession::postBuild() -{ - LLFolderViewItem::postBuild(); - - mItemPanel = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance()); - addChild(mItemPanel); - - mCallIconLayoutPanel = mItemPanel->getChild<LLPanel>("call_icon_panel"); - mSessionTitle = mItemPanel->getChild<LLTextBox>("conversation_title"); - - mActiveVoiceChannelConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLConversationViewSession::onCurrentVoiceSessionChanged, this, _1)); - mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); - - LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); - if (vmi) - { - switch(vmi->getType()) - { - case LLConversationItem::CONV_PARTICIPANT: - case LLConversationItem::CONV_SESSION_1_ON_1: - { - LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID()); - if (session) - { - LLAvatarIconCtrl* icon = mItemPanel->getChild<LLAvatarIconCtrl>("avatar_icon"); - icon->setVisible(true); - icon->setValue(session->mOtherParticipantID); - mSpeakingIndicator->setSpeakerId(session->mOtherParticipantID, session->mSessionID, true); - mHasArrow = false; - } - break; - } - case LLConversationItem::CONV_SESSION_AD_HOC: - { - LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon"); - icon->setVisible(true); - mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); - break; - } - case LLConversationItem::CONV_SESSION_GROUP: - { - LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon"); - icon->setVisible(true); - icon->setValue(vmi->getUUID()); - mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true); - break; - } - case LLConversationItem::CONV_SESSION_NEARBY: - { - LLIconCtrl* icon = mItemPanel->getChild<LLIconCtrl>("nearby_chat_icon"); - icon->setVisible(true); - mSpeakingIndicator->setSpeakerId(gAgentID, LLUUID::null, true); - mIsInActiveVoiceChannel = true; - if(LLVoiceClient::instanceExists()) - { - if (mVoiceClientObserver) - { - LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver); - delete mVoiceClientObserver; - } - mVoiceClientObserver = new LLNearbyVoiceClientStatusObserver(this); - LLVoiceClient::getInstance()->addObserver(mVoiceClientObserver); - } - break; - } - default: - break; - } - - refresh(); // requires vmi - } - - return true; -} - -void LLConversationViewSession::draw() -{ - getViewModelItem()->update(); - - const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>(); - const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false); - - // Indicate that flash can start (moot operation if already started, done or not flashing) - startFlashing(); - - // draw highlight for selected items - drawHighlight(show_context, true, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); - - // Draw children if root folder, or any other folder that is open. Do not draw children when animating to closed state or you get rendering overlap. - bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) || isOpen(); - - // Todo/fix this: arrange hides children 'out of bonds', session 'slowly' adjusts container size, unhides children - // this process repeats until children fit - for (folders_t::iterator iter = mFolders.begin(); - iter != mFolders.end();) - { - folders_t::iterator fit = iter++; - (*fit)->setVisible(draw_children); - } - for (items_t::iterator iter = mItems.begin(); - iter != mItems.end();) - { - items_t::iterator iit = iter++; - (*iit)->setVisible(draw_children); - } - - // we don't draw the open folder arrow in minimized mode - if (mHasArrow && !mCollapsedMode) - { - // update the rotation angle of open folder arrow - updateLabelRotation(); - drawOpenFolderArrow(default_params, sFgColor); - } - LLView::draw(); -} - -bool LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - //Will try to select a child node and then itself (if a child was not selected) - bool result = LLFolderViewFolder::handleMouseDown(x, y, mask); - - //This node (conversation) was selected and a child (participant) was not - if(result && getRoot()) - { - if(getRoot()->getCurSelectedItem() == this) - { - LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem()); - LLUUID session_id = item? item->getUUID() : LLUUID(); - - LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); - if (im_container->isConversationsPaneCollapsed() && im_container->getSelectedSession() == session_id) - { - im_container->collapseMessagesPane(!im_container->isMessagesPaneCollapsed()); - } - else - { - im_container->collapseMessagesPane(false); - } - } - selectConversationItem(); - } - - return result; -} - -bool LLConversationViewSession::handleMouseUp( S32 x, S32 y, MASK mask ) -{ - bool result = LLFolderViewFolder::handleMouseUp(x, y, mask); - - LLFloater* volume_floater = LLFloaterReg::findInstance("floater_voice_volume"); - LLFloater* chat_volume_floater = LLFloaterReg::findInstance("chat_voice"); - if (result - && getRoot() && (getRoot()->getCurSelectedItem() == this) - && !(volume_floater && volume_floater->isShown() && volume_floater->hasFocus()) - && !(chat_volume_floater && chat_volume_floater->isShown() && chat_volume_floater->hasFocus())) - { - LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem()); - LLUUID session_id = item? item->getUUID() : LLUUID(); - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id); - if(session_floater && !session_floater->hasFocus()) - { - session_floater->setFocus(true); - } - } - - return result; -} - -bool LLConversationViewSession::handleRightMouseDown( S32 x, S32 y, MASK mask ) -{ - bool result = LLFolderViewFolder::handleRightMouseDown(x, y, mask); - - if(result) - { - selectConversationItem(); - } - - return result; -} - -void LLConversationViewSession::selectConversationItem() -{ - if(getRoot()->getCurSelectedItem() == this) - { - LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem()); - LLUUID session_id = item? item->getUUID() : LLUUID(); - - LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); - im_container->flashConversationItemWidget(session_id,false); - im_container->selectConversationPair(session_id, false); - } -} - -// virtual -S32 LLConversationViewSession::arrange(S32* width, S32* height) -{ - //LLFolderViewFolder::arrange computes value for getIndentation() function below - S32 arranged = LLFolderViewFolder::arrange(width, height); - - S32 h_pad = mHasArrow ? getIndentation() + mArrowSize : getIndentation(); - - LLRect rect(mCollapsedMode ? getLocalRect().mLeft : h_pad, - getLocalRect().mTop, - getLocalRect().mRight, - getLocalRect().mTop - getItemHeight()); - mItemPanel->setShape(rect); - - return arranged; -} - -// virtual -void LLConversationViewSession::toggleOpen() -{ - // conversations should not be opened while in minimized mode - if (!mCollapsedMode) - { - LLFolderViewFolder::toggleOpen(); - - // do item's selection when opened - if (LLFolderViewFolder::isOpen()) - { - getParentFolder()->setSelection(this, true); - } - mContainer->reSelectConversation(); - } -} - -void LLConversationViewSession::toggleCollapsedMode(bool is_collapsed) -{ - mCollapsedMode = is_collapsed; - - // hide the layout stack which contains all item's child widgets - // except for the icon which we display in minimized mode - getChild<LLView>("conversation_item_stack")->setVisible(!mCollapsedMode); - - S32 h_pad = mHasArrow ? getIndentation() + mArrowSize : getIndentation(); - - mItemPanel->translate(mCollapsedMode ? -h_pad : h_pad, 0); -} - -void LLConversationViewSession::setVisibleIfDetached(bool visible) -{ - // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized - // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here - LLFloater* session_floater = getSessionFloater(); - if (session_floater && session_floater->isDetachedAndNotMinimized()) - { - session_floater->setVisible(visible); - } -} - -LLFloater* LLConversationViewSession::getSessionFloater() -{ - LLFolderViewModelItem* item = mViewModelItem; - LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID(); - return LLFloaterIMSessionTab::getConversation(session_uuid); -} - -LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id) -{ - // This is *not* a general tree parsing algorithm. We search only in the mItems list - // assuming there is no mFolders which makes sense for sessions (sessions don't contain - // sessions). - LLConversationViewParticipant* participant = NULL; - items_t::const_iterator iter; - for (iter = getItemsBegin(); iter != getItemsEnd(); iter++) - { - participant = dynamic_cast<LLConversationViewParticipant*>(*iter); - if (participant->hasSameValue(participant_id)) - { - break; - } - } - return (iter == getItemsEnd() ? NULL : participant); -} - -void LLConversationViewSession::showVoiceIndicator(bool visible) -{ - mCallIconLayoutPanel->setVisible(visible && LLVoiceChannel::getCurrentVoiceChannel()->getSessionID().isNull()); - requestArrange(); -} - -void LLConversationViewSession::refresh() -{ - // Refresh the session view from its model data - LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); - if (vmi) - { - vmi->resetRefresh(); - - if (mSessionTitle) - { - if (!highlightFriendTitle(vmi)) - { - LLStyle::Params title_style; - title_style.color = LLUIColorTable::instance().getColor("LabelTextColor"); - mSessionTitle->setText(vmi->getDisplayName(), title_style); - } - } - } - - // Update all speaking indicators - LLSpeakingIndicatorManager::updateSpeakingIndicators(); - - // we should show indicator for specified voice session only if this is current channel. EXT-5562. - if (mSpeakingIndicator) - { - mSpeakingIndicator->setIsActiveChannel(mIsInActiveVoiceChannel); - mSpeakingIndicator->setShowParticipantsSpeaking(mIsInActiveVoiceChannel); - } - - LLConversationViewParticipant* participant = NULL; - items_t::const_iterator iter; - for (iter = getItemsBegin(); iter != getItemsEnd(); iter++) - { - participant = dynamic_cast<LLConversationViewParticipant*>(*iter); - if (participant) - { - participant->allowSpeakingIndicator(mIsInActiveVoiceChannel); - } - } - - requestArrange(); - if (vmi) - { - // Do the regular upstream refresh - LLFolderViewFolder::refresh(); - } -} - -void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) -{ - LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem()); - - if (vmi) - { - bool old_value = mIsInActiveVoiceChannel; - mIsInActiveVoiceChannel = vmi->getUUID() == session_id; - mCallIconLayoutPanel->setVisible(mIsInActiveVoiceChannel); - if (old_value != mIsInActiveVoiceChannel) - { - refresh(); - } - } -} - -bool LLConversationViewSession::highlightFriendTitle(LLConversationItem* vmi) -{ - if(vmi->getType() == LLConversationItem::CONV_PARTICIPANT || vmi->getType() == LLConversationItem::CONV_SESSION_1_ON_1) - { - LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID()); - if (session && LLAvatarActions::isFriend(session->mOtherParticipantID)) - { - LLStyle::Params title_style; - title_style.color = LLUIColorTable::instance().getColor("ConversationFriendColor"); - mSessionTitle->setText(vmi->getDisplayName(), title_style); - return true; - } - } - return false; -} - -// -// Implementation of conversations list participant (avatar) widgets -// - -static LLDefaultChildRegistry::Register<LLConversationViewParticipant> r("conversation_view_participant"); -bool LLConversationViewParticipant::sStaticInitialized = false; -S32 LLConversationViewParticipant::sChildrenWidths[LLConversationViewParticipant::ALIC_COUNT]; - -LLConversationViewParticipant::Params::Params() : -container(), -participant_id(), -avatar_icon("avatar_icon"), -info_button("info_button"), -output_monitor("output_monitor") -{} - -LLConversationViewParticipant::LLConversationViewParticipant( const LLConversationViewParticipant::Params& p ): - LLFolderViewItem(p), - mAvatarIcon(NULL), - mInfoBtn(NULL), - mSpeakingIndicator(NULL), - mUUID(p.participant_id) -{ -} - -LLConversationViewParticipant::~LLConversationViewParticipant() -{ - mActiveVoiceChannelConnection.disconnect(); -} - -void LLConversationViewParticipant::initFromParams(const LLConversationViewParticipant::Params& params) -{ - LLAvatarIconCtrl::Params avatar_icon_params(params.avatar_icon()); - applyXUILayout(avatar_icon_params, this); - LLAvatarIconCtrl * avatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params); - addChild(avatarIcon); - - LLButton::Params info_button_params(params.info_button()); - applyXUILayout(info_button_params, this); - LLButton * button = LLUICtrlFactory::create<LLButton>(info_button_params); - addChild(button); - - LLOutputMonitorCtrl::Params output_monitor_params(params.output_monitor()); - applyXUILayout(output_monitor_params, this); - LLOutputMonitorCtrl * outputMonitor = LLUICtrlFactory::create<LLOutputMonitorCtrl>(output_monitor_params); - addChild(outputMonitor); -} - -bool LLConversationViewParticipant::postBuild() -{ - mAvatarIcon = getChild<LLAvatarIconCtrl>("avatar_icon"); - - mInfoBtn = getChild<LLButton>("info_btn"); - mInfoBtn->setClickedCallback(boost::bind(&LLConversationViewParticipant::onInfoBtnClick, this)); - mInfoBtn->setVisible(false); - - mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator"); - - if (!sStaticInitialized) - { - // Remember children widths including their padding from the next sibling, - // so that we can hide and show them again later. - initChildrenWidths(this); - sStaticInitialized = true; - } - - updateChildren(); - if (getViewModelItem()) - { - LLFolderViewItem::postBuild(); - refresh(); - } - return true; -} - -void LLConversationViewParticipant::draw() -{ - static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); - static LLUIColor sFgDisabledColor = LLUIColorTable::instance().getColor("MenuItemDisabledColor", DEFAULT_WHITE); - static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); - static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); - static LLUIColor sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE); - static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); - static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); - - const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false); - - const LLFontGL* font = getLabelFontForStyle(mLabelStyle); - F32 right_x = 0; - - F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad; - F32 text_left = (F32)getLabelXPos(); - - LLColor4 color; - - LLLocalSpeakerMgr *speakerMgr = LLLocalSpeakerMgr::getInstance(); - - if (speakerMgr && speakerMgr->isSpeakerToBeRemoved(mUUID)) - { - color = sFgDisabledColor; - } - else - { - if (LLAvatarActions::isFriend(mUUID)) - { - color = LLUIColorTable::instance().getColor("ConversationFriendColor"); - } - else - { - color = mIsSelected ? sHighlightFgColor : sFgColor; - } - } - - LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(getViewModelItem()); - if (participant_model) - { - mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted()); - } - - drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); - drawLabel(font, text_left, y, color, right_x); - - LLView::draw(); -} - -// virtual -S32 LLConversationViewParticipant::arrange(S32* width, S32* height) -{ - //Need to call arrange first since it computes value used in getIndentation() - S32 arranged = LLFolderViewItem::arrange(width, height); - - //Adjusts the avatar icon based upon the indentation - LLRect avatarRect(getIndentation(), - mAvatarIcon->getRect().mTop, - getIndentation() + mAvatarIcon->getRect().getWidth(), - mAvatarIcon->getRect().mBottom); - mAvatarIcon->setShape(avatarRect); - - //Since dimensions changed, adjust the children (info button, speaker indicator) - updateChildren(); - - return arranged; -} - -// virtual -void LLConversationViewParticipant::refresh() -{ - // Refresh the participant view from its model data - LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(getViewModelItem()); - if (participant_model) - { - participant_model->resetRefresh(); - - // *TODO: We should also do something with vmi->isModerator() to echo that state in the UI somewhat - mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted()); - - // Do the regular upstream refresh - LLFolderViewItem::refresh(); - } -} - -void LLConversationViewParticipant::addToFolder(LLFolderViewFolder* folder) -{ - // Add the item to the folder (conversation) - LLFolderViewItem::addToFolder(folder); - - // Retrieve the folder (conversation) UUID, which is also the speaker session UUID - LLFolderViewFolder *prnt = getParentFolder(); - if (prnt) - { - LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(prnt->getViewModelItem()); - if (vmi) - { - addToSession(vmi->getUUID()); - } - LLConversationViewSession* session = dynamic_cast<LLConversationViewSession*>(prnt); - if (session) - { - allowSpeakingIndicator(session->isInActiveVoiceChannel()); - } - } -} - -void LLConversationViewParticipant::addToSession(const LLUUID& session_id) -{ - //Allows speaking icon image to be loaded based on mUUID - mAvatarIcon->setValue(mUUID); - - //Allows the speaker indicator to be activated based on the user and conversation - mSpeakingIndicator->setSpeakerId(mUUID, session_id); -} - -void LLConversationViewParticipant::onInfoBtnClick() -{ - LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mUUID)); -} - -bool LLConversationViewParticipant::handleMouseDown( S32 x, S32 y, MASK mask ) -{ - bool result = LLFolderViewItem::handleMouseDown(x, y, mask); - - if(result && getRoot()) - { - if(getRoot()->getCurSelectedItem() == this) - { - LLConversationItem* vmi = getParentFolder() ? dynamic_cast<LLConversationItem*>(getParentFolder()->getViewModelItem()) : NULL; - LLUUID session_id = vmi? vmi->getUUID() : LLUUID(); - - LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container"); - LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id); - im_container->setSelectedSession(session_id); - im_container->flashConversationItemWidget(session_id,false); - im_container->selectFloater(session_floater); - im_container->collapseMessagesPane(false); - } - } - return result; -} - -void LLConversationViewParticipant::onMouseEnter(S32 x, S32 y, MASK mask) -{ - mInfoBtn->setVisible(true); - updateChildren(); - LLFolderViewItem::onMouseEnter(x, y, mask); -} - -void LLConversationViewParticipant::onMouseLeave(S32 x, S32 y, MASK mask) -{ - mInfoBtn->setVisible(false); - updateChildren(); - LLFolderViewItem::onMouseLeave(x, y, mask); -} - -S32 LLConversationViewParticipant::getLabelXPos() -{ - return getIndentation() + mAvatarIcon->getRect().getWidth() + mIconPad; -} - -// static -void LLConversationViewParticipant::initChildrenWidths(LLConversationViewParticipant* self) -{ - //speaking indicator width + padding - S32 speaking_indicator_width = self->getRect().getWidth() - self->mSpeakingIndicator->getRect().mLeft; - - //info btn width + padding - S32 info_btn_width = self->mSpeakingIndicator->getRect().mLeft - self->mInfoBtn->getRect().mLeft; - - S32 index = ALIC_COUNT; - sChildrenWidths[--index] = info_btn_width; - sChildrenWidths[--index] = speaking_indicator_width; - llassert(index == 0); -} - -void LLConversationViewParticipant::updateChildren() -{ - mLabelPaddingRight = DEFAULT_LABEL_PADDING_RIGHT; - LLView* control; - S32 ctrl_width; - LLRect controlRect; - - //Cycles through controls starting from right to left - for (S32 i = 0; i < ALIC_COUNT; ++i) - { - control = getItemChildView((EAvatarListItemChildIndex)i); - - // skip invisible views - if (!control->getVisible()) continue; - - //Get current pos/dimensions - controlRect = control->getRect(); - - ctrl_width = sChildrenWidths[i]; // including space between current & left controls - // accumulate the amount of space taken by the controls - mLabelPaddingRight += ctrl_width; - - //Reposition visible controls in case adjacent controls to the right are hidden. - controlRect.setLeftTopAndSize( - getLocalRect().getWidth() - mLabelPaddingRight, - controlRect.mTop, - controlRect.getWidth(), - controlRect.getHeight()); - - //Sets the new position - control->setShape(controlRect); - } -} - -LLView* LLConversationViewParticipant::getItemChildView(EAvatarListItemChildIndex child_view_index) -{ - LLView* child_view = NULL; - - switch (child_view_index) - { - case ALIC_SPEAKER_INDICATOR: - child_view = mSpeakingIndicator; - break; - case ALIC_INFO_BUTTON: - child_view = mInfoBtn; - break; - default: - LL_WARNS("AvatarItemReshape") << "Unexpected child view index is passed: " << child_view_index << LL_ENDL; - llassert(0); - break; - // leave child_view untouched - } - - return child_view; -} - -void LLConversationViewParticipant::allowSpeakingIndicator(bool val) -{ - mSpeakingIndicator->setIsActiveChannel(val); -} - -// EOF - +/**
+ * @file llconversationview.cpp
+ * @brief Implementation of conversations list widgets and views
+ *
+ * $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 "llconversationview.h"
+
+#include <boost/bind.hpp>
+#include "llagentdata.h"
+#include "llavataractions.h"
+#include "llconversationmodel.h"
+#include "llfloaterimsession.h"
+#include "llfloaterimnearbychat.h"
+#include "llfloaterimsessiontab.h"
+#include "llfloaterimcontainer.h"
+#include "llfloaterreg.h"
+#include "llgroupiconctrl.h"
+#include "lluictrlfactory.h"
+#include "lltoolbarview.h"
+
+//
+// Implementation of conversations list session widgets
+//
+static LLDefaultChildRegistry::Register<LLConversationViewSession> r_conversation_view_session("conversation_view_session");
+
+const LLColor4U DEFAULT_WHITE(255, 255, 255);
+
+class LLNearbyVoiceClientStatusObserver : public LLVoiceClientStatusObserver
+{
+public:
+
+ LLNearbyVoiceClientStatusObserver(LLConversationViewSession* conv)
+ : conversation(conv)
+ {}
+
+ virtual void onChange(EStatusType status, const std::string &channelURI, bool proximal)
+ {
+ conversation->showVoiceIndicator(conversation
+ && status != STATUS_JOINING
+ && status != STATUS_LEFT_CHANNEL
+ && LLVoiceClient::getInstance()->voiceEnabled()
+ && LLVoiceClient::getInstance()->isVoiceWorking());
+ }
+
+private:
+ LLConversationViewSession* conversation;
+};
+
+LLConversationViewSession::Params::Params() :
+ container()
+{}
+
+LLConversationViewSession::LLConversationViewSession(const LLConversationViewSession::Params& p):
+ LLFolderViewFolder(p),
+ mContainer(p.container),
+ mItemPanel(NULL),
+ mCallIconLayoutPanel(NULL),
+ mSessionTitle(NULL),
+ mSpeakingIndicator(NULL),
+ mVoiceClientObserver(NULL),
+ mCollapsedMode(false),
+ mHasArrow(true),
+ mIsInActiveVoiceChannel(false),
+ mFlashStateOn(false),
+ mFlashStarted(false)
+{
+ mFlashTimer = new LLFlashTimer();
+ mAreChildrenInited = true; // inventory only
+}
+
+LLConversationViewSession::~LLConversationViewSession()
+{
+ mActiveVoiceChannelConnection.disconnect();
+
+ if (mVoiceClientObserver)
+ {
+ if (LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver);
+ }
+ delete mVoiceClientObserver;
+ }
+
+ mFlashTimer->unset();
+ delete mFlashTimer;
+ mFlashStateOn = false;
+}
+
+void LLConversationViewSession::destroyView()
+{
+ // Chat can create and parent models(listeners) to session's model before creating
+ // coresponding views, such participant's models normally will wait for idle cycles
+ // but since we are deleting session and won't be processing any more events, make
+ // sure unowned LLConversationItemParticipant models are removed as well.
+
+ LLConversationItemSession* vmi = dynamic_cast<LLConversationItemSession*>(getViewModelItem());
+
+ // CONV_SESSION_1_ON_1 stores participants as two models that belong to views independent
+ // from session (nasty! These views are widgets in LLFloaterIMSessionTab, see buildConversationViewParticipant)
+ if (vmi && vmi->getType() != LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ // Destroy existing views
+ while (!mItems.empty())
+ {
+ LLFolderViewItem *itemp = mItems.back();
+ mItems.pop_back();
+
+ LLFolderViewModelItem* item_vmi = itemp->getViewModelItem();
+ if (item_vmi) // supposed to exist
+ {
+ // unparent to remove from child list
+ vmi->removeChild(item_vmi);
+ }
+ itemp->destroyView();
+ }
+
+ // Not needed in scope of sessions, but just in case
+ while (!mFolders.empty())
+ {
+ LLFolderViewFolder *folderp = mFolders.back();
+ mFolders.pop_back();
+
+ LLFolderViewModelItem* folder_vmi = folderp->getViewModelItem();
+ if (folder_vmi)
+ {
+ vmi->removeChild(folder_vmi);
+ }
+ folderp->destroyView();
+ }
+
+ // Now everything that is left in model(listener) is not owned by views,
+ // only by sessions, deparent so it won't point to soon to be dead model
+ vmi->clearAndDeparentModels();
+ }
+
+ LLFolderViewFolder::destroyView();
+}
+
+void LLConversationViewSession::setFlashState(bool flash_state)
+{
+ if (flash_state && !mFlashStateOn)
+ {
+ // flash chat toolbar button if scrolled out of sight (because flashing will not be visible)
+ if (mContainer->isScrolledOutOfSight(this))
+ {
+ gToolBarView->flashCommand(LLCommandId("chat"), true);
+ }
+ }
+
+ mFlashStateOn = flash_state;
+ mFlashStarted = false;
+ mFlashTimer->stopFlashing();
+}
+
+void LLConversationViewSession::setHighlightState(bool hihglight_state)
+{
+ mFlashStateOn = hihglight_state;
+ mFlashStarted = true;
+ mFlashTimer->stopFlashing();
+}
+
+void LLConversationViewSession::startFlashing()
+{
+ // Need to start flashing only when "Conversations" is opened or brought on top
+ if (isInVisibleChain()
+ && mFlashStateOn
+ && !mFlashStarted
+ && ! LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container")->isMinimized() )
+ {
+ mFlashStarted = true;
+ mFlashTimer->startFlashing();
+ }
+}
+
+bool LLConversationViewSession::isHighlightAllowed()
+{
+ return mFlashStateOn || mIsSelected;
+}
+
+bool LLConversationViewSession::isHighlightActive()
+{
+ return (mFlashStateOn ? (mFlashTimer->isFlashingInProgress() ? mFlashTimer->isCurrentlyHighlighted() : true) : mIsCurSelection);
+}
+
+bool LLConversationViewSession::postBuild()
+{
+ LLFolderViewItem::postBuild();
+
+ mItemPanel = LLUICtrlFactory::getInstance()->createFromFile<LLPanel>("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance());
+ addChild(mItemPanel);
+
+ mCallIconLayoutPanel = mItemPanel->getChild<LLPanel>("call_icon_panel");
+ mSessionTitle = mItemPanel->getChild<LLTextBox>("conversation_title");
+
+ mActiveVoiceChannelConnection = LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLConversationViewSession::onCurrentVoiceSessionChanged, this, _1));
+ mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator");
+
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem());
+ if (vmi)
+ {
+ switch(vmi->getType())
+ {
+ case LLConversationItem::CONV_PARTICIPANT:
+ case LLConversationItem::CONV_SESSION_1_ON_1:
+ {
+ LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID());
+ if (session)
+ {
+ LLAvatarIconCtrl* icon = mItemPanel->getChild<LLAvatarIconCtrl>("avatar_icon");
+ icon->setVisible(true);
+ icon->setValue(session->mOtherParticipantID);
+ mSpeakingIndicator->setSpeakerId(session->mOtherParticipantID, session->mSessionID, true);
+ mHasArrow = false;
+ }
+ break;
+ }
+ case LLConversationItem::CONV_SESSION_AD_HOC:
+ {
+ LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon");
+ icon->setVisible(true);
+ mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true);
+ break;
+ }
+ case LLConversationItem::CONV_SESSION_GROUP:
+ {
+ LLGroupIconCtrl* icon = mItemPanel->getChild<LLGroupIconCtrl>("group_icon");
+ icon->setVisible(true);
+ icon->setValue(vmi->getUUID());
+ mSpeakingIndicator->setSpeakerId(gAgentID, vmi->getUUID(), true);
+ break;
+ }
+ case LLConversationItem::CONV_SESSION_NEARBY:
+ {
+ LLIconCtrl* icon = mItemPanel->getChild<LLIconCtrl>("nearby_chat_icon");
+ icon->setVisible(true);
+ mSpeakingIndicator->setSpeakerId(gAgentID, LLUUID::null, true);
+ mIsInActiveVoiceChannel = true;
+ if(LLVoiceClient::instanceExists())
+ {
+ if (mVoiceClientObserver)
+ {
+ LLVoiceClient::getInstance()->removeObserver(mVoiceClientObserver);
+ delete mVoiceClientObserver;
+ }
+ mVoiceClientObserver = new LLNearbyVoiceClientStatusObserver(this);
+ LLVoiceClient::getInstance()->addObserver(mVoiceClientObserver);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ refresh(); // requires vmi
+ }
+
+ return true;
+}
+
+void LLConversationViewSession::draw()
+{
+ getViewModelItem()->update();
+
+ const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();
+ const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false);
+
+ // Indicate that flash can start (moot operation if already started, done or not flashing)
+ startFlashing();
+
+ // draw highlight for selected items
+ drawHighlight(show_context, true, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
+
+ // Draw children if root folder, or any other folder that is open. Do not draw children when animating to closed state or you get rendering overlap.
+ bool draw_children = getRoot() == static_cast<LLFolderViewFolder*>(this) || isOpen();
+
+ // Todo/fix this: arrange hides children 'out of bonds', session 'slowly' adjusts container size, unhides children
+ // this process repeats until children fit
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setVisible(draw_children);
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ (*iit)->setVisible(draw_children);
+ }
+
+ // we don't draw the open folder arrow in minimized mode
+ if (mHasArrow && !mCollapsedMode)
+ {
+ // update the rotation angle of open folder arrow
+ updateLabelRotation();
+ drawOpenFolderArrow(default_params, sFgColor);
+ }
+ LLView::draw();
+}
+
+bool LLConversationViewSession::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ //Will try to select a child node and then itself (if a child was not selected)
+ bool result = LLFolderViewFolder::handleMouseDown(x, y, mask);
+
+ //This node (conversation) was selected and a child (participant) was not
+ if(result && getRoot())
+ {
+ if(getRoot()->getCurSelectedItem() == this)
+ {
+ LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem());
+ LLUUID session_id = item? item->getUUID() : LLUUID();
+
+ LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ if (im_container->isConversationsPaneCollapsed() && im_container->getSelectedSession() == session_id)
+ {
+ im_container->collapseMessagesPane(!im_container->isMessagesPaneCollapsed());
+ }
+ else
+ {
+ im_container->collapseMessagesPane(false);
+ }
+ }
+ selectConversationItem();
+ }
+
+ return result;
+}
+
+bool LLConversationViewSession::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ bool result = LLFolderViewFolder::handleMouseUp(x, y, mask);
+
+ LLFloater* volume_floater = LLFloaterReg::findInstance("floater_voice_volume");
+ LLFloater* chat_volume_floater = LLFloaterReg::findInstance("chat_voice");
+ if (result
+ && getRoot() && (getRoot()->getCurSelectedItem() == this)
+ && !(volume_floater && volume_floater->isShown() && volume_floater->hasFocus())
+ && !(chat_volume_floater && chat_volume_floater->isShown() && chat_volume_floater->hasFocus()))
+ {
+ LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem());
+ LLUUID session_id = item? item->getUUID() : LLUUID();
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+ if(session_floater && !session_floater->hasFocus())
+ {
+ session_floater->setFocus(true);
+ }
+ }
+
+ return result;
+}
+
+bool LLConversationViewSession::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ bool result = LLFolderViewFolder::handleRightMouseDown(x, y, mask);
+
+ if(result)
+ {
+ selectConversationItem();
+ }
+
+ return result;
+}
+
+void LLConversationViewSession::selectConversationItem()
+{
+ if(getRoot()->getCurSelectedItem() == this)
+ {
+ LLConversationItem* item = dynamic_cast<LLConversationItem *>(getViewModelItem());
+ LLUUID session_id = item? item->getUUID() : LLUUID();
+
+ LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ im_container->flashConversationItemWidget(session_id,false);
+ im_container->selectConversationPair(session_id, false);
+ }
+}
+
+// virtual
+S32 LLConversationViewSession::arrange(S32* width, S32* height)
+{
+ //LLFolderViewFolder::arrange computes value for getIndentation() function below
+ S32 arranged = LLFolderViewFolder::arrange(width, height);
+
+ S32 h_pad = mHasArrow ? getIndentation() + mArrowSize : getIndentation();
+
+ LLRect rect(mCollapsedMode ? getLocalRect().mLeft : h_pad,
+ getLocalRect().mTop,
+ getLocalRect().mRight,
+ getLocalRect().mTop - getItemHeight());
+ mItemPanel->setShape(rect);
+
+ return arranged;
+}
+
+// virtual
+void LLConversationViewSession::toggleOpen()
+{
+ // conversations should not be opened while in minimized mode
+ if (!mCollapsedMode)
+ {
+ LLFolderViewFolder::toggleOpen();
+
+ // do item's selection when opened
+ if (LLFolderViewFolder::isOpen())
+ {
+ getParentFolder()->setSelection(this, true);
+ }
+ mContainer->reSelectConversation();
+ }
+}
+
+void LLConversationViewSession::toggleCollapsedMode(bool is_collapsed)
+{
+ mCollapsedMode = is_collapsed;
+
+ // hide the layout stack which contains all item's child widgets
+ // except for the icon which we display in minimized mode
+ getChild<LLView>("conversation_item_stack")->setVisible(!mCollapsedMode);
+
+ S32 h_pad = mHasArrow ? getIndentation() + mArrowSize : getIndentation();
+
+ mItemPanel->translate(mCollapsedMode ? -h_pad : h_pad, 0);
+}
+
+void LLConversationViewSession::setVisibleIfDetached(bool visible)
+{
+ // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized
+ // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here
+ LLFloater* session_floater = getSessionFloater();
+ if (session_floater && session_floater->isDetachedAndNotMinimized())
+ {
+ session_floater->setVisible(visible);
+ }
+}
+
+LLFloater* LLConversationViewSession::getSessionFloater()
+{
+ LLFolderViewModelItem* item = mViewModelItem;
+ LLUUID session_uuid = dynamic_cast<LLConversationItem*>(item)->getUUID();
+ return LLFloaterIMSessionTab::getConversation(session_uuid);
+}
+
+LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id)
+{
+ // This is *not* a general tree parsing algorithm. We search only in the mItems list
+ // assuming there is no mFolders which makes sense for sessions (sessions don't contain
+ // sessions).
+ LLConversationViewParticipant* participant = NULL;
+ items_t::const_iterator iter;
+ for (iter = getItemsBegin(); iter != getItemsEnd(); iter++)
+ {
+ participant = dynamic_cast<LLConversationViewParticipant*>(*iter);
+ if (participant->hasSameValue(participant_id))
+ {
+ break;
+ }
+ }
+ return (iter == getItemsEnd() ? NULL : participant);
+}
+
+void LLConversationViewSession::showVoiceIndicator(bool visible)
+{
+ mCallIconLayoutPanel->setVisible(visible && LLVoiceChannel::getCurrentVoiceChannel()->getSessionID().isNull());
+ requestArrange();
+}
+
+void LLConversationViewSession::refresh()
+{
+ // Refresh the session view from its model data
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem());
+ if (vmi)
+ {
+ vmi->resetRefresh();
+
+ if (mSessionTitle)
+ {
+ if (!highlightFriendTitle(vmi))
+ {
+ LLStyle::Params title_style;
+ title_style.color = LLUIColorTable::instance().getColor("LabelTextColor");
+ mSessionTitle->setText(vmi->getDisplayName(), title_style);
+ }
+ }
+ }
+
+ // Update all speaking indicators
+ LLSpeakingIndicatorManager::updateSpeakingIndicators();
+
+ // we should show indicator for specified voice session only if this is current channel. EXT-5562.
+ if (mSpeakingIndicator)
+ {
+ mSpeakingIndicator->setIsActiveChannel(mIsInActiveVoiceChannel);
+ mSpeakingIndicator->setShowParticipantsSpeaking(mIsInActiveVoiceChannel);
+ }
+
+ LLConversationViewParticipant* participant = NULL;
+ items_t::const_iterator iter;
+ for (iter = getItemsBegin(); iter != getItemsEnd(); iter++)
+ {
+ participant = dynamic_cast<LLConversationViewParticipant*>(*iter);
+ if (participant)
+ {
+ participant->allowSpeakingIndicator(mIsInActiveVoiceChannel);
+ }
+ }
+
+ requestArrange();
+ if (vmi)
+ {
+ // Do the regular upstream refresh
+ LLFolderViewFolder::refresh();
+ }
+}
+
+void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id)
+{
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(getViewModelItem());
+
+ if (vmi)
+ {
+ bool old_value = mIsInActiveVoiceChannel;
+ mIsInActiveVoiceChannel = vmi->getUUID() == session_id;
+ mCallIconLayoutPanel->setVisible(mIsInActiveVoiceChannel);
+ if (old_value != mIsInActiveVoiceChannel)
+ {
+ refresh();
+ }
+ }
+}
+
+bool LLConversationViewSession::highlightFriendTitle(LLConversationItem* vmi)
+{
+ if(vmi->getType() == LLConversationItem::CONV_PARTICIPANT || vmi->getType() == LLConversationItem::CONV_SESSION_1_ON_1)
+ {
+ LLIMModel::LLIMSession* session= LLIMModel::instance().findIMSession(vmi->getUUID());
+ if (session && LLAvatarActions::isFriend(session->mOtherParticipantID))
+ {
+ LLStyle::Params title_style;
+ title_style.color = LLUIColorTable::instance().getColor("ConversationFriendColor");
+ mSessionTitle->setText(vmi->getDisplayName(), title_style);
+ return true;
+ }
+ }
+ return false;
+}
+
+//
+// Implementation of conversations list participant (avatar) widgets
+//
+
+static LLDefaultChildRegistry::Register<LLConversationViewParticipant> r("conversation_view_participant");
+bool LLConversationViewParticipant::sStaticInitialized = false;
+S32 LLConversationViewParticipant::sChildrenWidths[LLConversationViewParticipant::ALIC_COUNT];
+
+LLConversationViewParticipant::Params::Params() :
+container(),
+participant_id(),
+avatar_icon("avatar_icon"),
+info_button("info_button"),
+output_monitor("output_monitor")
+{}
+
+LLConversationViewParticipant::LLConversationViewParticipant( const LLConversationViewParticipant::Params& p ):
+ LLFolderViewItem(p),
+ mAvatarIcon(NULL),
+ mInfoBtn(NULL),
+ mSpeakingIndicator(NULL),
+ mUUID(p.participant_id)
+{
+}
+
+LLConversationViewParticipant::~LLConversationViewParticipant()
+{
+ mActiveVoiceChannelConnection.disconnect();
+}
+
+void LLConversationViewParticipant::initFromParams(const LLConversationViewParticipant::Params& params)
+{
+ LLAvatarIconCtrl::Params avatar_icon_params(params.avatar_icon());
+ applyXUILayout(avatar_icon_params, this);
+ LLAvatarIconCtrl * avatarIcon = LLUICtrlFactory::create<LLAvatarIconCtrl>(avatar_icon_params);
+ addChild(avatarIcon);
+
+ LLButton::Params info_button_params(params.info_button());
+ applyXUILayout(info_button_params, this);
+ LLButton * button = LLUICtrlFactory::create<LLButton>(info_button_params);
+ addChild(button);
+
+ LLOutputMonitorCtrl::Params output_monitor_params(params.output_monitor());
+ applyXUILayout(output_monitor_params, this);
+ LLOutputMonitorCtrl * outputMonitor = LLUICtrlFactory::create<LLOutputMonitorCtrl>(output_monitor_params);
+ addChild(outputMonitor);
+}
+
+bool LLConversationViewParticipant::postBuild()
+{
+ mAvatarIcon = getChild<LLAvatarIconCtrl>("avatar_icon");
+
+ mInfoBtn = getChild<LLButton>("info_btn");
+ mInfoBtn->setClickedCallback(boost::bind(&LLConversationViewParticipant::onInfoBtnClick, this));
+ mInfoBtn->setVisible(false);
+
+ mSpeakingIndicator = getChild<LLOutputMonitorCtrl>("speaking_indicator");
+
+ if (!sStaticInitialized)
+ {
+ // Remember children widths including their padding from the next sibling,
+ // so that we can hide and show them again later.
+ initChildrenWidths(this);
+ sStaticInitialized = true;
+ }
+
+ updateChildren();
+ if (getViewModelItem())
+ {
+ LLFolderViewItem::postBuild();
+ refresh();
+ }
+ return true;
+}
+
+void LLConversationViewParticipant::draw()
+{
+ static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);
+ static LLUIColor sFgDisabledColor = LLUIColorTable::instance().getColor("MenuItemDisabledColor", DEFAULT_WHITE);
+ static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE);
+ static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);
+ static LLUIColor sFlashBgColor = LLUIColorTable::instance().getColor("MenuItemFlashBgColor", DEFAULT_WHITE);
+ static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);
+ static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE);
+
+ const bool show_context = (getRoot() ? getRoot()->getShowSelectionContext() : false);
+
+ const LLFontGL* font = getLabelFontForStyle(mLabelStyle);
+ F32 right_x = 0;
+
+ F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad;
+ F32 text_left = (F32)getLabelXPos();
+
+ LLColor4 color;
+
+ LLLocalSpeakerMgr *speakerMgr = LLLocalSpeakerMgr::getInstance();
+
+ if (speakerMgr && speakerMgr->isSpeakerToBeRemoved(mUUID))
+ {
+ color = sFgDisabledColor;
+ }
+ else
+ {
+ if (LLAvatarActions::isFriend(mUUID))
+ {
+ color = LLUIColorTable::instance().getColor("ConversationFriendColor");
+ }
+ else
+ {
+ color = mIsSelected ? sHighlightFgColor : sFgColor;
+ }
+ }
+
+ LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(getViewModelItem());
+ if (participant_model)
+ {
+ mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted());
+ }
+
+ drawHighlight(show_context, mIsSelected, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor);
+ drawLabel(font, text_left, y, color, right_x);
+
+ LLView::draw();
+}
+
+// virtual
+S32 LLConversationViewParticipant::arrange(S32* width, S32* height)
+{
+ //Need to call arrange first since it computes value used in getIndentation()
+ S32 arranged = LLFolderViewItem::arrange(width, height);
+
+ //Adjusts the avatar icon based upon the indentation
+ LLRect avatarRect(getIndentation(),
+ mAvatarIcon->getRect().mTop,
+ getIndentation() + mAvatarIcon->getRect().getWidth(),
+ mAvatarIcon->getRect().mBottom);
+ mAvatarIcon->setShape(avatarRect);
+
+ //Since dimensions changed, adjust the children (info button, speaker indicator)
+ updateChildren();
+
+ return arranged;
+}
+
+// virtual
+void LLConversationViewParticipant::refresh()
+{
+ // Refresh the participant view from its model data
+ LLConversationItemParticipant* participant_model = dynamic_cast<LLConversationItemParticipant*>(getViewModelItem());
+ if (participant_model)
+ {
+ participant_model->resetRefresh();
+
+ // *TODO: We should also do something with vmi->isModerator() to echo that state in the UI somewhat
+ mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted());
+
+ // Do the regular upstream refresh
+ LLFolderViewItem::refresh();
+ }
+}
+
+void LLConversationViewParticipant::addToFolder(LLFolderViewFolder* folder)
+{
+ // Add the item to the folder (conversation)
+ LLFolderViewItem::addToFolder(folder);
+
+ // Retrieve the folder (conversation) UUID, which is also the speaker session UUID
+ LLFolderViewFolder *prnt = getParentFolder();
+ if (prnt)
+ {
+ LLConversationItem* vmi = dynamic_cast<LLConversationItem*>(prnt->getViewModelItem());
+ if (vmi)
+ {
+ addToSession(vmi->getUUID());
+ }
+ LLConversationViewSession* session = dynamic_cast<LLConversationViewSession*>(prnt);
+ if (session)
+ {
+ allowSpeakingIndicator(session->isInActiveVoiceChannel());
+ }
+ }
+}
+
+void LLConversationViewParticipant::addToSession(const LLUUID& session_id)
+{
+ //Allows speaking icon image to be loaded based on mUUID
+ mAvatarIcon->setValue(mUUID);
+
+ //Allows the speaker indicator to be activated based on the user and conversation
+ mSpeakingIndicator->setSpeakerId(mUUID, session_id);
+}
+
+void LLConversationViewParticipant::onInfoBtnClick()
+{
+ LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", mUUID));
+}
+
+bool LLConversationViewParticipant::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ bool result = LLFolderViewItem::handleMouseDown(x, y, mask);
+
+ if(result && getRoot())
+ {
+ if(getRoot()->getCurSelectedItem() == this)
+ {
+ LLConversationItem* vmi = getParentFolder() ? dynamic_cast<LLConversationItem*>(getParentFolder()->getViewModelItem()) : NULL;
+ LLUUID session_id = vmi? vmi->getUUID() : LLUUID();
+
+ LLFloaterIMContainer *im_container = LLFloaterReg::getTypedInstance<LLFloaterIMContainer>("im_container");
+ LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::findConversation(session_id);
+ im_container->setSelectedSession(session_id);
+ im_container->flashConversationItemWidget(session_id,false);
+ im_container->selectFloater(session_floater);
+ im_container->collapseMessagesPane(false);
+ }
+ }
+ return result;
+}
+
+void LLConversationViewParticipant::onMouseEnter(S32 x, S32 y, MASK mask)
+{
+ mInfoBtn->setVisible(true);
+ updateChildren();
+ LLFolderViewItem::onMouseEnter(x, y, mask);
+}
+
+void LLConversationViewParticipant::onMouseLeave(S32 x, S32 y, MASK mask)
+{
+ mInfoBtn->setVisible(false);
+ updateChildren();
+ LLFolderViewItem::onMouseLeave(x, y, mask);
+}
+
+S32 LLConversationViewParticipant::getLabelXPos()
+{
+ return getIndentation() + mAvatarIcon->getRect().getWidth() + mIconPad;
+}
+
+// static
+void LLConversationViewParticipant::initChildrenWidths(LLConversationViewParticipant* self)
+{
+ //speaking indicator width + padding
+ S32 speaking_indicator_width = self->getRect().getWidth() - self->mSpeakingIndicator->getRect().mLeft;
+
+ //info btn width + padding
+ S32 info_btn_width = self->mSpeakingIndicator->getRect().mLeft - self->mInfoBtn->getRect().mLeft;
+
+ S32 index = ALIC_COUNT;
+ sChildrenWidths[--index] = info_btn_width;
+ sChildrenWidths[--index] = speaking_indicator_width;
+ llassert(index == 0);
+}
+
+void LLConversationViewParticipant::updateChildren()
+{
+ mLabelPaddingRight = DEFAULT_LABEL_PADDING_RIGHT;
+ LLView* control;
+ S32 ctrl_width;
+ LLRect controlRect;
+
+ //Cycles through controls starting from right to left
+ for (S32 i = 0; i < ALIC_COUNT; ++i)
+ {
+ control = getItemChildView((EAvatarListItemChildIndex)i);
+
+ // skip invisible views
+ if (!control->getVisible()) continue;
+
+ //Get current pos/dimensions
+ controlRect = control->getRect();
+
+ ctrl_width = sChildrenWidths[i]; // including space between current & left controls
+ // accumulate the amount of space taken by the controls
+ mLabelPaddingRight += ctrl_width;
+
+ //Reposition visible controls in case adjacent controls to the right are hidden.
+ controlRect.setLeftTopAndSize(
+ getLocalRect().getWidth() - mLabelPaddingRight,
+ controlRect.mTop,
+ controlRect.getWidth(),
+ controlRect.getHeight());
+
+ //Sets the new position
+ control->setShape(controlRect);
+ }
+}
+
+LLView* LLConversationViewParticipant::getItemChildView(EAvatarListItemChildIndex child_view_index)
+{
+ LLView* child_view = NULL;
+
+ switch (child_view_index)
+ {
+ case ALIC_SPEAKER_INDICATOR:
+ child_view = mSpeakingIndicator;
+ break;
+ case ALIC_INFO_BUTTON:
+ child_view = mInfoBtn;
+ break;
+ default:
+ LL_WARNS("AvatarItemReshape") << "Unexpected child view index is passed: " << child_view_index << LL_ENDL;
+ llassert(0);
+ break;
+ // leave child_view untouched
+ }
+
+ return child_view;
+}
+
+void LLConversationViewParticipant::allowSpeakingIndicator(bool val)
+{
+ mSpeakingIndicator->setIsActiveChannel(val);
+}
+
+// EOF
+
|
