llfloaterchat.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. /**
  2. * @file llfloaterchat.cpp
  3. * @brief LLFloaterChat class implementation
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-2009, 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. /**
  33. * Actually the "Chat History" floater.
  34. * Should be llfloaterchathistory, not llfloaterchat.
  35. */
  36. #include "llviewerprecompiledheaders.h"
  37. #include "llfloaterchat.h"
  38. #include "llavatarnamecache.h"
  39. #include "llbutton.h"
  40. #include "llcheckboxctrl.h"
  41. #include "llcombobox.h"
  42. #include "llconsole.h"
  43. #include "llstylemap.h"
  44. #include "lluictrlfactory.h"
  45. #include "llagent.h"
  46. #include "llchatbar.h"
  47. #include "llfloateractivespeakers.h"
  48. #include "llfloaterchatterbox.h"
  49. #include "llfloatermute.h"
  50. #include "llfloaterscriptdebug.h"
  51. #include "lllogchat.h"
  52. #include "llmutelist.h"
  53. //MK
  54. #include "mkrlinterface.h"
  55. //mk
  56. #include "llviewercontrol.h"
  57. #include "llviewergesture.h" // For triggering gestures
  58. #include "llviewermenu.h"
  59. #include "llviewertexteditor.h"
  60. #include "llvoavatarself.h"
  61. #include "llslurl.h"
  62. #include "llstatusbar.h"
  63. #include "hbviewerautomation.h"
  64. #include "llweb.h"
  65. // This name is used by and should stay in sync with the one used in
  66. // floater_chat_history*.xml files. HB
  67. const std::string gChatFloaterName = "chat";
  68. static std::set<std::string> sHighlightWords;
  69. //
  70. // Helper functions
  71. //
  72. LLColor4 get_agent_chat_color(const LLChat& chat)
  73. {
  74. if (gSavedSettings.getBool("HighlightOwnNameInChat"))
  75. {
  76. std::string text = chat.mText;
  77. size_t name_pos = text.find(chat.mFromName);
  78. if (name_pos == 0)
  79. {
  80. text = text.substr(chat.mFromName.length());
  81. if (text.find(": ") == 0)
  82. {
  83. text = text.substr(2);
  84. }
  85. else
  86. {
  87. text = text.substr(1);
  88. }
  89. }
  90. if (gAutomationp)
  91. {
  92. LLColor4 color;
  93. if (gAutomationp->onChatTextColoring(chat.mFromID, chat.mFromName,
  94. text, color))
  95. {
  96. return color;
  97. }
  98. }
  99. if (LLFloaterChat::isOwnNameInText(text))
  100. {
  101. return gSavedSettings.getColor4("OwnNameChatColor");
  102. }
  103. }
  104. return gSavedSettings.getColor4("AgentChatColor");
  105. }
  106. LLColor4 get_text_color(const LLChat& chat)
  107. {
  108. if (chat.mMuted)
  109. {
  110. return LLColor4(0.8f, 0.8f, 0.8f, 1.f);
  111. }
  112. LLColor4 text_color;
  113. switch (chat.mSourceType)
  114. {
  115. case CHAT_SOURCE_SYSTEM:
  116. case CHAT_SOURCE_UNKNOWN:
  117. text_color = gSavedSettings.getColor4("SystemChatColor");
  118. break;
  119. case CHAT_SOURCE_AGENT:
  120. if (gAgentID == chat.mFromID)
  121. {
  122. text_color = gSavedSettings.getColor4("UserChatColor");
  123. }
  124. else
  125. {
  126. text_color = get_agent_chat_color(chat);
  127. }
  128. break;
  129. case CHAT_SOURCE_OBJECT:
  130. if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
  131. {
  132. text_color = gSavedSettings.getColor4("ScriptErrorColor");
  133. }
  134. else if (chat.mChatType == CHAT_TYPE_OWNER)
  135. {
  136. // Message from one of our own objects
  137. text_color = gSavedSettings.getColor4("llOwnerSayChatColor");
  138. }
  139. else if (chat.mChatType == CHAT_TYPE_DIRECT)
  140. {
  141. // Used both for llRegionSayTo() and llInstantMesssage()
  142. // since there is no real reason to distinguish one from
  143. // another (both are seen only by us and the object may
  144. // pertain to anyone, us included).
  145. text_color = gSavedSettings.getColor4("DirectChatColor");
  146. }
  147. else
  148. {
  149. // Public object chat
  150. text_color = gSavedSettings.getColor4("ObjectChatColor");
  151. }
  152. break;
  153. default:
  154. text_color.setToWhite();
  155. }
  156. if (!chat.mPosAgent.isExactlyZero())
  157. {
  158. LLVector3 pos_agent = gAgent.getPositionAgent();
  159. F32 distance = dist_vec(pos_agent, chat.mPosAgent);
  160. if (distance > gAgent.getNearChatRadius())
  161. {
  162. // Diminish far-off chat
  163. text_color.mV[VALPHA] = 0.8f;
  164. }
  165. }
  166. return text_color;
  167. }
  168. void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat,
  169. const LLColor4& color)
  170. {
  171. if (!edit) return;
  172. std::string line = chat.mText;
  173. bool prepend_newline = true;
  174. if (gSavedSettings.getBool("ChatShowTimestamps"))
  175. {
  176. edit->appendTime(prepend_newline);
  177. prepend_newline = false;
  178. }
  179. // If the msg is from an agent (not yourself though), extract out the
  180. // sender name and replace it with the hotlinked name.
  181. if (chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID.notNull())
  182. {
  183. chat.mURL = llformat("secondlife:///app/agent/%s/about",
  184. chat.mFromID.asString().c_str());
  185. }
  186. // If the chat line has an associated url, link it up to the name.
  187. if (!chat.mURL.empty() && (line.length() > chat.mFromName.length() &&
  188. (chat.mFromName.empty() || line.find(chat.mFromName, 0) == 0)))
  189. {
  190. //MK
  191. if (!gRLenabled || !gRLInterface.mContainsShownames)
  192. {
  193. //mk
  194. size_t pos;
  195. if (chat.mFromName.empty() ||
  196. chat.mFromName.find_first_not_of(' ') == std::string::npos)
  197. {
  198. // Name is empty... Set the link on the first word instead
  199. // (skipping leading spaces and the ':' separator)...
  200. pos = line.find_first_not_of(" :");
  201. if (pos == std::string::npos)
  202. {
  203. // No word found !
  204. pos = line.length();
  205. line += " ";
  206. }
  207. else
  208. {
  209. pos = line.find(' ', pos);
  210. if (pos == std::string::npos)
  211. {
  212. // Only one word in the line...
  213. pos = line.length();
  214. line += " ";
  215. }
  216. }
  217. }
  218. else
  219. {
  220. pos = chat.mFromName.length() + 1;
  221. }
  222. std::string start_line = line.substr(0, pos);
  223. line = line.substr(pos);
  224. const LLStyleSP& sourceStyle = gStyleMap.lookup(chat.mFromID,
  225. chat.mURL);
  226. edit->appendStyledText(start_line, false, prepend_newline,
  227. sourceStyle);
  228. prepend_newline = false;
  229. //MK
  230. }
  231. //mk
  232. }
  233. edit->appendColoredText(line, false, prepend_newline, color);
  234. }
  235. void log_chat_text(const LLChat& chat)
  236. {
  237. std::string histstr = chat.mText;
  238. static LLCachedControl<bool> stamp(gSavedPerAccountSettings,
  239. "LogChatTimestamp");
  240. if (stamp)
  241. {
  242. histstr = LLLogChat::timestamp() + histstr;
  243. }
  244. LLLogChat::saveHistory(LLStringUtil::null, histstr);
  245. }
  246. bool make_words_list()
  247. {
  248. static std::string nicknames;
  249. static LLCachedControl<std::string> saved_nicks(gSavedPerAccountSettings,
  250. "HighlightNicknames");
  251. bool changed = false;
  252. if (nicknames != std::string(saved_nicks))
  253. {
  254. nicknames = saved_nicks;
  255. changed = true;
  256. }
  257. LLAvatarName avatar_name;
  258. bool do_highlight = gSavedPerAccountSettings.getBool("HighlightDisplayName") &&
  259. LLAvatarNameCache::useDisplayNames() &&
  260. LLAvatarNameCache::get(gAgentID, &avatar_name);
  261. static std::string display_name;
  262. static bool highlight_display_name = false;
  263. if (do_highlight != highlight_display_name)
  264. {
  265. highlight_display_name = do_highlight;
  266. changed = true;
  267. if (!highlight_display_name)
  268. {
  269. display_name.clear();
  270. }
  271. }
  272. std::string name;
  273. if (highlight_display_name)
  274. {
  275. if (!avatar_name.mIsDisplayNameDefault)
  276. {
  277. name = avatar_name.mDisplayName;
  278. LLStringUtil::toLower(name);
  279. }
  280. if (name != display_name)
  281. {
  282. display_name = name;
  283. changed = true;
  284. }
  285. }
  286. if (changed && isAgentAvatarValid())
  287. {
  288. // Rebuild the whole list
  289. sHighlightWords.clear();
  290. // First, fetch the avatar name (note: we do not use
  291. // gSavedSettings.getString("[First/Last]Name") here,
  292. // because those are not set when using --autologin).
  293. LLNameValue* firstname = gAgentAvatarp->getNVPair("FirstName");
  294. LLNameValue* lastname = gAgentAvatarp->getNVPair("LastName");
  295. name.assign(firstname->getString());
  296. LLStringUtil::toLower(name);
  297. sHighlightWords.emplace(name);
  298. name.assign(lastname->getString());
  299. if (name != "Resident" && sHighlightWords.count(name) == 0)
  300. {
  301. LLStringUtil::toLower(name);
  302. sHighlightWords.emplace(name);
  303. }
  304. std::string part;
  305. if (!display_name.empty())
  306. {
  307. name = display_name;
  308. size_t index;
  309. while ((index = name.find(' ')) != std::string::npos)
  310. {
  311. part = name.substr(0, index);
  312. name = name.substr(index + 1);
  313. if (part.length() > 3)
  314. {
  315. sHighlightWords.emplace(part);
  316. }
  317. }
  318. if (name.length() > 3 && sHighlightWords.count(name) == 0)
  319. {
  320. sHighlightWords.emplace(name);
  321. }
  322. }
  323. if (!nicknames.empty())
  324. {
  325. name = nicknames;
  326. LLStringUtil::toLower(name);
  327. // Accept space and comma separated list
  328. LLStringUtil::replaceChar(name, ' ', ',');
  329. size_t index;
  330. while ((index = name.find(',')) != std::string::npos)
  331. {
  332. part = name.substr(0, index);
  333. name = name.substr(index + 1);
  334. if (part.length() > 2)
  335. {
  336. sHighlightWords.emplace(part);
  337. }
  338. }
  339. if (name.length() > 2 && sHighlightWords.count(name) == 0)
  340. {
  341. sHighlightWords.emplace(name);
  342. }
  343. }
  344. }
  345. return changed;
  346. }
  347. //
  348. // Member Functions
  349. //
  350. LLFloaterChat::LLFloaterChat(const LLSD& seed)
  351. : LLFloater(gChatFloaterName, "FloaterChatRect", "", RESIZE_YES, 440, 100,
  352. DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES),
  353. mChatBarPanel(NULL),
  354. mSpeakerPanel(NULL),
  355. mToggleActiveSpeakersBtn(NULL),
  356. mHistoryWithoutMutes(NULL),
  357. mHistoryWithMutes(NULL),
  358. mFocused(false)
  359. {
  360. std::string xml_file;
  361. if (gSavedSettings.getBool("UseOldChatHistory"))
  362. {
  363. xml_file = "floater_chat_history2.xml";
  364. }
  365. else
  366. {
  367. xml_file = "floater_chat_history.xml";
  368. mFactoryMap["chat_panel"] = LLCallbackMap(createChatPanel, this);
  369. }
  370. mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel,
  371. this);
  372. // false so to not automatically open singleton floaters (as result of
  373. // getInstance())
  374. LLUICtrlFactory::getInstance()->buildFloater(this, xml_file,
  375. &getFactoryMap(), false);
  376. }
  377. //virtual
  378. bool LLFloaterChat::postBuild()
  379. {
  380. if (mChatBarPanel)
  381. {
  382. mChatBarPanel->setGestureCombo(getChild<LLComboBox>("Gesture",
  383. true, false));
  384. }
  385. childSetCommitCallback("show mutes", onClickToggleShowMute, this);
  386. mHistoryWithoutMutes = getChild<LLViewerTextEditor>("Chat History Editor");
  387. mHistoryWithoutMutes->setPreserveSegments(true);
  388. mHistoryWithoutMutes->setCustomMenuType("chat_history");
  389. mHistoryWithMutes = getChild<LLViewerTextEditor>("Chat History Editor with mute");
  390. mHistoryWithMutes->setPreserveSegments(true);
  391. mHistoryWithMutes->setVisible(false);
  392. mHistoryWithMutes->setCustomMenuType("chat_history");
  393. mToggleActiveSpeakersBtn = getChild<LLButton>("toggle_active_speakers_btn");
  394. mToggleActiveSpeakersBtn->setClickedCallback(onClickToggleActiveSpeakers,
  395. this);
  396. return true;
  397. }
  398. //virtual
  399. void LLFloaterChat::setVisible(bool visible)
  400. {
  401. LLFloater::setVisible(visible);
  402. gSavedSettings.setBool("ShowChatHistory", visible);
  403. }
  404. //virtual
  405. void LLFloaterChat::draw()
  406. {
  407. bool active_speakers_panel = mSpeakerPanel && mSpeakerPanel->getVisible();
  408. mToggleActiveSpeakersBtn->setValue(active_speakers_panel);
  409. if (active_speakers_panel)
  410. {
  411. mSpeakerPanel->refreshSpeakers();
  412. }
  413. if (mChatBarPanel)
  414. {
  415. mChatBarPanel->refresh();
  416. }
  417. LLFloater::draw();
  418. }
  419. //virtual
  420. void LLFloaterChat::onClose(bool app_quitting)
  421. {
  422. if (!app_quitting)
  423. {
  424. gSavedSettings.setBool("ShowChatHistory", false);
  425. }
  426. setVisible(false);
  427. mFocused = false;
  428. }
  429. //virtual
  430. void LLFloaterChat::onVisibilityChange(bool new_visibility)
  431. {
  432. // Hide the chat overlay when our history is visible.
  433. updateConsoleVisibility();
  434. // stop chat history tab from flashing when it appears
  435. if (new_visibility)
  436. {
  437. LLFloaterChatterBox::getInstance()->setFloaterFlashing(this, false);
  438. }
  439. LLFloater::onVisibilityChange(new_visibility);
  440. }
  441. //virtual
  442. void LLFloaterChat::setMinimized(bool minimized)
  443. {
  444. LLFloater::setMinimized(minimized);
  445. updateConsoleVisibility();
  446. }
  447. //virtual
  448. void LLFloaterChat::onFocusReceived()
  449. {
  450. // This keeps track of the panel focus, independently of the keyboard
  451. // focus (which might get stolen by the main chat bar). Also, we don't
  452. // register a focused event if the chat floater got its own chat bar
  453. // (in which case the latter will actually receive the keyboard focus).
  454. if (!mChatBarPanel)
  455. {
  456. mFocused = true;
  457. }
  458. }
  459. //virtual
  460. void LLFloaterChat::onFocusLost()
  461. {
  462. mFocused = false;
  463. }
  464. void LLFloaterChat::updateConsoleVisibility()
  465. {
  466. if (!gConsolep) return;
  467. // Determine whether we should show console due to not being visible
  468. gConsolep->setVisible(isMinimized() || // are we minimized ?
  469. // are we not in part of UI being drawn ?
  470. !isInVisibleChain() ||
  471. // are we hosted in a minimized floater ?
  472. (getHost() && getHost()->isMinimized()));
  473. }
  474. //static
  475. void LLFloaterChat::addChatHistory(LLChat& chat, bool log_to_file)
  476. {
  477. LLFloaterChat* self = LLFloaterChat::getInstance(LLSD());
  478. static LLCachedControl<bool> log_chat(gSavedPerAccountSettings, "LogChat");
  479. if (log_to_file && log_chat)
  480. {
  481. log_chat_text(chat);
  482. }
  483. LLColor4 color;
  484. if (log_to_file)
  485. {
  486. color = get_text_color(chat);
  487. }
  488. else
  489. {
  490. color = LLColor4::grey; // Recap from log file.
  491. }
  492. if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
  493. {
  494. LLFloaterScriptDebug::addScriptLine(chat.mText, chat.mFromName,
  495. color, chat.mFromID);
  496. if (!gSavedSettings.getBool("ScriptErrorsAsChat"))
  497. {
  498. return;
  499. }
  500. }
  501. // Could flash the chat button in the status bar here. JC
  502. self->mHistoryWithoutMutes->setParseHTML(true);
  503. self->mHistoryWithMutes->setParseHTML(true);
  504. if (!chat.mMuted)
  505. {
  506. add_timestamped_line(self->mHistoryWithoutMutes, chat, color);
  507. add_timestamped_line(self->mHistoryWithMutes, chat, color);
  508. }
  509. else
  510. {
  511. // Desaturate muted chat
  512. LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
  513. add_timestamped_line(self->mHistoryWithMutes, chat, muted_color);
  514. }
  515. // Add objects as transient speakers that can be muted
  516. if (chat.mSourceType == CHAT_SOURCE_OBJECT)
  517. {
  518. self->mSpeakerPanel->setSpeaker(chat.mFromID, chat.mFromName,
  519. LLSpeaker::STATUS_NOT_IN_CHANNEL,
  520. LLSpeaker::SPEAKER_OBJECT,
  521. chat.mOwnerID);
  522. }
  523. // Start tab flashing on incoming text from other users (ignoring system
  524. // text, etc)
  525. if (!self->isInVisibleChain() && chat.mSourceType == CHAT_SOURCE_AGENT)
  526. {
  527. LLFloaterChatterBox::getInstance()->setFloaterFlashing(self, true);
  528. }
  529. }
  530. //static
  531. void LLFloaterChat::setHistoryCursorAndScrollToEnd()
  532. {
  533. LLFloaterChat* self = LLFloaterChat::getInstance(LLSD());
  534. if (!self) return;
  535. if (self->mHistoryWithoutMutes)
  536. {
  537. self->mHistoryWithoutMutes->setCursorAndScrollToEnd();
  538. }
  539. if (self->mHistoryWithMutes)
  540. {
  541. self->mHistoryWithMutes->setCursorAndScrollToEnd();
  542. }
  543. }
  544. //static
  545. void LLFloaterChat::onClickMute(void* data)
  546. {
  547. LLFloaterChat* self = (LLFloaterChat*)data;
  548. LLComboBox* chatter_combo = self->getChild<LLComboBox>("chatter combobox");
  549. const std::string& name = chatter_combo->getSimple();
  550. LLUUID id = chatter_combo->getCurrentID();
  551. if (name.empty()) return;
  552. LLMute mute(id);
  553. mute.setFromDisplayName(name);
  554. if (LLMuteList::add(mute))
  555. {
  556. LLFloaterMute::selectMute(mute.mID);
  557. }
  558. }
  559. //static
  560. void LLFloaterChat::onClickToggleShowMute(LLUICtrl* ctrl, void* data)
  561. {
  562. LLFloaterChat* self = (LLFloaterChat*)data;
  563. LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl;
  564. if (!check || !self || !self->mHistoryWithoutMutes ||
  565. !self->mHistoryWithMutes)
  566. {
  567. return;
  568. }
  569. if (check->get())
  570. {
  571. self->mHistoryWithoutMutes->setVisible(false);
  572. self->mHistoryWithMutes->setVisible(true);
  573. self->mHistoryWithMutes->setCursorAndScrollToEnd();
  574. }
  575. else
  576. {
  577. self->mHistoryWithMutes->setVisible(false);
  578. self->mHistoryWithoutMutes->setVisible(true);
  579. self->mHistoryWithoutMutes->setCursorAndScrollToEnd();
  580. }
  581. }
  582. // Put a line of chat in all the right places
  583. //static
  584. void LLFloaterChat::addChat(LLChat& chat, bool from_im, bool local_agent)
  585. {
  586. LLFloaterChat* self = LLFloaterChat::getInstance(LLSD());
  587. if (!self) return;
  588. //MK
  589. if (gRLenabled && chat.mText == "")
  590. {
  591. // In case crunchEmote() returned an empty string, just abort.
  592. return;
  593. }
  594. //mk
  595. LLColor4 text_color = get_text_color(chat);
  596. bool no_script_debug = chat.mChatType == CHAT_TYPE_DEBUG_MSG &&
  597. !gSavedSettings.getBool("ScriptErrorsAsChat");
  598. if (!no_script_debug && !local_agent && gConsolep && !chat.mMuted)
  599. {
  600. if (chat.mSourceType == CHAT_SOURCE_SYSTEM)
  601. {
  602. text_color = gSavedSettings.getColor("SystemChatColor");
  603. }
  604. else if (from_im)
  605. {
  606. text_color = gSavedSettings.getColor("IMChatColor");
  607. }
  608. // We display anything if it is not an IM. If it's an IM, check pref.
  609. if (!from_im || gSavedSettings.getBool("IMInChatConsole"))
  610. {
  611. gConsolep->addConsoleLine(chat.mText, text_color);
  612. }
  613. }
  614. static LLCachedControl<bool> log_im(gSavedPerAccountSettings, "LogChatIM");
  615. if (from_im && log_im)
  616. {
  617. log_chat_text(chat);
  618. }
  619. if (from_im)
  620. {
  621. if (gSavedSettings.getBool("IMInChatHistory"))
  622. {
  623. addChatHistory(chat, false);
  624. }
  625. }
  626. else
  627. {
  628. addChatHistory(chat, true);
  629. }
  630. resolveSLURLs(chat);
  631. }
  632. //static
  633. void LLFloaterChat::resolveSLURLs(const LLChat& chat)
  634. {
  635. LLFloaterChat* self = LLFloaterChat::findInstance(LLSD());
  636. if (!self) return;
  637. // SLURLs resolving: fetch the Ids associated with avatar/group/experience
  638. // name SLURLs present in the text.
  639. uuid_list_t agent_ids = LLSLURL::findSLURLs(chat.mText);
  640. if (agent_ids.empty()) return;
  641. // Add to the existing list of pending Ids
  642. self->mPendingIds.insert(agent_ids.begin(), agent_ids.end());
  643. // Launch the SLURLs resolution. Note that the substituteSLURL() callback
  644. // will be invoked immediately for names already in cache. That's why we
  645. // needed to push the untranslated SLURLs in the chat first (together with
  646. // the fact that doing so, gets the SLURLs auto-parsed and puts a link
  647. // segment on them in the text editor, segment link that will be preserved
  648. // when the SLURL will be replaced with the corresponding name).
  649. LLSLURL::resolveSLURLs();
  650. }
  651. //static
  652. void LLFloaterChat::substituteSLURL(const LLUUID& id, const std::string& slurl,
  653. const std::string& substitute)
  654. {
  655. LLFloaterChat* self = LLFloaterChat::findInstance(LLSD());
  656. if (self && self->mPendingIds.count(id))
  657. {
  658. self->mHistoryWithoutMutes->replaceTextAll(slurl, substitute, true);
  659. self->mHistoryWithoutMutes->setEnabled(false);
  660. self->mHistoryWithMutes->replaceTextAll(slurl, substitute, true);
  661. self->mHistoryWithMutes->setEnabled(false);
  662. if (gConsolep)
  663. {
  664. gConsolep->replaceAllText(slurl, substitute, true);
  665. }
  666. }
  667. }
  668. //static
  669. void LLFloaterChat::substitutionDone(const LLUUID& id)
  670. {
  671. LLFloaterChat* self = LLFloaterChat::findInstance(LLSD());
  672. if (self)
  673. {
  674. self->mPendingIds.erase(id);
  675. }
  676. }
  677. //static
  678. void LLFloaterChat::loadHistory()
  679. {
  680. LLLogChat::loadHistory(LLStringUtil::null, &chatFromLog,
  681. (void*)LLFloaterChat::getInstance(LLSD()));
  682. }
  683. //static
  684. void LLFloaterChat::chatFromLog(S32 type, const LLSD& data, void* userdata)
  685. {
  686. if (type == LLLogChat::LOG_LINE)
  687. {
  688. LLChat chat;
  689. chat.mText = data["line"].asString();
  690. addChatHistory(chat, false);
  691. }
  692. }
  693. //static
  694. void* LLFloaterChat::createSpeakersPanel(void* data)
  695. {
  696. LLFloaterChat* self = (LLFloaterChat*)data;
  697. self->mSpeakerPanel =
  698. new LLPanelActiveSpeakers(LLLocalSpeakerMgr::getInstance(), true);
  699. return self->mSpeakerPanel;
  700. }
  701. //static
  702. void* LLFloaterChat::createChatPanel(void* data)
  703. {
  704. LLFloaterChat* self = (LLFloaterChat*)data;
  705. self->mChatBarPanel = new LLChatBar("floating_chat_bar");
  706. return self->mChatBarPanel;
  707. }
  708. //static
  709. void LLFloaterChat::onClickToggleActiveSpeakers(void* userdata)
  710. {
  711. LLFloaterChat* self = (LLFloaterChat*)userdata;
  712. //MK
  713. if (gRLenabled && gRLInterface.mContainsShownames)
  714. {
  715. if (!self->mSpeakerPanel->getVisible()) return;
  716. }
  717. //mk
  718. self->mSpeakerPanel->setVisible(!self->mSpeakerPanel->getVisible());
  719. }
  720. //static
  721. bool LLFloaterChat::visible(LLFloater* instance, const LLSD& key)
  722. {
  723. return VisibilityPolicy<LLFloater>::visible(instance, key);
  724. }
  725. //static
  726. void LLFloaterChat::show(LLFloater* instance, const LLSD& key)
  727. {
  728. VisibilityPolicy<LLFloater>::show(instance, key);
  729. }
  730. //static
  731. void LLFloaterChat::hide(LLFloater* instance, const LLSD& key)
  732. {
  733. if (instance->getHost())
  734. {
  735. LLFloaterChatterBox::hideInstance();
  736. }
  737. else
  738. {
  739. VisibilityPolicy<LLFloater>::hide(instance, key);
  740. }
  741. }
  742. //static
  743. void LLFloaterChat::focus()
  744. {
  745. LLFloaterChat* self = (LLFloaterChat*)findInstance();
  746. if (self)
  747. {
  748. self->setFocus(true);
  749. }
  750. }
  751. //static
  752. bool LLFloaterChat::isFocused()
  753. {
  754. LLFloaterChat* self = (LLFloaterChat*)findInstance();
  755. return self && self->mFocused;
  756. }
  757. //static
  758. bool LLFloaterChat::isOwnNameInText(const std::string& text_line)
  759. {
  760. if (!isAgentAvatarValid())
  761. {
  762. return false;
  763. }
  764. const std::string separators(" .,;:'!?*-()[]\"");
  765. bool flag;
  766. char before, after;
  767. size_t index = 0, larger_index, length = 0;
  768. std::set<std::string>::iterator it;
  769. std::string name;
  770. std::string text = " " + text_line + " ";
  771. LLStringUtil::toLower(text);
  772. if (make_words_list())
  773. {
  774. name = "Highlights words list changed to: ";
  775. flag = false;
  776. for (it = sHighlightWords.begin(); it != sHighlightWords.end(); ++it)
  777. {
  778. if (flag)
  779. {
  780. name += ", ";
  781. }
  782. name += *it;
  783. flag = true;
  784. }
  785. llinfos << name << llendl;
  786. }
  787. do
  788. {
  789. flag = false;
  790. larger_index = 0;
  791. for (it = sHighlightWords.begin(); it != sHighlightWords.end(); ++it)
  792. {
  793. name = *it;
  794. index = text.find(name);
  795. if (index != std::string::npos)
  796. {
  797. flag = true;
  798. before = text[index - 1];
  799. after = text[index + name.length()];
  800. if (separators.find(before) != std::string::npos &&
  801. separators.find(after) != std::string::npos)
  802. {
  803. return true;
  804. }
  805. if (index >= larger_index)
  806. {
  807. larger_index = index;
  808. length = name.length();
  809. }
  810. }
  811. }
  812. if (flag)
  813. {
  814. text = " " + text.substr(index + length);
  815. }
  816. }
  817. while (flag);
  818. return false;
  819. }