summaryrefslogtreecommitdiff
path: root/indra/newview/llconversationview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llconversationview.cpp')
-rw-r--r--indra/newview/llconversationview.cpp1772
1 files changed, 886 insertions, 886 deletions
diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp
index 0242e4d9eb..a5b2ee29c7 100644
--- a/indra/newview/llconversationview.cpp
+++ b/indra/newview/llconversationview.cpp
@@ -1,886 +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();
- 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
-
+/**
+ * @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
+