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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llimfloater.cpp
  3.  * @brief LLIMFloater class definition
  4.  *
  5.  * $LicenseInfo:firstyear=2009&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2009-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 "llimfloater.h"
  34. #include "llnotificationsutil.h"
  35. #include "llagent.h"
  36. #include "llappviewer.h"
  37. #include "llbutton.h"
  38. #include "llbottomtray.h"
  39. #include "llchannelmanager.h"
  40. #include "llchiclet.h"
  41. #include "llfloaterreg.h"
  42. #include "llimfloatercontainer.h" // to replace separate IM Floaters with multifloater container
  43. #include "lllayoutstack.h"
  44. #include "lllineeditor.h"
  45. #include "lllogchat.h"
  46. #include "llpanelimcontrolpanel.h"
  47. #include "llscreenchannel.h"
  48. #include "llsyswellwindow.h"
  49. #include "lltrans.h"
  50. #include "llchathistory.h"
  51. #include "llviewerwindow.h"
  52. #include "llvoicechannel.h"
  53. #include "lltransientfloatermgr.h"
  54. #include "llinventorymodel.h"
  55. #include "llrootview.h"
  56. #include "llspeakers.h"
  57. LLIMFloater::LLIMFloater(const LLUUID& session_id)
  58.   : LLTransientDockableFloater(NULL, true, session_id),
  59. mControlPanel(NULL),
  60. mSessionID(session_id),
  61. mLastMessageIndex(-1),
  62. mDialog(IM_NOTHING_SPECIAL),
  63. mChatHistory(NULL),
  64. mInputEditor(NULL),
  65. mSavedTitle(),
  66. mTypingStart(),
  67. mShouldSendTypingState(false),
  68. mMeTyping(false),
  69. mOtherTyping(false),
  70. mTypingTimer(),
  71. mTypingTimeoutTimer(),
  72. mPositioned(false),
  73. mSessionInitialized(false)
  74. {
  75. LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(mSessionID);
  76. if (im_session)
  77. {
  78. mSessionInitialized = im_session->mSessionInitialized;
  79. mDialog = im_session->mType;
  80. switch(mDialog){
  81. case IM_NOTHING_SPECIAL:
  82. case IM_SESSION_P2P_INVITE:
  83. mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this);
  84. break;
  85. case IM_SESSION_CONFERENCE_START:
  86. mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this);
  87. break;
  88. case IM_SESSION_GROUP_START:
  89. mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this);
  90. break;
  91. case IM_SESSION_INVITE:
  92. if (gAgent.isInGroup(mSessionID))
  93. {
  94. mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this);
  95. }
  96. else
  97. {
  98. mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelAdHocControl, this);
  99. }
  100. break;
  101. default: break;
  102. }
  103. }
  104. setOverlapsScreenChannel(true);
  105. LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
  106. }
  107. void LLIMFloater::onFocusLost()
  108. {
  109. LLIMModel::getInstance()->resetActiveSessionID();
  110. LLBottomTray::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, false);
  111. }
  112. void LLIMFloater::onFocusReceived()
  113. {
  114. LLIMModel::getInstance()->setActiveSessionID(mSessionID);
  115. // return focus to the input field when active tab in the multitab container is clicked.
  116. if (isChatMultiTab() && mInputEditor)
  117. {
  118. mInputEditor->setFocus(TRUE);
  119. }
  120. LLBottomTray::getInstance()->getChicletPanel()->setChicletToggleState(mSessionID, true);
  121. }
  122. // virtual
  123. void LLIMFloater::onClose(bool app_quitting)
  124. {
  125. setTyping(false);
  126. // The source of much argument and design thrashing
  127. // Should the window hide or the session close when the X is clicked?
  128. //
  129. // Last change:
  130. // EXT-3516 X Button should end IM session, _ button should hide
  131. gIMMgr->leaveSession(mSessionID);
  132. }
  133. /* static */
  134. void LLIMFloater::newIMCallback(const LLSD& data){
  135. if (data["num_unread"].asInteger() > 0 || data["from_id"].asUUID().isNull())
  136. {
  137. LLUUID session_id = data["session_id"].asUUID();
  138. LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id);
  139. if (floater == NULL) return;
  140.         // update if visible, otherwise will be updated when opened
  141. if (floater->getVisible())
  142. {
  143. floater->updateMessages();
  144. }
  145. }
  146. }
  147. void LLIMFloater::onVisibilityChange(const LLSD& new_visibility)
  148. {
  149. bool visible = new_visibility.asBoolean();
  150. LLVoiceChannel* voice_channel = LLIMModel::getInstance()->getVoiceChannel(mSessionID);
  151. if (visible && voice_channel &&
  152. voice_channel->getState() == LLVoiceChannel::STATE_CONNECTED)
  153. {
  154. LLFloaterReg::showInstance("voice_call", mSessionID);
  155. }
  156. else
  157. {
  158. LLFloaterReg::hideInstance("voice_call", mSessionID);
  159. }
  160. }
  161. void LLIMFloater::onSendMsg( LLUICtrl* ctrl, void* userdata )
  162. {
  163. LLIMFloater* self = (LLIMFloater*) userdata;
  164. self->sendMsg();
  165. self->setTyping(false);
  166. }
  167. void LLIMFloater::sendMsg()
  168. {
  169. if (!gAgent.isGodlike() 
  170. && (mDialog == IM_NOTHING_SPECIAL)
  171. && mOtherParticipantUUID.isNull())
  172. {
  173. llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
  174. return;
  175. }
  176. if (mInputEditor)
  177. {
  178. LLWString text = mInputEditor->getConvertedText();
  179. if(!text.empty())
  180. {
  181. // Truncate and convert to UTF8 for transport
  182. std::string utf8_text = wstring_to_utf8str(text);
  183. utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
  184. if (mSessionInitialized)
  185. {
  186. LLIMModel::sendMessage(utf8_text, mSessionID,
  187. mOtherParticipantUUID,mDialog);
  188. }
  189. else
  190. {
  191. //queue up the message to send once the session is initialized
  192. mQueuedMsgsForInit.append(utf8_text);
  193. }
  194. mInputEditor->setText(LLStringUtil::null);
  195. updateMessages();
  196. }
  197. }
  198. }
  199. LLIMFloater::~LLIMFloater()
  200. {
  201. LLTransientFloaterMgr::getInstance()->removeControlView(LLTransientFloaterMgr::IM, this);
  202. }
  203. //virtual
  204. BOOL LLIMFloater::postBuild()
  205. {
  206. // User-resizable control panels in P2P sessions look ugly (EXT-3470).
  207. if (mDialog == IM_NOTHING_SPECIAL || mDialog == IM_SESSION_P2P_INVITE)
  208. {
  209. getChild<LLLayoutStack>("im_panels")->setPanelUserResize("panel_im_control_panel", FALSE);
  210. }
  211. const LLUUID& other_party_id = LLIMModel::getInstance()->getOtherParticipantID(mSessionID);
  212. if (other_party_id.notNull())
  213. {
  214. mOtherParticipantUUID = other_party_id;
  215. }
  216. mControlPanel->setSessionId(mSessionID);
  217. mControlPanel->setVisible(gSavedSettings.getBOOL("IMShowControlPanel"));
  218. LLButton* slide_left = getChild<LLButton>("slide_left_btn");
  219. slide_left->setVisible(mControlPanel->getVisible());
  220. slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this));
  221. LLButton* slide_right = getChild<LLButton>("slide_right_btn");
  222. slide_right->setVisible(!mControlPanel->getVisible());
  223. slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this));
  224. mInputEditor = getChild<LLLineEditor>("chat_editor");
  225. mInputEditor->setMaxTextLength(1023);
  226. // enable line history support for instant message bar
  227. mInputEditor->setEnableLineHistory(TRUE);
  228. mInputEditor->setFocusReceivedCallback( boost::bind(onInputEditorFocusReceived, _1, this) );
  229. mInputEditor->setFocusLostCallback( boost::bind(onInputEditorFocusLost, _1, this) );
  230. mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this );
  231. mInputEditor->setCommitOnFocusLost( FALSE );
  232. mInputEditor->setRevertOnEsc( FALSE );
  233. mInputEditor->setReplaceNewlinesWithSpaces( FALSE );
  234. mInputEditor->setPassDelete( TRUE );
  235. std::string session_name(LLIMModel::instance().getName(mSessionID));
  236. mInputEditor->setLabel(LLTrans::getString("IM_to_label") + " " + session_name);
  237. setTitle(session_name);
  238. childSetCommitCallback("chat_editor", onSendMsg, this);
  239. mChatHistory = getChild<LLChatHistory>("chat_history");
  240. setDocked(true);
  241. mTypingStart = LLTrans::getString("IM_typing_start_string");
  242. // Disable input editor if session cannot accept text
  243. LLIMModel::LLIMSession* im_session =
  244. LLIMModel::instance().findIMSession(mSessionID);
  245. if( im_session && !im_session->mTextIMPossible )
  246. {
  247. mInputEditor->setEnabled(FALSE);
  248. mInputEditor->setLabel(LLTrans::getString("IM_unavailable_text_label"));
  249. }
  250. //*TODO if session is not initialized yet, add some sort of a warning message like "starting session...blablabla"
  251. //see LLFloaterIMPanel for how it is done (IB)
  252. if(isChatMultiTab())
  253. {
  254. return LLFloater::postBuild();
  255. }
  256. else
  257. {
  258. return LLDockableFloater::postBuild();
  259. }
  260. }
  261. // virtual
  262. void LLIMFloater::draw()
  263. {
  264. if ( mMeTyping )
  265. {
  266. // Time out if user hasn't typed for a while.
  267. if ( mTypingTimeoutTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS )
  268. {
  269. setTyping(false);
  270. }
  271. }
  272. LLTransientDockableFloater::draw();
  273. }
  274. // static
  275. void* LLIMFloater::createPanelIMControl(void* userdata)
  276. {
  277. LLIMFloater *self = (LLIMFloater*)userdata;
  278. self->mControlPanel = new LLPanelIMControlPanel();
  279. self->mControlPanel->setXMLFilename("panel_im_control_panel.xml");
  280. return self->mControlPanel;
  281. }
  282. // static
  283. void* LLIMFloater::createPanelGroupControl(void* userdata)
  284. {
  285. LLIMFloater *self = (LLIMFloater*)userdata;
  286. self->mControlPanel = new LLPanelGroupControlPanel(self->mSessionID);
  287. self->mControlPanel->setXMLFilename("panel_group_control_panel.xml");
  288. return self->mControlPanel;
  289. }
  290. // static
  291. void* LLIMFloater::createPanelAdHocControl(void* userdata)
  292. {
  293. LLIMFloater *self = (LLIMFloater*)userdata;
  294. self->mControlPanel = new LLPanelAdHocControlPanel(self->mSessionID);
  295. self->mControlPanel->setXMLFilename("panel_adhoc_control_panel.xml");
  296. return self->mControlPanel;
  297. }
  298. void LLIMFloater::onSlide()
  299. {
  300. mControlPanel->setVisible(!mControlPanel->getVisible());
  301. gSavedSettings.setBOOL("IMShowControlPanel", mControlPanel->getVisible());
  302. getChild<LLButton>("slide_left_btn")->setVisible(mControlPanel->getVisible());
  303. getChild<LLButton>("slide_right_btn")->setVisible(!mControlPanel->getVisible());
  304. LLLayoutStack* stack = getChild<LLLayoutStack>("im_panels");
  305. if (stack) stack->setAnimate(true);
  306. }
  307. //static
  308. LLIMFloater* LLIMFloater::show(const LLUUID& session_id)
  309. {
  310. if (!gIMMgr->hasSession(session_id)) return NULL;
  311. if(!isChatMultiTab())
  312. {
  313. //hide all
  314. LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel");
  315. for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
  316.  iter != inst_list.end(); ++iter)
  317. {
  318. LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter);
  319. if (floater && floater->isDocked())
  320. {
  321. floater->setVisible(false);
  322. }
  323. }
  324. }
  325. bool exist = findInstance(session_id);
  326. LLIMFloater* floater = getInstance(session_id);
  327. if (!floater) return NULL;
  328. if(isChatMultiTab())
  329. {
  330. LLIMFloaterContainer* floater_container = LLIMFloaterContainer::getInstance();
  331. // do not add existed floaters to avoid adding torn off instances
  332. if (!exist)
  333. {
  334. // LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END;
  335. // TODO: mantipov: use LLTabContainer::RIGHT_OF_CURRENT if it exists
  336. LLTabContainer::eInsertionPoint i_pt = LLTabContainer::END;
  337. if (floater_container)
  338. {
  339. floater_container->addFloater(floater, TRUE, i_pt);
  340. }
  341. }
  342. floater->openFloater(floater->getKey());
  343. }
  344. else
  345. {
  346. // Docking may move chat window, hide it before moving, or user will see how window "jumps"
  347. floater->setVisible(false);
  348. if (floater->getDockControl() == NULL)
  349. {
  350. LLChiclet* chiclet =
  351. LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLChiclet>(
  352. session_id);
  353. if (chiclet == NULL)
  354. {
  355. llerror("Dock chiclet for LLIMFloater doesn't exists", 0);
  356. }
  357. else
  358. {
  359. LLBottomTray::getInstance()->getChicletPanel()->scrollToChiclet(chiclet);
  360. }
  361. floater->setDockControl(new LLDockControl(chiclet, floater, floater->getDockTongue(),
  362. LLDockControl::TOP,  boost::bind(&LLIMFloater::getAllowedRect, floater, _1)));
  363. }
  364. // window is positioned, now we can show it.
  365. }
  366. floater->setVisible(TRUE);
  367. return floater;
  368. }
  369. void LLIMFloater::getAllowedRect(LLRect& rect)
  370. {
  371. rect = gViewerWindow->getWorldViewRectRaw();
  372. static S32 right_padding = 0;
  373. if (right_padding == 0)
  374. {
  375. LLPanel* side_bar_tabs =
  376. gViewerWindow->getRootView()->getChild<LLPanel> (
  377. "side_bar_tabs");
  378. right_padding = side_bar_tabs->getRect().getWidth();
  379. LLTransientFloaterMgr::getInstance()->addControlView(side_bar_tabs);
  380. }
  381. rect.mRight -= right_padding;
  382. }
  383. void LLIMFloater::setDocked(bool docked, bool pop_on_undock)
  384. {
  385. // update notification channel state
  386. LLNotificationsUI::LLScreenChannel* channel = dynamic_cast<LLNotificationsUI::LLScreenChannel*>
  387. (LLNotificationsUI::LLChannelManager::getInstance()->
  388. findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
  389. if(!isChatMultiTab())
  390. {
  391. LLTransientDockableFloater::setDocked(docked, pop_on_undock);
  392. }
  393. // update notification channel state
  394. if(channel)
  395. {
  396. channel->updateShowToastsState();
  397. channel->redrawToasts();
  398. }
  399. }
  400. void LLIMFloater::setVisible(BOOL visible)
  401. {
  402. LLNotificationsUI::LLScreenChannel* channel = dynamic_cast<LLNotificationsUI::LLScreenChannel*>
  403. (LLNotificationsUI::LLChannelManager::getInstance()->
  404. findChannelByID(LLUUID(gSavedSettings.getString("NotificationChannelUUID"))));
  405. LLTransientDockableFloater::setVisible(visible);
  406. // update notification channel state
  407. if(channel)
  408. {
  409. channel->updateShowToastsState();
  410. channel->redrawToasts();
  411. }
  412. BOOL is_minimized = visible && isChatMultiTab()
  413. ? LLIMFloaterContainer::getInstance()->isMinimized()
  414. : !visible;
  415. if (!is_minimized && mChatHistory && mInputEditor)
  416. {
  417. //only if floater was construced and initialized from xml
  418. updateMessages();
  419. //prevent steal focus when IM opened in multitab mode
  420. if (!isChatMultiTab())
  421. {
  422. mInputEditor->setFocus(TRUE);
  423. }
  424. }
  425. if(!visible)
  426. {
  427. LLIMChiclet* chiclet = LLBottomTray::getInstance()->getChicletPanel()->findChiclet<LLIMChiclet>(mSessionID);
  428. if(chiclet)
  429. {
  430. chiclet->setToggleState(false);
  431. }
  432. }
  433. }
  434. BOOL LLIMFloater::getVisible()
  435. {
  436. if(isChatMultiTab())
  437. {
  438. LLIMFloaterContainer* im_container = LLIMFloaterContainer::getInstance();
  439. // Treat inactive floater as invisible.
  440. bool is_active = im_container->getActiveFloater() == this;
  441. //torn off floater is always inactive
  442. if (!is_active && getHost() != im_container)
  443. {
  444. return LLTransientDockableFloater::getVisible();
  445. }
  446. // getVisible() returns TRUE when Tabbed IM window is minimized.
  447. return is_active && !im_container->isMinimized() && im_container->getVisible();
  448. }
  449. else
  450. {
  451. return LLTransientDockableFloater::getVisible();
  452. }
  453. }
  454. //static
  455. bool LLIMFloater::toggle(const LLUUID& session_id)
  456. {
  457. if(!isChatMultiTab())
  458. {
  459. LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id);
  460. if (floater && floater->getVisible() && floater->hasFocus())
  461. {
  462. // clicking on chiclet to close floater just hides it to maintain existing
  463. // scroll/text entry state
  464. floater->setVisible(false);
  465. return false;
  466. }
  467. else if(floater && (!floater->isDocked() || floater->getVisible() && !floater->hasFocus()))
  468. {
  469. floater->setVisible(TRUE);
  470. floater->setFocus(TRUE);
  471. return true;
  472. }
  473. }
  474. // ensure the list of messages is updated when floater is made visible
  475. show(session_id);
  476. return true;
  477. }
  478. //static
  479. LLIMFloater* LLIMFloater::findInstance(const LLUUID& session_id)
  480. {
  481. return LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id);
  482. }
  483. LLIMFloater* LLIMFloater::getInstance(const LLUUID& session_id)
  484. {
  485. return LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", session_id);
  486. }
  487. void LLIMFloater::sessionInitReplyReceived(const LLUUID& im_session_id)
  488. {
  489. mSessionInitialized = true;
  490. //will be different only for an ad-hoc im session
  491. if (mSessionID != im_session_id)
  492. {
  493. mSessionID = im_session_id;
  494. setKey(im_session_id);
  495. mControlPanel->setSessionId(im_session_id);
  496. }
  497. // updating "Call" button from group control panel here to enable it without placing into draw() (EXT-4796)
  498. if(gAgent.isInGroup(im_session_id))
  499. {
  500. mControlPanel->updateCallButton();
  501. }
  502. //*TODO here we should remove "starting session..." warning message if we added it in postBuild() (IB)
  503. //need to send delayed messaged collected while waiting for session initialization
  504. if (!mQueuedMsgsForInit.size()) return;
  505. LLSD::array_iterator iter;
  506. for ( iter = mQueuedMsgsForInit.beginArray();
  507. iter != mQueuedMsgsForInit.endArray();
  508. ++iter)
  509. {
  510. LLIMModel::sendMessage(iter->asString(), mSessionID,
  511. mOtherParticipantUUID, mDialog);
  512. }
  513. }
  514. void LLIMFloater::updateMessages()
  515. {
  516. bool use_plain_text_chat_history = gSavedSettings.getBOOL("PlainTextChatHistory");
  517. std::list<LLSD> messages;
  518. LLIMModel::instance().getMessages(mSessionID, messages, mLastMessageIndex+1);
  519. if (messages.size())
  520. {
  521. // LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor");
  522. LLSD chat_args;
  523. chat_args["use_plain_text_chat_history"] = use_plain_text_chat_history;
  524. std::ostringstream message;
  525. std::list<LLSD>::const_reverse_iterator iter = messages.rbegin();
  526. std::list<LLSD>::const_reverse_iterator iter_end = messages.rend();
  527. for (; iter != iter_end; ++iter)
  528. {
  529. LLSD msg = *iter;
  530. std::string time = msg["time"].asString();
  531. LLUUID from_id = msg["from_id"].asUUID();
  532. std::string from = msg["from"].asString();
  533. std::string message = msg["message"].asString();
  534. LLChat chat;
  535. chat.mFromID = from_id;
  536. chat.mSessionID = mSessionID;
  537. chat.mFromName = from;
  538. chat.mTimeStr = time;
  539. // process offer notification
  540. if (msg.has("notification_id"))
  541. {
  542. chat.mNotifId = msg["notification_id"].asUUID();
  543. }
  544. //process text message
  545. else
  546. {
  547. chat.mText = message;
  548. }
  549. mChatHistory->appendMessage(chat, chat_args);
  550. mLastMessageIndex = msg["index"].asInteger();
  551. }
  552. }
  553. }
  554. void LLIMFloater::reloadMessages()
  555. {
  556. mChatHistory->clear();
  557. mLastMessageIndex = -1;
  558. updateMessages();
  559. }
  560. // static
  561. void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata )
  562. {
  563. LLIMFloater* self= (LLIMFloater*) userdata;
  564. // Allow enabling the LLIMFloater input editor only if session can accept text
  565. LLIMModel::LLIMSession* im_session =
  566. LLIMModel::instance().findIMSession(self->mSessionID);
  567. //TODO: While disabled lllineeditor can receive focus we need to check if it is enabled (EK)
  568. if( im_session && im_session->mTextIMPossible && self->mInputEditor->getEnabled())
  569. {
  570. //in disconnected state IM input editor should be disabled
  571. self->mInputEditor->setEnabled(!gDisconnected);
  572. }
  573. // when IM Floater is a part of the multitab container LLTabContainer set focus to the first
  574. // child on tab button's mouse up. This leads input field lost focus. See EXT-3852.
  575. if (isChatMultiTab())
  576. {
  577. // So, clear control captured mouse to prevent LLTabContainer set focus on the panel's first child.
  578. // do not pass self->mInputEditor, this leads to have "Edit Text" mouse pointer wherever it is.
  579. gFocusMgr.setMouseCapture(NULL);
  580. }
  581. }
  582. // static
  583. void LLIMFloater::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata)
  584. {
  585. LLIMFloater* self = (LLIMFloater*) userdata;
  586. self->setTyping(false);
  587. }
  588. // static
  589. void LLIMFloater::onInputEditorKeystroke(LLLineEditor* caller, void* userdata)
  590. {
  591. LLIMFloater* self = (LLIMFloater*)userdata;
  592. std::string text = self->mInputEditor->getText();
  593. if (!text.empty())
  594. {
  595. self->setTyping(true);
  596. }
  597. else
  598. {
  599. // Deleting all text counts as stopping typing.
  600. self->setTyping(false);
  601. }
  602. }
  603. void LLIMFloater::setTyping(bool typing)
  604. {
  605. if ( typing )
  606. {
  607. // Started or proceeded typing, reset the typing timeout timer
  608. mTypingTimeoutTimer.reset();
  609. }
  610. if ( mMeTyping != typing )
  611. {
  612. // Typing state is changed
  613. mMeTyping = typing;
  614. // So, should send current state
  615. mShouldSendTypingState = true;
  616. // In case typing is started, send state after some delay
  617. mTypingTimer.reset();
  618. }
  619. // Don't want to send typing indicators to multiple people, potentially too
  620. // much network traffic. Only send in person-to-person IMs.
  621. if ( mShouldSendTypingState && mDialog == IM_NOTHING_SPECIAL )
  622. {
  623. if ( mMeTyping )
  624. {
  625. if ( mTypingTimer.getElapsedTimeF32() > 1.f )
  626. {
  627. // Still typing, send 'start typing' notification
  628. LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, TRUE);
  629. mShouldSendTypingState = false;
  630. }
  631. }
  632. else
  633. {
  634. // Send 'stop typing' notification immediately
  635. LLIMModel::instance().sendTypingState(mSessionID, mOtherParticipantUUID, FALSE);
  636. mShouldSendTypingState = false;
  637. }
  638. }
  639. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
  640. if (speaker_mgr)
  641. speaker_mgr->setSpeakerTyping(gAgent.getID(), FALSE);
  642. }
  643. void LLIMFloater::processIMTyping(const LLIMInfo* im_info, BOOL typing)
  644. {
  645. if ( typing )
  646. {
  647. // other user started typing
  648. addTypingIndicator(im_info);
  649. }
  650. else
  651. {
  652. // other user stopped typing
  653. removeTypingIndicator(im_info);
  654. }
  655. }
  656. void LLIMFloater::processAgentListUpdates(const LLSD& body)
  657. {
  658. if ( !body.isMap() ) return;
  659. if ( body.has("agent_updates") && body["agent_updates"].isMap() )
  660. {
  661. LLSD agent_data = body["agent_updates"].get(gAgentID.asString());
  662. if (agent_data.isMap() && agent_data.has("info"))
  663. {
  664. LLSD agent_info = agent_data["info"];
  665. if (agent_info.has("mutes"))
  666. {
  667. BOOL moderator_muted_text = agent_info["mutes"]["text"].asBoolean(); 
  668. mInputEditor->setEnabled(!moderator_muted_text);
  669. std::string label;
  670. if (moderator_muted_text)
  671. label = LLTrans::getString("IM_muted_text_label");
  672. else
  673. label = LLTrans::getString("IM_to_label") + " " + LLIMModel::instance().getName(mSessionID);
  674. mInputEditor->setLabel(label);
  675. if (moderator_muted_text)
  676. LLNotificationsUtil::add("TextChatIsMutedByModerator");
  677. }
  678. }
  679. }
  680. }
  681. void LLIMFloater::updateChatHistoryStyle()
  682. {
  683. mChatHistory->clear();
  684. mLastMessageIndex = -1;
  685. updateMessages();
  686. }
  687. void LLIMFloater::processChatHistoryStyleUpdate(const LLSD& newvalue)
  688. {
  689. LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("impanel");
  690. for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin();
  691.  iter != inst_list.end(); ++iter)
  692. {
  693. LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter);
  694. if (floater)
  695. {
  696. floater->updateChatHistoryStyle();
  697. }
  698. }
  699. }
  700. void LLIMFloater::processSessionUpdate(const LLSD& session_update)
  701. {
  702. // *TODO : verify following code when moderated mode will be implemented
  703. if ( false && session_update.has("moderated_mode") &&
  704.  session_update["moderated_mode"].has("voice") )
  705. {
  706. BOOL voice_moderated = session_update["moderated_mode"]["voice"];
  707. const std::string session_label = LLIMModel::instance().getName(mSessionID);
  708. if (voice_moderated)
  709. {
  710. setTitle(session_label + std::string(" ") + LLTrans::getString("IM_moderated_chat_label"));
  711. }
  712. else
  713. {
  714. setTitle(session_label);
  715. }
  716. // *TODO : uncomment this when/if LLPanelActiveSpeakers panel will be added
  717. //update the speakers dropdown too
  718. //mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated);
  719. }
  720. }
  721. BOOL LLIMFloater::handleDragAndDrop(S32 x, S32 y, MASK mask,
  722.    BOOL drop, EDragAndDropType cargo_type,
  723.    void *cargo_data, EAcceptance *accept,
  724.    std::string& tooltip_msg)
  725. {
  726. if (mDialog == IM_NOTHING_SPECIAL)
  727. {
  728. LLToolDragAndDrop::handleGiveDragAndDrop(mOtherParticipantUUID, mSessionID, drop,
  729.  cargo_type, cargo_data, accept);
  730. }
  731. // handle case for dropping calling cards (and folders of calling cards) onto invitation panel for invites
  732. else if (isInviteAllowed())
  733. {
  734. *accept = ACCEPT_NO;
  735. if (cargo_type == DAD_CALLINGCARD)
  736. {
  737. if (dropCallingCard((LLInventoryItem*)cargo_data, drop))
  738. {
  739. *accept = ACCEPT_YES_MULTI;
  740. }
  741. }
  742. else if (cargo_type == DAD_CATEGORY)
  743. {
  744. if (dropCategory((LLInventoryCategory*)cargo_data, drop))
  745. {
  746. *accept = ACCEPT_YES_MULTI;
  747. }
  748. }
  749. }
  750. return TRUE;
  751. }
  752. BOOL LLIMFloater::dropCallingCard(LLInventoryItem* item, BOOL drop)
  753. {
  754. BOOL rv = isInviteAllowed();
  755. if(rv && item && item->getCreatorUUID().notNull())
  756. {
  757. if(drop)
  758. {
  759. std::vector<LLUUID> ids;
  760. ids.push_back(item->getCreatorUUID());
  761. inviteToSession(ids);
  762. }
  763. }
  764. else
  765. {
  766. // set to false if creator uuid is null.
  767. rv = FALSE;
  768. }
  769. return rv;
  770. }
  771. BOOL LLIMFloater::dropCategory(LLInventoryCategory* category, BOOL drop)
  772. {
  773. BOOL rv = isInviteAllowed();
  774. if(rv && category)
  775. {
  776. LLInventoryModel::cat_array_t cats;
  777. LLInventoryModel::item_array_t items;
  778. LLUniqueBuddyCollector buddies;
  779. gInventory.collectDescendentsIf(category->getUUID(),
  780. cats,
  781. items,
  782. LLInventoryModel::EXCLUDE_TRASH,
  783. buddies);
  784. S32 count = items.count();
  785. if(count == 0)
  786. {
  787. rv = FALSE;
  788. }
  789. else if(drop)
  790. {
  791. std::vector<LLUUID> ids;
  792. ids.reserve(count);
  793. for(S32 i = 0; i < count; ++i)
  794. {
  795. ids.push_back(items.get(i)->getCreatorUUID());
  796. }
  797. inviteToSession(ids);
  798. }
  799. }
  800. return rv;
  801. }
  802. BOOL LLIMFloater::isInviteAllowed() const
  803. {
  804. return ( (IM_SESSION_CONFERENCE_START == mDialog)
  805.  || (IM_SESSION_INVITE == mDialog) );
  806. }
  807. class LLSessionInviteResponder : public LLHTTPClient::Responder
  808. {
  809. public:
  810. LLSessionInviteResponder(const LLUUID& session_id)
  811. {
  812. mSessionID = session_id;
  813. }
  814. void error(U32 statusNum, const std::string& reason)
  815. {
  816. llinfos << "Error inviting all agents to session" << llendl;
  817. //throw something back to the viewer here?
  818. }
  819. private:
  820. LLUUID mSessionID;
  821. };
  822. BOOL LLIMFloater::inviteToSession(const std::vector<LLUUID>& ids)
  823. {
  824. LLViewerRegion* region = gAgent.getRegion();
  825. if (!region)
  826. {
  827. return FALSE;
  828. }
  829. S32 count = ids.size();
  830. if( isInviteAllowed() && (count > 0) )
  831. {
  832. llinfos << "LLIMFloater::inviteToSession() - inviting participants" << llendl;
  833. std::string url = region->getCapability("ChatSessionRequest");
  834. LLSD data;
  835. data["params"] = LLSD::emptyArray();
  836. for (int i = 0; i < count; i++)
  837. {
  838. data["params"].append(ids[i]);
  839. }
  840. data["method"] = "invite";
  841. data["session-id"] = mSessionID;
  842. LLHTTPClient::post(
  843. url,
  844. data,
  845. new LLSessionInviteResponder(
  846. mSessionID));
  847. }
  848. else
  849. {
  850. llinfos << "LLIMFloater::inviteToSession -"
  851. << " no need to invite agents for "
  852. << mDialog << llendl;
  853. // successful add, because everyone that needed to get added
  854. // was added.
  855. }
  856. return TRUE;
  857. }
  858. void LLIMFloater::addTypingIndicator(const LLIMInfo* im_info)
  859. {
  860. // We may have lost a "stop-typing" packet, don't add it twice
  861. if ( im_info && !mOtherTyping )
  862. {
  863. mOtherTyping = true;
  864. // Create typing is started title string
  865. LLUIString typing_start(mTypingStart);
  866. typing_start.setArg("[NAME]", im_info->mName);
  867. // Save and set new title
  868. mSavedTitle = getTitle();
  869. setTitle (typing_start);
  870. // Update speaker
  871. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
  872. if ( speaker_mgr )
  873. {
  874. speaker_mgr->setSpeakerTyping(im_info->mFromID, TRUE);
  875. }
  876. }
  877. }
  878. void LLIMFloater::removeTypingIndicator(const LLIMInfo* im_info)
  879. {
  880. if ( mOtherTyping )
  881. {
  882. mOtherTyping = false;
  883. // Revert the title to saved one
  884. setTitle(mSavedTitle);
  885. if ( im_info )
  886. {
  887. // Update speaker
  888. LLIMSpeakerMgr* speaker_mgr = LLIMModel::getInstance()->getSpeakerManager(mSessionID);
  889. if ( speaker_mgr )
  890. {
  891. speaker_mgr->setSpeakerTyping(im_info->mFromID, FALSE);
  892. }
  893. }
  894. }
  895. }
  896. // static
  897. bool LLIMFloater::isChatMultiTab()
  898. {
  899. // Restart is required in order to change chat window type.
  900. static bool is_single_window = gSavedSettings.getS32("ChatWindow") == 1;
  901. return is_single_window;
  902. }
  903. // static
  904. void LLIMFloater::initIMFloater()
  905. {
  906. // This is called on viewer start up
  907. // init chat window type before user changed it in preferences
  908. isChatMultiTab();
  909. }
  910. //static
  911. void LLIMFloater::sRemoveTypingIndicator(const LLSD& data)
  912. {
  913. LLUUID session_id = data["session_id"];
  914. if (session_id.isNull()) return;
  915. LLUUID from_id = data["from_id"];
  916. if (gAgentID == from_id || LLUUID::null == from_id) return;
  917. LLIMFloater* floater = LLIMFloater::findInstance(session_id);
  918. if (!floater) return;
  919. if (IM_NOTHING_SPECIAL != floater->mDialog) return;
  920. floater->removeTypingIndicator();
  921. }
  922. void LLIMFloater::onIMChicletCreated( const LLUUID& session_id )
  923. {
  924. if (isChatMultiTab())
  925. {
  926. LLIMFloaterContainer* im_box = LLIMFloaterContainer::getInstance();
  927. if (!im_box) return;
  928. if (LLIMFloater::findInstance(session_id)) return;
  929. LLIMFloater* new_tab = LLIMFloater::getInstance(session_id);
  930. im_box->addFloater(new_tab, FALSE, LLTabContainer::END);
  931. }
  932. }