aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llimpanel.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:45:04 -0500
committerJacek Antonelli2008-08-15 23:45:04 -0500
commit117e22047c5752352342d64e3fb7ce00a4eb8113 (patch)
treee32de2cfba0dda8705ae528fcd1fbe23ba075685 /linden/indra/newview/llimpanel.cpp
parentSecond Life viewer sources 1.18.0.6 (diff)
downloadmeta-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.cpp1020
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]";
79static LLString sTypingStartString = "[NAME]: ..."; 85static LLString sTypingStartString = "[NAME]: ...";
80static LLString sSessionStartString = "Starting session with [NAME] please wait."; 86static LLString sSessionStartString = "Starting session with [NAME] please wait.";
81 87
88LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap;
89LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap;
90LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL;
91
82void session_starter_helper(const LLUUID& temp_session_id, 92void 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 174class LLVoiceCallCapResponder : public LLHTTPClient::Responder
175{
176public:
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
182private:
183 LLUUID mSessionID;
184};
185
186
187void 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
199void 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//
221LLVoiceChannel::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
239LLVoiceChannel::~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
255void 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
287void 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
304void 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
351void LLVoiceChannel::handleError(EStatusType type)
352{
353 deactivate();
354 setState(STATE_ERROR);
355}
356
357BOOL LLVoiceChannel::isActive()
358{
359 // only considered active when currently bound channel matches what our channel
360 return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI;
361}
362
363BOOL LLVoiceChannel::callStarted()
364{
365 return mState >= STATE_CALL_STARTED;
366}
367
368void 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
388void 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
416void 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
426LLVoiceChannel* 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
440LLVoiceChannel* 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
454void 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
461void LLVoiceChannel::setURI(LLString uri)
462{
463 sVoiceChannelURIMap.erase(mURI);
464 mURI = uri;
465 sVoiceChannelURIMap.insert(std::make_pair(mURI, this));
466}
467
468void 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
490void LLVoiceChannel::initClass()
491{
492 sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
493}
494
495//
496// LLVoiceChannelGroup
497//
498
499LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name) :
500 LLVoiceChannel(session_id, session_name)
501{
502}
503
504LLVoiceChannelGroup::~LLVoiceChannelGroup()
505{
506 deactivate();
507}
508
509void LLVoiceChannelGroup::deactivate()
510{
511 if (callStarted())
512 {
513 LLVoiceClient::getInstance()->leaveNonSpatialChannel();
514 }
515 LLVoiceChannel::deactivate();
516}
517
518void 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
533void 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
548void 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//
577LLVoiceChannelProximal::LLVoiceChannelProximal() :
578 LLVoiceChannel(LLUUID::null, LLString::null)
579{
580 activate();
581}
582
583LLVoiceChannelProximal::~LLVoiceChannelProximal()
584{
585 // DO NOT call deactivate() here, since this will only happen at atexit() time.
586}
587
588BOOL LLVoiceChannelProximal::isActive()
589{
590 return callStarted() && LLVoiceClient::getInstance()->inProximalChannel();
591}
592
593void 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
606void 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
623void 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
638void 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
660void LLVoiceChannelProximal::deactivate()
661{
662 if (callStarted())
663 {
664 setState(STATE_HUNG_UP);
665 }
666}
667
165// 668//
669// LLVoiceChannelP2P
670//
671LLVoiceChannelP2P::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
679LLVoiceChannelP2P::~LLVoiceChannelP2P()
680{
681 deactivate();
682}
683
684void 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
713void 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
727void LLVoiceChannelP2P::activate()
728{
729 if (callStarted()) return;
166 730
167LLFloaterIMPanel::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
750void 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
760void 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//
795LLFloaterIMPanel::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
191LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, 824LLFloaterIMPanel::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
218void LLFloaterIMPanel::init(const LLString& session_label) 856void 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
948LLFloaterIMPanel::~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
269BOOL LLFloaterIMPanel::postBuild() 962BOOL 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
1029void* 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
1037void 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
1057void 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
322void LLFloaterIMPanel::draw() 1068void 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
364BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids) 1149BOOL 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
1193void 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
402void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file) 1200void 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
544BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop) 1342BOOL 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
564BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop) 1362BOOL 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
595BOOL LLFloaterIMPanel::isAddAllowed() const 1393BOOL 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
1422void 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
624void LLFloaterIMPanel::onClickClose( void* userdata ) 1431void 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
1441void LLFloaterIMPanel::onClickStartCall(void* userdata)
1442{
1443 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
1444
1445 self->mVoiceChannel->activate();
1446}
1447
1448// static
1449void LLFloaterIMPanel::onClickEndCall(void* userdata)
1450{
1451 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
1452
1453 self->getVoiceChannel()->deactivate();
1454}
1455
1456// static
1457void LLFloaterIMPanel::onClickSend(void* userdata)
1458{
1459 LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
1460 self->sendMsg();
1461}
1462
1463// static
1464void 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
1472void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata)
1473{
1474 LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
1475 self->sendMsg();
1476}
1477
1478// static
634void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata ) 1479void 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
1645void LLFloaterIMPanel::updateSpeakersList(LLSD speaker_updates)
1646{
1647 mSpeakers->processSpeakerListUpdate(speaker_updates);
1648}
1649
1650void LLFloaterIMPanel::setSpeakersListFromMap(LLSD speaker_map)
1651{
1652 mSpeakers->processSpeakerMap(speaker_map);
1653}
1654
1655void LLFloaterIMPanel::setSpeakersList(LLSD speaker_list)
1656{
1657 mSpeakers->processSpeakerList(speaker_list);
1658}
1659
789void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) 1660void 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
1687void LLFloaterIMPanel::requestAutoConnect()
1688{
1689 mAutoConnect = TRUE;
1690}
814 1691
815void LLFloaterIMPanel::setTyping(BOOL typing) 1692void 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
868void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing) 1747void 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
883void LLFloaterIMPanel::addTypingIndicator(const LLIMInfo* im_info) 1762void 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
895void LLFloaterIMPanel::removeTypingIndicator() 1780void 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