diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/llchatbar.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/llchatbar.cpp')
-rw-r--r-- | linden/indra/newview/llchatbar.cpp | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/linden/indra/newview/llchatbar.cpp b/linden/indra/newview/llchatbar.cpp new file mode 100644 index 0000000..e62e0cb --- /dev/null +++ b/linden/indra/newview/llchatbar.cpp | |||
@@ -0,0 +1,731 @@ | |||
1 | /** | ||
2 | * @file llchatbar.cpp | ||
3 | * @brief LLChatBar class implementation | ||
4 | * | ||
5 | * Copyright (c) 2002-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "llviewerprecompiledheaders.h" | ||
29 | |||
30 | #include "llchatbar.h" | ||
31 | |||
32 | #include "imageids.h" | ||
33 | #include "llfontgl.h" | ||
34 | #include "llrect.h" | ||
35 | #include "llerror.h" | ||
36 | #include "llparcel.h" | ||
37 | #include "llstring.h" | ||
38 | #include "message.h" | ||
39 | #include "llfocusmgr.h" | ||
40 | |||
41 | #include "llagent.h" | ||
42 | #include "llbutton.h" | ||
43 | #include "llcombobox.h" | ||
44 | #include "llviewercontrol.h" | ||
45 | #include "llfloaterchat.h" | ||
46 | #include "llgesturemgr.h" | ||
47 | #include "llkeyboard.h" | ||
48 | #include "lllineeditor.h" | ||
49 | #include "lltextbox.h" | ||
50 | #include "lluiconstants.h" | ||
51 | #include "llviewergesture.h" // for triggering gestures | ||
52 | #include "llviewermenu.h" // for deleting object with DEL key | ||
53 | #include "llviewerstats.h" | ||
54 | #include "llviewerwindow.h" | ||
55 | #include "llframetimer.h" | ||
56 | #include "llresmgr.h" | ||
57 | #include "llworld.h" | ||
58 | #include "llinventorymodel.h" | ||
59 | #include "llmultigesture.h" | ||
60 | #include "llui.h" | ||
61 | #include "llviewermenu.h" | ||
62 | #include "llvieweruictrlfactory.h" | ||
63 | |||
64 | |||
65 | // | ||
66 | // Globals | ||
67 | // | ||
68 | const F32 AGENT_TYPING_TIMEOUT = 5.f; // seconds | ||
69 | |||
70 | LLChatBar *gChatBar = NULL; | ||
71 | |||
72 | LLChatBarGestureObserver* LLChatBar::sObserver = NULL; | ||
73 | |||
74 | |||
75 | class LLChatBarGestureObserver : public LLGestureManagerObserver | ||
76 | { | ||
77 | public: | ||
78 | LLChatBarGestureObserver() {} | ||
79 | virtual ~LLChatBarGestureObserver() {} | ||
80 | virtual void changed() { gChatBar->refreshGestures(); } | ||
81 | }; | ||
82 | |||
83 | |||
84 | // | ||
85 | // Functions | ||
86 | // | ||
87 | |||
88 | LLChatBar::LLChatBar(const std::string& name, const LLRect& rect) | ||
89 | : LLPanel(name, rect, BORDER_NO), | ||
90 | mInputEditor(NULL), | ||
91 | mGestureLabelTimer(), | ||
92 | mLastSpecialChatChannel(0), | ||
93 | mIsBuilt(FALSE) | ||
94 | { | ||
95 | setIsChrome(TRUE); | ||
96 | |||
97 | gUICtrlFactory->buildPanel(this,"panel_chat_bar.xml"); | ||
98 | |||
99 | mIsFocusRoot = TRUE; | ||
100 | |||
101 | setRect(rect); // override xml rect | ||
102 | |||
103 | setBackgroundOpaque(TRUE); | ||
104 | setBackgroundVisible(TRUE); | ||
105 | |||
106 | // Start visible if we left the app while chatting. | ||
107 | setVisible( gSavedSettings.getBOOL("ChatVisible") ); | ||
108 | |||
109 | mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor"); | ||
110 | if (mInputEditor) | ||
111 | { | ||
112 | mInputEditor->setCallbackUserData(this); | ||
113 | mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke); | ||
114 | mInputEditor->setFocusLostCallback(&onInputEditorFocusLost); | ||
115 | mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus ); | ||
116 | mInputEditor->setCommitOnFocusLost( FALSE ); | ||
117 | mInputEditor->setRevertOnEsc( FALSE ); | ||
118 | mInputEditor->setIgnoreTab(TRUE); | ||
119 | mInputEditor->setPassDelete(TRUE); | ||
120 | } | ||
121 | |||
122 | mInputEditor->setMaxTextLength(1023); | ||
123 | // Build the list of gestures | ||
124 | refreshGestures(); | ||
125 | |||
126 | sObserver = new LLChatBarGestureObserver; | ||
127 | gGestureManager.addObserver(sObserver); | ||
128 | |||
129 | mIsBuilt = TRUE; | ||
130 | |||
131 | // Apply custom layout. | ||
132 | layout(); | ||
133 | |||
134 | #if !LL_RELEASE_FOR_DOWNLOAD | ||
135 | childDisplayNotFound(); | ||
136 | #endif | ||
137 | |||
138 | } | ||
139 | |||
140 | |||
141 | LLChatBar::~LLChatBar() | ||
142 | { | ||
143 | delete sObserver; | ||
144 | sObserver = NULL; | ||
145 | // LLView destructor cleans up children | ||
146 | } | ||
147 | |||
148 | BOOL LLChatBar::postBuild() | ||
149 | { | ||
150 | childSetAction("History", LLFloaterChat::toggle, this); | ||
151 | childSetAction("Say", onClickSay, this); | ||
152 | childSetAction("Shout", onClickShout, this); | ||
153 | childSetCommitCallback("Gesture", onCommitGesture, this); | ||
154 | LLButton * sayp = static_cast<LLButton*>(getChildByName("Say")); | ||
155 | if(sayp) | ||
156 | { | ||
157 | setDefaultBtn(sayp); | ||
158 | } | ||
159 | |||
160 | return TRUE; | ||
161 | } | ||
162 | |||
163 | //----------------------------------------------------------------------- | ||
164 | // Overrides | ||
165 | //----------------------------------------------------------------------- | ||
166 | |||
167 | // virtual | ||
168 | void LLChatBar::reshape(S32 width, S32 height, BOOL called_from_parent) | ||
169 | { | ||
170 | LLPanel::reshape(width, height, called_from_parent); | ||
171 | if (mIsBuilt) | ||
172 | { | ||
173 | layout(); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // virtual | ||
178 | BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent ) | ||
179 | { | ||
180 | BOOL handled = FALSE; | ||
181 | |||
182 | if( getVisible() && getEnabled() && !called_from_parent) | ||
183 | { | ||
184 | // ALT-RETURN is reserved for windowed/fullscreen toggle | ||
185 | if( KEY_RETURN == key ) | ||
186 | { | ||
187 | //if (childGetValue("Chat Editor").asString().empty()) | ||
188 | //{ | ||
189 | // // no text, just close chat bar | ||
190 | // stopChat(); | ||
191 | // return TRUE; | ||
192 | //} | ||
193 | |||
194 | if (mask == MASK_CONTROL) | ||
195 | { | ||
196 | // shout | ||
197 | sendChat(CHAT_TYPE_SHOUT); | ||
198 | handled = TRUE; | ||
199 | } | ||
200 | else if (mask == MASK_NONE) | ||
201 | { | ||
202 | // say | ||
203 | sendChat( CHAT_TYPE_NORMAL ); | ||
204 | handled = TRUE; | ||
205 | } | ||
206 | } | ||
207 | else if ( KEY_ESCAPE == key ) | ||
208 | { | ||
209 | stopChat(); | ||
210 | |||
211 | handled = TRUE; | ||
212 | } | ||
213 | } | ||
214 | return handled; | ||
215 | } | ||
216 | |||
217 | |||
218 | void LLChatBar::layout() | ||
219 | { | ||
220 | S32 rect_width = mRect.getWidth(); | ||
221 | S32 count = 9; // number of elements in LLToolBar | ||
222 | S32 pad = 4; | ||
223 | |||
224 | LLRect gesture_rect; | ||
225 | S32 gesture_width = 0; | ||
226 | if (childGetRect("Gesture", gesture_rect)) | ||
227 | { | ||
228 | gesture_width = gesture_rect.getWidth(); | ||
229 | } | ||
230 | F32 segment_width = (F32)(rect_width - (pad + gesture_width)) / (F32)count; | ||
231 | |||
232 | S32 btn_width = lltrunc(segment_width-pad); | ||
233 | |||
234 | S32 x = 0; | ||
235 | S32 y = 1; | ||
236 | LLRect r; | ||
237 | |||
238 | x = llround(0 * segment_width); | ||
239 | r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT); | ||
240 | childSetRect("History", r); | ||
241 | |||
242 | x = llround(1 * segment_width); | ||
243 | // Hack this one up so it looks nice. | ||
244 | if (mInputEditor) | ||
245 | { | ||
246 | r.setOriginAndSize(x, y+2, llfloor(6*segment_width-pad), 18); | ||
247 | mInputEditor->reshape(r.getWidth(), r.getHeight(), TRUE); | ||
248 | mInputEditor->setRect(r); | ||
249 | } | ||
250 | |||
251 | x = llround(7 * segment_width); | ||
252 | r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT); | ||
253 | childSetRect("Say", r); | ||
254 | |||
255 | x = llround(8 * segment_width); | ||
256 | r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT); | ||
257 | childSetRect("Shout", r); | ||
258 | |||
259 | x = rect_width - (pad + gesture_width); | ||
260 | r.setOriginAndSize(x, y, gesture_width, BTN_HEIGHT); | ||
261 | childSetRect("Gesture", r); | ||
262 | } | ||
263 | |||
264 | |||
265 | void LLChatBar::refresh() | ||
266 | { | ||
267 | //BOOL chat_mode = gSavedSettings.getBOOL("ChatVisible"); | ||
268 | |||
269 | //// Grab focus when no one else has it, and we're in chat mode. | ||
270 | //if (!gFocusMgr.getKeyboardFocus() | ||
271 | // && chat_mode) | ||
272 | //{ | ||
273 | // childSetFocus("Chat Editor", TRUE); | ||
274 | //} | ||
275 | |||
276 | // Only show this view when user wants to be chatting | ||
277 | //setVisible(chat_mode); | ||
278 | |||
279 | // hide in mouselook, but keep previous visibility state | ||
280 | //BOOL mouselook = gAgent.cameraMouselook(); | ||
281 | // call superclass setVisible so that we don't overwrite the saved setting | ||
282 | LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible")); | ||
283 | |||
284 | // HACK: Leave the name of the gesture in place for a few seconds. | ||
285 | const F32 SHOW_GESTURE_NAME_TIME = 2.f; | ||
286 | if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME) | ||
287 | { | ||
288 | LLCtrlListInterface* gestures = childGetListInterface("Gesture"); | ||
289 | if (gestures) gestures->selectFirstItem(); | ||
290 | mGestureLabelTimer.stop(); | ||
291 | } | ||
292 | |||
293 | if ((gAgent.getTypingTime() > AGENT_TYPING_TIMEOUT) && (gAgent.getRenderState() & AGENT_STATE_TYPING)) | ||
294 | { | ||
295 | gAgent.stopTyping(); | ||
296 | } | ||
297 | |||
298 | childSetEnabled("Say", mInputEditor->getText().size() > 0); | ||
299 | childSetEnabled("Shout", mInputEditor->getText().size() > 0); | ||
300 | |||
301 | } | ||
302 | |||
303 | void LLChatBar::refreshGestures() | ||
304 | { | ||
305 | LLCtrlListInterface* gestures = childGetListInterface("Gesture"); | ||
306 | if (gestures) | ||
307 | { | ||
308 | //store current selection so we can maintain it | ||
309 | LLString cur_gesture = childGetValue("Gesture").asString(); | ||
310 | gestures->selectFirstItem(); | ||
311 | LLString label = childGetValue("Gesture").asString(); | ||
312 | // clear | ||
313 | gestures->clearRows(); | ||
314 | // add gestures | ||
315 | LLGestureManager::item_map_t::iterator it; | ||
316 | for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it) | ||
317 | { | ||
318 | LLMultiGesture* gesture = (*it).second; | ||
319 | if (gesture) | ||
320 | { | ||
321 | if (!gesture->mTrigger.empty()) | ||
322 | { | ||
323 | gestures->addSimpleElement(gesture->mTrigger); | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | gestures->sortByColumn(0, TRUE); | ||
328 | // Insert label after sorting | ||
329 | gestures->addSimpleElement(label, ADD_TOP); | ||
330 | |||
331 | if (!cur_gesture.empty()) | ||
332 | { | ||
333 | gestures->selectByValue(LLSD(cur_gesture)); | ||
334 | } | ||
335 | else | ||
336 | { | ||
337 | gestures->selectFirstItem(); | ||
338 | } | ||
339 | } | ||
340 | } | ||
341 | |||
342 | // Move the cursor to the correct input field. | ||
343 | void LLChatBar::setKeyboardFocus(BOOL focus) | ||
344 | { | ||
345 | if (focus) | ||
346 | { | ||
347 | if (mInputEditor) | ||
348 | { | ||
349 | mInputEditor->setFocus(TRUE); | ||
350 | mInputEditor->selectAll(); | ||
351 | } | ||
352 | } | ||
353 | else if (gFocusMgr.childHasKeyboardFocus(this)) | ||
354 | { | ||
355 | if (mInputEditor) | ||
356 | { | ||
357 | mInputEditor->deselect(); | ||
358 | } | ||
359 | setFocus(FALSE); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | |||
364 | // Ignore arrow keys in chat bar | ||
365 | void LLChatBar::setIgnoreArrowKeys(BOOL b) | ||
366 | { | ||
367 | if (mInputEditor) | ||
368 | { | ||
369 | mInputEditor->setIgnoreArrowKeys(b); | ||
370 | } | ||
371 | } | ||
372 | |||
373 | BOOL LLChatBar::inputEditorHasFocus() | ||
374 | { | ||
375 | return mInputEditor && mInputEditor->hasFocus(); | ||
376 | } | ||
377 | |||
378 | LLString LLChatBar::getCurrentChat() | ||
379 | { | ||
380 | return mInputEditor ? mInputEditor->getText() : LLString::null; | ||
381 | } | ||
382 | |||
383 | //----------------------------------------------------------------------- | ||
384 | // Internal functions | ||
385 | //----------------------------------------------------------------------- | ||
386 | |||
387 | // If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20. | ||
388 | // Otherwise returns input and channel 0. | ||
389 | LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel) | ||
390 | { | ||
391 | if (mesg[0] == '/' | ||
392 | && mesg[1] == '/') | ||
393 | { | ||
394 | // This is a "repeat channel send" | ||
395 | *channel = mLastSpecialChatChannel; | ||
396 | return mesg.substr(2, mesg.length() - 2); | ||
397 | } | ||
398 | else if (mesg[0] == '/' | ||
399 | && mesg[1] | ||
400 | && isdigit(mesg[1])) | ||
401 | { | ||
402 | // This a special "/20" speak on a channel | ||
403 | S32 pos = 0; | ||
404 | |||
405 | // Copy the channel number into a string | ||
406 | llwchar channel_string[64]; | ||
407 | llwchar c; | ||
408 | do | ||
409 | { | ||
410 | c = mesg[pos+1]; | ||
411 | channel_string[pos] = c; | ||
412 | pos++; | ||
413 | } | ||
414 | while(c && pos < 64 && isdigit(c)); | ||
415 | |||
416 | // Move the pointer forward to the first non-whitespace char | ||
417 | // Check isspace before looping, so we can handle "/33foo" | ||
418 | // as well as "/33 foo" | ||
419 | while(c && iswspace(c)) | ||
420 | { | ||
421 | c = mesg[pos+1]; | ||
422 | pos++; | ||
423 | } | ||
424 | |||
425 | |||
426 | mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10); | ||
427 | *channel = mLastSpecialChatChannel; | ||
428 | return mesg.substr(pos, mesg.length() - pos); | ||
429 | } | ||
430 | else | ||
431 | { | ||
432 | // This is normal chat. | ||
433 | *channel = 0; | ||
434 | return mesg; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | |||
439 | void LLChatBar::sendChat( EChatType type ) | ||
440 | { | ||
441 | LLWString text; | ||
442 | if (mInputEditor) text = mInputEditor->getWText(); | ||
443 | LLWString::trim(text); | ||
444 | |||
445 | if (!text.empty()) | ||
446 | { | ||
447 | // Check if this is destined for another channel | ||
448 | S32 channel = 0; | ||
449 | stripChannelNumber(text, &channel); | ||
450 | |||
451 | std::string utf8text = wstring_to_utf8str(text); | ||
452 | // Try to trigger a gesture, if not chat to a script. | ||
453 | std::string utf8_revised_text; | ||
454 | if (0 == channel) | ||
455 | { | ||
456 | // discard returned "found" boolean | ||
457 | gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text); | ||
458 | } | ||
459 | else | ||
460 | { | ||
461 | utf8_revised_text = utf8text; | ||
462 | } | ||
463 | |||
464 | utf8_revised_text = utf8str_trim(utf8_revised_text); | ||
465 | |||
466 | if (!utf8_revised_text.empty()) | ||
467 | { | ||
468 | // Chat with animation | ||
469 | sendChatFromViewer(utf8_revised_text, type, TRUE); | ||
470 | } | ||
471 | } | ||
472 | childSetValue("Chat Editor", LLSD(LLString::null)); | ||
473 | |||
474 | gAgent.stopTyping(); | ||
475 | |||
476 | // If the user wants to stop chatting on hitting return, lose focus | ||
477 | // and go out of chat mode. | ||
478 | if (gSavedSettings.getBOOL("CloseChatOnReturn")) | ||
479 | { | ||
480 | stopChat(); | ||
481 | } | ||
482 | } | ||
483 | |||
484 | |||
485 | //----------------------------------------------------------------------- | ||
486 | // Static functions | ||
487 | //----------------------------------------------------------------------- | ||
488 | |||
489 | // static | ||
490 | void LLChatBar::startChat(void* userdata) | ||
491 | { | ||
492 | const char* line = (const char*)userdata; | ||
493 | |||
494 | gChatBar->setVisible(TRUE); | ||
495 | gChatBar->setKeyboardFocus(TRUE); | ||
496 | gSavedSettings.setBOOL("ChatVisible", TRUE); | ||
497 | |||
498 | if (line && gChatBar->mInputEditor) | ||
499 | { | ||
500 | std::string line_string(line); | ||
501 | gChatBar->mInputEditor->setText(line_string); | ||
502 | } | ||
503 | // always move cursor to end so users don't obliterate chat when accidentally hitting WASD | ||
504 | gChatBar->mInputEditor->setCursorToEnd(); | ||
505 | } | ||
506 | |||
507 | |||
508 | // Exit "chat mode" and do the appropriate focus changes | ||
509 | // static | ||
510 | void LLChatBar::stopChat() | ||
511 | { | ||
512 | // In simple UI mode, we never release focus from the chat bar | ||
513 | gChatBar->setKeyboardFocus(FALSE); | ||
514 | |||
515 | // If we typed a movement key and pressed return during the | ||
516 | // same frame, the keyboard handlers will see the key as having | ||
517 | // gone down this frame and try to move the avatar. | ||
518 | gKeyboard->resetKeys(); | ||
519 | gKeyboard->resetMaskKeys(); | ||
520 | |||
521 | // stop typing animation | ||
522 | gAgent.stopTyping(); | ||
523 | |||
524 | // hide chat bar so it doesn't grab focus back | ||
525 | gChatBar->setVisible(FALSE); | ||
526 | } | ||
527 | |||
528 | void LLChatBar::setVisible(BOOL visible) | ||
529 | { | ||
530 | gSavedSettings.setBOOL("ChatVisible", visible); | ||
531 | LLPanel::setVisible(visible); | ||
532 | } | ||
533 | |||
534 | // static | ||
535 | void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata ) | ||
536 | { | ||
537 | LLChatBar* self = (LLChatBar *)userdata; | ||
538 | |||
539 | LLWString raw_text; | ||
540 | if (self->mInputEditor) raw_text = self->mInputEditor->getWText(); | ||
541 | |||
542 | // Can't trim the end, because that will cause autocompletion | ||
543 | // to eat trailing spaces that might be part of a gesture. | ||
544 | LLWString::trimHead(raw_text); | ||
545 | |||
546 | S32 length = raw_text.length(); | ||
547 | |||
548 | if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences | ||
549 | { | ||
550 | gAgent.startTyping(); | ||
551 | } | ||
552 | else | ||
553 | { | ||
554 | gAgent.stopTyping(); | ||
555 | } | ||
556 | |||
557 | /* Doesn't work -- can't tell the difference between a backspace | ||
558 | that killed the selection vs. backspace at the end of line. | ||
559 | if (length > 1 | ||
560 | && text[0] == '/' | ||
561 | && key == KEY_BACKSPACE) | ||
562 | { | ||
563 | // the selection will already be deleted, but we need to trim | ||
564 | // off the character before | ||
565 | LLString new_text = raw_text.substr(0, length-1); | ||
566 | self->mInputEditor->setText( new_text ); | ||
567 | self->mInputEditor->setCursorToEnd(); | ||
568 | length = length - 1; | ||
569 | } | ||
570 | */ | ||
571 | |||
572 | KEY key = gKeyboard->currentKey(); | ||
573 | |||
574 | // Ignore "special" keys, like backspace, arrows, etc. | ||
575 | if (length > 1 | ||
576 | && raw_text[0] == '/' | ||
577 | && key < KEY_SPECIAL) | ||
578 | { | ||
579 | // we're starting a gesture, attempt to autocomplete | ||
580 | |||
581 | std::string utf8_trigger = wstring_to_utf8str(raw_text); | ||
582 | std::string utf8_out_str(utf8_trigger); | ||
583 | |||
584 | if (gGestureManager.matchPrefix(utf8_trigger, &utf8_out_str)) | ||
585 | { | ||
586 | if (self->mInputEditor) | ||
587 | { | ||
588 | self->mInputEditor->setText(utf8_out_str); | ||
589 | S32 outlength = self->mInputEditor->getLength(); // in characters | ||
590 | |||
591 | // Select to end of line, starting from the character | ||
592 | // after the last one the user typed. | ||
593 | self->mInputEditor->setSelection(length, outlength); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | //llinfos << "GESTUREDEBUG " << trigger | ||
598 | // << " len " << length | ||
599 | // << " outlen " << out_str.getLength() | ||
600 | // << llendl; | ||
601 | } | ||
602 | // make sure we don't do UI-only render as it is apparent avatar isn't animating | ||
603 | gViewerWindow->finishFastFrame(); | ||
604 | } | ||
605 | |||
606 | // static | ||
607 | void LLChatBar::onInputEditorFocusLost( LLLineEditor* caller, void* userdata) | ||
608 | { | ||
609 | // stop typing animation | ||
610 | gAgent.stopTyping(); | ||
611 | } | ||
612 | |||
613 | // static | ||
614 | void LLChatBar::onInputEditorGainFocus( LLUICtrl* caller, void* userdata ) | ||
615 | { | ||
616 | LLFloaterChat::setHistoryCursorAndScrollToEnd(); | ||
617 | } | ||
618 | |||
619 | // static | ||
620 | void LLChatBar::onClickSay( void* userdata ) | ||
621 | { | ||
622 | LLChatBar* self = (LLChatBar*) userdata; | ||
623 | self->sendChat( CHAT_TYPE_NORMAL ); | ||
624 | } | ||
625 | |||
626 | // static | ||
627 | void LLChatBar::onClickShout( void* userdata ) | ||
628 | { | ||
629 | LLChatBar *self = (LLChatBar *)userdata; | ||
630 | self->sendChat( CHAT_TYPE_SHOUT ); | ||
631 | } | ||
632 | |||
633 | void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate) | ||
634 | { | ||
635 | sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate); | ||
636 | } | ||
637 | |||
638 | void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) | ||
639 | { | ||
640 | LLMessageSystem* msg = gMessageSystem; | ||
641 | |||
642 | // Look for "/20 foo" channel chats. | ||
643 | S32 channel = 0; | ||
644 | LLWString out_text = stripChannelNumber(wtext, &channel); | ||
645 | std::string utf8_out_text = wstring_to_utf8str(out_text); | ||
646 | std::string utf8_text = wstring_to_utf8str(wtext); | ||
647 | |||
648 | utf8_text = utf8str_trim(utf8_text); | ||
649 | if (!utf8_text.empty()) | ||
650 | { | ||
651 | utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1); | ||
652 | } | ||
653 | |||
654 | // Don't animate for chats people can't hear (chat to scripts) | ||
655 | if (animate && (channel == 0)) | ||
656 | { | ||
657 | if (type == CHAT_TYPE_WHISPER) | ||
658 | { | ||
659 | lldebugs << "You whisper " << utf8_text << llendl; | ||
660 | gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START); | ||
661 | } | ||
662 | else if (type == CHAT_TYPE_NORMAL) | ||
663 | { | ||
664 | lldebugs << "You say " << utf8_text << llendl; | ||
665 | gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START); | ||
666 | } | ||
667 | else if (type == CHAT_TYPE_SHOUT) | ||
668 | { | ||
669 | lldebugs << "You shout " << utf8_text << llendl; | ||
670 | gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START); | ||
671 | } | ||
672 | else | ||
673 | { | ||
674 | llinfos << "send_chat_from_viewer() - invalid volume" << llendl; | ||
675 | return; | ||
676 | } | ||
677 | } | ||
678 | else | ||
679 | { | ||
680 | if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) | ||
681 | { | ||
682 | lldebugs << "Channel chat: " << utf8_text << llendl; | ||
683 | } | ||
684 | } | ||
685 | |||
686 | msg->newMessageFast(_PREHASH_ChatFromViewer); | ||
687 | msg->nextBlockFast(_PREHASH_AgentData); | ||
688 | msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); | ||
689 | msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); | ||
690 | msg->nextBlockFast(_PREHASH_ChatData); | ||
691 | msg->addStringFast(_PREHASH_Message, utf8_out_text); | ||
692 | msg->addU8Fast(_PREHASH_Type, type); | ||
693 | msg->addS32("Channel", channel); | ||
694 | |||
695 | gAgent.sendReliableMessage(); | ||
696 | |||
697 | gViewerStats->incStat(LLViewerStats::ST_CHAT_COUNT); | ||
698 | } | ||
699 | |||
700 | |||
701 | // static | ||
702 | void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data) | ||
703 | { | ||
704 | LLChatBar* self = (LLChatBar*)data; | ||
705 | LLCtrlListInterface* gestures = self->childGetListInterface("Gesture"); | ||
706 | if (gestures) | ||
707 | { | ||
708 | S32 index = gestures->getFirstSelectedIndex(); | ||
709 | if (index == 0) | ||
710 | { | ||
711 | return; | ||
712 | } | ||
713 | const std::string& trigger = gestures->getSimpleSelectedValue().asString(); | ||
714 | |||
715 | // pretend the user chatted the trigger string, to invoke | ||
716 | // substitution and logging. | ||
717 | std::string text(trigger); | ||
718 | std::string revised_text; | ||
719 | gGestureManager.triggerAndReviseString(text, &revised_text); | ||
720 | |||
721 | revised_text = utf8str_trim(revised_text); | ||
722 | if (!revised_text.empty()) | ||
723 | { | ||
724 | // Don't play nodding animation | ||
725 | self->sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE); | ||
726 | } | ||
727 | } | ||
728 | self->mGestureLabelTimer.start(); | ||
729 | // free focus back to chat bar | ||
730 | self->childSetFocus("Gesture", FALSE); | ||
731 | } | ||