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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llnearbychatbar.cpp
  3.  * @brief LLNearbyChatBar class implementation
  4.  *
  5.  * $LicenseInfo:firstyear=2002&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2002-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 "message.h"
  34. #include "llfloaterreg.h"
  35. #include "lltrans.h"
  36. #include "llnearbychatbar.h"
  37. #include "llbottomtray.h"
  38. #include "llagent.h"
  39. #include "llgesturemgr.h"
  40. #include "llmultigesture.h"
  41. #include "llkeyboard.h"
  42. #include "llanimationstates.h"
  43. #include "llviewerstats.h"
  44. #include "llcommandhandler.h"
  45. #include "llviewercontrol.h"
  46. #include "llnavigationbar.h"
  47. #include "llwindow.h"
  48. #include "llviewerwindow.h"
  49. #include "llrootview.h"
  50. S32 LLNearbyChatBar::sLastSpecialChatChannel = 0;
  51. // legacy callback glue
  52. void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel);
  53. static LLDefaultChildRegistry::Register<LLGestureComboList> r("gesture_combo_list");
  54. struct LLChatTypeTrigger {
  55. std::string name;
  56. EChatType type;
  57. };
  58. static LLChatTypeTrigger sChatTypeTriggers[] = {
  59. { "/whisper" , CHAT_TYPE_WHISPER},
  60. { "/shout" , CHAT_TYPE_SHOUT}
  61. };
  62. LLGestureComboList::Params::Params()
  63. : combo_button("combo_button"),
  64. combo_list("combo_list")
  65. {
  66. }
  67. LLGestureComboList::LLGestureComboList(const LLGestureComboList::Params& p)
  68. : LLUICtrl(p)
  69. , mLabel(p.label)
  70. , mViewAllItemIndex(0)
  71. {
  72. LLButton::Params button_params = p.combo_button;
  73. button_params.follows.flags(FOLLOWS_LEFT|FOLLOWS_BOTTOM|FOLLOWS_RIGHT);
  74. mButton = LLUICtrlFactory::create<LLButton>(button_params);
  75. mButton->reshape(getRect().getWidth(),getRect().getHeight());
  76. mButton->setCommitCallback(boost::bind(&LLGestureComboList::onButtonCommit, this));
  77. addChild(mButton);
  78. LLScrollListCtrl::Params params = p.combo_list;
  79. params.name("GestureComboList");
  80. params.commit_callback.function(boost::bind(&LLGestureComboList::onItemSelected, this, _2));
  81. params.visible(false);
  82. params.commit_on_keyboard_movement(false);
  83. mList = LLUICtrlFactory::create<LLScrollListCtrl>(params);
  84. // *HACK: adding list as a child to FloaterViewHolder to make it fully visible without
  85. // making it top control (because it would cause problems).
  86. gViewerWindow->getFloaterViewHolder()->addChild(mList);
  87. mList->setVisible(FALSE);
  88. //****************************Gesture Part********************************/
  89. setCommitCallback(boost::bind(&LLGestureComboList::onCommitGesture, this));
  90. // now register us as observer since we have a place to put the results
  91. LLGestureManager::instance().addObserver(this);
  92. // refresh list from current active gestures
  93. refreshGestures();
  94. setFocusLostCallback(boost::bind(&LLGestureComboList::hideList, this));
  95. }
  96. BOOL LLGestureComboList::handleKey(KEY key, MASK mask, BOOL called_from_parent)
  97. {
  98. BOOL handled = FALSE;
  99. if (key == KEY_ESCAPE && mask == MASK_NONE )
  100. {
  101. hideList();
  102. handled = TRUE;
  103. }
  104. else
  105. {
  106. handled = mList->handleKey(key, mask, called_from_parent);
  107. }
  108. return handled; 
  109. }
  110. void LLGestureComboList::showList()
  111. {
  112. LLRect rect = mList->getRect();
  113. LLRect screen;
  114. mButton->localRectToScreen(getRect(), &screen);
  115. // Calculating amount of space between the navigation bar and gestures combo
  116. LLNavigationBar* nb = LLNavigationBar::getInstance();
  117. S32 x, nb_bottom;
  118. nb->localPointToScreen(0, 0, &x, &nb_bottom);
  119. S32 max_height = nb_bottom - screen.mTop;
  120. mList->calcColumnWidths();
  121. rect.setOriginAndSize(screen.mLeft, screen.mTop, llmax(mList->getMaxContentWidth(),mButton->getRect().getWidth()), max_height);
  122. mList->setRect(rect);
  123. mList->fitContents( llmax(mList->getMaxContentWidth(),mButton->getRect().getWidth()), max_height);
  124. gFocusMgr.setKeyboardFocus(this);
  125. // Show the list and push the button down
  126. mButton->setToggleState(TRUE);
  127. mList->setVisible(TRUE);
  128. }
  129. void LLGestureComboList::onButtonCommit()
  130. {
  131. if (!mList->getVisible())
  132. {
  133. // highlight the last selected item from the original selection before potentially selecting a new item
  134. // as visual cue to original value of combo box
  135. LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
  136. if (last_selected_item)
  137. {
  138. mList->mouseOverHighlightNthItem(mList->getItemIndex(last_selected_item));
  139. }
  140. if (mList->getItemCount() != 0)
  141. {
  142. showList();
  143. }
  144. }
  145. else
  146. {
  147. hideList();
  148. }
  149. void LLGestureComboList::hideList()
  150. {
  151. if (mList->getVisible())
  152. {
  153. mButton->setToggleState(FALSE);
  154. mList->setVisible(FALSE);
  155. mList->mouseOverHighlightNthItem(-1);
  156. gFocusMgr.setKeyboardFocus(NULL);
  157. }
  158. }
  159. S32 LLGestureComboList::getCurrentIndex() const
  160. {
  161. LLScrollListItem* item = mList->getFirstSelected();
  162. if( item )
  163. {
  164. return mList->getItemIndex( item );
  165. }
  166. return -1;
  167. }
  168. void LLGestureComboList::onItemSelected(const LLSD& data)
  169. {
  170. const std::string name = mList->getSelectedItemLabel();
  171. S32 cur_id = getCurrentIndex();
  172. mLastSelectedIndex = cur_id;
  173. if (cur_id != mList->getItemCount()-1 && cur_id != -1)
  174. {
  175. mButton->setLabel(name);
  176. }
  177. // hiding the list reasserts the old value stored in the text editor/dropdown button
  178. hideList();
  179. // commit does the reverse, asserting the value in the list
  180. onCommit();
  181. }
  182. void LLGestureComboList::sortByName(bool ascending)
  183. {
  184. mList->sortOnce(0, ascending);
  185. }
  186. LLSD LLGestureComboList::getValue() const
  187. {
  188. LLScrollListItem* item = mList->getFirstSelected();
  189. if( item )
  190. {
  191. return item->getValue();
  192. }
  193. else
  194. {
  195. return LLSD();
  196. }
  197. }
  198. void LLGestureComboList::refreshGestures()
  199. {
  200. //store current selection so we can maintain it
  201. LLSD cur_gesture = getValue();
  202. mList->selectFirstItem();
  203. mList->clearRows();
  204. mGestures.clear();
  205. LLGestureManager::item_map_t::const_iterator it;
  206. const LLGestureManager::item_map_t& active_gestures = LLGestureManager::instance().getActiveGestures();
  207. LLSD::Integer idx(0);
  208. for (it = active_gestures.begin(); it != active_gestures.end(); ++it)
  209. {
  210. LLMultiGesture* gesture = (*it).second;
  211. if (gesture)
  212. {
  213. mList->addSimpleElement(gesture->mName, ADD_BOTTOM, LLSD(idx));
  214. mGestures.push_back(gesture);
  215. idx++;
  216. }
  217. }
  218. sortByName();
  219. // store index followed by the last added Gesture and add View All item at bottom
  220. mViewAllItemIndex = idx;
  221. mList->addSimpleElement(LLTrans::getString("ViewAllGestures"), ADD_BOTTOM, LLSD(mViewAllItemIndex));
  222. // Insert label after sorting, at top, with separator below it
  223. mList->addSeparator(ADD_TOP);
  224. mList->addSimpleElement(mLabel, ADD_TOP);
  225. if (cur_gesture.isDefined())
  226. mList->selectByValue(cur_gesture);
  227. }
  228. else
  229. {
  230. mList->selectFirstItem();
  231. }
  232. LLCtrlListInterface* gestures = getListInterface();
  233. LLMultiGesture* gesture = NULL;
  234. if (gestures)
  235. {
  236. S32 index = gestures->getSelectedValue().asInteger();
  237. if(index > 0)
  238. gesture = mGestures.at(index);
  239. }
  240. if(gesture && LLGestureManager::instance().isGesturePlaying(gesture))
  241. {
  242. return;
  243. }
  244. mButton->setLabel(mLabel);
  245. }
  246. void LLGestureComboList::onCommitGesture()
  247. {
  248. LLCtrlListInterface* gestures = getListInterface();
  249. if (gestures)
  250. {
  251. S32 index = gestures->getFirstSelectedIndex();
  252. if (index == 0)
  253. {
  254. return;
  255. }
  256. index = gestures->getSelectedValue().asInteger();
  257. if (mViewAllItemIndex == index)
  258. {
  259. // The same behavior as Ctrl+G. EXT-823
  260. LLFloaterReg::toggleInstance("gestures");
  261. gestures->selectFirstItem();
  262. return;
  263. }
  264. LLMultiGesture* gesture = mGestures.at(index);
  265. if(gesture)
  266. {
  267. LLGestureManager::instance().playGesture(gesture);
  268. if(!gesture->mReplaceText.empty())
  269. {
  270. LLNearbyChatBar::sendChatFromViewer(gesture->mReplaceText, CHAT_TYPE_NORMAL, FALSE);
  271. }
  272. }
  273. }
  274. }
  275. LLGestureComboList::~LLGestureComboList()
  276. {
  277. LLGestureManager::instance().removeObserver(this);
  278. }
  279. LLNearbyChatBar::LLNearbyChatBar() 
  280. : LLPanel()
  281. , mChatBox(NULL)
  282. {
  283. mSpeakerMgr = LLLocalSpeakerMgr::getInstance();
  284. }
  285. //virtual
  286. BOOL LLNearbyChatBar::postBuild()
  287. {
  288. mChatBox = getChild<LLLineEditor>("chat_box");
  289. mChatBox->setCommitCallback(boost::bind(&LLNearbyChatBar::onChatBoxCommit, this));
  290. mChatBox->setKeystrokeCallback(&onChatBoxKeystroke, this);
  291. mChatBox->setFocusLostCallback(boost::bind(&onChatBoxFocusLost, _1, this));
  292. mChatBox->setIgnoreArrowKeys( FALSE ); 
  293. mChatBox->setCommitOnFocusLost( FALSE );
  294. mChatBox->setRevertOnEsc( FALSE );
  295. mChatBox->setIgnoreTab(TRUE);
  296. mChatBox->setPassDelete(TRUE);
  297. mChatBox->setReplaceNewlinesWithSpaces(FALSE);
  298. mChatBox->setEnableLineHistory(TRUE);
  299. mOutputMonitor = getChild<LLOutputMonitorCtrl>("chat_zone_indicator");
  300. mOutputMonitor->setVisible(FALSE);
  301. return TRUE;
  302. }
  303. //static
  304. LLNearbyChatBar* LLNearbyChatBar::getInstance()
  305. {
  306. return LLBottomTray::getInstance() ? LLBottomTray::getInstance()->getNearbyChatBar() : NULL;
  307. }
  308. //static
  309. bool LLNearbyChatBar::instanceExists()
  310. {
  311. return LLBottomTray::instanceExists() && LLBottomTray::getInstance()->getNearbyChatBar() != NULL;
  312. }
  313. void LLNearbyChatBar::draw()
  314. {
  315. displaySpeakingIndicator();
  316. LLPanel::draw();
  317. }
  318. std::string LLNearbyChatBar::getCurrentChat()
  319. {
  320. return mChatBox ? mChatBox->getText() : LLStringUtil::null;
  321. }
  322. // virtual
  323. BOOL LLNearbyChatBar::handleKeyHere( KEY key, MASK mask )
  324. {
  325. BOOL handled = FALSE;
  326. // ALT-RETURN is reserved for windowed/fullscreen toggle
  327. if( KEY_RETURN == key && mask == MASK_CONTROL)
  328. {
  329. // shout
  330. sendChat(CHAT_TYPE_SHOUT);
  331. handled = TRUE;
  332. }
  333. return handled;
  334. }
  335. S32 LLNearbyChatBar::getMinWidth() const
  336. {
  337. static S32 min_width = -1;
  338. if (min_width < 0)
  339. {
  340. const std::string& s = getString("min_width");
  341. min_width = !s.empty() ? atoi(s.c_str()) : 300;
  342. }
  343. return min_width;
  344. }
  345. S32 LLNearbyChatBar::getMaxWidth() const
  346. {
  347. static S32 max_width = -1;
  348. if (max_width < 0)
  349. {
  350. const std::string& s = getString("max_width");
  351. max_width = !s.empty() ? atoi(s.c_str()) : 510;
  352. }
  353. return max_width;
  354. }
  355. BOOL LLNearbyChatBar::matchChatTypeTrigger(const std::string& in_str, std::string* out_str)
  356. {
  357. U32 in_len = in_str.length();
  358. S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers);
  359. for (S32 n = 0; n < cnt; n++)
  360. {
  361. if (in_len > sChatTypeTriggers[n].name.length())
  362. continue;
  363. std::string trigger_trunc = sChatTypeTriggers[n].name;
  364. LLStringUtil::truncate(trigger_trunc, in_len);
  365. if (!LLStringUtil::compareInsensitive(in_str, trigger_trunc))
  366. {
  367. *out_str = sChatTypeTriggers[n].name;
  368. return TRUE;
  369. }
  370. }
  371. return FALSE;
  372. }
  373. void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata)
  374. {
  375. LLNearbyChatBar* self = (LLNearbyChatBar *)userdata;
  376. LLWString raw_text = self->mChatBox->getWText();
  377. // Can't trim the end, because that will cause autocompletion
  378. // to eat trailing spaces that might be part of a gesture.
  379. LLWStringUtil::trimHead(raw_text);
  380. S32 length = raw_text.length();
  381. if( (length > 0) && (raw_text[0] != '/') )  // forward slash is used for escape (eg. emote) sequences
  382. {
  383. gAgent.startTyping();
  384. }
  385. else
  386. {
  387. gAgent.stopTyping();
  388. }
  389. /* Doesn't work -- can't tell the difference between a backspace
  390.    that killed the selection vs. backspace at the end of line.
  391. if (length > 1 
  392. && text[0] == '/'
  393. && key == KEY_BACKSPACE)
  394. {
  395. // the selection will already be deleted, but we need to trim
  396. // off the character before
  397. std::string new_text = raw_text.substr(0, length-1);
  398. self->mInputEditor->setText( new_text );
  399. self->mInputEditor->setCursorToEnd();
  400. length = length - 1;
  401. }
  402. */
  403. KEY key = gKeyboard->currentKey();
  404. // Ignore "special" keys, like backspace, arrows, etc.
  405. if (length > 1 
  406. && raw_text[0] == '/'
  407. && key < KEY_SPECIAL)
  408. {
  409. // we're starting a gesture, attempt to autocomplete
  410. std::string utf8_trigger = wstring_to_utf8str(raw_text);
  411. std::string utf8_out_str(utf8_trigger);
  412. if (LLGestureManager::instance().matchPrefix(utf8_trigger, &utf8_out_str))
  413. {
  414. std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size());
  415. self->mChatBox->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part
  416. S32 outlength = self->mChatBox->getLength(); // in characters
  417. // Select to end of line, starting from the character
  418. // after the last one the user typed.
  419. self->mChatBox->setSelection(length, outlength);
  420. }
  421. else if (matchChatTypeTrigger(utf8_trigger, &utf8_out_str))
  422. {
  423. std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size());
  424. self->mChatBox->setText(utf8_trigger + rest_of_match + " "); // keep original capitalization for user-entered part
  425. self->mChatBox->setCursorToEnd();
  426. }
  427. //llinfos << "GESTUREDEBUG " << trigger 
  428. // << " len " << length
  429. // << " outlen " << out_str.getLength()
  430. // << llendl;
  431. }
  432. }
  433. // static
  434. void LLNearbyChatBar::onChatBoxFocusLost(LLFocusableElement* caller, void* userdata)
  435. {
  436. // stop typing animation
  437. gAgent.stopTyping();
  438. }
  439. EChatType LLNearbyChatBar::processChatTypeTriggers(EChatType type, std::string &str)
  440. {
  441. U32 length = str.length();
  442. S32 cnt = sizeof(sChatTypeTriggers) / sizeof(*sChatTypeTriggers);
  443. for (S32 n = 0; n < cnt; n++)
  444. {
  445. if (length >= sChatTypeTriggers[n].name.length())
  446. {
  447. std::string trigger = str.substr(0, sChatTypeTriggers[n].name.length());
  448. if (!LLStringUtil::compareInsensitive(trigger, sChatTypeTriggers[n].name))
  449. {
  450. U32 trigger_length = sChatTypeTriggers[n].name.length();
  451. // It's to remove space after trigger name
  452. if (length > trigger_length && str[trigger_length] == ' ')
  453. trigger_length++;
  454. str = str.substr(trigger_length, length);
  455. if (CHAT_TYPE_NORMAL == type)
  456. return sChatTypeTriggers[n].type;
  457. else
  458. break;
  459. }
  460. }
  461. }
  462. return type;
  463. }
  464. void LLNearbyChatBar::sendChat( EChatType type )
  465. {
  466. if (mChatBox)
  467. {
  468. LLWString text = mChatBox->getConvertedText();
  469. if (!text.empty())
  470. {
  471. // store sent line in history, duplicates will get filtered
  472. mChatBox->updateHistory();
  473. // Check if this is destined for another channel
  474. S32 channel = 0;
  475. stripChannelNumber(text, &channel);
  476. std::string utf8text = wstring_to_utf8str(text);
  477. // Try to trigger a gesture, if not chat to a script.
  478. std::string utf8_revised_text;
  479. if (0 == channel)
  480. {
  481. // discard returned "found" boolean
  482. LLGestureManager::instance().triggerAndReviseString(utf8text, &utf8_revised_text);
  483. }
  484. else
  485. {
  486. utf8_revised_text = utf8text;
  487. }
  488. utf8_revised_text = utf8str_trim(utf8_revised_text);
  489. type = processChatTypeTriggers(type, utf8_revised_text);
  490. if (!utf8_revised_text.empty())
  491. {
  492. // Chat with animation
  493. sendChatFromViewer(utf8_revised_text, type, TRUE);
  494. }
  495. }
  496. mChatBox->setText(LLStringExplicit(""));
  497. }
  498. gAgent.stopTyping();
  499. // If the user wants to stop chatting on hitting return, lose focus
  500. // and go out of chat mode.
  501. if (gSavedSettings.getBOOL("CloseChatOnReturn"))
  502. {
  503. stopChat();
  504. }
  505. }
  506. void LLNearbyChatBar::onChatBoxCommit()
  507. {
  508. if (mChatBox->getText().length() > 0)
  509. {
  510. sendChat(CHAT_TYPE_NORMAL);
  511. }
  512. gAgent.stopTyping();
  513. }
  514. void LLNearbyChatBar::displaySpeakingIndicator()
  515. {
  516. LLSpeakerMgr::speaker_list_t speaker_list;
  517. LLUUID id;
  518. id.setNull();
  519. mSpeakerMgr->update(TRUE);
  520. mSpeakerMgr->getSpeakerList(&speaker_list, FALSE);
  521. for (LLSpeakerMgr::speaker_list_t::iterator i = speaker_list.begin(); i != speaker_list.end(); ++i)
  522. {
  523. LLPointer<LLSpeaker> s = *i;
  524. if (s->mSpeechVolume > 0 || s->mStatus == LLSpeaker::STATUS_SPEAKING)
  525. {
  526. id = s->mID;
  527. break;
  528. }
  529. }
  530. if (!id.isNull())
  531. {
  532. mOutputMonitor->setVisible(TRUE);
  533. mOutputMonitor->setSpeakerId(id);
  534. }
  535. else
  536. {
  537. mOutputMonitor->setVisible(FALSE);
  538. }
  539. }
  540. void LLNearbyChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
  541. {
  542. sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
  543. }
  544. void LLNearbyChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
  545. {
  546. // Look for "/20 foo" channel chats.
  547. S32 channel = 0;
  548. LLWString out_text = stripChannelNumber(wtext, &channel);
  549. std::string utf8_out_text = wstring_to_utf8str(out_text);
  550. std::string utf8_text = wstring_to_utf8str(wtext);
  551. utf8_text = utf8str_trim(utf8_text);
  552. if (!utf8_text.empty())
  553. {
  554. utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
  555. }
  556. // Don't animate for chats people can't hear (chat to scripts)
  557. if (animate && (channel == 0))
  558. {
  559. if (type == CHAT_TYPE_WHISPER)
  560. {
  561. lldebugs << "You whisper " << utf8_text << llendl;
  562. gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
  563. }
  564. else if (type == CHAT_TYPE_NORMAL)
  565. {
  566. lldebugs << "You say " << utf8_text << llendl;
  567. gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
  568. }
  569. else if (type == CHAT_TYPE_SHOUT)
  570. {
  571. lldebugs << "You shout " << utf8_text << llendl;
  572. gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
  573. }
  574. else
  575. {
  576. llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
  577. return;
  578. }
  579. }
  580. else
  581. {
  582. if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
  583. {
  584. lldebugs << "Channel chat: " << utf8_text << llendl;
  585. }
  586. }
  587. send_chat_from_viewer(utf8_out_text, type, channel);
  588. }
  589. // static 
  590. void LLNearbyChatBar::startChat(const char* line)
  591. {
  592. LLBottomTray *bt = LLBottomTray::getInstance();
  593. if (!bt)
  594. return;
  595. LLNearbyChatBar* cb = bt->getNearbyChatBar();
  596. if (!cb )
  597. return;
  598. bt->setVisible(TRUE);
  599. cb->mChatBox->setFocus(TRUE);
  600. if (line)
  601. {
  602. std::string line_string(line);
  603. cb->mChatBox->setText(line_string);
  604. }
  605. cb->mChatBox->setCursorToEnd();
  606. }
  607. // Exit "chat mode" and do the appropriate focus changes
  608. // static
  609. void LLNearbyChatBar::stopChat()
  610. {
  611. LLBottomTray *bt = LLBottomTray::getInstance();
  612. if (!bt)
  613. return;
  614. LLNearbyChatBar* cb = bt->getNearbyChatBar();
  615. if (!cb)
  616. return;
  617. cb->mChatBox->setFocus(FALSE);
  618.   // stop typing animation
  619.   gAgent.stopTyping();
  620. }
  621. // If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20.
  622. // Otherwise returns input and channel 0.
  623. LLWString LLNearbyChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
  624. {
  625. if (mesg[0] == '/'
  626. && mesg[1] == '/')
  627. {
  628. // This is a "repeat channel send"
  629. *channel = sLastSpecialChatChannel;
  630. return mesg.substr(2, mesg.length() - 2);
  631. }
  632. else if (mesg[0] == '/'
  633.  && mesg[1]
  634.  && LLStringOps::isDigit(mesg[1]))
  635. {
  636. // This a special "/20" speak on a channel
  637. S32 pos = 0;
  638. // Copy the channel number into a string
  639. LLWString channel_string;
  640. llwchar c;
  641. do
  642. {
  643. c = mesg[pos+1];
  644. channel_string.push_back(c);
  645. pos++;
  646. }
  647. while(c && pos < 64 && LLStringOps::isDigit(c));
  648. // Move the pointer forward to the first non-whitespace char
  649. // Check isspace before looping, so we can handle "/33foo"
  650. // as well as "/33 foo"
  651. while(c && iswspace(c))
  652. {
  653. c = mesg[pos+1];
  654. pos++;
  655. }
  656. sLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
  657. *channel = sLastSpecialChatChannel;
  658. return mesg.substr(pos, mesg.length() - pos);
  659. }
  660. else
  661. {
  662. // This is normal chat.
  663. *channel = 0;
  664. return mesg;
  665. }
  666. }
  667. void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel)
  668. {
  669. LLMessageSystem* msg = gMessageSystem;
  670. msg->newMessageFast(_PREHASH_ChatFromViewer);
  671. msg->nextBlockFast(_PREHASH_AgentData);
  672. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  673. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  674. msg->nextBlockFast(_PREHASH_ChatData);
  675. msg->addStringFast(_PREHASH_Message, utf8_out_text);
  676. msg->addU8Fast(_PREHASH_Type, type);
  677. msg->addS32("Channel", channel);
  678. gAgent.sendReliableMessage();
  679. LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);
  680. }
  681. class LLChatHandler : public LLCommandHandler
  682. {
  683. public:
  684. // not allowed from outside the app
  685. LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
  686.     // Your code here
  687. bool handle(const LLSD& tokens, const LLSD& query_map,
  688. LLMediaCtrl* web)
  689. {
  690. if (tokens.size() < 2) return false;
  691. S32 channel = tokens[0].asInteger();
  692. std::string mesg = tokens[1].asString();
  693. send_chat_from_viewer(mesg, CHAT_TYPE_NORMAL, channel);
  694. return true;
  695. }
  696. };
  697. // Creating the object registers with the dispatcher.
  698. LLChatHandler gChatHandler;