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 "llviewercontrol.h"
00049 #include "llfloaterchat.h"
00050 #include "llgesturemgr.h"
00051 #include "llkeyboard.h"
00052 #include "lllineeditor.h"
00053 #include "llstatusbar.h"
00054 #include "lltextbox.h"
00055 #include "lluiconstants.h"
00056 #include "llviewergesture.h"
00057 #include "llviewermenu.h"
00058 #include "llviewerstats.h"
00059 #include "llviewerwindow.h"
00060 #include "llframetimer.h"
00061 #include "llresmgr.h"
00062 #include "llworld.h"
00063 #include "llinventorymodel.h"
00064 #include "llmultigesture.h"
00065 #include "llui.h"
00066 #include "llviewermenu.h"
00067 #include "llvieweruictrlfactory.h"
00068
00069 #include <stdio.h>
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
00081
00082 class LLChatBarGestureObserver : public LLGestureManagerObserver
00083 {
00084 public:
00085 LLChatBarGestureObserver(LLChatBar* chat_barp) : mChatBar(chat_barp){}
00086 virtual ~LLChatBarGestureObserver() {}
00087 virtual void changed() { mChatBar->refreshGestures(); }
00088 private:
00089 LLChatBar* mChatBar;
00090 };
00091
00092
00093
00094
00095
00096
00097
00098
00099 LLChatBar::LLChatBar(const std::string& name)
00100 : LLPanel(name, LLRect(), BORDER_NO),
00101 mInputEditor(NULL),
00102 mGestureLabelTimer(),
00103 mLastSpecialChatChannel(0),
00104 mIsBuilt(FALSE),
00105 mDynamicLayout(FALSE),
00106 mGestureCombo(NULL),
00107 mObserver(NULL)
00108 {
00109 }
00110
00111 LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
00112 : LLPanel(name, rect, BORDER_NO),
00113 mInputEditor(NULL),
00114 mGestureLabelTimer(),
00115 mLastSpecialChatChannel(0),
00116 mIsBuilt(FALSE),
00117 mDynamicLayout(TRUE),
00118 mGestureCombo(NULL),
00119 mObserver(NULL)
00120 {
00121 setIsChrome(TRUE);
00122
00123 gUICtrlFactory->buildPanel(this,"panel_chat_bar.xml");
00124
00125 mIsFocusRoot = TRUE;
00126
00127 setRect(rect);
00128
00129 setBackgroundOpaque(TRUE);
00130 setBackgroundVisible(TRUE);
00131
00132
00133 setVisible( gSavedSettings.getBOOL("ChatVisible") );
00134
00135
00136 layout();
00137
00138 #if !LL_RELEASE_FOR_DOWNLOAD
00139 childDisplayNotFound();
00140 #endif
00141
00142 }
00143
00144
00145 LLChatBar::~LLChatBar()
00146 {
00147 delete mObserver;
00148 mObserver = NULL;
00149
00150 }
00151
00152 BOOL LLChatBar::postBuild()
00153 {
00154 childSetAction("History", toggleChatHistory, this);
00155 childSetAction("Say", onClickSay, this);
00156 childSetAction("Shout", onClickShout, this);
00157
00158
00159 setGestureCombo(LLUICtrlFactory::getComboBoxByName(this, "Gesture"));
00160
00161 LLButton * sayp = static_cast<LLButton*>(getChildByName("Say", TRUE));
00162 if(sayp)
00163 {
00164 setDefaultBtn(sayp);
00165 }
00166
00167 mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
00168 if (mInputEditor)
00169 {
00170 mInputEditor->setCallbackUserData(this);
00171 mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
00172 mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
00173 mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
00174 mInputEditor->setCommitOnFocusLost( FALSE );
00175 mInputEditor->setRevertOnEsc( FALSE );
00176 mInputEditor->setIgnoreTab(TRUE);
00177 mInputEditor->setPassDelete(TRUE);
00178
00179 mInputEditor->setMaxTextLength(1023);
00180 mInputEditor->setEnableLineHistory(TRUE);
00181 }
00182
00183 mIsBuilt = TRUE;
00184
00185 return TRUE;
00186 }
00187
00188
00189
00190
00191
00192
00193 void LLChatBar::reshape(S32 width, S32 height, BOOL called_from_parent)
00194 {
00195 LLPanel::reshape(width, height, called_from_parent);
00196 if (mIsBuilt)
00197 {
00198 layout();
00199 }
00200 }
00201
00202
00203 BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
00204 {
00205 BOOL handled = FALSE;
00206
00207 if( getVisible() && getEnabled() && !called_from_parent)
00208 {
00209
00210 if( KEY_RETURN == key )
00211 {
00212
00213
00214
00215
00216
00217
00218
00219 if (mask == MASK_CONTROL)
00220 {
00221
00222 sendChat(CHAT_TYPE_SHOUT);
00223 handled = TRUE;
00224 }
00225 else if (mask == MASK_NONE)
00226 {
00227
00228 sendChat( CHAT_TYPE_NORMAL );
00229 handled = TRUE;
00230 }
00231 }
00232
00233 else if ( KEY_ESCAPE == key && gChatBar == this)
00234 {
00235 stopChat();
00236
00237 handled = TRUE;
00238 }
00239 }
00240 return handled;
00241 }
00242
00243
00244 void LLChatBar::layout()
00245 {
00246 if (!mDynamicLayout) return;
00247
00248 S32 rect_width = mRect.getWidth();
00249 S32 count = 9;
00250 S32 pad = 4;
00251
00252 LLRect gesture_rect;
00253 S32 gesture_width = 0;
00254 if (childGetRect("Gesture", gesture_rect))
00255 {
00256 gesture_width = gesture_rect.getWidth();
00257 }
00258 F32 segment_width = (F32)(rect_width - (pad + gesture_width)) / (F32)count;
00259
00260 S32 btn_width = lltrunc(segment_width-pad);
00261
00262 S32 x = 0;
00263 S32 y = 1;
00264 LLRect r;
00265
00266 x = llround(0 * segment_width);
00267 r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
00268 childSetRect("History", r);
00269
00270 x = llround(1 * segment_width);
00271
00272 if (mInputEditor)
00273 {
00274 r.setOriginAndSize(x, y+2, llfloor(6*segment_width-pad), 18);
00275 mInputEditor->reshape(r.getWidth(), r.getHeight(), TRUE);
00276 mInputEditor->setRect(r);
00277 }
00278
00279 x = llround(7 * segment_width);
00280 r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
00281 childSetRect("Say", r);
00282
00283 x = llround(8 * segment_width);
00284 r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
00285 childSetRect("Shout", r);
00286
00287 x = rect_width - (pad + gesture_width);
00288 r.setOriginAndSize(x, y, gesture_width, BTN_HEIGHT);
00289 childSetRect("Gesture", r);
00290 }
00291
00292
00293 void LLChatBar::refresh()
00294 {
00295
00296
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 if (mDynamicLayout)
00311 {
00312 LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
00313 }
00314
00315
00316 const F32 SHOW_GESTURE_NAME_TIME = 2.f;
00317 if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
00318 {
00319 LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
00320 if (gestures) gestures->selectFirstItem();
00321 mGestureLabelTimer.stop();
00322 }
00323
00324 if ((gAgent.getTypingTime() > AGENT_TYPING_TIMEOUT) && (gAgent.getRenderState() & AGENT_STATE_TYPING))
00325 {
00326 gAgent.stopTyping();
00327 }
00328
00329 childSetValue("History", LLFloaterChat::instanceVisible(LLSD()));
00330
00331 childSetEnabled("Say", mInputEditor->getText().size() > 0);
00332 childSetEnabled("Shout", mInputEditor->getText().size() > 0);
00333
00334 }
00335
00336 void LLChatBar::refreshGestures()
00337 {
00338 LLCtrlListInterface* gestures = mGestureCombo ? mGestureCombo->getListInterface() : NULL;
00339 if (mGestureCombo && gestures)
00340 {
00341
00342 LLString cur_gesture = mGestureCombo->getValue().asString();
00343 gestures->selectFirstItem();
00344 LLString label = mGestureCombo->getValue().asString();;
00345
00346 gestures->clearRows();
00347
00348
00349 std::map <std::string, BOOL> unique;
00350 LLGestureManager::item_map_t::iterator it;
00351 for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
00352 {
00353 LLMultiGesture* gesture = (*it).second;
00354 if (gesture)
00355 {
00356 if (!gesture->mTrigger.empty())
00357 {
00358 unique[gesture->mTrigger] = TRUE;
00359 }
00360 }
00361 }
00362
00363
00364 std::map <std::string, BOOL>::iterator it2;
00365 for (it2 = unique.begin(); it2 != unique.end(); ++it2)
00366 {
00367 gestures->addSimpleElement((*it2).first);
00368 }
00369
00370 gestures->sortByColumn(0, TRUE);
00371
00372 gestures->addSimpleElement(label, ADD_TOP);
00373
00374 if (!cur_gesture.empty())
00375 {
00376 gestures->selectByValue(LLSD(cur_gesture));
00377 }
00378 else
00379 {
00380 gestures->selectFirstItem();
00381 }
00382 }
00383 }
00384
00385
00386 void LLChatBar::setKeyboardFocus(BOOL focus)
00387 {
00388 if (focus)
00389 {
00390 if (mInputEditor)
00391 {
00392 mInputEditor->setFocus(TRUE);
00393 mInputEditor->selectAll();
00394 }
00395 }
00396 else if (gFocusMgr.childHasKeyboardFocus(this))
00397 {
00398 if (mInputEditor)
00399 {
00400 mInputEditor->deselect();
00401 }
00402 setFocus(FALSE);
00403 }
00404 }
00405
00406
00407
00408 void LLChatBar::setIgnoreArrowKeys(BOOL b)
00409 {
00410 if (mInputEditor)
00411 {
00412 mInputEditor->setIgnoreArrowKeys(b);
00413 }
00414 }
00415
00416 BOOL LLChatBar::inputEditorHasFocus()
00417 {
00418 return mInputEditor && mInputEditor->hasFocus();
00419 }
00420
00421 LLString LLChatBar::getCurrentChat()
00422 {
00423 return mInputEditor ? mInputEditor->getText() : LLString::null;
00424 }
00425
00426 void LLChatBar::setGestureCombo(LLComboBox* combo)
00427 {
00428 mGestureCombo = combo;
00429 if (mGestureCombo)
00430 {
00431 mGestureCombo->setCommitCallback(onCommitGesture);
00432 mGestureCombo->setCallbackUserData(this);
00433
00434
00435 mObserver = new LLChatBarGestureObserver(this);
00436 gGestureManager.addObserver(mObserver);
00437
00438
00439 refreshGestures();
00440 }
00441 }
00442
00443
00444
00445
00446
00447
00448
00449 LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
00450 {
00451 if (mesg[0] == '/'
00452 && mesg[1] == '/')
00453 {
00454
00455 *channel = mLastSpecialChatChannel;
00456 return mesg.substr(2, mesg.length() - 2);
00457 }
00458 else if (mesg[0] == '/'
00459 && mesg[1]
00460 && LLStringOps::isDigit(mesg[1]))
00461 {
00462
00463 S32 pos = 0;
00464
00465
00466 llwchar channel_string[64];
00467 llwchar c;
00468 do
00469 {
00470 c = mesg[pos+1];
00471 channel_string[pos] = c;
00472 pos++;
00473 }
00474 while(c && pos < 64 && LLStringOps::isDigit(c));
00475
00476
00477
00478
00479 while(c && iswspace(c))
00480 {
00481 c = mesg[pos+1];
00482 pos++;
00483 }
00484
00485
00486 mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
00487 *channel = mLastSpecialChatChannel;
00488 return mesg.substr(pos, mesg.length() - pos);
00489 }
00490 else
00491 {
00492
00493 *channel = 0;
00494 return mesg;
00495 }
00496 }
00497
00498
00499 void LLChatBar::sendChat( EChatType type )
00500 {
00501 LLWString text;
00502 if (mInputEditor) text = mInputEditor->getWText();
00503 LLWString::trim(text);
00504
00505 if (!text.empty())
00506 {
00507
00508 if (mInputEditor) mInputEditor->updateHistory();
00509
00510 S32 channel = 0;
00511 stripChannelNumber(text, &channel);
00512
00513 std::string utf8text = wstring_to_utf8str(text);
00514
00515 std::string utf8_revised_text;
00516 if (0 == channel)
00517 {
00518
00519 gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text);
00520 }
00521 else
00522 {
00523 utf8_revised_text = utf8text;
00524 }
00525
00526 utf8_revised_text = utf8str_trim(utf8_revised_text);
00527
00528 if (!utf8_revised_text.empty())
00529 {
00530
00531 sendChatFromViewer(utf8_revised_text, type, TRUE);
00532 }
00533 }
00534 childSetValue("Chat Editor", LLString::null);
00535
00536 gAgent.stopTyping();
00537
00538
00539
00540 if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn"))
00541 {
00542 stopChat();
00543 }
00544 }
00545
00546
00547
00548
00549
00550
00551
00552 void LLChatBar::startChat(void* userdata)
00553 {
00554 const char* line = (const char*)userdata;
00555
00556 gChatBar->setVisible(TRUE);
00557 gChatBar->setKeyboardFocus(TRUE);
00558 gSavedSettings.setBOOL("ChatVisible", TRUE);
00559
00560 if (line && gChatBar->mInputEditor)
00561 {
00562 std::string line_string(line);
00563 gChatBar->mInputEditor->setText(line_string);
00564 }
00565
00566 gChatBar->mInputEditor->setCursorToEnd();
00567 }
00568
00569
00570
00571
00572 void LLChatBar::stopChat()
00573 {
00574
00575 gChatBar->setKeyboardFocus(FALSE);
00576
00577
00578
00579
00580 gKeyboard->resetKeys();
00581 gKeyboard->resetMaskKeys();
00582
00583
00584 gAgent.stopTyping();
00585
00586
00587 gChatBar->setVisible(FALSE);
00588 }
00589
00590 void LLChatBar::setVisible(BOOL visible)
00591 {
00592 gSavedSettings.setBOOL("ChatVisible", visible);
00593 LLPanel::setVisible(visible);
00594 }
00595
00596
00597 void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata )
00598 {
00599 LLChatBar* self = (LLChatBar *)userdata;
00600
00601 LLWString raw_text;
00602 if (self->mInputEditor) raw_text = self->mInputEditor->getWText();
00603
00604
00605
00606 LLWString::trimHead(raw_text);
00607
00608 S32 length = raw_text.length();
00609
00610 if( (length > 0) && (raw_text[0] != '/') )
00611 {
00612 gAgent.startTyping();
00613 }
00614 else
00615 {
00616 gAgent.stopTyping();
00617 }
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634 KEY key = gKeyboard->currentKey();
00635
00636
00637 if (length > 1
00638 && raw_text[0] == '/'
00639 && key < KEY_SPECIAL)
00640 {
00641
00642
00643 std::string utf8_trigger = wstring_to_utf8str(raw_text);
00644 std::string utf8_out_str(utf8_trigger);
00645
00646 if (gGestureManager.matchPrefix(utf8_trigger, &utf8_out_str))
00647 {
00648 if (self->mInputEditor)
00649 {
00650 self->mInputEditor->setText(utf8_out_str);
00651 S32 outlength = self->mInputEditor->getLength();
00652
00653
00654
00655 self->mInputEditor->setSelection(length, outlength);
00656 }
00657 }
00658
00659
00660
00661
00662
00663 }
00664
00665 LLString keytext = "";
00666 switch(key)
00667 {
00668 case KEY_BACKSPACE: keytext = "BACKSPACE"; break;
00669 case KEY_DELETE: keytext = "DELETE"; break;
00670 case KEY_RETURN: keytext = "ENTER";break;
00671 default:
00672 if ( key < KEY_SPECIAL )
00673 {
00674 char tmp[8];
00675 sprintf(tmp, "%c", key);
00676 keytext = tmp;
00677 }
00678 }
00679
00680 S32 relay_channel = gSavedPerAccountSettings.getS32("RealtimeChatRelayChannel");
00681
00682 if ( !keytext.empty() && relay_channel != 0 )
00683 {
00684 LLMessageSystem* msg = gMessageSystem;
00685 msg->newMessageFast(_PREHASH_ChatFromViewer);
00686 msg->nextBlockFast(_PREHASH_AgentData);
00687 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00688 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00689 msg->nextBlockFast(_PREHASH_ChatData);
00690 msg->addStringFast(_PREHASH_Message, keytext);
00691 msg->addU8Fast(_PREHASH_Type, CHAT_TYPE_WHISPER);
00692 msg->addS32("Channel", relay_channel);
00693
00694 gAgent.sendReliableMessage();
00695 }
00696
00697
00698 }
00699
00700
00701 void LLChatBar::onInputEditorFocusLost( LLUICtrl* caller, void* userdata)
00702 {
00703
00704 gAgent.stopTyping();
00705 }
00706
00707
00708 void LLChatBar::onInputEditorGainFocus( LLUICtrl* caller, void* userdata )
00709 {
00710 LLFloaterChat::setHistoryCursorAndScrollToEnd();
00711 }
00712
00713
00714 void LLChatBar::onClickSay( void* userdata )
00715 {
00716 LLChatBar* self = (LLChatBar*) userdata;
00717 self->sendChat( CHAT_TYPE_NORMAL );
00718 }
00719
00720
00721 void LLChatBar::onClickShout( void* userdata )
00722 {
00723 LLChatBar *self = (LLChatBar *)userdata;
00724 self->sendChat( CHAT_TYPE_SHOUT );
00725 }
00726
00727 void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
00728 {
00729 sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
00730 }
00731
00732 void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
00733 {
00734
00735 S32 channel = 0;
00736 LLWString out_text = stripChannelNumber(wtext, &channel);
00737 std::string utf8_out_text = wstring_to_utf8str(out_text);
00738 std::string utf8_text = wstring_to_utf8str(wtext);
00739
00740 utf8_text = utf8str_trim(utf8_text);
00741 if (!utf8_text.empty())
00742 {
00743 utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
00744 }
00745
00746 gAgent.sendChat(utf8_out_text, channel, type, animate);
00747 }
00748
00749
00750
00751 void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
00752 {
00753 LLChatBar* self = (LLChatBar*)data;
00754 LLCtrlListInterface* gestures = self->mGestureCombo ? self->mGestureCombo->getListInterface() : NULL;
00755 if (gestures)
00756 {
00757 S32 index = gestures->getFirstSelectedIndex();
00758 if (index == 0)
00759 {
00760 return;
00761 }
00762 const std::string& trigger = gestures->getSimpleSelectedValue().asString();
00763
00764
00765
00766 std::string text(trigger);
00767 std::string revised_text;
00768 gGestureManager.triggerAndReviseString(text, &revised_text);
00769
00770 revised_text = utf8str_trim(revised_text);
00771 if (!revised_text.empty())
00772 {
00773
00774 self->sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE);
00775 }
00776 }
00777 self->mGestureLabelTimer.start();
00778 if (self->mGestureCombo != NULL)
00779 {
00780
00781 self->mGestureCombo->setFocus(FALSE);
00782 }
00783 }
00784
00785 void toggleChatHistory(void* user_data)
00786 {
00787 LLFloaterChat::toggleInstance(LLSD());
00788 }