123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857 |
- /**
- * @file llfloaterim.cpp
- * @brief LLFloaterIM and LLFloaterIMSession classes definition
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "llviewerprecompiledheaders.h"
- #include "llfloaterim.h"
- #include "llbutton.h"
- #include "llcachename.h"
- #include "llcombobox.h" // For class LLFlyoutButton
- #include "llconsole.h"
- #include "llcorehttputil.h"
- #include "llkeyboard.h"
- #include "lllineeditor.h"
- #include "llmessage.h"
- #include "llnotifications.h"
- #include "llsliderctrl.h"
- #include "llstylemap.h"
- #include "lltabcontainer.h"
- #include "lltextbox.h"
- #include "lluictrlfactory.h"
- #include "llagent.h"
- #include "llavataractions.h"
- #include "llchat.h"
- #include "llfloateractivespeakers.h"
- #include "llfloateravatarinfo.h"
- #include "llfloaterchat.h"
- #include "llfloatergroupinfo.h"
- #include "llfloatermediabrowser.h"
- #include "hbfloatertextinput.h"
- #include "llimmgr.h"
- #include "llinventory.h"
- #include "llinventorymodel.h"
- #include "lllogchat.h"
- #include "llmutelist.h"
- //MK
- #include "mkrlinterface.h"
- //mk
- #include "lltooldraganddrop.h"
- #include "llviewertexteditor.h"
- #include "llviewerstats.h"
- #include "llviewercontrol.h"
- #include "llviewerwindow.h"
- #include "llvoicechannel.h"
- #include "llweb.h"
- // Static variables
- static std::string sTypingStartString;
- static std::string sSessionStartString;
- static std::string sDefaultTextString;
- static std::string sUnavailableTextString;
- static std::string sMutedTextString;
- LLFloaterIMSession::instances_list_t LLFloaterIMSession::sFloaterIMSessions;
- std::string LLFloaterIM::sOnlyUserMessage;
- std::string LLFloaterIM::sOfflineMessage;
- std::string LLFloaterIM::sMutedMessage;
- LLFloaterIM::strings_map_t LLFloaterIM::sMsgStringsMap;
- ///////////////////////////////////////////////////////////////////////////////
- // LLFloaterIMSession class
- ///////////////////////////////////////////////////////////////////////////////
- //static
- LLFloaterIMSession* LLFloaterIMSession::findInstance(const LLUUID& session_id)
- {
- for (instances_list_t::const_iterator it = sFloaterIMSessions.begin(),
- end = sFloaterIMSessions.end();
- it != end; ++it)
- {
- LLFloaterIMSession* inst = *it;
- if (inst && inst->mSessionUUID == session_id)
- {
- return inst;
- }
- }
- return NULL;
- }
- //static
- void LLFloaterIMSession::closeAllInstances()
- {
- instances_list_t instances_copy = sFloaterIMSessions;
- while (!instances_copy.empty())
- {
- LLFloaterIMSession* inst = *instances_copy.begin();
- if (inst)
- {
- inst->setEnabled(false);
- inst->close(true);
- instances_copy.erase(inst);
- }
- }
- }
- LLFloaterIMSession::LLFloaterIMSession(const std::string& session_label,
- const LLUUID& session_id,
- const LLUUID& other_participant_id,
- EInstantMessage dialog)
- : LLFloater(session_label, LLRect(), session_label),
- mSendButton(NULL),
- mOpenTextEditorButton(NULL),
- mStartCallButton(NULL),
- mEndCallButton(NULL),
- mSnoozeButton(NULL),
- mViewLogButton(NULL),
- mToggleSpeakersButton(NULL),
- mSpeakerVolumeSlider(NULL),
- mMuteButton(NULL),
- mSessionUUID(session_id),
- mVoiceChannel(NULL),
- mSessionInitialized(false),
- mOtherParticipantUUID(other_participant_id),
- mDialog(dialog),
- mIsGroupSession(false),
- mHasScrolledOnce(false),
- mTyping(false),
- mOtherTyping(false),
- mTypingLineStartIndex(0),
- mSentTypingState(true),
- mNumUnreadMessages(0),
- mShowSpeakersOnConnect(true),
- mAutoConnect(false),
- mProfileButtonEnabled(true),
- mFetchingLog(false),
- mSpeakers(NULL),
- mSpeakerPanel(NULL)
- {
- sFloaterIMSessions.insert(this);
- init(session_label);
- }
- LLFloaterIMSession::LLFloaterIMSession(const std::string& session_label,
- const LLUUID& session_id,
- const LLUUID& other_participant_id,
- const uuid_vec_t& ids,
- const LLSD& voice_channel_info,
- EInstantMessage dialog)
- : LLFloater(session_label, LLRect(), session_label),
- mSessionUUID(session_id),
- mOtherParticipantUUID(other_participant_id),
- mSendButton(NULL),
- mOpenTextEditorButton(NULL),
- mStartCallButton(NULL),
- mEndCallButton(NULL),
- mSnoozeButton(NULL),
- mToggleSpeakersButton(NULL),
- mSpeakerVolumeSlider(NULL),
- mMuteButton(NULL),
- mVoiceChannel(NULL),
- mVoiceChannelInfo(voice_channel_info),
- mSpeakers(NULL),
- mSpeakerPanel(NULL),
- mSessionInitialized(false),
- mSnoozeDuration(0),
- mDialog(dialog),
- mIsGroupSession(false),
- mHasScrolledOnce(false),
- mTyping(false),
- mOtherTyping(false),
- mTypingLineStartIndex(0),
- mSentTypingState(true),
- mShowSpeakersOnConnect(true),
- mAutoConnect(false),
- mProfileButtonEnabled(true)
- {
- sFloaterIMSessions.insert(this);
- mSessionInitialTargetIDs = ids;
- // On Agni, we may get an empty voice channel info for Vivox servers: reset
- // it to undefined in this case.
- if (!mVoiceChannelInfo.isMap() || !mVoiceChannelInfo.size())
- {
- mVoiceChannelInfo.clear();
- }
- init(session_label);
- }
- void LLFloaterIMSession::init(const std::string& session_label)
- {
- LL_DEBUGS("InstantMessaging") << "Initializing session '" << session_label
- << "' from status: "
- << im_status_to_string(mDialog) << LL_ENDL;
- mSessionLabel = mSessionLog = session_label;
- mProfileButtonEnabled = false;
- U32 server_type = 0;
- if (mVoiceChannelInfo.isDefined())
- {
- server_type = gVoiceClient.getVoiceServerType(mVoiceChannelInfo);
- }
- bool use_p2p = false;
- std::string xml_filename;
- switch (mDialog)
- {
- case IM_SESSION_GROUP_START:
- mFactoryMap["active_speakers_panel"] =
- LLCallbackMap(createSpeakersPanel, this);
- xml_filename = "floater_instant_message_group.xml";
- mIsGroupSession = true;
- mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID,
- mSessionLabel, false);
- break;
- case IM_SESSION_INVITE:
- mFactoryMap["active_speakers_panel"] =
- LLCallbackMap(createSpeakersPanel, this);
- if (gAgent.isInGroup(mSessionUUID, true))
- {
- xml_filename = "floater_instant_message_group.xml";
- mIsGroupSession = true;
- }
- else // Must be invite to ad hoc IM
- {
- xml_filename = "floater_instant_message_ad_hoc.xml";
- }
- mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID,
- mSessionLabel, false);
- break;
- case IM_SESSION_P2P_INVITE:
- xml_filename = "floater_instant_message.xml";
- mProfileButtonEnabled = true;
- if (LLAvatarName::sOmitResidentAsLastName)
- {
- mSessionLabel = LLCacheName::cleanFullName(mSessionLabel);
- }
- if (server_type)
- {
- mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID,
- mSessionLabel,
- mOtherParticipantUUID,
- server_type);
- use_p2p = server_type == LLVoiceClient::WEBRTC_SERVER;
- }
- else
- {
- mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID,
- mSessionLabel, true);
- }
- break;
- case IM_SESSION_CONFERENCE_START:
- mFactoryMap["active_speakers_panel"] =
- LLCallbackMap(createSpeakersPanel, this);
- xml_filename = "floater_instant_message_ad_hoc.xml";
- mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID,
- mSessionLabel);
- break;
- case IM_NOTHING_SPECIAL: // Just received text from another user
- xml_filename = "floater_instant_message.xml";
- mProfileButtonEnabled = true;
- if (mProfileButtonEnabled && LLAvatarName::sOmitResidentAsLastName)
- {
- mSessionLabel = LLCacheName::cleanFullName(mSessionLabel);
- }
- if (server_type)
- {
- mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID,
- mSessionLabel,
- mOtherParticipantUUID,
- server_type);
- use_p2p = server_type == LLVoiceClient::WEBRTC_SERVER;
- }
- else
- {
- mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID,
- mSessionLabel, true);
- }
- break;
- default:
- llwarns << "Unknown session type" << llendl;
- xml_filename = "floater_instant_message.xml";
- llassert(false);
- }
- if (mVoiceChannel)
- {
- if (mVoiceChannelInfo.isDefined())
- {
- mVoiceChannel->setChannelInfo(mVoiceChannelInfo);
- }
- mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
- }
- LLUICtrlFactory::getInstance()->buildFloater(this, xml_filename,
- &getFactoryMap(), false);
- if (mProfileButtonEnabled && mSessionLog.find(' ') == std::string::npos)
- {
- // Make sure the IM log file will be unique (avoid getting both
- // "JohnDoe.txt" and "JohnDoe Resident.txt", depending on how the IM
- // session was started)
- mSessionLog += " Resident";
- }
- setTitle(mSessionLabel);
- if (mProfileButtonEnabled)
- {
- lookupName();
- }
- mInputEditor->setMaxTextLength(DB_IM_MSG_STR_LEN);
- // Enable line history support for instant message bar
- mInputEditor->setEnableLineHistory(true);
- if (mViewLogButton)
- {
- // This button is visible only if a log file exists
- mViewLogButton->setVisible(false);
- }
- if (gSavedPerAccountSettings.getBool("LogShowHistory"))
- {
- LLUUID log_session_id;
- if (mIsGroupSession)
- {
- log_session_id = mSessionUUID;
- }
- LLLogChat::loadHistory(mSessionLog, &chatFromLog, (void*)this,
- log_session_id);
- }
- if (LLIMMgr::sendStartSessionMessages(mSessionUUID, mOtherParticipantUUID,
- mSessionInitialTargetIDs, mDialog,
- use_p2p))
- {
- // We need to wait for session initialization for outgoing ad-hoc and
- // group chat session; correct session id for initiated ad-hoc chat
- // will be received from the server.
- mInputEditor->setEnabled(false);
- // Use the starting session message as the input line editor label. We
- // used to echo a temporary message in the text editor, but it relied
- // on the fact that it would stay the last printed message, so that we
- // could remove it later; this is no more true with the server logs
- // asynchronous fetching. HB
- LLUIString session_start = sSessionStartString;
- session_start.setArg("[NAME]", getTitle());
- mInputEditor->setLabel(session_start);
- }
- else
- {
- // We do not need to wait for any responses so we are already
- // initialized
- mSessionInitialized = true;
- }
- }
- void LLFloaterIMSession::lookupName()
- {
- LLAvatarNameCache::get(mOtherParticipantUUID,
- boost::bind(&LLFloaterIMSession::onAvatarNameLookup,
- _1, _2, this));
- }
- //static
- void LLFloaterIMSession::onAvatarNameLookup(const LLUUID& id,
- const LLAvatarName& avatar_name,
- void* user_data)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)user_data;
- if (self && sFloaterIMSessions.count(self) != 0)
- {
- // Always show "Display Name [Legacy Name]" for security reasons
- std::string title = avatar_name.getNames();
- if (!title.empty())
- {
- self->setTitle(title);
- }
- }
- }
- LLFloaterIMSession::~LLFloaterIMSession()
- {
- sFloaterIMSessions.erase(this);
- delete mSpeakers;
- mSpeakers = NULL;
- // Kicks you out of the voice channel if it is currently active
- if (mVoiceChannel)
- {
- // HAVE to do this here: if it happens in the LLVoiceChannel destructor
- // it will call the wrong version (since the object is partially
- // deconstructed at that point).
- mVoiceChannel->deactivate();
- delete mVoiceChannel;
- mVoiceChannel = NULL;
- }
- // Delete focus lost callback
- mInputEditor->setFocusLostCallback(NULL);
- }
- bool LLFloaterIMSession::postBuild()
- {
- if (sDefaultTextString.empty())
- {
- sDefaultTextString = getString("default_text_label");
- sSessionStartString = getString("session_start_string");
- sTypingStartString = getString("typing_start_string");
- sUnavailableTextString = getString("unavailable_text_label");
- sMutedTextString = getString("muted_text_label");
- }
- mInputEditor = getChild<LLLineEditor>("chat_editor");
- mInputEditor->setFocusReceivedCallback(onInputEditorFocusReceived, this);
- mInputEditor->setFocusLostCallback(onInputEditorFocusLost, this);
- mInputEditor->setKeystrokeCallback(onInputEditorKeystroke);
- mInputEditor->setScrolledCallback(onInputEditorScrolled, this);
- mInputEditor->setCommitCallback(onCommitChat);
- mInputEditor->setCallbackUserData(this);
- mInputEditor->setCommitOnFocusLost(false);
- mInputEditor->setRevertOnEsc(false);
- mInputEditor->setReplaceNewlinesWithSpaces(false);
- mInputEditor->setCustomMenuType("im_input");
- if (getChild<LLFlyoutButton>("avatar_btn", true, false))
- {
- childSetCommitCallback("avatar_btn", onCommitAvatar, this);
- if (!mProfileButtonEnabled)
- {
- childSetEnabled("avatar_btn", false);
- }
- }
- if (getChild<LLButton>("group_info_btn", true, false))
- {
- childSetAction("group_info_btn", onClickGroupInfo, this);
- }
- mStartCallButton = getChild<LLButton>("start_call_btn", true, false);
- if (mStartCallButton)
- {
- mStartCallButton->setClickedCallback(onClickStartCall, this);
- mEndCallButton = getChild<LLButton>("end_call_btn");
- mEndCallButton->setClickedCallback(onClickEndCall, this);
- }
- mViewLogButton = getChild<LLButton>("view_log_btn", true, false);
- if (mViewLogButton)
- {
- mViewLogButton->setClickedCallback(onClickViewLog, this);
- }
- mSendButton = getChild<LLButton>("send_btn", true, false);
- if (mSendButton)
- {
- mSendButton->setClickedCallback(onClickSend, this);
- }
- mOpenTextEditorButton = getChild<LLButton>("open_text_editor_btn", true,
- false);
- if (mOpenTextEditorButton)
- {
- mOpenTextEditorButton->setClickedCallback(onClickOpenTextEditor, this);
- }
- mToggleSpeakersButton = getChild<LLButton>("toggle_active_speakers_btn",
- true, false);
- if (mToggleSpeakersButton)
- {
- mToggleSpeakersButton->setClickedCallback(onClickToggleActiveSpeakers,
- this);
- }
- #if 0
- LLButton* close_btn = getChild<LLButton>("close_btn");
- close_btn->setClickedCallback(&LLFloaterIMSession::onClickClose, this);
- #endif
- mHistoryEditor = getChild<LLViewerTextEditor>("im_history");
- mHistoryEditor->setParseHTML(true);
- mHistoryEditor->setCustomMenuType("im_history");
- if (mIsGroupSession)
- {
- mSnoozeButton = getChild<LLButton>("snooze_btn");
- mSnoozeButton->setClickedCallback(onClickSnooze, this);
- childSetEnabled("profile_btn", false);
- }
- if (mSpeakerPanel)
- {
- mSpeakerPanel->refreshSpeakers();
- }
- if (mDialog == IM_NOTHING_SPECIAL)
- {
- mMuteButton = getChild<LLButton>("mute_btn", true, false);
- if (mMuteButton)
- {
- mMuteButton->setClickedCallback(onClickMuteVoice, this);
- mSpeakerVolumeSlider = getChild<LLSliderCtrl>("speaker_volume");
- childSetCommitCallback("speaker_volume", onVolumeChange, this);
- }
- }
- setDefaultBtn("send_btn");
- return true;
- }
- bool LLFloaterIMSession::setSnoozeDuration(U32 duration)
- {
- if (mIsGroupSession)
- {
- mSnoozeDuration = duration;
- return true;
- }
- return false;
- }
- void* LLFloaterIMSession::createSpeakersPanel(void* data)
- {
- LLFloaterIMSession* floaterp = (LLFloaterIMSession*)data;
- if (floaterp && floaterp->mSpeakers)
- {
- floaterp->mSpeakerPanel =
- new LLPanelActiveSpeakers(floaterp->mSpeakers, true);
- return floaterp->mSpeakerPanel;
- }
- else
- {
- if (floaterp)
- {
- llwarns << "NULL LLIMSpeakerMgr object" << llendl;
- }
- else
- {
- llwarns << "Called with a NULL pointer" << llendl;
- llassert(false);
- }
- return NULL;
- }
- }
- //static
- void LLFloaterIMSession::onClickMuteVoice(void* user_data)
- {
- LLFloaterIMSession* floaterp = (LLFloaterIMSession*)user_data;
- if (floaterp)
- {
- bool is_muted = LLMuteList::isMuted(floaterp->mOtherParticipantUUID,
- LLMute::flagVoiceChat);
- LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(),
- LLMute::AGENT);
- if (!is_muted)
- {
- LLMuteList::add(mute, LLMute::flagVoiceChat);
- }
- else
- {
- LLMuteList::remove(mute, LLMute::flagVoiceChat);
- }
- }
- }
- //static
- void LLFloaterIMSession::onVolumeChange(LLUICtrl* source, void* user_data)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)user_data;
- if (self)
- {
- gVoiceClient.setUserVolume(self->mOtherParticipantUUID,
- (F32)source->getValue().asReal());
- }
- }
- // virtual
- void LLFloaterIMSession::draw()
- {
- bool voice_enabled = LLVoiceClient::voiceEnabled();
- bool enable_connect = voice_enabled && mSessionInitialized;
- if (mStartCallButton)
- {
- // Hide/show start call and end call buttons
- bool call_started =
- mVoiceChannel &&
- mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED;
- mStartCallButton->setVisible(voice_enabled && !call_started);
- mStartCallButton->setEnabled(enable_connect);
- mEndCallButton->setVisible(voice_enabled && call_started);
- }
- if (mSnoozeButton)
- {
- static LLCachedControl<U32> snooze_duration(gSavedSettings,
- "GroupIMSnoozeDuration");
- mSnoozeButton->setVisible(snooze_duration > 0);
- }
- bool has_text_editor = HBFloaterTextInput::hasFloaterFor(mInputEditor);
- bool empty = mInputEditor->getText().size() == 0;
- if (empty && !has_text_editor)
- {
- // Reset this flag if the chat input line is empty
- mHasScrolledOnce = false;
- }
- if (mSendButton)
- {
- mSendButton->setEnabled(!empty && !has_text_editor);
- }
- // Test mSessionInitialized to keep "Starting session..." when not yet
- // ready. HB
- if (mSessionInitialized)
- {
- LLPointer<LLSpeaker> self_speaker;
- if (mSpeakers)
- {
- self_speaker = mSpeakers->findSpeaker(gAgentID);
- }
- if (self_speaker.notNull() && self_speaker->mModeratorMutedText)
- {
- mInputEditor->setEnabled(false);
- mInputEditor->setLabel(sMutedTextString);
- }
- else
- {
- mInputEditor->setEnabled(!has_text_editor);
- mInputEditor->setLabel(sDefaultTextString);
- }
- }
- if (mAutoConnect && enable_connect)
- {
- onClickStartCall(this);
- mAutoConnect = false;
- }
- // Show speakers window when voice first connects
- if (mShowSpeakersOnConnect && mSpeakerPanel &&
- mVoiceChannel && mVoiceChannel->isActive())
- {
- mSpeakerPanel->setVisible(true);
- mShowSpeakersOnConnect = false;
- }
- if (mToggleSpeakersButton)
- {
- mToggleSpeakersButton->setValue(mSpeakerPanel &&
- mSpeakerPanel->getVisible());
- }
- if (mTyping)
- {
- // Time out if user has not typed for a while.
- if (mLastKeystrokeTimer.getElapsedTimeF32() >
- LLAgent::TYPING_TIMEOUT_SECS)
- {
- setTyping(false);
- }
- // If we are typing, and it has been a little while, send the
- // typing indicator
- if (!mSentTypingState &&
- mFirstKeystrokeTimer.getElapsedTimeF32() > 1.f)
- {
- sendTypingState(true);
- mSentTypingState = true;
- }
- }
- // Use embedded panel if available
- if (mSpeakerPanel)
- {
- if (mSpeakerPanel->getVisible())
- {
- mSpeakerPanel->refreshSpeakers();
- }
- }
- else if (mMuteButton)
- {
- // Refresh volume and mute
- bool voice_active = voice_enabled && mVoiceChannel &&
- mVoiceChannel->isActive();
- mSpeakerVolumeSlider->setVisible(voice_active);
- mMuteButton->setVisible(voice_active);
- if (voice_active)
- {
- F32 volume = gVoiceClient.getUserVolume(mOtherParticipantUUID);
- mSpeakerVolumeSlider->setValue(volume);
- mMuteButton->setValue(LLMuteList::isMuted(mOtherParticipantUUID,
- LLMute::flagVoiceChat));
- }
- }
- LLFloater::draw();
- }
- bool LLFloaterIMSession::inviteToSession(const uuid_vec_t& ids)
- {
- const std::string& url = gAgent.getRegionCapability("ChatSessionRequest");
- if (url.empty())
- {
- return false;
- }
- S32 count = ids.size();
- if (isInviteAllowed() && count > 0)
- {
- llinfos << "Inviting participants" << llendl;
- LLSD data;
- data["params"] = LLSD::emptyArray();
- for (S32 i = 0; i < count; ++i)
- {
- data["params"].append(ids[i]);
- }
- data["method"] = "invite";
- data["session-id"] = mSessionUUID;
- LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, data,
- "Session invite sent",
- "Session invite failed");
- }
- else
- {
- llinfos << "No need to invite agents for " << mDialog << llendl;
- // Successful add, because everyone that needed to get added was added.
- }
- return true;
- }
- void LLFloaterIMSession::addQueuedMessages()
- {
- mFetchingLog = false;
- for (U32 i = 0, count = mMessagesBuffer.size(); i < count; ++i)
- {
- const QueuedMessage& data = mMessagesBuffer[i];
- addHistoryLine(data.mText, data.mColor, data.mLog, data.mSourceId,
- data.mFrom);
- }
- mMessagesBuffer.clear();
- }
- void LLFloaterIMSession::addHistoryLine(const std::string& utf8msg,
- const LLColor4& color,
- bool log_to_file,
- const LLUUID& source,
- const std::string& const_name)
- {
- if (mFetchingLog)
- {
- // We must queue this message until the log is fully recovered. HB
- mMessagesBuffer.emplace_back(source, const_name, utf8msg, color,
- log_to_file);
- return;
- }
- std::string name = const_name;
- // Start tab flashing when receiving im for background session from user
- if (source.notNull())
- {
- LLMultiFloater* hostp = getHost();
- if (!isInVisibleChain() && hostp && source != gAgentID)
- {
- hostp->setFloaterFlashing(this, true);
- }
- }
- // Now we are adding the actual line of text, so erase the "Foo is
- // typing..." text segment, and the optional timestamp if it was present.
- // JC
- removeTypingIndicator();
- // Actually add the line
- std::string timestring;
- bool prepend_newline = true;
- static LLCachedControl<bool> show_time(gSavedSettings, "IMShowTimestamps");
- if (show_time)
- {
- timestring = mHistoryEditor->appendTime(prepend_newline);
- prepend_newline = false;
- }
- // 'name' is a sender name that we want to hotlink so that clicking on it
- // opens a profile. If name exists then add it to the front of the message.
- if (!name.empty())
- {
- // Do not hotlink any messages from the system (e.g. "Second Life:"),
- // so just add those in plain text.
- if (name == SYSTEM_FROM)
- {
- mHistoryEditor->appendColoredText(name, false, prepend_newline,
- color);
- }
- else
- {
- LLUUID av_id = source;
- if (av_id.isNull())
- {
- std::string self_name;
- gAgent.buildFullname(self_name);
- if (name == self_name)
- {
- av_id = gAgentID;
- }
- }
- else if (LLAvatarNameCache::useDisplayNames())
- {
- LLAvatarName avatar_name;
- if (LLAvatarNameCache::get(av_id, &avatar_name))
- {
- if (LLAvatarNameCache::useDisplayNames() == 2)
- {
- name = avatar_name.mDisplayName;
- }
- else
- {
- name = avatar_name.getNames();
- }
- }
- }
- // Convert the name to a hotlink and add to message.
- const LLStyleSP& source_style = gStyleMap.lookupAgent(source);
- mHistoryEditor->appendStyledText(name, false, prepend_newline,
- source_style);
- }
- prepend_newline = false;
- }
- mHistoryEditor->appendColoredText(utf8msg, false, prepend_newline, color);
- if (log_to_file)
- {
- logToFile(name + utf8msg);
- }
- if (!isInVisibleChain())
- {
- ++mNumUnreadMessages;
- }
- if (source.notNull())
- {
- if (mSpeakers)
- {
- mSpeakers->speakerChatted(source);
- mSpeakers->setSpeakerTyping(source, false);
- }
- if (mSpeakerPanel)
- {
- // Make sure this speaker is listed...
- mSpeakerPanel->addSpeaker(source, true);
- if (source != gAgentID)
- {
- // And that we are here too !
- mSpeakerPanel->addSpeaker(gAgentID, true);
- }
- }
- }
- }
- void LLFloaterIMSession::logToFile(const std::string& line,
- bool allow_timestamp)
- {
- static LLCachedControl<bool> log_im(gSavedPerAccountSettings,
- "LogInstantMessages");
- if (!log_im)
- {
- return;
- }
- static LLCachedControl<bool> stamp(gSavedPerAccountSettings,
- "IMLogTimestamp");
- if (allow_timestamp && stamp)
- {
- LLLogChat::saveHistory(mSessionLog, LLLogChat::timestamp() + line);
- }
- else
- {
- LLLogChat::saveHistory(mSessionLog, line);
- }
- }
- void LLFloaterIMSession::setVisible(bool b)
- {
- LLPanel::setVisible(b);
- LLMultiFloater* hostp = getHost();
- if (b && hostp)
- {
- hostp->setFloaterFlashing(this, false);
- }
- }
- void LLFloaterIMSession::setInputFocus(bool b)
- {
- mInputEditor->setFocus(b);
- }
- void LLFloaterIMSession::selectAll()
- {
- mInputEditor->selectAll();
- }
- void LLFloaterIMSession::selectNone()
- {
- mInputEditor->deselect();
- }
- bool LLFloaterIMSession::handleKeyHere(KEY key, MASK mask)
- {
- bool handled = false;
- if (key == KEY_RETURN)
- {
- if (HBFloaterTextInput::hasFloaterFor(mInputEditor))
- {
- HBFloaterTextInput::show(mInputEditor);
- return true;
- }
- if (mask == MASK_NONE || mask == MASK_CONTROL || mask == MASK_SHIFT)
- {
- sendMsg();
- handled = true;
- // Close talk panels on hitting return but not shift-return or
- // control-return
- if (gIMMgrp && !gSavedSettings.getBool("PinTalkViewOpen") &&
- !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT))
- {
- gIMMgrp->toggle(NULL);
- }
- }
- else if (mask == (MASK_SHIFT | MASK_CONTROL))
- {
- S32 cursor = mInputEditor->getCursor();
- std::string text = mInputEditor->getText();
- // For some reason, the event is triggered twice: let's insert only
- // one newline character.
- if (cursor == 0 || text[cursor - 1] != '\n')
- {
- text = text.insert(cursor, "\n");
- mInputEditor->setText(text);
- mInputEditor->setCursor(cursor + 1);
- }
- handled = true;
- }
- }
- else if (key == KEY_ESCAPE && mask == MASK_NONE)
- {
- handled = true;
- gFocusMgr.setKeyboardFocus(NULL);
- // Close talk panel with escape
- if (gIMMgrp && !gSavedSettings.getBool("PinTalkViewOpen"))
- {
- gIMMgrp->toggle(NULL);
- }
- }
- // May need to call base class LLPanel::handleKeyHere if not handled in
- // order to tab between buttons. JNC 1.2.2002
- return handled;
- }
- bool LLFloaterIMSession::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
- EDragAndDropType cargo_type,
- void* cargo_data,
- EAcceptance* accept,
- std::string& tooltip_msg)
- {
- if (mDialog == IM_NOTHING_SPECIAL)
- {
- LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID,
- mSessionUUID, drop,
- cargo_type, cargo_data,
- accept);
- }
- else if (isInviteAllowed())
- {
- // Handle case for dropping calling cards (and folders of calling
- // cards) onto invitation panel for invites
- *accept = ACCEPT_NO;
- if (cargo_type == DAD_CALLINGCARD)
- {
- if (dropCallingCard((LLInventoryItem*)cargo_data, drop))
- {
- *accept = ACCEPT_YES_MULTI;
- }
- }
- else if (cargo_type == DAD_CATEGORY)
- {
- if (dropCategory((LLInventoryCategory*)cargo_data, drop))
- {
- *accept = ACCEPT_YES_MULTI;
- }
- }
- }
- return true;
- }
- bool LLFloaterIMSession::dropCallingCard(LLInventoryItem* item, bool drop)
- {
- bool rv = isInviteAllowed();
- if (rv && item && item->getCreatorUUID().notNull())
- {
- if (drop)
- {
- uuid_vec_t ids;
- ids.emplace_back(item->getCreatorUUID());
- inviteToSession(ids);
- }
- }
- else
- {
- // Set to false if creator uuid is null.
- rv = false;
- }
- return rv;
- }
- bool LLFloaterIMSession::dropCategory(LLInventoryCategory* category, bool drop)
- {
- bool rv = isInviteAllowed();
- if (rv && category)
- {
- LLInventoryModel::cat_array_t cats;
- LLInventoryModel::item_array_t items;
- LLUniqueBuddyCollector buddies;
- gInventory.collectDescendentsIf(category->getUUID(), cats, items,
- LLInventoryModel::EXCLUDE_TRASH,
- buddies);
- U32 count = items.size();
- if (!count)
- {
- return false;
- }
- if (drop)
- {
- uuid_vec_t ids;
- for (U32 i = 0; i < count; ++i)
- {
- ids.emplace_back(items[i]->getCreatorUUID());
- }
- inviteToSession(ids);
- }
- }
- return rv;
- }
- bool LLFloaterIMSession::isInviteAllowed() const
- {
- return mDialog == IM_SESSION_CONFERENCE_START ||
- mDialog == IM_SESSION_INVITE;
- }
- //static
- void LLFloaterIMSession::onTabClick(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self)
- {
- self->setInputFocus(true);
- }
- }
- //static
- void LLFloaterIMSession::onCommitAvatar(LLUICtrl* ctrl, void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (!self || !ctrl) return;
- LLUUID id = self->mOtherParticipantUUID;
- if (id.notNull())
- {
- std::string valstr = ctrl->getValue().asString();
- if (valstr == "offer_tp")
- {
- LLAvatarActions::offerTeleport(id);
- }
- else if (valstr == "request_tp")
- {
- LLAvatarActions::teleportRequest(id);
- }
- else
- {
- // Bring up the Profile window
- LLFloaterAvatarInfo::showFromDirectory(id);
- }
- }
- }
- //static
- void LLFloaterIMSession::onClickViewLog(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self && !self->mLogFileName.empty() && gViewerWindowp)
- {
- #if LL_WINDOWS
- std::string url = "file:///";
- #else
- std::string url = "file://";
- #endif
- url += LLWeb::escapeURL(self->mLogFileName);
- if (gSavedPerAccountSettings.getBool("OpenIMLogsInBuiltInBrowser"))
- {
- LLFloaterMediaBrowser::showInstance(url);
- }
- else
- {
- gWindowp->spawnWebBrowser(url, true);
- }
- }
- }
- //static
- void LLFloaterIMSession::onClickGroupInfo(void* userdata)
- {
- // Bring up the Profile window
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- LLFloaterGroupInfo::showFromUUID(self->mSessionUUID);
- }
- //static
- void LLFloaterIMSession::onClickClose(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self)
- {
- self->close();
- }
- }
- //static
- void LLFloaterIMSession::onClickSnooze(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self)
- {
- if (self->mIsGroupSession)
- {
- self->mSnoozeDuration =
- gSavedSettings.getU32("GroupIMSnoozeDuration");
- }
- self->close();
- }
- }
- //static
- void LLFloaterIMSession::onClickStartCall(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self && self->mVoiceChannel)
- {
- self->mVoiceChannel->activate();
- }
- }
- //static
- void LLFloaterIMSession::onClickEndCall(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self && self->mVoiceChannel)
- {
- self->mVoiceChannel->deactivate();
- }
- }
- //static
- void LLFloaterIMSession::onClickSend(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self)
- {
- self->sendMsg();
- }
- }
- //static
- void LLFloaterIMSession::onClickOpenTextEditor(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self && !self->mSessionLabel.empty())
- {
- self->mHasScrolledOnce = true;
- HBFloaterTextInput::show(self->mInputEditor, self->mSessionLabel,
- &setIMTyping, self);
- }
- }
- //static
- void LLFloaterIMSession::onClickToggleActiveSpeakers(void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (self && self->mSpeakerPanel)
- {
- self->mSpeakerPanel->setVisible(!self->mSpeakerPanel->getVisible());
- }
- }
- //static
- void LLFloaterIMSession::onCommitChat(LLUICtrl* caller, void* userdata)
- {
- LLFloaterIMSession* self= (LLFloaterIMSession*)userdata;
- self->sendMsg();
- }
- //static
- void LLFloaterIMSession::onInputEditorFocusReceived(LLFocusableElement* caller,
- void* userdata)
- {
- LLFloaterIMSession* self= (LLFloaterIMSession*)userdata;
- self->mHistoryEditor->setCursorAndScrollToEnd();
- }
- //static
- void LLFloaterIMSession::onInputEditorFocusLost(LLFocusableElement* caller,
- void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- self->setTyping(false);
- }
- //static
- void LLFloaterIMSession::onInputEditorKeystroke(LLLineEditor* caller,
- void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- std::string text = self->mInputEditor->getText();
- if (!text.empty())
- {
- self->setTyping(true);
- }
- else
- {
- // Deleting all text counts as stopping typing.
- self->setTyping(false);
- }
- }
- //static
- void LLFloaterIMSession::onInputEditorScrolled(LLLineEditor* caller,
- void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- if (!self || !caller) return;
- if (!self->mHasScrolledOnce && !self->mSessionLabel.empty() &&
- gSavedSettings.getBool("AutoOpenTextInput"))
- {
- self->mHasScrolledOnce = true;
- HBFloaterTextInput::show(caller, self->mSessionLabel, &setIMTyping,
- self);
- }
- }
- void LLFloaterIMSession::onClose(bool app_quitting)
- {
- HBFloaterTextInput::abort(mInputEditor);
- setTyping(false);
- if (gIMMgrp)
- {
- gIMMgrp->removeSession(mSessionUUID, mOtherParticipantUUID,
- mSnoozeDuration);
- }
- destroy();
- }
- void LLFloaterIMSession::onVisibilityChange(bool new_visibility)
- {
- if (new_visibility)
- {
- mNumUnreadMessages = 0;
- }
- }
- void LLFloaterIMSession::sendText(LLWString text)
- {
- if (!gAgent.isGodlike() && mDialog == IM_NOTHING_SPECIAL &&
- mOtherParticipantUUID.isNull())
- {
- llinfos << "Cannot send IM to everyone unless you are a god."
- << llendl;
- return;
- }
- //MK
- if (gRLenabled)
- {
- bool allowed;
- if (mIsGroupSession)
- {
- allowed = gRLInterface.canSendGroupIM(mSessionLabel);
- }
- else
- {
- allowed = gRLInterface.canSendIM(mOtherParticipantUUID);
- }
- if (!allowed)
- {
- // User is forbidden to send IMs and the receiver is no exception
- // signal both the sender and the receiver
- text = utf8str_to_wstring(RLInterface::sSendimMessage);
- }
- }
- //mk
- if (!text.empty())
- {
- // Store sent line in history, duplicates will get filtered
- mInputEditor->updateHistory();
- // Convert to UTF8 for transport
- std::string utf8_text = wstring_to_utf8str(text);
- if (utf8_text.length() > 3)
- {
- if (gSavedSettings.getBool("AutoCloseOOC"))
- {
- // Try to find any unclosed OOC chat (i.e. an opening double
- // parenthesis without a matching closing double parenthesis.
- size_t i = utf8_text.find("((");
- if (i != std::string::npos)
- {
- size_t j = utf8_text.rfind("))");
- if (j == std::string::npos || j < i)
- {
- if (utf8_text.back() == ')')
- {
- // Cosmetic: add a space first to avoid a closing
- // triple parenthesis
- utf8_text += ' ';
- }
- // Add the missing closing double parenthesis.
- utf8_text += "))";
- }
- }
- }
- // Convert MU*s style poses into IRC emotes here.
- if (utf8_text[0] == ':'&& gSavedSettings.getBool("AllowMUpose"))
- {
- if (utf8_text.compare(0, 2, ":'") == 0)
- {
- utf8_text.replace(0, 1, "/me");
- }
- // Do not prevent smileys and such.
- else if (isalpha(utf8_text[1]))
- {
- utf8_text.replace(0, 1, "/me ");
- }
- }
- }
- // Truncate
- utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
- if (mSessionInitialized)
- {
- LLIMMgr::deliverMessage(utf8_text, mSessionUUID,
- mOtherParticipantUUID, mDialog);
- // Local echo
- if (mDialog == IM_NOTHING_SPECIAL &&
- mOtherParticipantUUID.notNull())
- {
- std::string history_echo;
- gAgent.buildFullname(history_echo);
- if (LLAvatarNameCache::useDisplayNames())
- {
- LLAvatarName avatar_name;
- if (LLAvatarNameCache::get(gAgentID, &avatar_name))
- {
- if (LLAvatarNameCache::useDisplayNames() == 2)
- {
- history_echo = avatar_name.mDisplayName;
- }
- else
- {
- history_echo = avatar_name.getNames();
- }
- }
- }
- // Look for IRC-style emotes here.
- std::string prefix = utf8_text.substr(0, 4);
- if (prefix == "/me " || prefix == "/me'")
- {
- utf8_text.replace(0, 3, "");
- }
- else
- {
- history_echo += ": ";
- }
- history_echo += utf8_text;
- bool other_was_typing = mOtherTyping;
- addHistoryLine(history_echo,
- gSavedSettings.getColor("IMChatColor"), true,
- gAgentID);
- if (other_was_typing)
- {
- addTypingIndicator(mOtherParticipantUUID,
- mOtherTypingName);
- }
- }
- }
- else
- {
- // Queue up the message to send once the session is initialized
- mQueuedMsgsForInit.append(utf8_text);
- }
- }
- gViewerStats.incStat(LLViewerStats::ST_IM_COUNT);
- // We do not need to actually send the typing stop message, the other
- // client will infer it from receiving the message.
- mTyping = false;
- mSentTypingState = true;
- }
- void LLFloaterIMSession::sendMsg()
- {
- sendText(mInputEditor->getConvertedText());
- mInputEditor->setText(LLStringUtil::null);
- }
- void LLFloaterIMSession::updateSpeakersList(const LLSD& speaker_updates)
- {
- if (mSpeakers)
- {
- mSpeakers->updateSpeakers(speaker_updates);
- }
- }
- void LLFloaterIMSession::processSessionUpdate(const LLSD& session_update)
- {
- if (session_update.has("moderated_mode") &&
- session_update["moderated_mode"].has("voice"))
- {
- bool voice_moderated = session_update["moderated_mode"]["voice"];
- if (voice_moderated)
- {
- setTitle(mSessionLabel + " " + getString("moderated_chat_label"));
- }
- else
- {
- setTitle(mSessionLabel);
- }
- // Update the speakers drop-down too
- if (mSpeakerPanel)
- {
- mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated);
- }
- }
- }
- void LLFloaterIMSession::setSpeakers(const LLSD& speaker_list)
- {
- if (mSpeakers)
- {
- mSpeakers->setSpeakers(speaker_list);
- }
- }
- void LLFloaterIMSession::sessionInitReplyReceived(const LLUUID& session_id)
- {
- mSessionUUID = session_id;
- if (mVoiceChannel)
- {
- mVoiceChannel->updateSessionID(session_id);
- }
- mSessionInitialized = true;
- // Reenable now that the session has started. The "Starting session..."
- // label will be reset to what it should finally be in draw(). HB
- mInputEditor->setEnabled(true);
- // And now, send the queued message.
- LLSD::array_iterator iter;
- for (iter = mQueuedMsgsForInit.beginArray();
- iter != mQueuedMsgsForInit.endArray(); ++iter)
- {
- LLIMMgr::deliverMessage(iter->asString(), mSessionUUID,
- mOtherParticipantUUID, mDialog);
- }
- }
- void LLFloaterIMSession::requestAutoConnect()
- {
- mAutoConnect = true;
- }
- void LLFloaterIMSession::setTyping(bool typing)
- {
- if (typing)
- {
- // Every time the user types something, reset this timer
- mLastKeystrokeTimer.reset();
- if (!mTyping)
- {
- // The user just started typing.
- mFirstKeystrokeTimer.reset();
- // Will send typing state after a short delay.
- mSentTypingState = false;
- }
- if (mSpeakers)
- {
- mSpeakers->setSpeakerTyping(gAgentID, true);
- }
- }
- else
- {
- if (mTyping)
- {
- // The user just stopped typing, send state immediately
- sendTypingState(false);
- mSentTypingState = true;
- }
- if (mSpeakers)
- {
- mSpeakers->setSpeakerTyping(gAgentID, false);
- }
- }
- mTyping = typing;
- }
- void LLFloaterIMSession::sendTypingState(bool typing)
- {
- // Do not want to send typing indicators to multiple people, potentially
- // too much network traffic. Only send in person-to-person IMs.
- if (mDialog != IM_NOTHING_SPECIAL) return;
- std::string name;
- gAgent.buildFullname(name);
- pack_instant_message(gAgentID, false, gAgentSessionID,
- mOtherParticipantUUID, name, std::string("typing"),
- IM_ONLINE, typing ? IM_TYPING_START : IM_TYPING_STOP,
- mSessionUUID);
- gAgent.sendReliableMessage();
- }
- void LLFloaterIMSession::processIMTyping(const LLUUID& from_id,
- const std::string& name, bool typing)
- {
- if (typing)
- {
- // Other user started typing
- addTypingIndicator(from_id, name);
- }
- else
- {
- // Other user stopped typing
- removeTypingIndicator(from_id);
- }
- }
- void LLFloaterIMSession::addTypingIndicator(const LLUUID& from_id,
- const std::string& from_name)
- {
- // We may have lost a "stop-typing" packet, do not add it twice.
- // Also, do not add any indicator while fetching the server log; for
- // now, this should never happen since the server log is so far reserved
- // to group IM sessions, for which the typing state is never sent. HB
- if (!mOtherTyping && !mFetchingLog)
- {
- mTypingLineStartIndex = mHistoryEditor->getWText().length();
- LLUIString typing_start = sTypingStartString;
- typing_start.setArg("[NAME]", from_name);
- addHistoryLine(typing_start,
- gSavedSettings.getColor4("SystemChatColor"), false);
- mOtherTypingName = from_name;
- mOtherTyping = true;
- if (from_id.notNull() && mSpeakers)
- {
- mSpeakers->setSpeakerTyping(from_id, true);
- }
- }
- }
- void LLFloaterIMSession::removeTypingIndicator(const LLUUID& from_id)
- {
- if (mOtherTyping)
- {
- // Must do this first, otherwise addHistoryLine calls us again.
- mOtherTyping = false;
- S32 chars_to_remove = mHistoryEditor->getWText().length() -
- mTypingLineStartIndex;
- mHistoryEditor->removeTextFromEnd(chars_to_remove);
- }
- if (from_id.notNull() && mSpeakers)
- {
- mSpeakers->setSpeakerTyping(from_id, false);
- }
- }
- //static
- void LLFloaterIMSession::setIMTyping(void* caller, bool typing)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)caller;
- if (self)
- {
- self->setTyping(typing);
- }
- }
- //static
- void LLFloaterIMSession::chatFromLog(S32 type, const LLSD& data,
- void* userdata)
- {
- LLFloaterIMSession* self = (LLFloaterIMSession*)userdata;
- std::string message;
- LLColor4 color = LLColor4::grey;
- switch (type)
- {
- case LLLogChat::LOG_FILENAME:
- self->mLogFileName = data["filename"].asString();
- // Nothing to print in the IM window.
- return;
- case LLLogChat::LOG_SERVER_FETCH:
- // The server log is being fetched: at this point we must queue all
- // incoming messages until the full log has been (asynchronously)
- // recovered and printed into mHistoryEditor, or else we would get
- // out of order messages in the latter. HB
- self->mFetchingLog = true;
- // Nothing to print in the IM window.
- return;
- case LLLogChat::LOG_SERVER:
- {
- // Compare this log message against queued messages; the server
- // will have the first message of an opening sesssion already
- // logged when that message arrives on our side, for example. HB
- U32 count = self->mMessagesBuffer.size();
- if (count)
- {
- LLUUID src_id(data["from_id"].asString());
- message = data["message"].asString();
- for (U32 i = 0; i < count; ++i)
- {
- // Here, we can compare the message originator, based on
- // their UUID. HB
- const QueuedMessage& msg = self->mMessagesBuffer[i];
- if (src_id.notNull() && msg.mSourceId != src_id)
- {
- continue;
- }
- // Match the text. HB
- size_t pos = msg.mText.find(message);
- if (pos == std::string::npos)
- {
- continue;
- }
- if (pos + message.size() == msg.mText.size())
- {
- // This message is already queued. Skip from log. HB
- LL_DEBUGS("ServerIMLog") << "Skipping log server message that we did receive: "
- << message << LL_ENDL;
- return;
- }
- }
- }
- // Add an IM chat log line, with a slighty lighter grey color. HB
- color = LLColor4::grey2;
- message = data["line"].asString();
- // Add it to our log file too, if configured for logging, but do
- // not attempt to add a timestamp, which was already added if the
- // user does want it, in LLLogChat::fetchHistoryCoro(). HB
- self->logToFile(message, false);
- break;
- }
- case LLLogChat::LOG_LINE:
- // Add an IM chat log line.
- message = data["line"].asString();
- break;
- case LLLogChat::LOG_END:
- // Add log end message
- if (gSavedPerAccountSettings.getBool("LogInstantMessages"))
- {
- message =
- LLFloaterChat::getInstance()->getString("IM_logging_string");
- }
- // Enable the View log button only when the file exists; it may
- // have been created as the result of the server log retreival,
- // thus why we waited until now to take this action. HB
- if (self->mViewLogButton && !self->mLogFileName.empty() &&
- LLFile::exists(self->mLogFileName))
- {
- self->mViewLogButton->setVisible(true);
- }
- break;
- default:
- llerrs << "Unknown callback response type: " << type << llendl;
- }
- if (!message.empty())
- {
- self->mHistoryEditor->appendColoredText(message, false, true, color);
- }
- // If the log is fully printed and messages got queued, add them now. HB
- if (type == LLLogChat::LOG_END && self->mFetchingLog)
- {
- self->addQueuedMessages(); // Note: this resets mFetchingLog. HB
- }
- }
- void LLFloaterIMSession::showSessionStartError(const std::string& error_string)
- {
- // The error strings etc should really be static and local to this file
- // instead of in the LLFloaterIM, but they were in llimmgr.cpp first and
- // unfortunately some translations into non English languages already
- // occurred thus making it a tad harder to change over to a "correct"
- // solution. The best solution would be to store all of the misc strings
- // into their own XML file which would be read in by any LLIMPanel post
- // build function instead of repeating the same info in the group, adhoc
- // and normal IM xml files.
- LLSD args;
- args["REASON"] = LLFloaterIM::sMsgStringsMap[error_string];
- std::string recipient = getTitle();
- args["RECIPIENT"] = recipient.empty() ? mSessionUUID.asString()
- : recipient;
- LLSD payload;
- payload["session_id"] = mSessionUUID;
- gNotifications.add("ChatterBoxSessionStartError", args, payload,
- onConfirmForceCloseError);
- }
- void LLFloaterIMSession::showSessionEventError(const std::string& event_string,
- const std::string& error_string)
- {
- LLSD args;
- args["REASON"] = LLFloaterIM::sMsgStringsMap[error_string];
- LLUIString event_str = LLFloaterIM::sMsgStringsMap[event_string];
- std::string recipient = getTitle();
- event_str.setArg("[RECIPIENT]",
- recipient.empty() ? mSessionUUID.asString() : recipient);
- args["EVENT"] = event_str;
- gNotifications.add("ChatterBoxSessionEventError", args);
- }
- void LLFloaterIMSession::showSessionForceClose(const std::string& reason_string)
- {
- LLSD args;
- args["NAME"] = getTitle();
- args["REASON"] = LLFloaterIM::sMsgStringsMap[reason_string];
- LLSD payload;
- payload["session_id"] = mSessionUUID;
- gNotifications.add("ForceCloseChatterBoxSession", args, payload,
- onConfirmForceCloseError);
- }
- bool LLFloaterIMSession::onConfirmForceCloseError(const LLSD& notification,
- const LLSD& response)
- {
- LLUUID session_id = notification["payload"]["session_id"];
- LLFloaterIMSession* floaterp = findInstance(session_id);
- if (floaterp)
- {
- floaterp->close();
- }
- return false;
- }
- ///////////////////////////////////////////////////////////////////////////////
- // LLFloaterIM class
- ///////////////////////////////////////////////////////////////////////////////
- LLFloaterIM::LLFloaterIM()
- {
- // autoresize=false is necessary to avoid resizing of the IM window
- // whenever a session is opened or closed (it would otherwise resize the
- // window to match the size of the im-sesssion when they were created.
- // This happens in LLMultiFloater::resizeToContents() when called through
- // LLMultiFloater::addFloater())
- this->mAutoResize = false;
- LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im.xml");
- }
- bool LLFloaterIM::postBuild()
- {
- if (sOnlyUserMessage.empty())
- {
- sOnlyUserMessage = getString("only_user_message");
- sOfflineMessage = getString("offline_message");
- sMutedMessage = getString("muted_message");
- sMsgStringsMap["generic"] = getString("generic_request_error");
- sMsgStringsMap["unverified"] = getString("insufficient_perms_error");
- sMsgStringsMap["no_ability"] = getString("no_ability_error");
- sMsgStringsMap["muted"] = getString("muted_error");
- sMsgStringsMap["not_a_moderator"] = getString("not_a_mod_error");
- sMsgStringsMap["does not exist"] = getString("session_does_not_exist_error");
- sMsgStringsMap["add"] = getString("add_session_event");
- sMsgStringsMap["message"] = getString("message_session_event");
- sMsgStringsMap["removed"] = getString("removed_from_group");
- sMsgStringsMap["no ability"] = getString("close_on_no_ability");
- }
- return true;
- }
|