llimpanel.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:25k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llimpanel.cpp
  3.  * @brief LLIMPanel class definition
  4.  *
  5.  * $LicenseInfo:firstyear=2001&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2001-2010, Linden Research, Inc.
  8.  * 
  9.  * Second Life Viewer Source Code
  10.  * The source code in this file ("Source Code") is provided by Linden Lab
  11.  * to you under the terms of the GNU General Public License, version 2.0
  12.  * ("GPL"), unless you have obtained a separate licensing agreement
  13.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  14.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16.  * 
  17.  * There are special exceptions to the terms and conditions of the GPL as
  18.  * it is applied to this Source Code. View the full text of the exception
  19.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  20.  * online at
  21.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22.  * 
  23.  * By copying, modifying or distributing this software, you acknowledge
  24.  * that you have read and understood your obligations described above,
  25.  * and agree to abide by those obligations.
  26.  * 
  27.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29.  * COMPLETENESS OR PERFORMANCE.
  30.  * $/LicenseInfo$
  31.  */
  32. #include "llviewerprecompiledheaders.h"
  33. #include "llimpanel.h"
  34. #include "indra_constants.h"
  35. #include "llfloaterreg.h"
  36. #include "llfocusmgr.h"
  37. #include "llfontgl.h"
  38. #include "llrect.h"
  39. #include "llerror.h"
  40. #include "llmultifloater.h"
  41. #include "llstring.h"
  42. #include "message.h"
  43. #include "lltextbox.h"
  44. #include "llagent.h"
  45. #include "llbutton.h"
  46. #include "llbottomtray.h"
  47. #include "llcallingcard.h"
  48. #include "llchannelmanager.h"
  49. #include "llchat.h"
  50. #include "llchiclet.h"
  51. #include "llconsole.h"
  52. #include "llgroupactions.h"
  53. #include "llfloater.h"
  54. #include "llfloateractivespeakers.h"
  55. #include "llavataractions.h"
  56. #include "llinventory.h"
  57. #include "llinventorymodel.h"
  58. #include "llfloaterinventory.h"
  59. #include "lliconctrl.h"
  60. #include "llkeyboard.h"
  61. #include "lllineeditor.h"
  62. #include "llpanelimcontrolpanel.h"
  63. #include "llrecentpeople.h"
  64. #include "llresmgr.h"
  65. #include "lltooldraganddrop.h"
  66. #include "lltrans.h"
  67. #include "lltabcontainer.h"
  68. #include "llviewertexteditor.h"
  69. #include "llviewermessage.h"
  70. #include "llviewerstats.h"
  71. #include "llviewercontrol.h"
  72. #include "lluictrlfactory.h"
  73. #include "llviewerwindow.h"
  74. #include "llvoicechannel.h"
  75. #include "lllogchat.h"
  76. #include "llweb.h"
  77. #include "llhttpclient.h"
  78. #include "llmutelist.h"
  79. #include "llstylemap.h"
  80. #include "llappviewer.h"
  81. //
  82. // Constants
  83. //
  84. const S32 LINE_HEIGHT = 16;
  85. const S32 MIN_WIDTH = 200;
  86. const S32 MIN_HEIGHT = 130;
  87. //
  88. // Statics
  89. //
  90. //
  91. static std::string sTitleString = "Instant Message with [NAME]";
  92. static std::string sTypingStartString = "[NAME]: ...";
  93. static std::string sSessionStartString = "Starting session with [NAME] please wait.";
  94. //
  95. // LLFloaterIMPanel
  96. //
  97. LLFloaterIMPanel::LLFloaterIMPanel(const std::string& session_label,
  98.    const LLUUID& session_id,
  99.    const LLUUID& other_participant_id,
  100.    const std::vector<LLUUID>& ids,
  101.    EInstantMessage dialog)
  102. : LLFloater(session_id),
  103. mInputEditor(NULL),
  104. mHistoryEditor(NULL),
  105. mSessionUUID(session_id),
  106. mSessionLabel(session_label),
  107. mSessionInitialized(FALSE),
  108. mSessionStartMsgPos(0),
  109. mOtherParticipantUUID(other_participant_id),
  110. mDialog(dialog),
  111. mSessionInitialTargetIDs(ids),
  112. mTyping(FALSE),
  113. mOtherTyping(FALSE),
  114. mTypingLineStartIndex(0),
  115. mSentTypingState(TRUE),
  116. mNumUnreadMessages(0),
  117. mShowSpeakersOnConnect(TRUE),
  118. mTextIMPossible(TRUE),
  119. mProfileButtonEnabled(TRUE),
  120. mCallBackEnabled(TRUE),
  121. mSpeakerPanel(NULL),
  122. mFirstKeystrokeTimer(),
  123. mLastKeystrokeTimer()
  124. {
  125. std::string xml_filename;
  126. switch(mDialog)
  127. {
  128. case IM_SESSION_GROUP_START:
  129. mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
  130. xml_filename = "floater_instant_message_group.xml";
  131. break;
  132. case IM_SESSION_INVITE:
  133. mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
  134. if (gAgent.isInGroup(mSessionUUID))
  135. {
  136. xml_filename = "floater_instant_message_group.xml";
  137. }
  138. else // must be invite to ad hoc IM
  139. {
  140. xml_filename = "floater_instant_message_ad_hoc.xml";
  141. }
  142. break;
  143. case IM_SESSION_P2P_INVITE:
  144. xml_filename = "floater_instant_message.xml";
  145. break;
  146. case IM_SESSION_CONFERENCE_START:
  147. mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
  148. xml_filename = "floater_instant_message_ad_hoc.xml";
  149. break;
  150. // just received text from another user
  151. case IM_NOTHING_SPECIAL:
  152. xml_filename = "floater_instant_message.xml";
  153. mTextIMPossible = LLVoiceClient::getInstance()->isSessionTextIMPossible(mSessionUUID);
  154. mProfileButtonEnabled = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionUUID);
  155. mCallBackEnabled = LLVoiceClient::getInstance()->isSessionCallBackPossible(mSessionUUID);
  156. break;
  157. default:
  158. llwarns << "Unknown session type" << llendl;
  159. xml_filename = "floater_instant_message.xml";
  160. break;
  161. }
  162. LLUICtrlFactory::getInstance()->buildFloater(this, xml_filename, NULL);
  163. setTitle(mSessionLabel);
  164. mInputEditor->setMaxTextLength(1023);
  165. // enable line history support for instant message bar
  166. mInputEditor->setEnableLineHistory(TRUE);
  167. //*TODO we probably need the same "awaiting message" thing in LLIMFloater
  168. LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionUUID);
  169. if (!im_session)
  170. {
  171. llerror("im session with id " + mSessionUUID.asString() + " does not exist!", 0);
  172. return;
  173. }
  174. mSessionInitialized =  im_session->mSessionInitialized;
  175. if (!mSessionInitialized)
  176. {
  177. //locally echo a little "starting session" message
  178. LLUIString session_start = sSessionStartString;
  179. session_start.setArg("[NAME]", getTitle());
  180. mSessionStartMsgPos = 
  181. mHistoryEditor->getWText().length();
  182. addHistoryLine(
  183. session_start,
  184. LLUIColorTable::instance().getColor("SystemChatColor"),
  185. false);
  186. }
  187. }
  188. LLFloaterIMPanel::~LLFloaterIMPanel()
  189. {
  190. //delete focus lost callback
  191. mFocusCallbackConnection.disconnect();
  192. }
  193. BOOL LLFloaterIMPanel::postBuild() 
  194. {
  195. setVisibleCallback(boost::bind(&LLFloaterIMPanel::onVisibilityChange, this, _2));
  196. mInputEditor = getChild<LLLineEditor>("chat_editor");
  197. mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) );
  198. mFocusCallbackConnection = mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this));
  199. mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this );
  200. mInputEditor->setCommitCallback( onCommitChat, this );
  201. mInputEditor->setCommitOnFocusLost( FALSE );
  202. mInputEditor->setRevertOnEsc( FALSE );
  203. mInputEditor->setReplaceNewlinesWithSpaces( FALSE );
  204. childSetAction("profile_callee_btn", onClickProfile, this);
  205. childSetAction("group_info_btn", onClickGroupInfo, this);
  206. childSetAction("start_call_btn", onClickStartCall, this);
  207. childSetAction("end_call_btn", onClickEndCall, this);
  208. childSetAction("send_btn", onClickSend, this);
  209. childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
  210. childSetAction("moderator_kick_speaker", onKickSpeaker, this);
  211. //LLButton* close_btn = getChild<LLButton>("close_btn");
  212. //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this);
  213. mHistoryEditor = getChild<LLViewerTextEditor>("im_history");
  214. if ( IM_SESSION_GROUP_START == mDialog )
  215. {
  216. childSetEnabled("profile_btn", FALSE);
  217. }
  218. if(!mProfileButtonEnabled)
  219. {
  220. childSetEnabled("profile_callee_btn", FALSE);
  221. }
  222. sTitleString = getString("title_string");
  223. sTypingStartString = getString("typing_start_string");
  224. sSessionStartString = getString("session_start_string");
  225. if (mSpeakerPanel)
  226. {
  227. mSpeakerPanel->refreshSpeakers();
  228. }
  229. if (mDialog == IM_NOTHING_SPECIAL)
  230. {
  231. childSetAction("mute_btn", onClickMuteVoice, this);
  232. childSetCommitCallback("speaker_volume", onVolumeChange, this);
  233. }
  234. setDefaultBtn("send_btn");
  235. return TRUE;
  236. }
  237. void* LLFloaterIMPanel::createSpeakersPanel(void* data)
  238. {
  239. LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)data;
  240. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(floaterp->mSessionUUID);
  241. floaterp->mSpeakerPanel = new LLPanelActiveSpeakers(speaker_mgr, TRUE);
  242. return floaterp->mSpeakerPanel;
  243. }
  244. //static 
  245. void LLFloaterIMPanel::onClickMuteVoice(void* user_data)
  246. {
  247. LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
  248. if (floaterp)
  249. {
  250. BOOL is_muted = LLMuteList::getInstance()->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat);
  251. LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT);
  252. if (!is_muted)
  253. {
  254. LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat);
  255. }
  256. else
  257. {
  258. LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat);
  259. }
  260. }
  261. }
  262. //static 
  263. void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data)
  264. {
  265. LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
  266. if (floaterp)
  267. {
  268. gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal());
  269. }
  270. }
  271. // virtual
  272. void LLFloaterIMPanel::draw()
  273. {
  274. LLViewerRegion* region = gAgent.getRegion();
  275. BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "")
  276.   && mSessionInitialized
  277.   && LLVoiceClient::voiceEnabled()
  278.   && mCallBackEnabled;
  279. // hide/show start call and end call buttons
  280. LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionUUID);
  281. if (!voice_channel)
  282. return;
  283. childSetVisible("end_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED);
  284. childSetVisible("start_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED);
  285. childSetEnabled("start_call_btn", enable_connect);
  286. childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty());
  287. LLPointer<LLSpeaker> self_speaker;
  288. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionUUID);
  289. if (speaker_mgr)
  290. {
  291. self_speaker = speaker_mgr->findSpeaker(gAgent.getID());
  292. }
  293. if(!mTextIMPossible)
  294. {
  295. mInputEditor->setEnabled(FALSE);
  296. mInputEditor->setLabel(getString("unavailable_text_label"));
  297. }
  298. else if (self_speaker.notNull() && self_speaker->mModeratorMutedText)
  299. {
  300. mInputEditor->setEnabled(FALSE);
  301. mInputEditor->setLabel(getString("muted_text_label"));
  302. }
  303. else
  304. {
  305. mInputEditor->setEnabled(TRUE);
  306. mInputEditor->setLabel(getString("default_text_label"));
  307. }
  308. // show speakers window when voice first connects
  309. if (mShowSpeakersOnConnect && voice_channel->isActive())
  310. {
  311. childSetVisible("active_speakers_panel", TRUE);
  312. mShowSpeakersOnConnect = FALSE;
  313. }
  314. childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
  315. if (mTyping)
  316. {
  317. // Time out if user hasn't typed for a while.
  318. if (mLastKeystrokeTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
  319. {
  320. setTyping(FALSE);
  321. }
  322. // If we are typing, and it's been a little while, send the
  323. // typing indicator
  324. if (!mSentTypingState
  325. && mFirstKeystrokeTimer.getElapsedTimeF32() > 1.f)
  326. {
  327. sendTypingState(TRUE);
  328. mSentTypingState = TRUE;
  329. }
  330. }
  331. // use embedded panel if available
  332. if (mSpeakerPanel)
  333. {
  334. if (mSpeakerPanel->getVisible())
  335. {
  336. mSpeakerPanel->refreshSpeakers();
  337. }
  338. }
  339. else
  340. {
  341. // refresh volume and mute checkbox
  342. childSetVisible("speaker_volume", LLVoiceClient::voiceEnabled() && voice_channel->isActive());
  343. childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID));
  344. childSetValue("mute_btn", LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat));
  345. childSetVisible("mute_btn", LLVoiceClient::voiceEnabled() && voice_channel->isActive());
  346. }
  347. LLFloater::draw();
  348. }
  349. class LLSessionInviteResponder : public LLHTTPClient::Responder
  350. {
  351. public:
  352. LLSessionInviteResponder(const LLUUID& session_id)
  353. {
  354. mSessionID = session_id;
  355. }
  356. void error(U32 statusNum, const std::string& reason)
  357. {
  358. llinfos << "Error inviting all agents to session" << llendl;
  359. //throw something back to the viewer here?
  360. }
  361. private:
  362. LLUUID mSessionID;
  363. };
  364. BOOL LLFloaterIMPanel::inviteToSession(const std::vector<LLUUID>& ids)
  365. {
  366. LLViewerRegion* region = gAgent.getRegion();
  367. if (!region)
  368. {
  369. return FALSE;
  370. }
  371. S32 count = ids.size();
  372. if( isInviteAllowed() && (count > 0) )
  373. {
  374. llinfos << "LLFloaterIMPanel::inviteToSession() - inviting participants" << llendl;
  375. std::string url = region->getCapability("ChatSessionRequest");
  376. LLSD data;
  377. data["params"] = LLSD::emptyArray();
  378. for (int i = 0; i < count; i++)
  379. {
  380. data["params"].append(ids[i]);
  381. }
  382. data["method"] = "invite";
  383. data["session-id"] = mSessionUUID;
  384. LLHTTPClient::post(
  385. url,
  386. data,
  387. new LLSessionInviteResponder(
  388. mSessionUUID));
  389. }
  390. else
  391. {
  392. llinfos << "LLFloaterIMPanel::inviteToSession -"
  393. << " no need to invite agents for "
  394. << mDialog << llendl;
  395. // successful add, because everyone that needed to get added
  396. // was added.
  397. }
  398. return TRUE;
  399. }
  400. void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file, const LLUUID& source, const std::string& name)
  401. {
  402. // start tab flashing when receiving im for background session from user
  403. if (source != LLUUID::null)
  404. {
  405. LLMultiFloater* hostp = getHost();
  406. if( !isInVisibleChain() 
  407. && hostp 
  408. && source != gAgent.getID())
  409. {
  410. hostp->setFloaterFlashing(this, TRUE);
  411. }
  412. }
  413. // Now we're adding the actual line of text, so erase the 
  414. // "Foo is typing..." text segment, and the optional timestamp
  415. // if it was present. JC
  416. removeTypingIndicator(NULL);
  417. // Actually add the line
  418. std::string timestring;
  419. bool prepend_newline = true;
  420. if (gSavedSettings.getBOOL("IMShowTimestamps"))
  421. {
  422. timestring = mHistoryEditor->appendTime(prepend_newline);
  423. prepend_newline = false;
  424. }
  425. std::string separator_string(": ");
  426. // 'name' is a sender name that we want to hotlink so that clicking on it opens a profile.
  427. if (!name.empty()) // If name exists, then add it to the front of the message.
  428. {
  429. // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text.
  430. if (name == SYSTEM_FROM)
  431. {
  432. mHistoryEditor->appendText(name + separator_string, prepend_newline, LLStyle::Params().color(color));
  433. }
  434. else
  435. {
  436. // Convert the name to a hotlink and add to message.
  437. mHistoryEditor->appendText(name + separator_string, prepend_newline, LLStyleMap::instance().lookupAgent(source));
  438. }
  439. prepend_newline = false;
  440. }
  441. mHistoryEditor->appendText(utf8msg, prepend_newline, LLStyle::Params().color(color));
  442. mHistoryEditor->blockUndo();
  443. if (!isInVisibleChain())
  444. {
  445. mNumUnreadMessages++;
  446. }
  447. }
  448. void LLFloaterIMPanel::setInputFocus( BOOL b )
  449. {
  450. mInputEditor->setFocus( b );
  451. }
  452. void LLFloaterIMPanel::selectAll()
  453. {
  454. mInputEditor->selectAll();
  455. }
  456. void LLFloaterIMPanel::selectNone()
  457. {
  458. mInputEditor->deselect();
  459. }
  460. BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask )
  461. {
  462. BOOL handled = FALSE;
  463. if( KEY_RETURN == key && mask == MASK_NONE)
  464. {
  465. sendMsg();
  466. handled = TRUE;
  467. }
  468. else if ( KEY_ESCAPE == key )
  469. {
  470. handled = TRUE;
  471. gFocusMgr.setKeyboardFocus(NULL);
  472. }
  473. // May need to call base class LLPanel::handleKeyHere if not handled
  474. // in order to tab between buttons.  JNC 1.2.2002
  475. return handled;
  476. }
  477. BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
  478.   EDragAndDropType cargo_type,
  479.   void* cargo_data,
  480.   EAcceptance* accept,
  481.   std::string& tooltip_msg)
  482. {
  483. if (mDialog == IM_NOTHING_SPECIAL)
  484. {
  485. LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionUUID, drop,
  486.  cargo_type, cargo_data, accept);
  487. }
  488. // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites
  489. else if (isInviteAllowed())
  490. {
  491. *accept = ACCEPT_NO;
  492. if (cargo_type == DAD_CALLINGCARD)
  493. {
  494. if (dropCallingCard((LLInventoryItem*)cargo_data, drop))
  495. {
  496. *accept = ACCEPT_YES_MULTI;
  497. }
  498. }
  499. else if (cargo_type == DAD_CATEGORY)
  500. {
  501. if (dropCategory((LLInventoryCategory*)cargo_data, drop))
  502. {
  503. *accept = ACCEPT_YES_MULTI;
  504. }
  505. }
  506. }
  507. return TRUE;
  508. BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop)
  509. {
  510. BOOL rv = isInviteAllowed();
  511. if(rv && item && item->getCreatorUUID().notNull())
  512. {
  513. if(drop)
  514. {
  515. std::vector<LLUUID> ids;
  516. ids.push_back(item->getCreatorUUID());
  517. inviteToSession(ids);
  518. }
  519. }
  520. else
  521. {
  522. // set to false if creator uuid is null.
  523. rv = FALSE;
  524. }
  525. return rv;
  526. }
  527. BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop)
  528. {
  529. BOOL rv = isInviteAllowed();
  530. if(rv && category)
  531. {
  532. LLInventoryModel::cat_array_t cats;
  533. LLInventoryModel::item_array_t items;
  534. LLUniqueBuddyCollector buddies;
  535. gInventory.collectDescendentsIf(category->getUUID(),
  536. cats,
  537. items,
  538. LLInventoryModel::EXCLUDE_TRASH,
  539. buddies);
  540. S32 count = items.count();
  541. if(count == 0)
  542. {
  543. rv = FALSE;
  544. }
  545. else if(drop)
  546. {
  547. std::vector<LLUUID> ids;
  548. ids.reserve(count);
  549. for(S32 i = 0; i < count; ++i)
  550. {
  551. ids.push_back(items.get(i)->getCreatorUUID());
  552. }
  553. inviteToSession(ids);
  554. }
  555. }
  556. return rv;
  557. }
  558. BOOL LLFloaterIMPanel::isInviteAllowed() const
  559. {
  560. return ( (IM_SESSION_CONFERENCE_START == mDialog) 
  561.  || (IM_SESSION_INVITE == mDialog) );
  562. }
  563. // static
  564. void LLFloaterIMPanel::onTabClick(void* userdata)
  565. {
  566. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  567. self->setInputFocus(TRUE);
  568. }
  569. // static
  570. void LLFloaterIMPanel::onClickProfile( void* userdata )
  571. {
  572. //  Bring up the Profile window
  573. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  574. if (self->getOtherParticipantID().notNull())
  575. {
  576. LLAvatarActions::showProfile(self->getOtherParticipantID());
  577. }
  578. }
  579. // static
  580. void LLFloaterIMPanel::onClickGroupInfo( void* userdata )
  581. {
  582. //  Bring up the Profile window
  583. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  584. LLGroupActions::show(self->mSessionUUID);
  585. }
  586. // static
  587. void LLFloaterIMPanel::onClickClose( void* userdata )
  588. {
  589. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  590. if(self)
  591. {
  592. self->closeFloater();
  593. }
  594. }
  595. // static
  596. void LLFloaterIMPanel::onClickStartCall(void* userdata)
  597. {
  598. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  599. gIMMgr->startCall(self->mSessionUUID);
  600. }
  601. // static
  602. void LLFloaterIMPanel::onClickEndCall(void* userdata)
  603. {
  604. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  605. gIMMgr->endCall(self->mSessionUUID);
  606. }
  607. // static
  608. void LLFloaterIMPanel::onClickSend(void* userdata)
  609. {
  610. LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
  611. self->sendMsg();
  612. }
  613. // static
  614. void LLFloaterIMPanel::onClickToggleActiveSpeakers(void* userdata)
  615. {
  616. LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
  617. self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
  618. }
  619. // static
  620. void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata)
  621. {
  622. LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
  623. self->sendMsg();
  624. }
  625. // static
  626. void LLFloaterIMPanel::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata )
  627. {
  628. LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
  629. self->mHistoryEditor->setCursorAndScrollToEnd();
  630. }
  631. // static
  632. void LLFloaterIMPanel::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata)
  633. {
  634. LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
  635. self->setTyping(FALSE);
  636. }
  637. // static
  638. void LLFloaterIMPanel::onInputEditorKeystroke(LLLineEditor* caller, void* userdata)
  639. {
  640. LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
  641. std::string text = self->mInputEditor->getText();
  642. if (!text.empty())
  643. {
  644. self->setTyping(TRUE);
  645. }
  646. else
  647. {
  648. // Deleting all text counts as stopping typing.
  649. self->setTyping(FALSE);
  650. }
  651. }
  652. // virtual
  653. void LLFloaterIMPanel::onClose(bool app_quitting)
  654. {
  655. setTyping(FALSE);
  656. gIMMgr->leaveSession(mSessionUUID);
  657. // *HACK hide the voice floater
  658. LLFloaterReg::hideInstance("voice_call", mSessionUUID);
  659. }
  660. void LLFloaterIMPanel::onVisibilityChange(const LLSD& new_visibility)
  661. {
  662. if (new_visibility.asBoolean())
  663. {
  664. mNumUnreadMessages = 0;
  665. }
  666. LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionUUID);
  667. if (voice_channel && voice_channel->getState() == LLVoiceChannel::STATE_CONNECTED)
  668. {
  669. if (new_visibility.asBoolean())
  670. LLFloaterReg::showInstance("voice_call", mSessionUUID);
  671. else
  672. LLFloaterReg::hideInstance("voice_call", mSessionUUID);
  673. }
  674. }
  675. void LLFloaterIMPanel::sendMsg()
  676. {
  677. if (!gAgent.isGodlike() 
  678. && (mDialog == IM_NOTHING_SPECIAL)
  679. && mOtherParticipantUUID.isNull())
  680. {
  681. llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
  682. return;
  683. }
  684. if (mInputEditor)
  685. {
  686. LLWString text = mInputEditor->getConvertedText();
  687. if(!text.empty())
  688. {
  689. // store sent line in history, duplicates will get filtered
  690. if (mInputEditor) mInputEditor->updateHistory();
  691. // Truncate and convert to UTF8 for transport
  692. std::string utf8_text = wstring_to_utf8str(text);
  693. utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
  694. if ( mSessionInitialized )
  695. {
  696. LLIMModel::sendMessage(utf8_text,
  697. mSessionUUID,
  698. mOtherParticipantUUID,
  699. mDialog);
  700. }
  701. else
  702. {
  703. //queue up the message to send once the session is
  704. //initialized
  705. mQueuedMsgsForInit.append(utf8_text);
  706. }
  707. }
  708. LLViewerStats::getInstance()->incStat(LLViewerStats::ST_IM_COUNT);
  709. mInputEditor->setText(LLStringUtil::null);
  710. }
  711. // Don't need to actually send the typing stop message, the other
  712. // client will infer it from receiving the message.
  713. mTyping = FALSE;
  714. mSentTypingState = TRUE;
  715. }
  716. void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update)
  717. {
  718. if (
  719. session_update.has("moderated_mode") &&
  720. session_update["moderated_mode"].has("voice") )
  721. {
  722. BOOL voice_moderated = session_update["moderated_mode"]["voice"];
  723. if (voice_moderated)
  724. {
  725. setTitle(mSessionLabel + std::string(" ") + getString("moderated_chat_label"));
  726. }
  727. else
  728. {
  729. setTitle(mSessionLabel);
  730. }
  731. //update the speakers dropdown too, if it's available
  732. if (mSpeakerPanel)
  733. {
  734. mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated);
  735. }
  736. }
  737. }
  738. void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id)
  739. {
  740. mSessionUUID = session_id;
  741. mSessionInitialized = TRUE;
  742. //we assume the history editor hasn't moved at all since
  743. //we added the starting session message
  744. //so, we count how many characters to remove
  745. S32 chars_to_remove = mHistoryEditor->getWText().length() -
  746. mSessionStartMsgPos;
  747. mHistoryEditor->removeTextFromEnd(chars_to_remove);
  748. //and now, send the queued msg
  749. LLSD::array_iterator iter;
  750. for ( iter = mQueuedMsgsForInit.beginArray();
  751.   iter != mQueuedMsgsForInit.endArray();
  752.   ++iter)
  753. {
  754. LLIMModel::sendMessage(
  755. iter->asString(),
  756. mSessionUUID,
  757. mOtherParticipantUUID,
  758. mDialog);
  759. }
  760. }
  761. void LLFloaterIMPanel::setTyping(BOOL typing)
  762. {
  763. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionUUID);
  764. if (typing)
  765. {
  766. // Every time you type something, reset this timer
  767. mLastKeystrokeTimer.reset();
  768. if (!mTyping)
  769. {
  770. // You just started typing.
  771. mFirstKeystrokeTimer.reset();
  772. // Will send typing state after a short delay.
  773. mSentTypingState = FALSE;
  774. }
  775. if (speaker_mgr)
  776. speaker_mgr->setSpeakerTyping(gAgent.getID(), TRUE);
  777. }
  778. else
  779. {
  780. if (mTyping)
  781. {
  782. // you just stopped typing, send state immediately
  783. sendTypingState(FALSE);
  784. mSentTypingState = TRUE;
  785. }
  786. if (speaker_mgr)
  787. speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE);
  788. }
  789. mTyping = typing;
  790. }
  791. void LLFloaterIMPanel::sendTypingState(BOOL typing)
  792. {
  793. // Don't want to send typing indicators to multiple people, potentially too
  794. // much network traffic.  Only send in person-to-person IMs.
  795. if (mDialog != IM_NOTHING_SPECIAL) return;
  796. LLIMModel::instance().sendTypingState(mSessionUUID, mOtherParticipantUUID, typing);
  797. }
  798. void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing)
  799. {
  800. if (typing)
  801. {
  802. // other user started typing
  803. addTypingIndicator(im_info->mName);
  804. }
  805. else
  806. {
  807. // other user stopped typing
  808. removeTypingIndicator(im_info);
  809. }
  810. }
  811. void LLFloaterIMPanel::addTypingIndicator(const std::string &name)
  812. {
  813. // we may have lost a "stop-typing" packet, don't add it twice
  814. if (!mOtherTyping)
  815. {
  816. mTypingLineStartIndex = mHistoryEditor->getWText().length();
  817. LLUIString typing_start = sTypingStartString;
  818. typing_start.setArg("[NAME]", name);
  819. addHistoryLine(typing_start, LLUIColorTable::instance().getColor("SystemChatColor"), false);
  820. mOtherTypingName = name;
  821. mOtherTyping = TRUE;
  822. }
  823. // MBW -- XXX -- merge from release broke this (argument to this function changed from an LLIMInfo to a name)
  824. // Richard will fix.
  825. // mSpeakers->setSpeakerTyping(im_info->mFromID, TRUE);
  826. }
  827. void LLFloaterIMPanel::removeTypingIndicator(const LLIMInfo* im_info)
  828. {
  829. if (mOtherTyping)
  830. {
  831. // Must do this first, otherwise addHistoryLine calls us again.
  832. mOtherTyping = FALSE;
  833. S32 chars_to_remove = mHistoryEditor->getWText().length() - mTypingLineStartIndex;
  834. mHistoryEditor->removeTextFromEnd(chars_to_remove);
  835. if (im_info)
  836. {
  837. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionUUID);
  838. if (speaker_mgr)
  839. {
  840. speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE);
  841. }
  842. }
  843. }
  844. }
  845. //static 
  846. void LLFloaterIMPanel::onKickSpeaker(void* user_data)
  847. {
  848. }