/indra/newview/llfloaterchat.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 485 lines · 340 code · 59 blank · 86 comment · 59 complexity · 26249f8437f008c9d903febc8df64343 MD5 · raw file

  1. /**
  2. * @file llfloaterchat.cpp
  3. * @brief LLFloaterChat class implementation
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. /**
  27. * Actually the "Chat History" floater.
  28. * Should be llfloaterchathistory, not llfloaterchat.
  29. */
  30. #include "llviewerprecompiledheaders.h"
  31. // project include
  32. #include "llagent.h"
  33. #include "llappviewer.h"
  34. #include "llbutton.h"
  35. #include "llcheckboxctrl.h"
  36. #include "llcombobox.h"
  37. #include "llconsole.h"
  38. #include "llfloateractivespeakers.h"
  39. #include "llfloaterchatterbox.h"
  40. #include "llfloaterreg.h"
  41. #include "llfloaterscriptdebug.h"
  42. #include "llkeyboard.h"
  43. //#include "lllineeditor.h"
  44. #include "llmutelist.h"
  45. //#include "llresizehandle.h"
  46. #include "llchatbar.h"
  47. #include "llrecentpeople.h"
  48. #include "llpanelblockedlist.h"
  49. #include "llslurl.h"
  50. #include "llstatusbar.h"
  51. #include "llviewertexteditor.h"
  52. #include "llviewergesture.h" // for triggering gestures
  53. #include "llviewermessage.h"
  54. #include "llviewerwindow.h"
  55. #include "llviewercontrol.h"
  56. #include "lluictrlfactory.h"
  57. #include "lllogchat.h"
  58. #include "lltexteditor.h"
  59. #include "lltextparser.h"
  60. #include "llweb.h"
  61. #include "llstylemap.h"
  62. // linden library includes
  63. #include "llaudioengine.h"
  64. #include "llchat.h"
  65. #include "llfontgl.h"
  66. #include "llrect.h"
  67. #include "llerror.h"
  68. #include "llstring.h"
  69. #include "llwindow.h"
  70. #include "message.h"
  71. //
  72. // Constants
  73. //
  74. const F32 INSTANT_MSG_SIZE = 8.0f;
  75. const F32 CHAT_MSG_SIZE = 8.0f;
  76. //
  77. // Global statics
  78. //
  79. LLColor4 get_text_color(const LLChat& chat);
  80. //
  81. // Member Functions
  82. //
  83. LLFloaterChat::LLFloaterChat(const LLSD& seed)
  84. : LLFloater(seed),
  85. mPanel(NULL)
  86. {
  87. mFactoryMap["chat_panel"] = LLCallbackMap(createChatPanel, NULL);
  88. mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL);
  89. //Called from floater reg: LLUICtrlFactory::getInstance()->buildFloater(this,"floater_chat_history.xml");
  90. }
  91. LLFloaterChat::~LLFloaterChat()
  92. {
  93. // Children all cleaned up by default view destructor.
  94. }
  95. void LLFloaterChat::draw()
  96. {
  97. // enable say and shout only when text available
  98. childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
  99. LLChatBar* chat_barp = findChild<LLChatBar>("chat_panel", TRUE);
  100. if (chat_barp)
  101. {
  102. chat_barp->refresh();
  103. }
  104. mPanel->refreshSpeakers();
  105. LLFloater::draw();
  106. }
  107. BOOL LLFloaterChat::postBuild()
  108. {
  109. // Hide the chat overlay when our history is visible.
  110. setVisibleCallback(boost::bind(&LLFloaterChat::updateConsoleVisibility, this));
  111. mPanel = (LLPanelActiveSpeakers*)getChild<LLPanel>("active_speakers_panel");
  112. childSetCommitCallback("show mutes",onClickToggleShowMute,this); //show mutes
  113. childSetVisible("Chat History Editor with mute",FALSE);
  114. childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
  115. return TRUE;
  116. }
  117. void LLFloaterChat::updateConsoleVisibility()
  118. {
  119. if(gDisconnected)
  120. {
  121. return;
  122. }
  123. // determine whether we should show console due to not being visible
  124. gConsole->setVisible( !isInVisibleChain() // are we not in part of UI being drawn?
  125. || isMinimized() // are we minimized?
  126. || (getHost() && getHost()->isMinimized() )); // are we hosted in a minimized floater?
  127. }
  128. void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& color)
  129. {
  130. std::string line = chat.mText;
  131. bool prepend_newline = true;
  132. if (gSavedSettings.getBOOL("ChatShowTimestamps"))
  133. {
  134. edit->appendTime(prepend_newline);
  135. prepend_newline = false;
  136. }
  137. // If the msg is from an agent (not yourself though),
  138. // extract out the sender name and replace it with the hotlinked name.
  139. if (chat.mSourceType == CHAT_SOURCE_AGENT &&
  140. chat.mFromID != LLUUID::null)
  141. {
  142. chat.mURL = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString();
  143. }
  144. // If the chat line has an associated url, link it up to the name.
  145. if (!chat.mURL.empty()
  146. && (line.length() > chat.mFromName.length() && line.find(chat.mFromName,0) == 0))
  147. {
  148. std::string start_line = line.substr(0, chat.mFromName.length() + 1);
  149. line = line.substr(chat.mFromName.length() + 1);
  150. edit->appendText(start_line, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,chat.mURL));
  151. edit->blockUndo();
  152. prepend_newline = false;
  153. }
  154. edit->appendText(line, prepend_newline, LLStyle::Params().color(color));
  155. edit->blockUndo();
  156. }
  157. // static
  158. void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
  159. {
  160. if (log_to_file && (gSavedPerAccountSettings.getBOOL("LogChat")))
  161. {
  162. if (chat.mChatType != CHAT_TYPE_WHISPER && chat.mChatType != CHAT_TYPE_SHOUT)
  163. {
  164. LLLogChat::saveHistory("chat", chat.mFromName, chat.mFromID, chat.mText);
  165. }
  166. else
  167. {
  168. LLLogChat::saveHistory("chat", "", chat.mFromID, chat.mFromName + " " + chat.mText);
  169. }
  170. }
  171. LLColor4 color = get_text_color(chat);
  172. if (!log_to_file) color = LLColor4::grey; //Recap from log file.
  173. if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
  174. {
  175. if(gSavedSettings.getBOOL("ShowScriptErrors") == FALSE)
  176. return;
  177. if (gSavedSettings.getS32("ShowScriptErrorsLocation") == 1)
  178. {
  179. LLFloaterScriptDebug::addScriptLine(chat.mText,
  180. chat.mFromName,
  181. color,
  182. chat.mFromID);
  183. return;
  184. }
  185. }
  186. // could flash the chat button in the status bar here. JC
  187. LLFloaterChat* chat_floater = LLFloaterChat::getInstance();
  188. LLViewerTextEditor* history_editor = chat_floater->getChild<LLViewerTextEditor>("Chat History Editor");
  189. LLViewerTextEditor* history_editor_with_mute = chat_floater->getChild<LLViewerTextEditor>("Chat History Editor with mute");
  190. if (!chat.mMuted)
  191. {
  192. add_timestamped_line(history_editor, chat, color);
  193. add_timestamped_line(history_editor_with_mute, chat, color);
  194. }
  195. else
  196. {
  197. // desaturate muted chat
  198. LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
  199. add_timestamped_line(history_editor_with_mute, chat, color);
  200. }
  201. // add objects as transient speakers that can be muted
  202. if (chat.mSourceType == CHAT_SOURCE_OBJECT)
  203. {
  204. chat_floater->mPanel->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT);
  205. }
  206. // start tab flashing on incoming text from other users (ignoring system text, etc)
  207. if (!chat_floater->isInVisibleChain() && chat.mSourceType == CHAT_SOURCE_AGENT)
  208. {
  209. LLFloaterChatterBox::getInstance()->setFloaterFlashing(chat_floater, TRUE);
  210. }
  211. }
  212. // static
  213. void LLFloaterChat::setHistoryCursorAndScrollToEnd()
  214. {
  215. LLViewerTextEditor* history_editor = LLFloaterChat::getInstance()->getChild<LLViewerTextEditor>("Chat History Editor");
  216. LLViewerTextEditor* history_editor_with_mute = LLFloaterChat::getInstance()->getChild<LLViewerTextEditor>("Chat History Editor with mute");
  217. if (history_editor)
  218. {
  219. history_editor->setCursorAndScrollToEnd();
  220. }
  221. if (history_editor_with_mute)
  222. {
  223. history_editor_with_mute->setCursorAndScrollToEnd();
  224. }
  225. }
  226. //static
  227. void LLFloaterChat::onClickMute(void *data)
  228. {
  229. LLFloaterChat* self = (LLFloaterChat*)data;
  230. LLComboBox* chatter_combo = self->getChild<LLComboBox>("chatter combobox");
  231. const std::string& name = chatter_combo->getSimple();
  232. LLUUID id = chatter_combo->getCurrentID();
  233. if (name.empty()) return;
  234. LLMute mute(id);
  235. mute.setFromDisplayName(name);
  236. LLMuteList::getInstance()->add(mute);
  237. LLPanelBlockedList::showPanelAndSelect(mute.mID);
  238. }
  239. //static
  240. void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
  241. {
  242. LLFloaterChat* floater = (LLFloaterChat*)data;
  243. //LLCheckBoxCtrl*
  244. BOOL show_mute = floater->getChild<LLCheckBoxCtrl>("show mutes")->get();
  245. LLViewerTextEditor* history_editor = floater->getChild<LLViewerTextEditor>("Chat History Editor");
  246. LLViewerTextEditor* history_editor_with_mute = floater->getChild<LLViewerTextEditor>("Chat History Editor with mute");
  247. if (!history_editor || !history_editor_with_mute)
  248. return;
  249. //BOOL show_mute = floater->mShowMuteCheckBox->get();
  250. if (show_mute)
  251. {
  252. history_editor->setVisible(FALSE);
  253. history_editor_with_mute->setVisible(TRUE);
  254. history_editor_with_mute->setCursorAndScrollToEnd();
  255. }
  256. else
  257. {
  258. history_editor->setVisible(TRUE);
  259. history_editor_with_mute->setVisible(FALSE);
  260. history_editor->setCursorAndScrollToEnd();
  261. }
  262. }
  263. // Put a line of chat in all the right places
  264. void LLFloaterChat::addChat(const LLChat& chat, BOOL local_agent)
  265. {
  266. triggerAlerts(chat.mText);
  267. // Add the sender to the list of people with which we've recently interacted.
  268. // this is not the best place to add _all_ messages to recent list
  269. // comment this for now, may remove later on code cleanup
  270. //if(chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID.notNull())
  271. // LLRecentPeople::instance().add(chat.mFromID);
  272. addChatHistory(chat, true);
  273. }
  274. // Moved from lltextparser.cpp to break llui/llaudio library dependency.
  275. //static
  276. void LLFloaterChat::triggerAlerts(const std::string& text)
  277. {
  278. LLTextParser* parser = LLTextParser::getInstance();
  279. // bool spoken=FALSE;
  280. for (S32 i=0;i<parser->mHighlights.size();i++)
  281. {
  282. LLSD& highlight = parser->mHighlights[i];
  283. if (parser->findPattern(text,highlight) >= 0 )
  284. {
  285. if(gAudiop)
  286. {
  287. if ((std::string)highlight["sound_lluuid"] != LLUUID::null.asString())
  288. {
  289. gAudiop->triggerSound(highlight["sound_lluuid"].asUUID(),
  290. gAgent.getID(),
  291. 1.f,
  292. LLAudioEngine::AUDIO_TYPE_UI,
  293. gAgent.getPositionGlobal() );
  294. }
  295. /*
  296. if (!spoken)
  297. {
  298. LLTextToSpeech* text_to_speech = NULL;
  299. text_to_speech = LLTextToSpeech::getInstance();
  300. spoken = text_to_speech->speak((LLString)highlight["voice"],text);
  301. }
  302. */
  303. }
  304. if (highlight["flash"])
  305. {
  306. LLWindow* viewer_window = gViewerWindow->getWindow();
  307. if (viewer_window && viewer_window->getMinimized())
  308. {
  309. viewer_window->flashIcon(5.f);
  310. }
  311. }
  312. }
  313. }
  314. }
  315. LLColor4 get_text_color(const LLChat& chat)
  316. {
  317. LLColor4 text_color;
  318. if(chat.mMuted)
  319. {
  320. text_color.setVec(0.8f, 0.8f, 0.8f, 1.f);
  321. }
  322. else
  323. {
  324. switch(chat.mSourceType)
  325. {
  326. case CHAT_SOURCE_SYSTEM:
  327. text_color = LLUIColorTable::instance().getColor("SystemChatColor");
  328. break;
  329. case CHAT_SOURCE_AGENT:
  330. if (chat.mFromID.isNull())
  331. {
  332. text_color = LLUIColorTable::instance().getColor("SystemChatColor");
  333. }
  334. else
  335. {
  336. if(gAgent.getID() == chat.mFromID)
  337. {
  338. text_color = LLUIColorTable::instance().getColor("UserChatColor");
  339. }
  340. else
  341. {
  342. text_color = LLUIColorTable::instance().getColor("AgentChatColor");
  343. }
  344. }
  345. break;
  346. case CHAT_SOURCE_OBJECT:
  347. if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
  348. {
  349. text_color = LLUIColorTable::instance().getColor("ScriptErrorColor");
  350. }
  351. else if ( chat.mChatType == CHAT_TYPE_OWNER )
  352. {
  353. text_color = LLUIColorTable::instance().getColor("llOwnerSayChatColor");
  354. }
  355. else
  356. {
  357. text_color = LLUIColorTable::instance().getColor("ObjectChatColor");
  358. }
  359. break;
  360. default:
  361. text_color.setToWhite();
  362. }
  363. if (!chat.mPosAgent.isExactlyZero())
  364. {
  365. LLVector3 pos_agent = gAgent.getPositionAgent();
  366. F32 distance_squared = dist_vec_squared(pos_agent, chat.mPosAgent);
  367. F32 dist_near_chat = gAgent.getNearChatRadius();
  368. if (distance_squared > dist_near_chat * dist_near_chat)
  369. {
  370. // diminish far-off chat
  371. text_color.mV[VALPHA] = 0.8f;
  372. }
  373. }
  374. }
  375. return text_color;
  376. }
  377. //static
  378. void LLFloaterChat::loadHistory()
  379. {
  380. LLLogChat::loadHistory(std::string("chat"), &chatFromLogFile, (void *)LLFloaterChat::getInstance());
  381. }
  382. //static
  383. void LLFloaterChat::chatFromLogFile(LLLogChat::ELogLineType type , const LLSD& line, void* userdata)
  384. {
  385. switch (type)
  386. {
  387. case LLLogChat::LOG_EMPTY:
  388. case LLLogChat::LOG_END:
  389. // *TODO: nice message from XML file here
  390. break;
  391. case LLLogChat::LOG_LINE:
  392. case LLLogChat::LOG_LLSD:
  393. {
  394. LLChat chat;
  395. chat.mText = line["message"].asString();
  396. get_text_color(chat);
  397. addChatHistory(chat, FALSE);
  398. }
  399. break;
  400. default:
  401. // nothing
  402. break;
  403. }
  404. }
  405. //static
  406. void* LLFloaterChat::createSpeakersPanel(void* data)
  407. {
  408. return new LLPanelActiveSpeakers(LLLocalSpeakerMgr::getInstance(), TRUE);
  409. }
  410. //static
  411. void* LLFloaterChat::createChatPanel(void* data)
  412. {
  413. LLChatBar* chatp = new LLChatBar();
  414. return chatp;
  415. }
  416. // static
  417. void LLFloaterChat::onClickToggleActiveSpeakers(void* userdata)
  418. {
  419. LLFloaterChat* self = (LLFloaterChat*)userdata;
  420. self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
  421. }
  422. //static
  423. LLFloaterChat* LLFloaterChat::getInstance()
  424. {
  425. return LLFloaterReg::getTypedInstance<LLFloaterChat>("chat", LLSD()) ;
  426. }