phoenix-firestorm/indra/newview/fsfloaterimcontainer.cpp

589 lines
17 KiB
C++

/**
* @file fsfloaterimcontainer.cpp
* @brief Multifloater containing active IM sessions in separate tab container tabs
*
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
// Original file: llimfloatercontainer.cpp
#include "llviewerprecompiledheaders.h"
#include "fsfloaterimcontainer.h"
#include "fsfloatercontacts.h"
#include "fsfloaterim.h"
#include "fsfloaternearbychat.h"
#include "llfloaterreg.h"
#include "llchiclet.h"
#include "llchicletbar.h"
#include "lltoolbarview.h"
#include "llvoiceclient.h"
static const F32 VOICE_STATUS_UPDATE_INTERVAL = 1.0f;
//
// FSFloaterIMContainer
//
FSFloaterIMContainer::FSFloaterIMContainer(const LLSD& seed)
: LLMultiFloater(seed),
mActiveVoiceFloater(NULL),
mCurrentVoiceState(VOICE_STATE_NONE),
mForceVoiceStateUpdate(false),
mIsAddingNewSession(false)
{
mAutoResize = FALSE;
LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
// Firstly add our self to IMSession observers, so we catch session events
LLIMMgr::getInstance()->addSessionObserver(this);
}
FSFloaterIMContainer::~FSFloaterIMContainer()
{
mNewMessageConnection.disconnect();
LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
if (LLIMMgr::instanceExists())
{
LLIMMgr::getInstance()->removeSessionObserver(this);
}
}
BOOL FSFloaterIMContainer::postBuild()
{
mNewMessageConnection = LLIMModel::instance().mNewMsgSignal.connect(boost::bind(&FSFloaterIMContainer::onNewMessageReceived, this, _1));
// Do not call base postBuild to not connect to mCloseSignal to not close all floaters via Close button
// mTabContainer will be initialized in LLMultiFloater::addChild()
mTabContainer->setAllowRearrange(true);
mTabContainer->setRearrangeCallback(boost::bind(&FSFloaterIMContainer::onIMTabRearrange, this, _1, _2));
mActiveVoiceUpdateTimer.setTimerExpirySec(VOICE_STATUS_UPDATE_INTERVAL);
mActiveVoiceUpdateTimer.start();
gSavedSettings.getControl("FSShowConversationVoiceStateIndicator")->getSignal()->connect(boost::bind(&FSFloaterIMContainer::onVoiceStateIndicatorChanged, this, _2));
return TRUE;
}
void FSFloaterIMContainer::initTabs()
{
// If we're using multitabs, and we open up for the first time
// Add localchat by default if it's not already on the screen somewhere else. -AO
// But only if it hasnt been already so we can reopen it to the same tab -KC
// Improved handling to leave most of the work to the LL tear-off code -Zi
// This is mirrored from FSFloaterContacts::onOpen() and FSFloaterNearbyChat::onOpen()
// respectively: If those floaters are hosted, they don't store their visibility state.
// Instead, the visibility state of the hosting container is stored. (See Zi's changes to
// LLFloater::storeVisibilityControl()) That means if contacts and/or nearby chat floater
// are hosted and FSFloaterIMContainer was visible at logout, we will end up here during
// next login and have to configure those floaters so their tear off state and icon is
// correct. Configure contacts first and nearby chat last so nearby chat will be active
// once FSFloaterIMContainer has opened. -AH
FSFloaterContacts* floater_contacts = FSFloaterContacts::getInstance();
if (!LLFloater::isVisible(floater_contacts) && (floater_contacts->getHost() != this))
{
if (gSavedSettings.getBOOL("ContactsTornOff"))
{
// first set the tear-off host to the conversations container
floater_contacts->setHost(this);
// clear the tear-off host right after, the "last host used" will still stick
floater_contacts->setHost(NULL);
// reparent to floater view
gFloaterView->addChild(floater_contacts);
}
else
{
addFloater(floater_contacts, TRUE);
}
}
LLFloater* floater_chat = FSFloaterNearbyChat::getInstance();
if (!LLFloater::isVisible(floater_chat) && (floater_chat->getHost() != this))
{
if (gSavedSettings.getBOOL("ChatHistoryTornOff"))
{
// first set the tear-off host to this floater
floater_chat->setHost(this);
// clear the tear-off host right after, the "last host used" will still stick
floater_chat->setHost(NULL);
// reparent to floater view
gFloaterView->addChild(floater_chat);
}
else
{
addFloater(floater_chat, TRUE);
}
}
}
// [SL:KB] - Patch: UI-TabRearrange | Checked: 2012-05-05 (Catznip-3.3.0)
void FSFloaterIMContainer::onIMTabRearrange(S32 tab_index, LLPanel* tab_panel)
{
LLFloater* pIMFloater = dynamic_cast<LLFloater*>(tab_panel);
if (!pIMFloater)
return;
const LLUUID& idSession = pIMFloater->getKey().asUUID();
if (idSession.isNull())
return;
LLChicletPanel* pChicletPanel = LLChicletBar::instance().getChicletPanel();
LLChiclet* pIMChiclet = pChicletPanel->findChiclet<LLChiclet>(idSession);
pChicletPanel->setChicletIndex(pIMChiclet, tab_index - mTabContainer->getNumLockedTabs());
}
// [/SL:KB]
void FSFloaterIMContainer::onOpen(const LLSD& key)
{
LLMultiFloater::onOpen(key);
initTabs();
LLFloater* active_floater = getActiveFloater();
if (active_floater && !active_floater->hasFocus())
{
mTabContainer->setFocus(TRUE);
}
}
void FSFloaterIMContainer::onClose(bool app_quitting)
{
if (app_quitting)
{
for (S32 i = 0; i < mTabContainer->getTabCount(); ++i)
{
FSFloaterIM* floater = dynamic_cast<FSFloaterIM*>(mTabContainer->getPanelByIndex(i));
if (floater)
{
floater->onClose(app_quitting);
}
}
}
}
void FSFloaterIMContainer::addFloater(LLFloater* floaterp,
BOOL select_added_floater,
LLTabContainer::eInsertionPoint insertion_point)
{
if(!floaterp) return;
// already here
if (floaterp->getHost() == this)
{
openFloater(floaterp->getKey());
return;
}
// Need to force an update on the voice state because torn off floater might get re-attached
mForceVoiceStateUpdate = true;
if (floaterp->getName() == "imcontacts" || floaterp->getName() == "nearby_chat")
{
S32 num_locked_tabs = mTabContainer->getNumLockedTabs();
mTabContainer->unlockTabs();
// add contacts window as first tab
if (floaterp->getName() == "imcontacts")
{
LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START);
gSavedSettings.setBOOL("ContactsTornOff", FALSE);
}
else
{
// add chat history as second tab if contact window is present, first tab otherwise
if (dynamic_cast<FSFloaterContacts*>(mTabContainer->getPanelByIndex(0)))
{
// assuming contacts window is first tab, select it
mTabContainer->selectFirstTab();
// and add ourselves after
LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::RIGHT_OF_CURRENT);
}
else
{
LLMultiFloater::addFloater(floaterp, select_added_floater, LLTabContainer::START);
}
gSavedSettings.setBOOL("ChatHistoryTornOff", FALSE);
}
// make sure first two tabs are now locked
mTabContainer->lockTabs(num_locked_tabs + 1);
floaterp->setCanClose(FALSE);
return;
}
else
{
// [SL:KB] - Patch: UI-TabRearrange | Checked: 2012-06-22 (Catznip-3.3.0)
// If we're redocking a torn off IM floater, return it back to its previous place
if (!mIsAddingNewSession && (floaterp->isTornOff()) && (LLTabContainer::END == insertion_point) )
{
LLChicletPanel* pChicletPanel = LLChicletBar::instance().getChicletPanel();
LLIMChiclet* pChiclet = pChicletPanel->findChiclet<LLIMChiclet>(floaterp->getKey());
if (pChiclet)
{
S32 idxChiclet = pChicletPanel->getChicletIndex(pChiclet);
if ((idxChiclet > 0) && (idxChiclet < pChicletPanel->getChicletCount()))
{
// Look for the first IM session to the left of this one
while (--idxChiclet >= 0)
{
if ((pChiclet = dynamic_cast<LLIMChiclet*>(pChicletPanel->getChiclet(idxChiclet))))
{
FSFloaterIM* pFloater = FSFloaterIM::findInstance(pChiclet->getSessionId());
if (pFloater)
{
insertion_point = (LLTabContainer::eInsertionPoint)(mTabContainer->getIndexForPanel(pFloater) + 1);
break;
}
}
}
}
else
{
insertion_point = (0 == idxChiclet) ? LLTabContainer::START : LLTabContainer::END;
}
}
}
// [/SL:KB]
}
LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
LLUUID session_id = floaterp->getKey();
mSessions[session_id] = floaterp;
floaterp->mCloseSignal.connect(boost::bind(&FSFloaterIMContainer::onCloseFloater, this, session_id));
}
void FSFloaterIMContainer::addNewSession(LLFloater* floaterp)
{
// Make sure we don't do some strange re-arranging if we add a new IM floater due to a new session
mIsAddingNewSession = true;
addFloater(floaterp, FALSE, LLTabContainer::END);
mIsAddingNewSession = false;
}
// [SL:KB] - Patch: Chat-NearbyChatBar | Checked: 2011-12-11 (Catznip-3.2.0d) | Added: Catznip-3.2.0d
void FSFloaterIMContainer::removeFloater(LLFloater* floaterp)
{
const std::string floater_name = floaterp->getName();
std::string setting_name = "";
bool needs_unlock = false;
if (floater_name == "nearby_chat")
{
setting_name = "ChatHistoryTornOff";
needs_unlock = true;
}
else if (floater_name == "imcontacts")
{
setting_name = "ContactsTornOff";
needs_unlock = true;
}
if (needs_unlock)
{
// Calling lockTabs with 0 will lock ALL tabs - need to call unlockTabs instead!
S32 num_locked_tabs = mTabContainer->getNumLockedTabs();
if (num_locked_tabs > 1)
{
mTabContainer->lockTabs(num_locked_tabs - 1);
}
else
{
mTabContainer->unlockTabs();
}
gSavedSettings.setBOOL(setting_name, TRUE);
floaterp->setCanClose(TRUE);
}
LLMultiFloater::removeFloater(floaterp);
}
// [/SL:KB]
bool FSFloaterIMContainer::hasFloater(LLFloater* floaterp)
{
for (S32 i = 0; i < mTabContainer->getTabCount(); ++i)
{
if (dynamic_cast<LLFloater*>(mTabContainer->getPanelByIndex(i)) == floaterp)
{
return true;
}
}
return false;
}
void FSFloaterIMContainer::onCloseFloater(LLUUID& id)
{
mSessions.erase(id);
if (isShown())
{
setFocus(TRUE);
}
else if (isMinimized())
{
setMinimized(TRUE); // Make sure console output that needs to be shown is still doing so
}
}
void FSFloaterIMContainer::onNewMessageReceived(const LLSD& data)
{
LLUUID session_id = data["session_id"].asUUID();
LLFloater* floaterp = get_ptr_in_map(mSessions, session_id);
LLFloater* current_floater = LLMultiFloater::getActiveFloater();
// KC: Don't flash tab on friend status changes per setting
if (floaterp && current_floater && floaterp != current_floater
&& (gSavedSettings.getBOOL("FSIMChatFlashOnFriendStatusChange") || !data.has("from_id") || data["from_id"].asUUID().notNull()))
{
if (LLMultiFloater::isFloaterFlashing(floaterp))
{
LLMultiFloater::setFloaterFlashing(floaterp, FALSE);
}
LLMultiFloater::setFloaterFlashing(floaterp, TRUE);
}
}
FSFloaterIMContainer* FSFloaterIMContainer::findInstance()
{
return LLFloaterReg::findTypedInstance<FSFloaterIMContainer>("fs_im_container");
}
FSFloaterIMContainer* FSFloaterIMContainer::getInstance()
{
return LLFloaterReg::getTypedInstance<FSFloaterIMContainer>("fs_im_container");
}
void FSFloaterIMContainer::setVisible(BOOL b)
{
LLMultiFloater::setVisible(b);
if (b)
{
mFlashingSessions.clear();
}
}
void FSFloaterIMContainer::setMinimized(BOOL b)
{
if (mTabContainer)
{
FSFloaterNearbyChat* nearby_floater = dynamic_cast<FSFloaterNearbyChat*>(mTabContainer->getCurrentPanel());
if (nearby_floater)
{
nearby_floater->handleMinimized(b);
}
FSFloaterIM* im_floater = dynamic_cast<FSFloaterIM*>(mTabContainer->getCurrentPanel());
if (im_floater)
{
im_floater->handleMinimized(b);
}
}
LLMultiFloater::setMinimized(b);
}
//virtual
void FSFloaterIMContainer::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id, BOOL has_offline_msg)
{
LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(session_id);
if (!session) return;
// no need to spawn chiclets for participants in P2P calls called through Avaline
if (session->isP2P() && session->isOtherParticipantAvaline()) return;
FSFloaterIM::onNewIMReceived(session_id);
}
//virtual
void FSFloaterIMContainer::sessionRemoved(const LLUUID& session_id)
{
FSFloaterIM* iMfloater = LLFloaterReg::findTypedInstance<FSFloaterIM>("fs_impanel", session_id);
if (iMfloater != NULL)
{
iMfloater->closeFloater();
}
uuid_vec_t::iterator found = std::find(mFlashingSessions.begin(), mFlashingSessions.end(), session_id);
if (found != mFlashingSessions.end())
{
mFlashingSessions.erase(found);
checkFlashing();
}
}
// static
void FSFloaterIMContainer::reloadEmptyFloaters()
{
LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("fs_impanel");
for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
iter != inst_list.end(); ++iter)
{
FSFloaterIM* floater = dynamic_cast<FSFloaterIM*>(*iter);
if (floater && floater->getLastChatMessageIndex() == -1)
{
floater->reloadMessages(true);
}
}
FSFloaterNearbyChat* nearby_chat = LLFloaterReg::findTypedInstance<FSFloaterNearbyChat>("fs_nearby_chat");
if (nearby_chat && nearby_chat->getMessageArchiveLength() == 0)
{
nearby_chat->reloadMessages(true);
}
}
void FSFloaterIMContainer::onVoiceStateIndicatorChanged(const LLSD& data)
{
if (!data.asBoolean())
{
if (mActiveVoiceFloater)
{
mTabContainer->setTabImage(mActiveVoiceFloater, "");
mActiveVoiceFloater = NULL;
}
mCurrentVoiceState = VOICE_STATE_NONE;
}
}
// virtual
void FSFloaterIMContainer::draw()
{
static LLCachedControl<bool> fsShowConversationVoiceStateIndicator(gSavedSettings, "FSShowConversationVoiceStateIndicator");
if (fsShowConversationVoiceStateIndicator && (mActiveVoiceUpdateTimer.hasExpired() || mForceVoiceStateUpdate))
{
LLFloater* current_voice_floater = getCurrentVoiceFloater();
if (mActiveVoiceFloater != current_voice_floater)
{
if (mActiveVoiceFloater)
{
mTabContainer->setTabImage(mActiveVoiceFloater, "");
mCurrentVoiceState = VOICE_STATE_NONE;
}
}
if (current_voice_floater)
{
static LLUIColor voice_connected_color = LLUIColorTable::instance().getColor("VoiceConnectedColor", LLColor4::green);
static LLUIColor voice_error_color = LLUIColorTable::instance().getColor("VoiceErrorColor", LLColor4::red);
static LLUIColor voice_not_connected_color = LLUIColorTable::instance().getColor("VoiceNotConnectedColor", LLColor4::yellow);
eVoiceState voice_state = VOICE_STATE_UNKNOWN;
LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
if (voice_channel)
{
if (voice_channel->isActive())
{
voice_state = VOICE_STATE_CONNECTED;
}
else if (voice_channel->getState() == LLVoiceChannel::STATE_ERROR)
{
voice_state = VOICE_STATE_ERROR;
}
else
{
voice_state = VOICE_STATE_NOT_CONNECTED;
}
}
if (voice_state != mCurrentVoiceState || mForceVoiceStateUpdate)
{
LLColor4 icon_color;
switch (voice_state)
{
case VOICE_STATE_CONNECTED:
icon_color = voice_connected_color.get();
break;
case VOICE_STATE_ERROR:
icon_color = voice_error_color.get();
break;
case VOICE_STATE_NOT_CONNECTED:
icon_color = voice_not_connected_color.get();
break;
default:
icon_color = LLColor4::white;
break;
}
mTabContainer->setTabImage(current_voice_floater, "Active_Voice_Tab", LLFontGL::RIGHT, icon_color, icon_color);
mCurrentVoiceState = voice_state;
}
}
mForceVoiceStateUpdate = false;
mActiveVoiceFloater = current_voice_floater;
mActiveVoiceUpdateTimer.setTimerExpirySec(VOICE_STATUS_UPDATE_INTERVAL);
}
LLMultiFloater::draw();
}
LLFloater* FSFloaterIMContainer::getCurrentVoiceFloater()
{
if (!LLVoiceClient::instance().voiceEnabled())
{
return NULL;
}
if (LLVoiceChannelProximal::getInstance() == LLVoiceChannel::getCurrentVoiceChannel())
{
return FSFloaterNearbyChat::getInstance();
}
for (S32 i = 0; i < mTabContainer->getTabCount(); ++i)
{
FSFloaterIM* im_floater = dynamic_cast<FSFloaterIM*>(mTabContainer->getPanelByIndex(i));
if (im_floater && im_floater->getVoiceChannel() == LLVoiceChannel::getCurrentVoiceChannel())
{
return im_floater;
}
}
return NULL;
}
void FSFloaterIMContainer::addFlashingSession(const LLUUID& session_id)
{
uuid_vec_t::iterator found = std::find(mFlashingSessions.begin(), mFlashingSessions.end(), session_id);
if (found == mFlashingSessions.end())
{
mFlashingSessions.push_back(session_id);
}
}
void FSFloaterIMContainer::checkFlashing()
{
if (mFlashingSessions.empty())
{
gToolBarView->flashCommand(LLCommandId("chat"), false);
}
}
void FSFloaterIMContainer::sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id)
{
avatarID_panel_map_t::iterator found = mSessions.find(old_session_id);
if (found != mSessions.end())
{
LLFloater* floaterp = found->second;
mSessions.erase(found);
mSessions[new_session_id] = floaterp;
}
}
// EOF