00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "llchatbar.h"
00035
00036 #include "imageids.h"
00037 #include "llfontgl.h"
00038 #include "llrect.h"
00039 #include "llerror.h"
00040 #include "llparcel.h"
00041 #include "llstring.h"
00042 #include "message.h"
00043 #include "llfocusmgr.h"
00044
00045 #include "llagent.h"
00046 #include "llbutton.h"
00047 #include "llcombobox.h"
00048 #include "llcommandhandler.h"
00049 #include "llviewercontrol.h"
00050 #include "llfloaterchat.h"
00051 #include "llgesturemgr.h"
00052 #include "llkeyboard.h"
00053 #include "lllineeditor.h"
00054 #include "llstatusbar.h"
00055 #include "lltextbox.h"
00056 #include "lluiconstants.h"
00057 #include "llviewergesture.h"
00058 #include "llviewermenu.h"
00059 #include "llviewerstats.h"
00060 #include "llviewerwindow.h"
00061 #include "llframetimer.h"
00062 #include "llresmgr.h"
00063 #include "llworld.h"
00064 #include "llinventorymodel.h"
00065 #include "llmultigesture.h"
00066 #include "llui.h"
00067 #include "llviewermenu.h"
00068 #include "lluictrlfactory.h"
00069
00070
00071
00072
00073
00074 const F32 AGENT_TYPING_TIMEOUT = 5.f;
00075
00076 LLChatBar *gChatBar = NULL;
00077
00078
00079 void toggleChatHistory(void* user_data);
00080 void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel);
00081
00082
00083 class LLChatBarGestureObserver : public LLGestureManagerObserver
00084 {
00085 public:
00086 LLChatBarGestureObserver(LLChatBar* chat_barp) : mChatBar(chat_barp){}
00087 virtual ~LLChatBarGestureObserver() {}
00088 virtual void changed() { mChatBar->refreshGestures(); }
00089 private:
00090 LLChatBar* mChatBar;
00091 };
00092
00093
00094
00095
00096
00097
00098 LLChatBar::LLChatBar()
00099 : LLPanel("", LLRect(), BORDER_NO),
00100 mInputEditor(NULL),
00101 mGestureLabelTimer(),
00102 mLastSpecialChatChannel(0),
00103 mIsBuilt(FALSE),
00104 mGestureCombo(NULL),
00105 mObserver(NULL)
00106 {
00107 setIsChrome(TRUE);
00108
00109 #if !LL_RELEASE_FOR_DOWNLOAD
00110 childDisplayNotFound();
00111 #endif
00112 }
00113
00114
00115 LLChatBar::~LLChatBar()
00116 {
00117 delete mObserver;
00118 mObserver = NULL;
00119
00120 }
00121
00122 BOOL LLChatBar::postBuild()
00123 {
00124 childSetAction("History", toggleChatHistory, this);
00125 childSetCommitCallback("Say", onClickSay, this);
00126
00127
00128 setGestureCombo(getChild<LLComboBox>( "Gesture"));
00129
00130 LLButton * sayp = getChild<LLButton>("Say");
00131 if(sayp)
00132 {
00133 setDefaultBtn(sayp);
00134 }
00135
00136 mInputEditor = getChild<LLLineEditor>("Chat Editor");
00137 if (mInputEditor)
00138 {
00139 mInputEditor->setCallbackUserData(this);
00140 mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
00141 mInputEditor->setFocusLostCallback(&onInputEditorFocusLost, this);
00142 mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus, this );
00143 mInputEditor->setCommitOnFocusLost( FALSE );
00144 mInputEditor->setRevertOnEsc( FALSE );
00145 mInputEditor->setIgnoreTab(TRUE);
00146 mInputEditor->setPassDelete(TRUE);
00147 mInputEditor->setReplaceNewlinesWithSpaces(FALSE);
00148
00149 mInputEditor->setMaxTextLength(1023);
00150 mInputEditor->setEnableLineHistory(TRUE);
00151 }
00152
00153 mIsBuilt = TRUE;
00154
00155 return TRUE;
00156 }
00157
00158
00159
00160
00161
00162
00163 BOOL LLChatBar::handleKeyHere( KEY key, MASK mask )
00164 {
00165 BOOL handled = FALSE;
00166
00167
00168 if( KEY_RETURN == key )
00169 {
00170 if (mask == MASK_CONTROL)
00171 {
00172
00173 sendChat(CHAT_TYPE_SHOUT);
00174 handled = TRUE;
00175 }
00176 else if (mask == MASK_NONE)
00177 {
00178
00179 sendChat( CHAT_TYPE_NORMAL );
00180 handled = TRUE;
00181 }
00182 }
00183
00184 else if ( KEY_ESCAPE == key && gChatBar == this)
00185 {
00186 stopChat();
00187
00188 handled = TRUE;
00189 }
00190
00191 return handled;
00192 }
00193
00194 void LLChatBar::refresh()
00195 {
00196
00197 const F32 SHOW_GESTURE_NAME_TIME = 2.f;
00198 if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
00199 {
00200 LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
00201 if (gestures) gestures->selectFirstItem();
00202 mGestureLabelTimer.stop();
00203 }
00204
00205 if ((gAgent.getTypingTime() > AGENT_TYPING_TIMEOUT) && (gAgent.getRenderState() & AGENT_STATE_TYPING))
00206 {
00207 gAgent.stopTyping();
00208 }
00209
00210 childSetValue("History", LLFloaterChat::instanceVisible(LLSD()));
00211
00212 childSetEnabled("Say", mInputEditor->getText().size() > 0);
00213 childSetEnabled("Shout", mInputEditor->getText().size() > 0);
00214
00215 }
00216
00217 void LLChatBar::refreshGestures()
00218 {
00219 LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
00220 if (mGestureCombo && gestures)
00221 {
00222
00223 LLString cur_gesture = mGestureCombo->getValue().asString();
00224 gestures->selectFirstItem();
00225 LLString label = mGestureCombo->getValue().asString();;
00226
00227 gestures->clearRows();
00228
00229
00230 std::map <std::string, BOOL> unique;
00231 LLGestureManager::item_map_t::iterator it;
00232 for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
00233 {
00234 LLMultiGesture* gesture = (*it).second;
00235 if (gesture)
00236 {
00237 if (!gesture->mTrigger.empty())
00238 {
00239 unique[gesture->mTrigger] = TRUE;
00240 }
00241 }
00242 }
00243
00244
00245 std::map <std::string, BOOL>::iterator it2;
00246 for (it2 = unique.begin(); it2 != unique.end(); ++it2)
00247 {
00248 gestures->addSimpleElement((*it2).first);
00249 }
00250
00251 gestures->sortByColumn(0, TRUE);
00252
00253 gestures->addSimpleElement(label, ADD_TOP);
00254
00255 if (!cur_gesture.empty())
00256 {
00257 gestures->selectByValue(LLSD(cur_gesture));
00258 }
00259 else
00260 {
00261 gestures->selectFirstItem();
00262 }
00263 }
00264 }
00265
00266
00267 void LLChatBar::setKeyboardFocus(BOOL focus)
00268 {
00269 if (focus)
00270 {
00271 if (mInputEditor)
00272 {
00273 mInputEditor->setFocus(TRUE);
00274 mInputEditor->selectAll();
00275 }
00276 }
00277 else if (gFocusMgr.childHasKeyboardFocus(this))
00278 {
00279 if (mInputEditor)
00280 {
00281 mInputEditor->deselect();
00282 }
00283 setFocus(FALSE);
00284 }
00285 }
00286
00287
00288
00289 void LLChatBar::setIgnoreArrowKeys(BOOL b)
00290 {
00291 if (mInputEditor)
00292 {
00293 mInputEditor->setIgnoreArrowKeys(b);
00294 }
00295 }
00296
00297 BOOL LLChatBar::inputEditorHasFocus()
00298 {
00299 return mInputEditor && mInputEditor->hasFocus();
00300 }
00301
00302 LLString LLChatBar::getCurrentChat()
00303 {
00304 return mInputEditor ? mInputEditor->getText() : LLString::null;
00305 }
00306
00307 void LLChatBar::setGestureCombo(LLComboBox* combo)
00308 {
00309 mGestureCombo = combo;
00310 if (mGestureCombo)
00311 {
00312 mGestureCombo->setCommitCallback(onCommitGesture);
00313 mGestureCombo->setCallbackUserData(this);
00314
00315
00316 mObserver = new LLChatBarGestureObserver(this);
00317 gGestureManager.addObserver(mObserver);
00318
00319
00320 refreshGestures();
00321 }
00322 }
00323
00324
00325
00326
00327
00328
00329
00330 LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
00331 {
00332 if (mesg[0] == '/'
00333 && mesg[1] == '/')
00334 {
00335
00336 *channel = mLastSpecialChatChannel;
00337 return mesg.substr(2, mesg.length() - 2);
00338 }
00339 else if (mesg[0] == '/'
00340 && mesg[1]
00341 && LLStringOps::isDigit(mesg[1]))
00342 {
00343
00344 S32 pos = 0;
00345
00346
00347 llwchar channel_string[64];
00348 llwchar c;
00349 do
00350 {
00351 c = mesg[pos+1];
00352 channel_string[pos] = c;
00353 pos++;
00354 }
00355 while(c && pos < 64 && LLStringOps::isDigit(c));
00356
00357
00358
00359
00360 while(c && iswspace(c))
00361 {
00362 c = mesg[pos+1];
00363 pos++;
00364 }
00365
00366
00367 mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
00368 *channel = mLastSpecialChatChannel;
00369 return mesg.substr(pos, mesg.length() - pos);
00370 }
00371 else
00372 {
00373
00374 *channel = 0;
00375 return mesg;
00376 }
00377 }
00378
00379
00380 void LLChatBar::sendChat( EChatType type )
00381 {
00382 LLWString text;
00383 if (mInputEditor) text = mInputEditor->getWText();
00384 LLWString::trim(text);
00385
00386 if (!text.empty())
00387 {
00388
00389 if (mInputEditor) mInputEditor->updateHistory();
00390
00391 S32 channel = 0;
00392 stripChannelNumber(text, &channel);
00393
00394 std::string utf8text = wstring_to_utf8str(text);
00395
00396 std::string utf8_revised_text;
00397 if (0 == channel)
00398 {
00399
00400 gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text);
00401 }
00402 else
00403 {
00404 utf8_revised_text = utf8text;
00405 }
00406
00407 utf8_revised_text = utf8str_trim(utf8_revised_text);
00408
00409 if (!utf8_revised_text.empty())
00410 {
00411
00412 sendChatFromViewer(utf8_revised_text, type, TRUE);
00413 }
00414 }
00415 childSetValue("Chat Editor", LLString::null);
00416
00417 gAgent.stopTyping();
00418
00419
00420
00421 if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn"))
00422 {
00423 stopChat();
00424 }
00425 }
00426
00427
00428
00429
00430
00431
00432
00433 void LLChatBar::startChat(const char* line)
00434 {
00435 gChatBar->setVisible(TRUE);
00436 gChatBar->setKeyboardFocus(TRUE);
00437 gSavedSettings.setBOOL("ChatVisible", TRUE);
00438
00439 if (line && gChatBar->mInputEditor)
00440 {
00441 std::string line_string(line);
00442 gChatBar->mInputEditor->setText(line_string);
00443 }
00444
00445 gChatBar->mInputEditor->setCursorToEnd();
00446 }
00447
00448
00449
00450
00451 void LLChatBar::stopChat()
00452 {
00453
00454 gChatBar->setKeyboardFocus(FALSE);
00455
00456
00457
00458
00459 gKeyboard->resetKeys();
00460 gKeyboard->resetMaskKeys();
00461
00462
00463 gAgent.stopTyping();
00464
00465
00466 gChatBar->setVisible(FALSE);
00467 gSavedSettings.setBOOL("ChatVisible", FALSE);
00468 }
00469
00470
00471 void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata )
00472 {
00473 LLChatBar* self = (LLChatBar *)userdata;
00474
00475 LLWString raw_text;
00476 if (self->mInputEditor) raw_text = self->mInputEditor->getWText();
00477
00478
00479
00480 LLWString::trimHead(raw_text);
00481
00482 S32 length = raw_text.length();
00483
00484 if( (length > 0) && (raw_text[0] != '/') )
00485 {
00486 gAgent.startTyping();
00487 }
00488 else
00489 {
00490 gAgent.stopTyping();
00491 }
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508 KEY key = gKeyboard->currentKey();
00509
00510
00511 if (length > 1
00512 && raw_text[0] == '/'
00513 && key < KEY_SPECIAL)
00514 {
00515
00516
00517 std::string utf8_trigger = wstring_to_utf8str(raw_text);
00518 std::string utf8_out_str(utf8_trigger);
00519
00520 if (gGestureManager.matchPrefix(utf8_trigger, &utf8_out_str))
00521 {
00522 if (self->mInputEditor)
00523 {
00524 self->mInputEditor->setText(utf8_out_str);
00525 S32 outlength = self->mInputEditor->getLength();
00526
00527
00528
00529 self->mInputEditor->setSelection(length, outlength);
00530 }
00531 }
00532
00533
00534
00535
00536
00537 }
00538 }
00539
00540
00541 void LLChatBar::onInputEditorFocusLost( LLFocusableElement* caller, void* userdata)
00542 {
00543
00544 gAgent.stopTyping();
00545 }
00546
00547
00548 void LLChatBar::onInputEditorGainFocus( LLFocusableElement* caller, void* userdata )
00549 {
00550 LLFloaterChat::setHistoryCursorAndScrollToEnd();
00551 }
00552
00553
00554 void LLChatBar::onClickSay( LLUICtrl* ctrl, void* userdata )
00555 {
00556 LLChatBar* self = (LLChatBar*) userdata;
00557 if (ctrl->getValue().asString() == "shout")
00558 {
00559 self->sendChat( CHAT_TYPE_SHOUT );
00560 }
00561 else
00562 {
00563 self->sendChat( CHAT_TYPE_NORMAL );
00564 }
00565 }
00566
00567 void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
00568 {
00569 sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
00570 }
00571
00572 void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
00573 {
00574
00575 S32 channel = 0;
00576 LLWString out_text = stripChannelNumber(wtext, &channel);
00577 std::string utf8_out_text = wstring_to_utf8str(out_text);
00578 std::string utf8_text = wstring_to_utf8str(wtext);
00579
00580 utf8_text = utf8str_trim(utf8_text);
00581 if (!utf8_text.empty())
00582 {
00583 utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
00584 }
00585
00586
00587 if (animate && (channel == 0))
00588 {
00589 if (type == CHAT_TYPE_WHISPER)
00590 {
00591 lldebugs << "You whisper " << utf8_text << llendl;
00592 gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
00593 }
00594 else if (type == CHAT_TYPE_NORMAL)
00595 {
00596 lldebugs << "You say " << utf8_text << llendl;
00597 gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
00598 }
00599 else if (type == CHAT_TYPE_SHOUT)
00600 {
00601 lldebugs << "You shout " << utf8_text << llendl;
00602 gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
00603 }
00604 else
00605 {
00606 llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
00607 return;
00608 }
00609 }
00610 else
00611 {
00612 if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
00613 {
00614 lldebugs << "Channel chat: " << utf8_text << llendl;
00615 }
00616 }
00617
00618 send_chat_from_viewer(utf8_out_text, type, channel);
00619 }
00620
00621 void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel)
00622 {
00623 LLMessageSystem* msg = gMessageSystem;
00624 msg->newMessageFast(_PREHASH_ChatFromViewer);
00625 msg->nextBlockFast(_PREHASH_AgentData);
00626 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00627 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00628 msg->nextBlockFast(_PREHASH_ChatData);
00629 msg->addStringFast(_PREHASH_Message, utf8_out_text);
00630 msg->addU8Fast(_PREHASH_Type, type);
00631 msg->addS32("Channel", channel);
00632
00633 gAgent.sendReliableMessage();
00634
00635 LLViewerStats::getInstance()->incStat(LLViewerStats::ST_CHAT_COUNT);
00636 }
00637
00638
00639
00640 void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
00641 {
00642 LLChatBar* self = (LLChatBar*)data;
00643 LLCtrlListInterface* gestures = self->mGestureCombo ? self->mGestureCombo->getListInterface() : NULL;
00644 if (gestures)
00645 {
00646 S32 index = gestures->getFirstSelectedIndex();
00647 if (index == 0)
00648 {
00649 return;
00650 }
00651 const std::string& trigger = gestures->getSelectedValue().asString();
00652
00653
00654
00655 std::string text(trigger);
00656 std::string revised_text;
00657 gGestureManager.triggerAndReviseString(text, &revised_text);
00658
00659 revised_text = utf8str_trim(revised_text);
00660 if (!revised_text.empty())
00661 {
00662
00663 self->sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE);
00664 }
00665 }
00666 self->mGestureLabelTimer.start();
00667 if (self->mGestureCombo != NULL)
00668 {
00669
00670 self->mGestureCombo->setFocus(FALSE);
00671 }
00672 }
00673
00674 void toggleChatHistory(void* user_data)
00675 {
00676 LLFloaterChat::toggleInstance(LLSD());
00677 }
00678
00679
00680 class LLChatHandler : public LLCommandHandler
00681 {
00682 public:
00683
00684 LLChatHandler() : LLCommandHandler("chat", false) { }
00685
00686
00687 bool handle(const LLSD& tokens, const LLSD& queryMap)
00688 {
00689 if (tokens.size() < 2) return false;
00690 S32 channel = tokens[0].asInteger();
00691 std::string mesg = tokens[1].asString();
00692 send_chat_from_viewer(mesg, CHAT_TYPE_NORMAL, channel);
00693 return true;
00694 }
00695 };
00696
00697
00698 LLChatHandler gChatHandler;