diff options
author | Jacek Antonelli | 2008-08-15 23:45:04 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:45:04 -0500 |
commit | 117e22047c5752352342d64e3fb7ce00a4eb8113 (patch) | |
tree | e32de2cfba0dda8705ae528fcd1fbe23ba075685 /linden/indra/newview/llimpanel.cpp | |
parent | Second Life viewer sources 1.18.0.6 (diff) | |
download | meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.zip meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.tar.gz meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.tar.bz2 meta-impy-117e22047c5752352342d64e3fb7ce00a4eb8113.tar.xz |
Second Life viewer sources 1.18.1.2
Diffstat (limited to 'linden/indra/newview/llimpanel.cpp')
-rw-r--r-- | linden/indra/newview/llimpanel.cpp | 1020 |
1 files changed, 954 insertions, 66 deletions
diff --git a/linden/indra/newview/llimpanel.cpp b/linden/indra/newview/llimpanel.cpp index b74fff0..2529d04 100644 --- a/linden/indra/newview/llimpanel.cpp +++ b/linden/indra/newview/llimpanel.cpp | |||
@@ -33,7 +33,6 @@ | |||
33 | #include "indra_constants.h" | 33 | #include "indra_constants.h" |
34 | #include "llfocusmgr.h" | 34 | #include "llfocusmgr.h" |
35 | #include "llfontgl.h" | 35 | #include "llfontgl.h" |
36 | #include "llhttpclient.h" | ||
37 | #include "llrect.h" | 36 | #include "llrect.h" |
38 | #include "llerror.h" | 37 | #include "llerror.h" |
39 | #include "llstring.h" | 38 | #include "llstring.h" |
@@ -43,12 +42,14 @@ | |||
43 | #include "llagent.h" | 42 | #include "llagent.h" |
44 | #include "llbutton.h" | 43 | #include "llbutton.h" |
45 | #include "llcallingcard.h" | 44 | #include "llcallingcard.h" |
45 | #include "llchat.h" | ||
46 | #include "llconsole.h" | 46 | #include "llconsole.h" |
47 | #include "llfloater.h" | 47 | #include "llfloater.h" |
48 | #include "llinventory.h" | 48 | #include "llinventory.h" |
49 | #include "llinventorymodel.h" | 49 | #include "llinventorymodel.h" |
50 | #include "llinventoryview.h" | 50 | #include "llinventoryview.h" |
51 | #include "llfloateravatarinfo.h" | 51 | #include "llfloateravatarinfo.h" |
52 | #include "llfloaterchat.h" | ||
52 | #include "llkeyboard.h" | 53 | #include "llkeyboard.h" |
53 | #include "lllineeditor.h" | 54 | #include "lllineeditor.h" |
54 | #include "llresmgr.h" | 55 | #include "llresmgr.h" |
@@ -61,8 +62,13 @@ | |||
61 | #include "llvieweruictrlfactory.h" | 62 | #include "llvieweruictrlfactory.h" |
62 | #include "lllogchat.h" | 63 | #include "lllogchat.h" |
63 | #include "llfloaterhtml.h" | 64 | #include "llfloaterhtml.h" |
64 | #include "llviewerregion.h" | ||
65 | #include "llweb.h" | 65 | #include "llweb.h" |
66 | #include "llhttpclient.h" | ||
67 | #include "llfloateractivespeakers.h" // LLSpeakerMgr | ||
68 | #include "llfloatergroupinfo.h" | ||
69 | #include "llsdutil.h" | ||
70 | #include "llnotify.h" | ||
71 | #include "llmutelist.h" | ||
66 | 72 | ||
67 | // | 73 | // |
68 | // Constants | 74 | // Constants |
@@ -79,6 +85,10 @@ static LLString sTitleString = "Instant Message with [NAME]"; | |||
79 | static LLString sTypingStartString = "[NAME]: ..."; | 85 | static LLString sTypingStartString = "[NAME]: ..."; |
80 | static LLString sSessionStartString = "Starting session with [NAME] please wait."; | 86 | static LLString sSessionStartString = "Starting session with [NAME] please wait."; |
81 | 87 | ||
88 | LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; | ||
89 | LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; | ||
90 | LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; | ||
91 | |||
82 | void session_starter_helper(const LLUUID& temp_session_id, | 92 | void session_starter_helper(const LLUUID& temp_session_id, |
83 | const LLUUID& other_participant_id, | 93 | const LLUUID& other_participant_id, |
84 | EInstantMessage im_type) | 94 | EInstantMessage im_type) |
@@ -161,45 +171,669 @@ bool send_start_session_messages(const LLUUID& temp_session_id, | |||
161 | return false; | 171 | return false; |
162 | } | 172 | } |
163 | 173 | ||
164 | // Member Functions | 174 | class LLVoiceCallCapResponder : public LLHTTPClient::Responder |
175 | { | ||
176 | public: | ||
177 | LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; | ||
178 | |||
179 | virtual void error(U32 status, const std::string& reason); // called with bad status codes | ||
180 | virtual void result(const LLSD& content); | ||
181 | |||
182 | private: | ||
183 | LLUUID mSessionID; | ||
184 | }; | ||
185 | |||
186 | |||
187 | void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) | ||
188 | { | ||
189 | llwarns << "LLVoiceCallCapResponder::error(" | ||
190 | << status << ": " << reason << ")" | ||
191 | << llendl; | ||
192 | LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); | ||
193 | if (channelp) | ||
194 | { | ||
195 | channelp->deactivate(); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | void LLVoiceCallCapResponder::result(const LLSD& content) | ||
200 | { | ||
201 | LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); | ||
202 | if (channelp) | ||
203 | { | ||
204 | //*TODO: DEBUG SPAM | ||
205 | LLSD::map_const_iterator iter; | ||
206 | for(iter = content.beginMap(); iter != content.endMap(); ++iter) | ||
207 | { | ||
208 | llinfos << "LLVoiceCallCapResponder::result got " | ||
209 | << iter->first << llendl; | ||
210 | } | ||
211 | |||
212 | channelp->setChannelInfo( | ||
213 | content["voice_credentials"]["channel_uri"].asString(), | ||
214 | content["voice_credentials"]["channel_credentials"].asString()); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | // | ||
219 | // LLVoiceChannel | ||
220 | // | ||
221 | LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const LLString& session_name) : | ||
222 | mSessionID(session_id), | ||
223 | mState(STATE_NO_CHANNEL_INFO), | ||
224 | mSessionName(session_name), | ||
225 | mIgnoreNextSessionLeave(FALSE) | ||
226 | { | ||
227 | mNotifyArgs["[VOICE_CHANNEL_NAME]"] = mSessionName; | ||
228 | |||
229 | if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) | ||
230 | { | ||
231 | // a voice channel already exists for this session id, so this instance will be orphaned | ||
232 | // the end result should simply be the failure to make voice calls | ||
233 | llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; | ||
234 | } | ||
235 | |||
236 | LLVoiceClient::getInstance()->addStatusObserver(this); | ||
237 | } | ||
238 | |||
239 | LLVoiceChannel::~LLVoiceChannel() | ||
240 | { | ||
241 | // CANNOT do this here, since it will crash on quit in the LLVoiceChannelProximal singleton destructor. | ||
242 | // Do it in all other subclass destructors instead. | ||
243 | // deactivate(); | ||
244 | |||
245 | // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. | ||
246 | if(gVoiceClient) | ||
247 | { | ||
248 | gVoiceClient->removeStatusObserver(this); | ||
249 | } | ||
250 | |||
251 | sVoiceChannelMap.erase(mSessionID); | ||
252 | sVoiceChannelURIMap.erase(mURI); | ||
253 | } | ||
254 | |||
255 | void LLVoiceChannel::setChannelInfo( | ||
256 | const LLString& uri, | ||
257 | const LLString& credentials) | ||
258 | { | ||
259 | setURI(uri); | ||
260 | |||
261 | mCredentials = credentials; | ||
262 | |||
263 | if (mState == STATE_NO_CHANNEL_INFO) | ||
264 | { | ||
265 | if(!mURI.empty() && !mCredentials.empty()) | ||
266 | { | ||
267 | setState(STATE_READY); | ||
268 | |||
269 | // if we are supposed to be active, reconnect | ||
270 | // this will happen on initial connect, as we request credentials on first use | ||
271 | if (sCurrentVoiceChannel == this) | ||
272 | { | ||
273 | // just in case we got new channel info while active | ||
274 | // should move over to new channel | ||
275 | activate(); | ||
276 | } | ||
277 | } | ||
278 | else | ||
279 | { | ||
280 | //*TODO: notify user | ||
281 | llwarns << "Received invalid credentials for channel " << mSessionName << llendl; | ||
282 | deactivate(); | ||
283 | } | ||
284 | } | ||
285 | } | ||
286 | |||
287 | void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) | ||
288 | { | ||
289 | if (channelURI != mURI) | ||
290 | { | ||
291 | return; | ||
292 | } | ||
293 | |||
294 | if (type < BEGIN_ERROR_STATUS) | ||
295 | { | ||
296 | handleStatusChange(type); | ||
297 | } | ||
298 | else | ||
299 | { | ||
300 | handleError(type); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | void LLVoiceChannel::handleStatusChange(EStatusType type) | ||
305 | { | ||
306 | // status updates | ||
307 | switch(type) | ||
308 | { | ||
309 | case STATUS_LOGIN_RETRY: | ||
310 | mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); | ||
311 | break; | ||
312 | case STATUS_LOGGED_IN: | ||
313 | if (!mLoginNotificationHandle.isDead()) | ||
314 | { | ||
315 | LLNotifyBox* notifyp = (LLNotifyBox*)LLPanel::getPanelByHandle(mLoginNotificationHandle); | ||
316 | if (notifyp) | ||
317 | { | ||
318 | notifyp->close(); | ||
319 | } | ||
320 | mLoginNotificationHandle.markDead(); | ||
321 | } | ||
322 | break; | ||
323 | case STATUS_LEFT_CHANNEL: | ||
324 | if (callStarted() && !mIgnoreNextSessionLeave) | ||
325 | { | ||
326 | // if forceably removed from channel | ||
327 | // update the UI and revert to default channel | ||
328 | LLNotifyBox::showXml("VoiceChannelDisconnected", mNotifyArgs); | ||
329 | deactivate(); | ||
330 | } | ||
331 | mIgnoreNextSessionLeave = FALSE; | ||
332 | break; | ||
333 | case STATUS_JOINING: | ||
334 | if (callStarted()) | ||
335 | { | ||
336 | setState(STATE_RINGING); | ||
337 | } | ||
338 | break; | ||
339 | case STATUS_JOINED: | ||
340 | if (callStarted()) | ||
341 | { | ||
342 | setState(STATE_CONNECTED); | ||
343 | } | ||
344 | default: | ||
345 | break; | ||
346 | } | ||
347 | } | ||
348 | |||
349 | // default behavior is to just deactivate channel | ||
350 | // derived classes provide specific error messages | ||
351 | void LLVoiceChannel::handleError(EStatusType type) | ||
352 | { | ||
353 | deactivate(); | ||
354 | setState(STATE_ERROR); | ||
355 | } | ||
356 | |||
357 | BOOL LLVoiceChannel::isActive() | ||
358 | { | ||
359 | // only considered active when currently bound channel matches what our channel | ||
360 | return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; | ||
361 | } | ||
362 | |||
363 | BOOL LLVoiceChannel::callStarted() | ||
364 | { | ||
365 | return mState >= STATE_CALL_STARTED; | ||
366 | } | ||
367 | |||
368 | void LLVoiceChannel::deactivate() | ||
369 | { | ||
370 | if (mState >= STATE_RINGING) | ||
371 | { | ||
372 | // ignore session leave event | ||
373 | mIgnoreNextSessionLeave = TRUE; | ||
374 | } | ||
375 | |||
376 | if (callStarted()) | ||
377 | { | ||
378 | setState(STATE_HUNG_UP); | ||
379 | } | ||
380 | if (sCurrentVoiceChannel == this) | ||
381 | { | ||
382 | // default channel is proximal channel | ||
383 | sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); | ||
384 | sCurrentVoiceChannel->activate(); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | void LLVoiceChannel::activate() | ||
389 | { | ||
390 | if (callStarted()) | ||
391 | { | ||
392 | return; | ||
393 | } | ||
394 | |||
395 | // deactivate old channel and mark ourselves as the active one | ||
396 | if (sCurrentVoiceChannel != this) | ||
397 | { | ||
398 | if (sCurrentVoiceChannel) | ||
399 | { | ||
400 | sCurrentVoiceChannel->deactivate(); | ||
401 | } | ||
402 | sCurrentVoiceChannel = this; | ||
403 | } | ||
404 | |||
405 | if (mState == STATE_NO_CHANNEL_INFO) | ||
406 | { | ||
407 | // responsible for setting status to active | ||
408 | getChannelInfo(); | ||
409 | } | ||
410 | else | ||
411 | { | ||
412 | setState(STATE_CALL_STARTED); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | void LLVoiceChannel::getChannelInfo() | ||
417 | { | ||
418 | // pretend we have everything we need | ||
419 | if (sCurrentVoiceChannel == this) | ||
420 | { | ||
421 | setState(STATE_CALL_STARTED); | ||
422 | } | ||
423 | } | ||
424 | |||
425 | //static | ||
426 | LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) | ||
427 | { | ||
428 | voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); | ||
429 | if (found_it == sVoiceChannelMap.end()) | ||
430 | { | ||
431 | return NULL; | ||
432 | } | ||
433 | else | ||
434 | { | ||
435 | return found_it->second; | ||
436 | } | ||
437 | } | ||
438 | |||
439 | //static | ||
440 | LLVoiceChannel* LLVoiceChannel::getChannelByURI(LLString uri) | ||
441 | { | ||
442 | voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); | ||
443 | if (found_it == sVoiceChannelURIMap.end()) | ||
444 | { | ||
445 | return NULL; | ||
446 | } | ||
447 | else | ||
448 | { | ||
449 | return found_it->second; | ||
450 | } | ||
451 | } | ||
452 | |||
453 | |||
454 | void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) | ||
455 | { | ||
456 | sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); | ||
457 | mSessionID = new_session_id; | ||
458 | sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); | ||
459 | } | ||
460 | |||
461 | void LLVoiceChannel::setURI(LLString uri) | ||
462 | { | ||
463 | sVoiceChannelURIMap.erase(mURI); | ||
464 | mURI = uri; | ||
465 | sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); | ||
466 | } | ||
467 | |||
468 | void LLVoiceChannel::setState(EState state) | ||
469 | { | ||
470 | switch(state) | ||
471 | { | ||
472 | case STATE_RINGING: | ||
473 | gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); | ||
474 | break; | ||
475 | case STATE_CONNECTED: | ||
476 | gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); | ||
477 | break; | ||
478 | case STATE_HUNG_UP: | ||
479 | gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); | ||
480 | break; | ||
481 | default: | ||
482 | break; | ||
483 | } | ||
484 | |||
485 | mState = state; | ||
486 | } | ||
487 | |||
488 | |||
489 | //static | ||
490 | void LLVoiceChannel::initClass() | ||
491 | { | ||
492 | sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); | ||
493 | } | ||
494 | |||
495 | // | ||
496 | // LLVoiceChannelGroup | ||
497 | // | ||
498 | |||
499 | LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name) : | ||
500 | LLVoiceChannel(session_id, session_name) | ||
501 | { | ||
502 | } | ||
503 | |||
504 | LLVoiceChannelGroup::~LLVoiceChannelGroup() | ||
505 | { | ||
506 | deactivate(); | ||
507 | } | ||
508 | |||
509 | void LLVoiceChannelGroup::deactivate() | ||
510 | { | ||
511 | if (callStarted()) | ||
512 | { | ||
513 | LLVoiceClient::getInstance()->leaveNonSpatialChannel(); | ||
514 | } | ||
515 | LLVoiceChannel::deactivate(); | ||
516 | } | ||
517 | |||
518 | void LLVoiceChannelGroup::activate() | ||
519 | { | ||
520 | if (callStarted()) return; | ||
521 | |||
522 | LLVoiceChannel::activate(); | ||
523 | |||
524 | if (callStarted()) | ||
525 | { | ||
526 | // we have the channel info, just need to use it now | ||
527 | LLVoiceClient::getInstance()->setNonSpatialChannel( | ||
528 | mURI, | ||
529 | mCredentials); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | void LLVoiceChannelGroup::getChannelInfo() | ||
534 | { | ||
535 | LLViewerRegion* region = gAgent.getRegion(); | ||
536 | if (region) | ||
537 | { | ||
538 | std::string url = region->getCapability("ChatSessionRequest"); | ||
539 | LLSD data; | ||
540 | data["method"] = "call"; | ||
541 | data["session-id"] = mSessionID; | ||
542 | LLHTTPClient::post(url, | ||
543 | data, | ||
544 | new LLVoiceCallCapResponder(mSessionID)); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | void LLVoiceChannelGroup::handleError(EStatusType status) | ||
549 | { | ||
550 | std::string notify; | ||
551 | switch(status) | ||
552 | { | ||
553 | case ERROR_CHANNEL_LOCKED: | ||
554 | case ERROR_CHANNEL_FULL: | ||
555 | notify = "VoiceChannelFull"; | ||
556 | break; | ||
557 | case ERROR_UNKNOWN: | ||
558 | break; | ||
559 | default: | ||
560 | break; | ||
561 | } | ||
562 | |||
563 | // notification | ||
564 | if (!notify.empty()) | ||
565 | { | ||
566 | LLNotifyBox::showXml(notify, mNotifyArgs); | ||
567 | // echo to im window | ||
568 | gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, LLNotifyBox::getTemplateMessage(notify, mNotifyArgs).c_str()); | ||
569 | } | ||
570 | |||
571 | LLVoiceChannel::handleError(status); | ||
572 | } | ||
573 | |||
574 | // | ||
575 | // LLVoiceChannelProximal | ||
576 | // | ||
577 | LLVoiceChannelProximal::LLVoiceChannelProximal() : | ||
578 | LLVoiceChannel(LLUUID::null, LLString::null) | ||
579 | { | ||
580 | activate(); | ||
581 | } | ||
582 | |||
583 | LLVoiceChannelProximal::~LLVoiceChannelProximal() | ||
584 | { | ||
585 | // DO NOT call deactivate() here, since this will only happen at atexit() time. | ||
586 | } | ||
587 | |||
588 | BOOL LLVoiceChannelProximal::isActive() | ||
589 | { | ||
590 | return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); | ||
591 | } | ||
592 | |||
593 | void LLVoiceChannelProximal::activate() | ||
594 | { | ||
595 | if (callStarted()) return; | ||
596 | |||
597 | LLVoiceChannel::activate(); | ||
598 | |||
599 | if (callStarted()) | ||
600 | { | ||
601 | // this implicitly puts you back in the spatial channel | ||
602 | LLVoiceClient::getInstance()->leaveNonSpatialChannel(); | ||
603 | } | ||
604 | } | ||
605 | |||
606 | void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) | ||
607 | { | ||
608 | if (!proximal) | ||
609 | { | ||
610 | return; | ||
611 | } | ||
612 | |||
613 | if (type < BEGIN_ERROR_STATUS) | ||
614 | { | ||
615 | handleStatusChange(type); | ||
616 | } | ||
617 | else | ||
618 | { | ||
619 | handleError(type); | ||
620 | } | ||
621 | } | ||
622 | |||
623 | void LLVoiceChannelProximal::handleStatusChange(EStatusType status) | ||
624 | { | ||
625 | // status updates | ||
626 | switch(status) | ||
627 | { | ||
628 | case STATUS_LEFT_CHANNEL: | ||
629 | // do not notify user when leaving proximal channel | ||
630 | return; | ||
631 | default: | ||
632 | break; | ||
633 | } | ||
634 | LLVoiceChannel::handleStatusChange(status); | ||
635 | } | ||
636 | |||
637 | |||
638 | void LLVoiceChannelProximal::handleError(EStatusType status) | ||
639 | { | ||
640 | std::string notify; | ||
641 | switch(status) | ||
642 | { | ||
643 | case ERROR_CHANNEL_LOCKED: | ||
644 | case ERROR_CHANNEL_FULL: | ||
645 | notify = "ProximalVoiceChannelFull"; | ||
646 | break; | ||
647 | default: | ||
648 | break; | ||
649 | } | ||
650 | |||
651 | // notification | ||
652 | if (!notify.empty()) | ||
653 | { | ||
654 | LLNotifyBox::showXml(notify, mNotifyArgs); | ||
655 | } | ||
656 | |||
657 | LLVoiceChannel::handleError(status); | ||
658 | } | ||
659 | |||
660 | void LLVoiceChannelProximal::deactivate() | ||
661 | { | ||
662 | if (callStarted()) | ||
663 | { | ||
664 | setState(STATE_HUNG_UP); | ||
665 | } | ||
666 | } | ||
667 | |||
165 | // | 668 | // |
669 | // LLVoiceChannelP2P | ||
670 | // | ||
671 | LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id) : | ||
672 | LLVoiceChannelGroup(session_id, session_name), | ||
673 | mOtherUserID(other_user_id) | ||
674 | { | ||
675 | // make sure URI reflects encoded version of other user's agent id | ||
676 | setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); | ||
677 | } | ||
678 | |||
679 | LLVoiceChannelP2P::~LLVoiceChannelP2P() | ||
680 | { | ||
681 | deactivate(); | ||
682 | } | ||
683 | |||
684 | void LLVoiceChannelP2P::handleStatusChange(EStatusType type) | ||
685 | { | ||
686 | // status updates | ||
687 | switch(type) | ||
688 | { | ||
689 | case STATUS_LEFT_CHANNEL: | ||
690 | if (callStarted() && !mIgnoreNextSessionLeave) | ||
691 | { | ||
692 | if (mState == STATE_RINGING) | ||
693 | { | ||
694 | // other user declined call | ||
695 | LLNotifyBox::showXml("P2PCallDeclined", mNotifyArgs); | ||
696 | } | ||
697 | else | ||
698 | { | ||
699 | // other user hung up | ||
700 | LLNotifyBox::showXml("VoiceChannelDisconnectedP2P", mNotifyArgs); | ||
701 | } | ||
702 | deactivate(); | ||
703 | } | ||
704 | mIgnoreNextSessionLeave = FALSE; | ||
705 | return; | ||
706 | default: | ||
707 | break; | ||
708 | } | ||
709 | |||
710 | LLVoiceChannelGroup::handleStatusChange(type); | ||
711 | } | ||
712 | |||
713 | void LLVoiceChannelP2P::handleError(EStatusType type) | ||
714 | { | ||
715 | switch(type) | ||
716 | { | ||
717 | case ERROR_NOT_AVAILABLE: | ||
718 | LLNotifyBox::showXml("P2PCallNoAnswer", mNotifyArgs); | ||
719 | break; | ||
720 | default: | ||
721 | break; | ||
722 | } | ||
723 | |||
724 | LLVoiceChannelGroup::handleError(type); | ||
725 | } | ||
726 | |||
727 | void LLVoiceChannelP2P::activate() | ||
728 | { | ||
729 | if (callStarted()) return; | ||
166 | 730 | ||
167 | LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, | 731 | LLVoiceChannel::activate(); |
168 | const LLRect& rect, | 732 | |
169 | const std::string& session_label, | 733 | if (callStarted()) |
170 | const LLUUID& session_id, | 734 | { |
171 | const LLUUID& other_participant_id, | 735 | // no session handle yet, we're starting the call |
172 | EInstantMessage dialog) : | 736 | if (mSessionHandle.empty()) |
737 | { | ||
738 | LLVoiceClient::getInstance()->callUser(mOtherUserID); | ||
739 | } | ||
740 | // otherwise answering the call | ||
741 | else | ||
742 | { | ||
743 | LLVoiceClient::getInstance()->answerInvite(mSessionHandle, mOtherUserID); | ||
744 | // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. | ||
745 | mSessionHandle.clear(); | ||
746 | } | ||
747 | } | ||
748 | } | ||
749 | |||
750 | void LLVoiceChannelP2P::getChannelInfo() | ||
751 | { | ||
752 | // pretend we have everything we need, since P2P doesn't use channel info | ||
753 | if (sCurrentVoiceChannel == this) | ||
754 | { | ||
755 | setState(STATE_CALL_STARTED); | ||
756 | } | ||
757 | } | ||
758 | |||
759 | // receiving session from other user who initiated call | ||
760 | void LLVoiceChannelP2P::setSessionHandle(const LLString& handle) | ||
761 | { | ||
762 | BOOL needs_activate = FALSE; | ||
763 | if (callStarted()) | ||
764 | { | ||
765 | // defer to lower agent id when already active | ||
766 | if (mOtherUserID < gAgent.getID()) | ||
767 | { | ||
768 | // pretend we haven't started the call yet, so we can connect to this session instead | ||
769 | deactivate(); | ||
770 | needs_activate = TRUE; | ||
771 | } | ||
772 | else | ||
773 | { | ||
774 | // we are active and have priority, invite the other user again | ||
775 | // under the assumption they will join this new session | ||
776 | mSessionHandle.clear(); | ||
777 | LLVoiceClient::getInstance()->callUser(mOtherUserID); | ||
778 | return; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | mSessionHandle = handle; | ||
783 | // The URI of a p2p session should always be the other end's SIP URI. | ||
784 | setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); | ||
785 | |||
786 | if (needs_activate) | ||
787 | { | ||
788 | activate(); | ||
789 | } | ||
790 | } | ||
791 | |||
792 | // | ||
793 | // LLFloaterIMPanel | ||
794 | // | ||
795 | LLFloaterIMPanel::LLFloaterIMPanel( | ||
796 | const std::string& name, | ||
797 | const LLRect& rect, | ||
798 | const std::string& session_label, | ||
799 | const LLUUID& session_id, | ||
800 | const LLUUID& other_participant_id, | ||
801 | EInstantMessage dialog) : | ||
173 | LLFloater(name, rect, session_label), | 802 | LLFloater(name, rect, session_label), |
174 | mInputEditor(NULL), | 803 | mInputEditor(NULL), |
175 | mHistoryEditor(NULL), | 804 | mHistoryEditor(NULL), |
176 | mSessionUUID(session_id), | 805 | mSessionUUID(session_id), |
177 | mSessionInitRequested(FALSE), | 806 | mVoiceChannel(NULL), |
178 | mSessionInitialized(FALSE), | 807 | mSessionInitialized(FALSE), |
808 | |||
179 | mOtherParticipantUUID(other_participant_id), | 809 | mOtherParticipantUUID(other_participant_id), |
180 | mDialog(dialog), | 810 | mDialog(dialog), |
181 | mTyping(FALSE), | 811 | mTyping(FALSE), |
182 | mOtherTyping(FALSE), | 812 | mOtherTyping(FALSE), |
183 | mTypingLineStartIndex(0), | 813 | mTypingLineStartIndex(0), |
184 | mSentTypingState(TRUE), | 814 | mSentTypingState(TRUE), |
815 | mShowSpeakersOnConnect(TRUE), | ||
816 | mAutoConnect(FALSE), | ||
817 | mSpeakerPanel(NULL), | ||
185 | mFirstKeystrokeTimer(), | 818 | mFirstKeystrokeTimer(), |
186 | mLastKeystrokeTimer() | 819 | mLastKeystrokeTimer() |
187 | { | 820 | { |
188 | init(session_label); | 821 | init(session_label); |
189 | } | 822 | } |
190 | 823 | ||
191 | LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, | 824 | LLFloaterIMPanel::LLFloaterIMPanel( |
192 | const LLRect& rect, | 825 | const std::string& name, |
193 | const std::string& session_label, | 826 | const LLRect& rect, |
194 | const LLUUID& session_id, | 827 | const std::string& session_label, |
195 | const LLUUID& other_participant_id, | 828 | const LLUUID& session_id, |
196 | const LLDynamicArray<LLUUID>& ids, | 829 | const LLUUID& other_participant_id, |
197 | EInstantMessage dialog) : | 830 | const LLDynamicArray<LLUUID>& ids, |
831 | EInstantMessage dialog) : | ||
198 | LLFloater(name, rect, session_label), | 832 | LLFloater(name, rect, session_label), |
199 | mInputEditor(NULL), | 833 | mInputEditor(NULL), |
200 | mHistoryEditor(NULL), | 834 | mHistoryEditor(NULL), |
201 | mSessionUUID(session_id), | 835 | mSessionUUID(session_id), |
202 | mSessionInitRequested(FALSE), | 836 | mVoiceChannel(NULL), |
203 | mSessionInitialized(FALSE), | 837 | mSessionInitialized(FALSE), |
204 | mOtherParticipantUUID(other_participant_id), | 838 | mOtherParticipantUUID(other_participant_id), |
205 | mDialog(dialog), | 839 | mDialog(dialog), |
@@ -207,6 +841,10 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, | |||
207 | mOtherTyping(FALSE), | 841 | mOtherTyping(FALSE), |
208 | mTypingLineStartIndex(0), | 842 | mTypingLineStartIndex(0), |
209 | mSentTypingState(TRUE), | 843 | mSentTypingState(TRUE), |
844 | mShowSpeakersOnConnect(TRUE), | ||
845 | mAutoConnect(FALSE), | ||
846 | mSpeakers(NULL), | ||
847 | mSpeakerPanel(NULL), | ||
210 | mFirstKeystrokeTimer(), | 848 | mFirstKeystrokeTimer(), |
211 | mLastKeystrokeTimer() | 849 | mLastKeystrokeTimer() |
212 | { | 850 | { |
@@ -217,11 +855,53 @@ LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, | |||
217 | 855 | ||
218 | void LLFloaterIMPanel::init(const LLString& session_label) | 856 | void LLFloaterIMPanel::init(const LLString& session_label) |
219 | { | 857 | { |
858 | LLString xml_filename; | ||
859 | switch(mDialog) | ||
860 | { | ||
861 | case IM_SESSION_GROUP_START: | ||
862 | mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); | ||
863 | xml_filename = "floater_instant_message_group.xml"; | ||
864 | mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); | ||
865 | break; | ||
866 | case IM_SESSION_INVITE: | ||
867 | mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); | ||
868 | if (gAgent.isInGroup(mSessionUUID)) | ||
869 | { | ||
870 | xml_filename = "floater_instant_message_group.xml"; | ||
871 | } | ||
872 | else // must be invite to ad hoc IM | ||
873 | { | ||
874 | xml_filename = "floater_instant_message_ad_hoc.xml"; | ||
875 | } | ||
876 | mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); | ||
877 | break; | ||
878 | case IM_SESSION_P2P_INVITE: | ||
879 | xml_filename = "floater_instant_message.xml"; | ||
880 | mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID); | ||
881 | break; | ||
882 | case IM_SESSION_CONFERENCE_START: | ||
883 | mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this); | ||
884 | xml_filename = "floater_instant_message_ad_hoc.xml"; | ||
885 | mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label); | ||
886 | break; | ||
887 | // just received text from another user | ||
888 | case IM_NOTHING_SPECIAL: | ||
889 | xml_filename = "floater_instant_message.xml"; | ||
890 | mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID); | ||
891 | break; | ||
892 | default: | ||
893 | llwarns << "Unknown session type" << llendl; | ||
894 | xml_filename = "floater_instant_message.xml"; | ||
895 | break; | ||
896 | } | ||
897 | |||
898 | mSpeakers = new LLIMSpeakerMgr(mVoiceChannel); | ||
899 | |||
220 | gUICtrlFactory->buildFloater(this, | 900 | gUICtrlFactory->buildFloater(this, |
221 | "floater_instant_message.xml", | 901 | xml_filename, |
222 | NULL, | 902 | &getFactoryMap(), |
223 | FALSE); | 903 | FALSE); |
224 | 904 | ||
225 | setLabel(session_label); | 905 | setLabel(session_label); |
226 | setTitle(session_label); | 906 | setTitle(session_label); |
227 | mInputEditor->setMaxTextLength(1023); | 907 | mInputEditor->setMaxTextLength(1023); |
@@ -256,21 +936,32 @@ void LLFloaterIMPanel::init(const LLString& session_label) | |||
256 | mSessionStartMsgPos = | 936 | mSessionStartMsgPos = |
257 | mHistoryEditor->getText().length(); | 937 | mHistoryEditor->getText().length(); |
258 | 938 | ||
259 | bool log_to_file = false; | ||
260 | addHistoryLine( | 939 | addHistoryLine( |
261 | session_start, | 940 | session_start, |
262 | LLColor4::grey, | 941 | gSavedSettings.getColor4("SystemChatColor"), |
263 | log_to_file); | 942 | false); |
264 | } | 943 | } |
265 | } | 944 | } |
266 | } | 945 | } |
267 | 946 | ||
268 | 947 | ||
948 | LLFloaterIMPanel::~LLFloaterIMPanel() | ||
949 | { | ||
950 | delete mSpeakers; | ||
951 | mSpeakers = NULL; | ||
952 | |||
953 | //kicks you out of the voice channel if it is currently active | ||
954 | |||
955 | // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point). | ||
956 | mVoiceChannel->deactivate(); | ||
957 | |||
958 | delete mVoiceChannel; | ||
959 | mVoiceChannel = NULL; | ||
960 | } | ||
961 | |||
269 | BOOL LLFloaterIMPanel::postBuild() | 962 | BOOL LLFloaterIMPanel::postBuild() |
270 | { | 963 | { |
271 | requires("chat_editor", WIDGET_TYPE_LINE_EDITOR); | 964 | requires("chat_editor", WIDGET_TYPE_LINE_EDITOR); |
272 | requires("profile_btn", WIDGET_TYPE_BUTTON); | ||
273 | requires("close_btn", WIDGET_TYPE_BUTTON); | ||
274 | requires("im_history", WIDGET_TYPE_TEXT_EDITOR); | 965 | requires("im_history", WIDGET_TYPE_TEXT_EDITOR); |
275 | requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX); | 966 | requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX); |
276 | requires("title_string", WIDGET_TYPE_TEXT_BOX); | 967 | requires("title_string", WIDGET_TYPE_TEXT_BOX); |
@@ -283,22 +974,28 @@ BOOL LLFloaterIMPanel::postBuild() | |||
283 | mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived ); | 974 | mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived ); |
284 | mInputEditor->setFocusLostCallback( onInputEditorFocusLost ); | 975 | mInputEditor->setFocusLostCallback( onInputEditorFocusLost ); |
285 | mInputEditor->setKeystrokeCallback( onInputEditorKeystroke ); | 976 | mInputEditor->setKeystrokeCallback( onInputEditorKeystroke ); |
977 | mInputEditor->setCommitCallback( onCommitChat ); | ||
286 | mInputEditor->setCallbackUserData(this); | 978 | mInputEditor->setCallbackUserData(this); |
287 | mInputEditor->setCommitOnFocusLost( FALSE ); | 979 | mInputEditor->setCommitOnFocusLost( FALSE ); |
288 | mInputEditor->setRevertOnEsc( FALSE ); | 980 | mInputEditor->setRevertOnEsc( FALSE ); |
289 | 981 | ||
290 | LLButton* profile_btn = LLUICtrlFactory::getButtonByName(this, "profile_btn"); | 982 | childSetAction("profile_callee_btn", onClickProfile, this); |
291 | profile_btn->setClickedCallback(&LLFloaterIMPanel::onClickProfile, this); | 983 | childSetAction("group_info_btn", onClickGroupInfo, this); |
984 | |||
985 | childSetAction("start_call_btn", onClickStartCall, this); | ||
986 | childSetAction("end_call_btn", onClickEndCall, this); | ||
987 | childSetAction("send_btn", onClickSend, this); | ||
988 | childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this); | ||
292 | 989 | ||
293 | LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn"); | 990 | //LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn"); |
294 | close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); | 991 | //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); |
295 | 992 | ||
296 | mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history"); | 993 | mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history"); |
297 | mHistoryEditor->setParseHTML(TRUE); | 994 | mHistoryEditor->setParseHTML(TRUE); |
298 | 995 | ||
299 | if (IM_SESSION_GROUP_START == mDialog) | 996 | if ( IM_SESSION_GROUP_START == mDialog ) |
300 | { | 997 | { |
301 | profile_btn->setEnabled(FALSE); | 998 | childSetEnabled("profile_btn", FALSE); |
302 | } | 999 | } |
303 | LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string"); | 1000 | LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string"); |
304 | sTitleString = title->getText(); | 1001 | sTitleString = title->getText(); |
@@ -311,16 +1008,91 @@ BOOL LLFloaterIMPanel::postBuild() | |||
311 | this, | 1008 | this, |
312 | "session_start_string"); | 1009 | "session_start_string"); |
313 | sSessionStartString = session_start->getText(); | 1010 | sSessionStartString = session_start->getText(); |
1011 | if (mSpeakerPanel) | ||
1012 | { | ||
1013 | mSpeakerPanel->refreshSpeakers(); | ||
1014 | } | ||
314 | 1015 | ||
1016 | if (mDialog == IM_NOTHING_SPECIAL) | ||
1017 | { | ||
1018 | childSetCommitCallback("mute_btn", onClickMuteVoice, this); | ||
1019 | childSetCommitCallback("speaker_volume", onVolumeChange, this); | ||
1020 | } | ||
1021 | |||
1022 | setDefaultBtn("send_btn"); | ||
315 | return TRUE; | 1023 | return TRUE; |
316 | } | 1024 | } |
317 | 1025 | ||
318 | return FALSE; | 1026 | return FALSE; |
319 | } | 1027 | } |
320 | 1028 | ||
1029 | void* LLFloaterIMPanel::createSpeakersPanel(void* data) | ||
1030 | { | ||
1031 | LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)data; | ||
1032 | floaterp->mSpeakerPanel = new LLPanelActiveSpeakers(floaterp->mSpeakers, TRUE); | ||
1033 | return floaterp->mSpeakerPanel; | ||
1034 | } | ||
1035 | |||
1036 | //static | ||
1037 | void LLFloaterIMPanel::onClickMuteVoice(LLUICtrl* source, void* user_data) | ||
1038 | { | ||
1039 | LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; | ||
1040 | if (floaterp) | ||
1041 | { | ||
1042 | BOOL is_muted = gMuteListp->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat); | ||
1043 | |||
1044 | LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT); | ||
1045 | if (!is_muted) | ||
1046 | { | ||
1047 | gMuteListp->add(mute, LLMute::flagVoiceChat); | ||
1048 | } | ||
1049 | else | ||
1050 | { | ||
1051 | gMuteListp->remove(mute, LLMute::flagVoiceChat); | ||
1052 | } | ||
1053 | } | ||
1054 | } | ||
1055 | |||
1056 | //static | ||
1057 | void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data) | ||
1058 | { | ||
1059 | LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; | ||
1060 | if (floaterp) | ||
1061 | { | ||
1062 | gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); | ||
1063 | } | ||
1064 | } | ||
1065 | |||
1066 | |||
321 | // virtual | 1067 | // virtual |
322 | void LLFloaterIMPanel::draw() | 1068 | void LLFloaterIMPanel::draw() |
323 | { | 1069 | { |
1070 | LLViewerRegion* region = gAgent.getRegion(); | ||
1071 | |||
1072 | BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "") | ||
1073 | && mSessionInitialized | ||
1074 | && LLVoiceClient::voiceEnabled(); | ||
1075 | |||
1076 | // hide/show start call and end call buttons | ||
1077 | childSetVisible("end_call_btn", mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); | ||
1078 | childSetVisible("start_call_btn", mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); | ||
1079 | childSetEnabled("start_call_btn", enable_connect); | ||
1080 | childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty()); | ||
1081 | |||
1082 | if (mAutoConnect && enable_connect) | ||
1083 | { | ||
1084 | onClickStartCall(this); | ||
1085 | mAutoConnect = FALSE; | ||
1086 | } | ||
1087 | |||
1088 | // show speakers window when voice first connects | ||
1089 | if (mShowSpeakersOnConnect && mVoiceChannel->isActive()) | ||
1090 | { | ||
1091 | childSetVisible("active_speakers_panel", TRUE); | ||
1092 | mShowSpeakersOnConnect = FALSE; | ||
1093 | } | ||
1094 | childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel")); | ||
1095 | |||
324 | if (mTyping) | 1096 | if (mTyping) |
325 | { | 1097 | { |
326 | // Time out if user hasn't typed for a while. | 1098 | // Time out if user hasn't typed for a while. |
@@ -339,6 +1111,19 @@ void LLFloaterIMPanel::draw() | |||
339 | } | 1111 | } |
340 | } | 1112 | } |
341 | 1113 | ||
1114 | if (mSpeakerPanel) | ||
1115 | { | ||
1116 | mSpeakerPanel->refreshSpeakers(); | ||
1117 | } | ||
1118 | else | ||
1119 | { | ||
1120 | // refresh volume and mute checkbox | ||
1121 | childSetEnabled("speaker_volume", mVoiceChannel->isActive()); | ||
1122 | childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID)); | ||
1123 | |||
1124 | childSetValue("mute_btn", gMuteListp->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)); | ||
1125 | childSetEnabled("mute_btn", mVoiceChannel->isActive()); | ||
1126 | } | ||
342 | LLFloater::draw(); | 1127 | LLFloater::draw(); |
343 | } | 1128 | } |
344 | 1129 | ||
@@ -363,16 +1148,22 @@ private: | |||
363 | 1148 | ||
364 | BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) | 1149 | BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) |
365 | { | 1150 | { |
1151 | LLViewerRegion* region = gAgent.getRegion(); | ||
1152 | if (!region) | ||
1153 | { | ||
1154 | return FALSE; | ||
1155 | } | ||
1156 | |||
366 | S32 count = ids.count(); | 1157 | S32 count = ids.count(); |
367 | 1158 | ||
368 | if( isAddAllowed() && (count > 0) ) | 1159 | if( isInviteAllowed() && (count > 0) ) |
369 | { | 1160 | { |
370 | llinfos << "LLFloaterIMPanel::inviteToSession() - adding participants" << llendl; | 1161 | llinfos << "LLFloaterIMPanel::inviteToSession() - inviting participants" << llendl; |
371 | 1162 | ||
372 | std::string url = | 1163 | std::string url = region->getCapability("ChatSessionRequest"); |
373 | gAgent.getRegion()->getCapability("ChatSessionRequest"); | ||
374 | 1164 | ||
375 | LLSD data; | 1165 | LLSD data; |
1166 | |||
376 | data["params"] = LLSD::emptyArray(); | 1167 | data["params"] = LLSD::emptyArray(); |
377 | for (int i = 0; i < count; i++) | 1168 | for (int i = 0; i < count; i++) |
378 | { | 1169 | { |
@@ -399,6 +1190,13 @@ BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) | |||
399 | return TRUE; | 1190 | return TRUE; |
400 | } | 1191 | } |
401 | 1192 | ||
1193 | void LLFloaterIMPanel::addHistoryLine(const LLUUID& source, const std::string &utf8msg, const LLColor4& color, bool log_to_file) | ||
1194 | { | ||
1195 | addHistoryLine(utf8msg, color, log_to_file); | ||
1196 | mSpeakers->speakerChatted(source); | ||
1197 | mSpeakers->setSpeakerTyping(source, FALSE); | ||
1198 | } | ||
1199 | |||
402 | void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file) | 1200 | void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file) |
403 | { | 1201 | { |
404 | LLMultiFloater* hostp = getHost(); | 1202 | LLMultiFloater* hostp = getHost(); |
@@ -412,7 +1210,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4 | |||
412 | // Now we're adding the actual line of text, so erase the | 1210 | // Now we're adding the actual line of text, so erase the |
413 | // "Foo is typing..." text segment, and the optional timestamp | 1211 | // "Foo is typing..." text segment, and the optional timestamp |
414 | // if it was present. JC | 1212 | // if it was present. JC |
415 | removeTypingIndicator(); | 1213 | removeTypingIndicator(NULL); |
416 | 1214 | ||
417 | // Actually add the line | 1215 | // Actually add the line |
418 | LLString timestring; | 1216 | LLString timestring; |
@@ -491,7 +1289,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren | |||
491 | // but not shift-return or control-return | 1289 | // but not shift-return or control-return |
492 | if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) ) | 1290 | if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) ) |
493 | { | 1291 | { |
494 | gIMView->toggle(NULL); | 1292 | gIMMgr->toggle(NULL); |
495 | } | 1293 | } |
496 | } | 1294 | } |
497 | else if ( KEY_ESCAPE == key ) | 1295 | else if ( KEY_ESCAPE == key ) |
@@ -502,7 +1300,7 @@ BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_paren | |||
502 | // Close talk panel with escape | 1300 | // Close talk panel with escape |
503 | if( !gSavedSettings.getBOOL("PinTalkViewOpen") ) | 1301 | if( !gSavedSettings.getBOOL("PinTalkViewOpen") ) |
504 | { | 1302 | { |
505 | gIMView->toggle(NULL); | 1303 | gIMMgr->toggle(NULL); |
506 | } | 1304 | } |
507 | } | 1305 | } |
508 | } | 1306 | } |
@@ -543,7 +1341,7 @@ BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, | |||
543 | 1341 | ||
544 | BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop) | 1342 | BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop) |
545 | { | 1343 | { |
546 | BOOL rv = isAddAllowed(); | 1344 | BOOL rv = isInviteAllowed(); |
547 | if(rv && item && item->getCreatorUUID().notNull()) | 1345 | if(rv && item && item->getCreatorUUID().notNull()) |
548 | { | 1346 | { |
549 | if(drop) | 1347 | if(drop) |
@@ -563,7 +1361,7 @@ BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop) | |||
563 | 1361 | ||
564 | BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop) | 1362 | BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop) |
565 | { | 1363 | { |
566 | BOOL rv = isAddAllowed(); | 1364 | BOOL rv = isInviteAllowed(); |
567 | if(rv && category) | 1365 | if(rv && category) |
568 | { | 1366 | { |
569 | LLInventoryModel::cat_array_t cats; | 1367 | LLInventoryModel::cat_array_t cats; |
@@ -592,11 +1390,11 @@ BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop) | |||
592 | return rv; | 1390 | return rv; |
593 | } | 1391 | } |
594 | 1392 | ||
595 | BOOL LLFloaterIMPanel::isAddAllowed() const | 1393 | BOOL LLFloaterIMPanel::isInviteAllowed() const |
596 | { | 1394 | { |
597 | 1395 | ||
598 | return ((IM_SESSION_CONFERENCE_START == mDialog) | 1396 | return ( (IM_SESSION_CONFERENCE_START == mDialog) |
599 | || (IM_SESSION_INVITE) ); | 1397 | || (IM_SESSION_INVITE == mDialog) ); |
600 | } | 1398 | } |
601 | 1399 | ||
602 | 1400 | ||
@@ -621,6 +1419,15 @@ void LLFloaterIMPanel::onClickProfile( void* userdata ) | |||
621 | } | 1419 | } |
622 | 1420 | ||
623 | // static | 1421 | // static |
1422 | void LLFloaterIMPanel::onClickGroupInfo( void* userdata ) | ||
1423 | { | ||
1424 | // Bring up the Profile window | ||
1425 | LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; | ||
1426 | |||
1427 | LLFloaterGroupInfo::showFromUUID(self->mSessionUUID); | ||
1428 | } | ||
1429 | |||
1430 | // static | ||
624 | void LLFloaterIMPanel::onClickClose( void* userdata ) | 1431 | void LLFloaterIMPanel::onClickClose( void* userdata ) |
625 | { | 1432 | { |
626 | LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; | 1433 | LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; |
@@ -631,6 +1438,44 @@ void LLFloaterIMPanel::onClickClose( void* userdata ) | |||
631 | } | 1438 | } |
632 | 1439 | ||
633 | // static | 1440 | // static |
1441 | void LLFloaterIMPanel::onClickStartCall(void* userdata) | ||
1442 | { | ||
1443 | LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; | ||
1444 | |||
1445 | self->mVoiceChannel->activate(); | ||
1446 | } | ||
1447 | |||
1448 | // static | ||
1449 | void LLFloaterIMPanel::onClickEndCall(void* userdata) | ||
1450 | { | ||
1451 | LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; | ||
1452 | |||
1453 | self->getVoiceChannel()->deactivate(); | ||
1454 | } | ||
1455 | |||
1456 | // static | ||
1457 | void LLFloaterIMPanel::onClickSend(void* userdata) | ||
1458 | { | ||
1459 | LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata; | ||
1460 | self->sendMsg(); | ||
1461 | } | ||
1462 | |||
1463 | // static | ||
1464 | void LLFloaterIMPanel::onClickToggleActiveSpeakers(void* userdata) | ||
1465 | { | ||
1466 | LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata; | ||
1467 | |||
1468 | self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel")); | ||
1469 | } | ||
1470 | |||
1471 | // static | ||
1472 | void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata) | ||
1473 | { | ||
1474 | LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; | ||
1475 | self->sendMsg(); | ||
1476 | } | ||
1477 | |||
1478 | // static | ||
634 | void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata ) | 1479 | void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata ) |
635 | { | 1480 | { |
636 | LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; | 1481 | LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata; |
@@ -681,7 +1526,7 @@ void LLFloaterIMPanel::onClose(bool app_quitting) | |||
681 | mSessionUUID); | 1526 | mSessionUUID); |
682 | gAgent.sendReliableMessage(); | 1527 | gAgent.sendReliableMessage(); |
683 | } | 1528 | } |
684 | gIMView->removeSession(mSessionUUID); | 1529 | gIMMgr->removeSession(mSessionUUID); |
685 | 1530 | ||
686 | destroy(); | 1531 | destroy(); |
687 | } | 1532 | } |
@@ -768,11 +1613,22 @@ void LLFloaterIMPanel::sendMsg() | |||
768 | history_echo += ": "; | 1613 | history_echo += ": "; |
769 | } | 1614 | } |
770 | history_echo += utf8_text; | 1615 | history_echo += utf8_text; |
771 | addHistoryLine(history_echo); | 1616 | |
1617 | BOOL other_was_typing = mOtherTyping; | ||
1618 | |||
1619 | addHistoryLine(gAgent.getID(), history_echo); | ||
1620 | |||
1621 | if (other_was_typing) | ||
1622 | { | ||
1623 | addTypingIndicator(mOtherTypingName); | ||
1624 | } | ||
1625 | |||
772 | } | 1626 | } |
773 | } | 1627 | } |
774 | else | 1628 | else |
775 | { | 1629 | { |
1630 | //queue up the message to send once the session is | ||
1631 | //initialized | ||
776 | mQueuedMsgsForInit.append(utf8_text); | 1632 | mQueuedMsgsForInit.append(utf8_text); |
777 | } | 1633 | } |
778 | 1634 | ||
@@ -786,15 +1642,31 @@ void LLFloaterIMPanel::sendMsg() | |||
786 | mSentTypingState = TRUE; | 1642 | mSentTypingState = TRUE; |
787 | } | 1643 | } |
788 | 1644 | ||
1645 | void LLFloaterIMPanel::updateSpeakersList(LLSD speaker_updates) | ||
1646 | { | ||
1647 | mSpeakers->processSpeakerListUpdate(speaker_updates); | ||
1648 | } | ||
1649 | |||
1650 | void LLFloaterIMPanel::setSpeakersListFromMap(LLSD speaker_map) | ||
1651 | { | ||
1652 | mSpeakers->processSpeakerMap(speaker_map); | ||
1653 | } | ||
1654 | |||
1655 | void LLFloaterIMPanel::setSpeakersList(LLSD speaker_list) | ||
1656 | { | ||
1657 | mSpeakers->processSpeakerList(speaker_list); | ||
1658 | } | ||
1659 | |||
789 | void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) | 1660 | void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) |
790 | { | 1661 | { |
791 | mSessionUUID = session_id; | 1662 | mSessionUUID = session_id; |
1663 | mVoiceChannel->updateSessionID(session_id); | ||
792 | mSessionInitialized = TRUE; | 1664 | mSessionInitialized = TRUE; |
793 | 1665 | ||
794 | //we assume the history editor hasn't moved at all since | 1666 | //we assume the history editor hasn't moved at all since |
795 | //we added the starting session message | 1667 | //we added the starting session message |
796 | //so, we count how many characters to remove | 1668 | //so, we count how many characters to remove |
797 | S32 chars_to_remove = mHistoryEditor->getText().length() - | 1669 | S32 chars_to_remove = mHistoryEditor->getText().length() - |
798 | mSessionStartMsgPos; | 1670 | mSessionStartMsgPos; |
799 | mHistoryEditor->removeTextFromEnd(chars_to_remove); | 1671 | mHistoryEditor->removeTextFromEnd(chars_to_remove); |
800 | 1672 | ||
@@ -804,13 +1676,18 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) | |||
804 | iter != mQueuedMsgsForInit.endArray(); | 1676 | iter != mQueuedMsgsForInit.endArray(); |
805 | ++iter) | 1677 | ++iter) |
806 | { | 1678 | { |
807 | deliver_message(iter->asString(), | 1679 | deliver_message( |
808 | mSessionUUID, | 1680 | iter->asString(), |
809 | mOtherParticipantUUID, | 1681 | mSessionUUID, |
810 | mDialog); | 1682 | mOtherParticipantUUID, |
1683 | mDialog); | ||
811 | } | 1684 | } |
812 | } | 1685 | } |
813 | 1686 | ||
1687 | void LLFloaterIMPanel::requestAutoConnect() | ||
1688 | { | ||
1689 | mAutoConnect = TRUE; | ||
1690 | } | ||
814 | 1691 | ||
815 | void LLFloaterIMPanel::setTyping(BOOL typing) | 1692 | void LLFloaterIMPanel::setTyping(BOOL typing) |
816 | { | 1693 | { |
@@ -827,6 +1704,8 @@ void LLFloaterIMPanel::setTyping(BOOL typing) | |||
827 | // Will send typing state after a short delay. | 1704 | // Will send typing state after a short delay. |
828 | mSentTypingState = FALSE; | 1705 | mSentTypingState = FALSE; |
829 | } | 1706 | } |
1707 | |||
1708 | mSpeakers->setSpeakerTyping(gAgent.getID(), TRUE); | ||
830 | } | 1709 | } |
831 | else | 1710 | else |
832 | { | 1711 | { |
@@ -836,6 +1715,7 @@ void LLFloaterIMPanel::setTyping(BOOL typing) | |||
836 | sendTypingState(FALSE); | 1715 | sendTypingState(FALSE); |
837 | mSentTypingState = TRUE; | 1716 | mSentTypingState = TRUE; |
838 | } | 1717 | } |
1718 | mSpeakers->setSpeakerTyping(gAgent.getID(), FALSE); | ||
839 | } | 1719 | } |
840 | 1720 | ||
841 | mTyping = typing; | 1721 | mTyping = typing; |
@@ -864,35 +1744,40 @@ void LLFloaterIMPanel::sendTypingState(BOOL typing) | |||
864 | gAgent.sendReliableMessage(); | 1744 | gAgent.sendReliableMessage(); |
865 | } | 1745 | } |
866 | 1746 | ||
867 | |||
868 | void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing) | 1747 | void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing) |
869 | { | 1748 | { |
870 | if (typing) | 1749 | if (typing) |
871 | { | 1750 | { |
872 | // other user started typing | 1751 | // other user started typing |
873 | addTypingIndicator(im_info); | 1752 | addTypingIndicator(im_info->mName); |
874 | } | 1753 | } |
875 | else | 1754 | else |
876 | { | 1755 | { |
877 | // other user stopped typing | 1756 | // other user stopped typing |
878 | removeTypingIndicator(); | 1757 | removeTypingIndicator(im_info); |
879 | } | 1758 | } |
880 | } | 1759 | } |
881 | 1760 | ||
882 | 1761 | ||
883 | void LLFloaterIMPanel::addTypingIndicator(const LLIMInfo* im_info) | 1762 | void LLFloaterIMPanel::addTypingIndicator(const std::string &name) |
884 | { | 1763 | { |
885 | mTypingLineStartIndex = mHistoryEditor->getText().length(); | 1764 | // we may have lost a "stop-typing" packet, don't add it twice |
886 | 1765 | if (!mOtherTyping) | |
887 | LLUIString typing_start = sTypingStartString; | 1766 | { |
888 | typing_start.setArg("[NAME]", im_info->mName); | 1767 | mTypingLineStartIndex = mHistoryEditor->getText().length(); |
889 | bool log_to_file = false; | 1768 | LLUIString typing_start = sTypingStartString; |
890 | addHistoryLine(typing_start, LLColor4::grey, log_to_file); | 1769 | typing_start.setArg("[NAME]", name); |
891 | mOtherTyping = TRUE; | 1770 | addHistoryLine(typing_start, gSavedSettings.getColor4("SystemChatColor"), false); |
1771 | mOtherTypingName = name; | ||
1772 | mOtherTyping = TRUE; | ||
1773 | } | ||
1774 | // MBW -- XXX -- merge from release broke this (argument to this function changed from an LLIMInfo to a name) | ||
1775 | // Richard will fix. | ||
1776 | // mSpeakers->setSpeakerTyping(im_info->mFromID, TRUE); | ||
892 | } | 1777 | } |
893 | 1778 | ||
894 | 1779 | ||
895 | void LLFloaterIMPanel::removeTypingIndicator() | 1780 | void LLFloaterIMPanel::removeTypingIndicator(const LLIMInfo* im_info) |
896 | { | 1781 | { |
897 | if (mOtherTyping) | 1782 | if (mOtherTyping) |
898 | { | 1783 | { |
@@ -901,6 +1786,10 @@ void LLFloaterIMPanel::removeTypingIndicator() | |||
901 | 1786 | ||
902 | S32 chars_to_remove = mHistoryEditor->getText().length() - mTypingLineStartIndex; | 1787 | S32 chars_to_remove = mHistoryEditor->getText().length() - mTypingLineStartIndex; |
903 | mHistoryEditor->removeTextFromEnd(chars_to_remove); | 1788 | mHistoryEditor->removeTextFromEnd(chars_to_remove); |
1789 | if (im_info) | ||
1790 | { | ||
1791 | mSpeakers->setSpeakerTyping(im_info->mFromID, FALSE); | ||
1792 | } | ||
904 | } | 1793 | } |
905 | } | 1794 | } |
906 | 1795 | ||
@@ -913,4 +1802,3 @@ void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata) | |||
913 | self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey); | 1802 | self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey); |
914 | 1803 | ||
915 | } | 1804 | } |
916 | |||