PageRenderTime 1332ms CodeModel.GetById 1311ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/llchatbar.cpp

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