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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llchatbar.cpp
  3.  * @brief LLChatBar 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 "llchatbar.h"
  34. #include "imageids.h"
  35. #include "llfontgl.h"
  36. #include "llrect.h"
  37. #include "llerror.h"
  38. #include "llparcel.h"
  39. #include "llstring.h"
  40. #include "message.h"
  41. #include "llfocusmgr.h"
  42. #include "llagent.h"
  43. #include "llbutton.h"
  44. #include "llcombobox.h"
  45. #include "llcommandhandler.h" // secondlife:///app/chat/ support
  46. #include "llviewercontrol.h"
  47. #include "llgesturemgr.h"
  48. #include "llkeyboard.h"
  49. #include "lllineeditor.h"
  50. #include "llstatusbar.h"
  51. #include "lltextbox.h"
  52. #include "lluiconstants.h"
  53. #include "llviewergesture.h" // for triggering gestures
  54. #include "llviewermenu.h" // for deleting object with DEL key
  55. #include "llviewerstats.h"
  56. #include "llviewerwindow.h"
  57. #include "llframetimer.h"
  58. #include "llresmgr.h"
  59. #include "llworld.h"
  60. #include "llinventorymodel.h"
  61. #include "llmultigesture.h"
  62. #include "llui.h"
  63. #include "llviewermenu.h"
  64. #include "lluictrlfactory.h"
  65. #include "llbottomtray.h"
  66. //
  67. // Globals
  68. //
  69. const F32 AGENT_TYPING_TIMEOUT = 5.f; // seconds
  70. LLChatBar *gChatBar = NULL;
  71. class LLChatBarGestureObserver : public LLGestureManagerObserver
  72. {
  73. public:
  74. LLChatBarGestureObserver(LLChatBar* chat_barp) : mChatBar(chat_barp){}
  75. virtual ~LLChatBarGestureObserver() {}
  76. virtual void changed() { mChatBar->refreshGestures(); }
  77. private:
  78. LLChatBar* mChatBar;
  79. };
  80. extern void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel);
  81. //
  82. // Functions
  83. //
  84. LLChatBar::LLChatBar() 
  85. : LLPanel(),
  86. mInputEditor(NULL),
  87. mGestureLabelTimer(),
  88. mLastSpecialChatChannel(0),
  89. mIsBuilt(FALSE),
  90. mGestureCombo(NULL),
  91. mObserver(NULL)
  92. {
  93. setIsChrome(TRUE);
  94. }
  95. LLChatBar::~LLChatBar()
  96. {
  97. LLGestureManager::instance().removeObserver(mObserver);
  98. delete mObserver;
  99. mObserver = NULL;
  100. // LLView destructor cleans up children
  101. }
  102. BOOL LLChatBar::postBuild()
  103. {
  104. getChild<LLUICtrl>("Say")->setCommitCallback(boost::bind(&LLChatBar::onClickSay, this, _1));
  105. // * NOTE: mantipov: getChild with default parameters returns dummy widget.
  106. // Seems this class will be completle removed
  107. // attempt to bind to an existing combo box named gesture
  108. setGestureCombo(findChild<LLComboBox>( "Gesture"));
  109. mInputEditor = getChild<LLLineEditor>("Chat Editor");
  110. mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke, this);
  111. mInputEditor->setFocusLostCallback(boost::bind(&LLChatBar::onInputEditorFocusLost));
  112. mInputEditor->setFocusReceivedCallback(boost::bind(&LLChatBar::onInputEditorGainFocus));
  113. mInputEditor->setCommitOnFocusLost( FALSE );
  114. mInputEditor->setRevertOnEsc( FALSE );
  115. mInputEditor->setIgnoreTab(TRUE);
  116. mInputEditor->setPassDelete(TRUE);
  117. mInputEditor->setReplaceNewlinesWithSpaces(FALSE);
  118. mInputEditor->setMaxTextLength(1023);
  119. mInputEditor->setEnableLineHistory(TRUE);
  120. mIsBuilt = TRUE;
  121. return TRUE;
  122. }
  123. //-----------------------------------------------------------------------
  124. // Overrides
  125. //-----------------------------------------------------------------------
  126. // virtual
  127. BOOL LLChatBar::handleKeyHere( KEY key, MASK mask )
  128. {
  129. BOOL handled = FALSE;
  130. // ALT-RETURN is reserved for windowed/fullscreen toggle
  131. if( KEY_RETURN == key )
  132. {
  133. if (mask == MASK_CONTROL)
  134. {
  135. // shout
  136. sendChat(CHAT_TYPE_SHOUT);
  137. handled = TRUE;
  138. }
  139. else if (mask == MASK_NONE)
  140. {
  141. // say
  142. sendChat( CHAT_TYPE_NORMAL );
  143. handled = TRUE;
  144. }
  145. }
  146. // only do this in main chatbar
  147. else if ( KEY_ESCAPE == key && gChatBar == this)
  148. {
  149. stopChat();
  150. handled = TRUE;
  151. }
  152. return handled;
  153. }
  154. void LLChatBar::refresh()
  155. {
  156. // HACK: Leave the name of the gesture in place for a few seconds.
  157. const F32 SHOW_GESTURE_NAME_TIME = 2.f;
  158. if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
  159. {
  160. LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
  161. if (gestures) gestures->selectFirstItem();
  162. mGestureLabelTimer.stop();
  163. }
  164. if ((gAgent.getTypingTime() > AGENT_TYPING_TIMEOUT) && (gAgent.getRenderState() & AGENT_STATE_TYPING))
  165. {
  166. gAgent.stopTyping();
  167. }
  168. childSetEnabled("Say", mInputEditor->getText().size() > 0);
  169. }
  170. void LLChatBar::refreshGestures()
  171. {
  172. if (mGestureCombo)
  173. {
  174. //store current selection so we can maintain it
  175. std::string cur_gesture = mGestureCombo->getValue().asString();
  176. mGestureCombo->selectFirstItem();
  177. std::string label = mGestureCombo->getValue().asString();;
  178. // clear
  179. mGestureCombo->clearRows();
  180. // collect list of unique gestures
  181. std::map <std::string, BOOL> unique;
  182. LLGestureManager::item_map_t::const_iterator it;
  183. const LLGestureManager::item_map_t& active_gestures = LLGestureManager::instance().getActiveGestures();
  184. for (it = active_gestures.begin(); it != active_gestures.end(); ++it)
  185. {
  186. LLMultiGesture* gesture = (*it).second;
  187. if (gesture)
  188. {
  189. if (!gesture->mTrigger.empty())
  190. {
  191. unique[gesture->mTrigger] = TRUE;
  192. }
  193. }
  194. }
  195. // add unique gestures
  196. std::map <std::string, BOOL>::iterator it2;
  197. for (it2 = unique.begin(); it2 != unique.end(); ++it2)
  198. {
  199. mGestureCombo->addSimpleElement((*it2).first);
  200. }
  201. mGestureCombo->sortByName();
  202. // Insert label after sorting, at top, with separator below it
  203. mGestureCombo->addSeparator(ADD_TOP);
  204. mGestureCombo->addSimpleElement(getString("gesture_label"), ADD_TOP);
  205. if (!cur_gesture.empty())
  206. mGestureCombo->selectByValue(LLSD(cur_gesture));
  207. }
  208. else
  209. {
  210. mGestureCombo->selectFirstItem();
  211. }
  212. }
  213. }
  214. // Move the cursor to the correct input field.
  215. void LLChatBar::setKeyboardFocus(BOOL focus)
  216. {
  217. if (focus)
  218. {
  219. if (mInputEditor)
  220. {
  221. mInputEditor->setFocus(TRUE);
  222. mInputEditor->selectAll();
  223. }
  224. }
  225. else if (gFocusMgr.childHasKeyboardFocus(this))
  226. {
  227. if (mInputEditor)
  228. {
  229. mInputEditor->deselect();
  230. }
  231. setFocus(FALSE);
  232. }
  233. }
  234. // Ignore arrow keys in chat bar
  235. void LLChatBar::setIgnoreArrowKeys(BOOL b)
  236. {
  237. if (mInputEditor)
  238. {
  239. mInputEditor->setIgnoreArrowKeys(b);
  240. }
  241. }
  242. BOOL LLChatBar::inputEditorHasFocus()
  243. {
  244. return mInputEditor && mInputEditor->hasFocus();
  245. }
  246. std::string LLChatBar::getCurrentChat()
  247. {
  248. return mInputEditor ? mInputEditor->getText() : LLStringUtil::null;
  249. }
  250. void LLChatBar::setGestureCombo(LLComboBox* combo)
  251. {
  252. mGestureCombo = combo;
  253. if (mGestureCombo)
  254. {
  255. mGestureCombo->setCommitCallback(boost::bind(&LLChatBar::onCommitGesture, this, _1));
  256. // now register observer since we have a place to put the results
  257. mObserver = new LLChatBarGestureObserver(this);
  258. LLGestureManager::instance().addObserver(mObserver);
  259. // refresh list from current active gestures
  260. refreshGestures();
  261. }
  262. }
  263. //-----------------------------------------------------------------------
  264. // Internal functions
  265. //-----------------------------------------------------------------------
  266. // If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20.
  267. // Otherwise returns input and channel 0.
  268. LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
  269. {
  270. if (mesg[0] == '/'
  271. && mesg[1] == '/')
  272. {
  273. // This is a "repeat channel send"
  274. *channel = mLastSpecialChatChannel;
  275. return mesg.substr(2, mesg.length() - 2);
  276. }
  277. else if (mesg[0] == '/'
  278.  && mesg[1]
  279.  && LLStringOps::isDigit(mesg[1]))
  280. {
  281. // This a special "/20" speak on a channel
  282. S32 pos = 0;
  283. // Copy the channel number into a string
  284. LLWString channel_string;
  285. llwchar c;
  286. do
  287. {
  288. c = mesg[pos+1];
  289. channel_string.push_back(c);
  290. pos++;
  291. }
  292. while(c && pos < 64 && LLStringOps::isDigit(c));
  293. // Move the pointer forward to the first non-whitespace char
  294. // Check isspace before looping, so we can handle "/33foo"
  295. // as well as "/33 foo"
  296. while(c && iswspace(c))
  297. {
  298. c = mesg[pos+1];
  299. pos++;
  300. }
  301. mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
  302. *channel = mLastSpecialChatChannel;
  303. return mesg.substr(pos, mesg.length() - pos);
  304. }
  305. else
  306. {
  307. // This is normal chat.
  308. *channel = 0;
  309. return mesg;
  310. }
  311. }
  312. void LLChatBar::sendChat( EChatType type )
  313. {
  314. if (mInputEditor)
  315. {
  316. LLWString text = mInputEditor->getConvertedText();
  317. if (!text.empty())
  318. {
  319. // store sent line in history, duplicates will get filtered
  320. if (mInputEditor) mInputEditor->updateHistory();
  321. // Check if this is destined for another channel
  322. S32 channel = 0;
  323. stripChannelNumber(text, &channel);
  324. std::string utf8text = wstring_to_utf8str(text);
  325. // Try to trigger a gesture, if not chat to a script.
  326. std::string utf8_revised_text;
  327. if (0 == channel)
  328. {
  329. // discard returned "found" boolean
  330. LLGestureManager::instance().triggerAndReviseString(utf8text, &utf8_revised_text);
  331. }
  332. else
  333. {
  334. utf8_revised_text = utf8text;
  335. }
  336. utf8_revised_text = utf8str_trim(utf8_revised_text);
  337. if (!utf8_revised_text.empty())
  338. {
  339. // Chat with animation
  340. sendChatFromViewer(utf8_revised_text, type, TRUE);
  341. }
  342. }
  343. }
  344. childSetValue("Chat Editor", LLStringUtil::null);
  345. gAgent.stopTyping();
  346. // If the user wants to stop chatting on hitting return, lose focus
  347. // and go out of chat mode.
  348. if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn"))
  349. {
  350. stopChat();
  351. }
  352. }
  353. //-----------------------------------------------------------------------
  354. // Static functions
  355. //-----------------------------------------------------------------------
  356. // static 
  357. void LLChatBar::startChat(const char* line)
  358. {
  359. //TODO* remove DUMMY chat
  360. //if(gBottomTray && gBottomTray->getChatBox())
  361. //{
  362. // gBottomTray->setVisible(TRUE);
  363. // gBottomTray->getChatBox()->setFocus(TRUE);
  364. //}
  365. // *TODO Vadim: Why was this code commented out?
  366. //  gChatBar->setVisible(TRUE);
  367. //  gChatBar->setKeyboardFocus(TRUE);
  368. //  gSavedSettings.setBOOL("ChatVisible", TRUE);
  369. // 
  370. //  if (line && gChatBar->mInputEditor)
  371. //  {
  372. //  std::string line_string(line);
  373. //  gChatBar->mInputEditor->setText(line_string);
  374. //  }
  375. //  // always move cursor to end so users don't obliterate chat when accidentally hitting WASD
  376. //  gChatBar->mInputEditor->setCursorToEnd();
  377. }
  378. // Exit "chat mode" and do the appropriate focus changes
  379. // static
  380. void LLChatBar::stopChat()
  381. {
  382. //TODO* remove DUMMY chat
  383. //if(gBottomTray && gBottomTray->getChatBox())
  384. ///{
  385. // gBottomTray->getChatBox()->setFocus(FALSE);
  386. //}
  387. // *TODO Vadim: Why was this code commented out?
  388. //  // In simple UI mode, we never release focus from the chat bar
  389. //  gChatBar->setKeyboardFocus(FALSE);
  390. // 
  391. //  // If we typed a movement key and pressed return during the
  392. //  // same frame, the keyboard handlers will see the key as having
  393. //  // gone down this frame and try to move the avatar.
  394. //  gKeyboard->resetKeys();
  395. //  gKeyboard->resetMaskKeys();
  396. // 
  397. //  // stop typing animation
  398. //  gAgent.stopTyping();
  399. // 
  400. //  // hide chat bar so it doesn't grab focus back
  401. //  gChatBar->setVisible(FALSE);
  402. //  gSavedSettings.setBOOL("ChatVisible", FALSE);
  403. }
  404. // static
  405. void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata )
  406. {
  407. LLChatBar* self = (LLChatBar *)userdata;
  408. LLWString raw_text;
  409. if (self->mInputEditor) raw_text = self->mInputEditor->getWText();
  410. // Can't trim the end, because that will cause autocompletion
  411. // to eat trailing spaces that might be part of a gesture.
  412. LLWStringUtil::trimHead(raw_text);
  413. S32 length = raw_text.length();
  414. if( (length > 0) && (raw_text[0] != '/') )  // forward slash is used for escape (eg. emote) sequences
  415. {
  416. gAgent.startTyping();
  417. }
  418. else
  419. {
  420. gAgent.stopTyping();
  421. }
  422. /* Doesn't work -- can't tell the difference between a backspace
  423.    that killed the selection vs. backspace at the end of line.
  424. if (length > 1 
  425. && text[0] == '/'
  426. && key == KEY_BACKSPACE)
  427. {
  428. // the selection will already be deleted, but we need to trim
  429. // off the character before
  430. std::string new_text = raw_text.substr(0, length-1);
  431. self->mInputEditor->setText( new_text );
  432. self->mInputEditor->setCursorToEnd();
  433. length = length - 1;
  434. }
  435. */
  436. KEY key = gKeyboard->currentKey();
  437. // Ignore "special" keys, like backspace, arrows, etc.
  438. if (length > 1 
  439. && raw_text[0] == '/'
  440. && key < KEY_SPECIAL)
  441. {
  442. // we're starting a gesture, attempt to autocomplete
  443. std::string utf8_trigger = wstring_to_utf8str(raw_text);
  444. std::string utf8_out_str(utf8_trigger);
  445. if (LLGestureManager::instance().matchPrefix(utf8_trigger, &utf8_out_str))
  446. {
  447. if (self->mInputEditor)
  448. {
  449. std::string rest_of_match = utf8_out_str.substr(utf8_trigger.size());
  450. self->mInputEditor->setText(utf8_trigger + rest_of_match); // keep original capitalization for user-entered part
  451. S32 outlength = self->mInputEditor->getLength(); // in characters
  452. // Select to end of line, starting from the character
  453. // after the last one the user typed.
  454. self->mInputEditor->setSelection(length, outlength);
  455. }
  456. }
  457. //llinfos << "GESTUREDEBUG " << trigger 
  458. // << " len " << length
  459. // << " outlen " << out_str.getLength()
  460. // << llendl;
  461. }
  462. }
  463. // static
  464. void LLChatBar::onInputEditorFocusLost()
  465. {
  466. // stop typing animation
  467. gAgent.stopTyping();
  468. }
  469. // static
  470. void LLChatBar::onInputEditorGainFocus()
  471. {
  472. //LLFloaterChat::setHistoryCursorAndScrollToEnd();
  473. }
  474. void LLChatBar::onClickSay( LLUICtrl* ctrl )
  475. {
  476. std::string cmd = ctrl->getValue().asString();
  477. e_chat_type chat_type = CHAT_TYPE_NORMAL;
  478. if (cmd == "shout")
  479. {
  480. chat_type = CHAT_TYPE_SHOUT;
  481. }
  482. else if (cmd == "whisper")
  483. {
  484. chat_type = CHAT_TYPE_WHISPER;
  485. }
  486. sendChat(chat_type);
  487. }
  488. void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
  489. {
  490. sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
  491. }
  492. void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
  493. {
  494. // Look for "/20 foo" channel chats.
  495. S32 channel = 0;
  496. LLWString out_text = stripChannelNumber(wtext, &channel);
  497. std::string utf8_out_text = wstring_to_utf8str(out_text);
  498. std::string utf8_text = wstring_to_utf8str(wtext);
  499. utf8_text = utf8str_trim(utf8_text);
  500. if (!utf8_text.empty())
  501. {
  502. utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
  503. }
  504. // Don't animate for chats people can't hear (chat to scripts)
  505. if (animate && (channel == 0))
  506. {
  507. if (type == CHAT_TYPE_WHISPER)
  508. {
  509. lldebugs << "You whisper " << utf8_text << llendl;
  510. gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
  511. }
  512. else if (type == CHAT_TYPE_NORMAL)
  513. {
  514. lldebugs << "You say " << utf8_text << llendl;
  515. gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
  516. }
  517. else if (type == CHAT_TYPE_SHOUT)
  518. {
  519. lldebugs << "You shout " << utf8_text << llendl;
  520. gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
  521. }
  522. else
  523. {
  524. llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
  525. return;
  526. }
  527. }
  528. else
  529. {
  530. if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
  531. {
  532. lldebugs << "Channel chat: " << utf8_text << llendl;
  533. }
  534. }
  535. send_chat_from_viewer(utf8_out_text, type, channel);
  536. }
  537. /*
  538. void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel)
  539. {
  540. LLMessageSystem* msg = gMessageSystem;
  541. msg->newMessageFast(_PREHASH_ChatFromViewer);
  542. msg->nextBlockFast(_PREHASH_AgentData);
  543. msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
  544. msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
  545. msg->nextBlockFast(_PREHASH_ChatData);
  546. msg->addStringFast(_PREHASH_Message, utf8_out_text);
  547. msg->addU8Fast(_PREHASH_Type, type);
  548. msg->addS32("Channel", channel);
  549. gAgent.sendReliableMessage();
  550. LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);
  551. }
  552. */
  553. void LLChatBar::onCommitGesture(LLUICtrl* ctrl)
  554. {
  555. LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
  556. if (gestures)
  557. {
  558. S32 index = gestures->getFirstSelectedIndex();
  559. if (index == 0)
  560. {
  561. return;
  562. }
  563. const std::string& trigger = gestures->getSelectedValue().asString();
  564. // pretend the user chatted the trigger string, to invoke
  565. // substitution and logging.
  566. std::string text(trigger);
  567. std::string revised_text;
  568. LLGestureManager::instance().triggerAndReviseString(text, &revised_text);
  569. revised_text = utf8str_trim(revised_text);
  570. if (!revised_text.empty())
  571. {
  572. // Don't play nodding animation
  573. sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE);
  574. }
  575. }
  576. mGestureLabelTimer.start();
  577. if (mGestureCombo != NULL)
  578. {
  579. // free focus back to chat bar
  580. mGestureCombo->setFocus(FALSE);
  581. }
  582. }
  583. class LLChatHandler : public LLCommandHandler
  584. {
  585. public:
  586. // not allowed from outside the app
  587. LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { }
  588.     // Your code here
  589. bool handle(const LLSD& tokens, const LLSD& query_map,
  590. LLMediaCtrl* web)
  591. {
  592. if (tokens.size() < 2) return false;
  593. S32 channel = tokens[0].asInteger();
  594. std::string mesg = tokens[1].asString();
  595. send_chat_from_viewer(mesg, CHAT_TYPE_NORMAL, channel);
  596. return true;
  597. }
  598. };
  599. // Creating the object registers with the dispatcher.
  600. //LLChatHandler gChatHandler;