00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034 #include "llimpanel.h"
00035
00036 #include "indra_constants.h"
00037 #include "llfocusmgr.h"
00038 #include "llfontgl.h"
00039 #include "llrect.h"
00040 #include "llerror.h"
00041 #include "llstring.h"
00042 #include "message.h"
00043 #include "lltextbox.h"
00044
00045 #include "llagent.h"
00046 #include "llbutton.h"
00047 #include "llcallingcard.h"
00048 #include "llchat.h"
00049 #include "llconsole.h"
00050 #include "llfloater.h"
00051 #include "llinventory.h"
00052 #include "llinventorymodel.h"
00053 #include "llinventoryview.h"
00054 #include "llfloateravatarinfo.h"
00055 #include "llfloaterchat.h"
00056 #include "llkeyboard.h"
00057 #include "lllineeditor.h"
00058 #include "llresmgr.h"
00059 #include "lltabcontainer.h"
00060 #include "llimview.h"
00061 #include "llviewertexteditor.h"
00062 #include "llviewermessage.h"
00063 #include "llviewerstats.h"
00064 #include "viewer.h"
00065 #include "llvieweruictrlfactory.h"
00066 #include "lllogchat.h"
00067 #include "llfloaterhtml.h"
00068 #include "llweb.h"
00069 #include "llhttpclient.h"
00070 #include "llfloateractivespeakers.h"
00071 #include "llfloatergroupinfo.h"
00072 #include "llsdutil.h"
00073 #include "llnotify.h"
00074 #include "llmutelist.h"
00075
00076
00077
00078
00079 const S32 LINE_HEIGHT = 16;
00080 const S32 MIN_WIDTH = 200;
00081 const S32 MIN_HEIGHT = 130;
00082 const U32 DEFAULT_RETRIES_COUNT = 3;
00083
00084
00085
00086
00087
00088 static LLString sTitleString = "Instant Message with [NAME]";
00089 static LLString sTypingStartString = "[NAME]: ...";
00090 static LLString sSessionStartString = "Starting session with [NAME] please wait.";
00091
00092 LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap;
00093 LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap;
00094 LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL;
00095
00096 void session_starter_helper(const LLUUID& temp_session_id,
00097 const LLUUID& other_participant_id,
00098 EInstantMessage im_type)
00099 {
00100 LLMessageSystem *msg = gMessageSystem;
00101
00102 msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
00103 msg->nextBlockFast(_PREHASH_AgentData);
00104 msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00105 msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00106
00107 msg->nextBlockFast(_PREHASH_MessageBlock);
00108 msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
00109 msg->addUUIDFast(_PREHASH_ToAgentID, other_participant_id);
00110 msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
00111 msg->addU8Fast(_PREHASH_Dialog, im_type);
00112 msg->addUUIDFast(_PREHASH_ID, temp_session_id);
00113 msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP);
00114
00115 std::string name;
00116 gAgent.buildFullname(name);
00117
00118 msg->addStringFast(_PREHASH_FromAgentName, name);
00119 msg->addStringFast(_PREHASH_Message, LLString::null);
00120 msg->addU32Fast(_PREHASH_ParentEstateID, 0);
00121 msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
00122 msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
00123 }
00124
00125
00126
00127 bool send_start_session_messages(const LLUUID& temp_session_id,
00128 const LLUUID& other_participant_id,
00129 const LLDynamicArray<LLUUID>& ids,
00130 EInstantMessage dialog)
00131 {
00132 if ( (dialog == IM_SESSION_GROUP_START) ||
00133 (dialog == IM_SESSION_CONFERENCE_START) )
00134 {
00135 S32 count = ids.size();
00136 S32 bucket_size = UUID_BYTES * count;
00137 U8* bucket;
00138 U8* pos;
00139
00140 session_starter_helper(temp_session_id,
00141 other_participant_id,
00142 dialog);
00143
00144 switch(dialog)
00145 {
00146 case IM_SESSION_GROUP_START:
00147 gMessageSystem->addBinaryDataFast(_PREHASH_BinaryBucket,
00148 EMPTY_BINARY_BUCKET,
00149 EMPTY_BINARY_BUCKET_SIZE);
00150 break;
00151 case IM_SESSION_CONFERENCE_START:
00152 bucket = new U8[bucket_size];
00153 pos = bucket;
00154
00155
00156 for(S32 i = 0; i < count; ++i)
00157 {
00158 memcpy(pos, &(ids.get(i)), UUID_BYTES);
00159 pos += UUID_BYTES;
00160 }
00161 gMessageSystem->addBinaryDataFast(_PREHASH_BinaryBucket,
00162 bucket,
00163 bucket_size);
00164 delete[] bucket;
00165
00166 break;
00167 default:
00168 break;
00169 }
00170 gAgent.sendReliableMessage();
00171
00172 return true;
00173 }
00174
00175 return false;
00176 }
00177
00178 class LLVoiceCallCapResponder : public LLHTTPClient::Responder
00179 {
00180 public:
00181 LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {};
00182
00183 virtual void error(U32 status, const std::string& reason);
00184 virtual void result(const LLSD& content);
00185
00186 private:
00187 LLUUID mSessionID;
00188 };
00189
00190
00191 void LLVoiceCallCapResponder::error(U32 status, const std::string& reason)
00192 {
00193 llwarns << "LLVoiceCallCapResponder::error("
00194 << status << ": " << reason << ")"
00195 << llendl;
00196 LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
00197 if (channelp)
00198 {
00199 channelp->deactivate();
00200 }
00201 }
00202
00203 void LLVoiceCallCapResponder::result(const LLSD& content)
00204 {
00205 LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);
00206 if (channelp)
00207 {
00208
00209 LLSD::map_const_iterator iter;
00210 for(iter = content.beginMap(); iter != content.endMap(); ++iter)
00211 {
00212 llinfos << "LLVoiceCallCapResponder::result got "
00213 << iter->first << llendl;
00214 }
00215
00216 channelp->setChannelInfo(
00217 content["voice_credentials"]["channel_uri"].asString(),
00218 content["voice_credentials"]["channel_credentials"].asString());
00219 }
00220 }
00221
00222
00223
00224
00225 LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const LLString& session_name) :
00226 mSessionID(session_id),
00227 mState(STATE_NO_CHANNEL_INFO),
00228 mSessionName(session_name),
00229 mIgnoreNextSessionLeave(FALSE)
00230 {
00231 mNotifyArgs["[VOICE_CHANNEL_NAME]"] = mSessionName;
00232
00233 if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second)
00234 {
00235
00236
00237 llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl;
00238 }
00239
00240 LLVoiceClient::getInstance()->addStatusObserver(this);
00241 }
00242
00243 LLVoiceChannel::~LLVoiceChannel()
00244 {
00245
00246
00247
00248
00249
00250 if(gVoiceClient)
00251 {
00252 gVoiceClient->removeStatusObserver(this);
00253 }
00254
00255 sVoiceChannelMap.erase(mSessionID);
00256 sVoiceChannelURIMap.erase(mURI);
00257 }
00258
00259 void LLVoiceChannel::setChannelInfo(
00260 const LLString& uri,
00261 const LLString& credentials)
00262 {
00263 setURI(uri);
00264
00265 mCredentials = credentials;
00266
00267 if (mState == STATE_NO_CHANNEL_INFO)
00268 {
00269 if(!mURI.empty() && !mCredentials.empty())
00270 {
00271 setState(STATE_READY);
00272
00273
00274
00275 if (sCurrentVoiceChannel == this)
00276 {
00277
00278
00279 activate();
00280 }
00281 }
00282 else
00283 {
00284
00285 llwarns << "Received invalid credentials for channel " << mSessionName << llendl;
00286 deactivate();
00287 }
00288 }
00289 }
00290
00291 void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal)
00292 {
00293 if (channelURI != mURI)
00294 {
00295 return;
00296 }
00297
00298 if (type < BEGIN_ERROR_STATUS)
00299 {
00300 handleStatusChange(type);
00301 }
00302 else
00303 {
00304 handleError(type);
00305 }
00306 }
00307
00308 void LLVoiceChannel::handleStatusChange(EStatusType type)
00309 {
00310
00311 switch(type)
00312 {
00313 case STATUS_LOGIN_RETRY:
00314 mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle();
00315 break;
00316 case STATUS_LOGGED_IN:
00317 if (!mLoginNotificationHandle.isDead())
00318 {
00319 LLNotifyBox* notifyp = (LLNotifyBox*)LLPanel::getPanelByHandle(mLoginNotificationHandle);
00320 if (notifyp)
00321 {
00322 notifyp->close();
00323 }
00324 mLoginNotificationHandle.markDead();
00325 }
00326 break;
00327 case STATUS_LEFT_CHANNEL:
00328 if (callStarted() && !mIgnoreNextSessionLeave)
00329 {
00330
00331
00332 LLNotifyBox::showXml("VoiceChannelDisconnected", mNotifyArgs);
00333 deactivate();
00334 }
00335 mIgnoreNextSessionLeave = FALSE;
00336 break;
00337 case STATUS_JOINING:
00338 if (callStarted())
00339 {
00340 setState(STATE_RINGING);
00341 }
00342 break;
00343 case STATUS_JOINED:
00344 if (callStarted())
00345 {
00346 setState(STATE_CONNECTED);
00347 }
00348 default:
00349 break;
00350 }
00351 }
00352
00353
00354
00355 void LLVoiceChannel::handleError(EStatusType type)
00356 {
00357 deactivate();
00358 setState(STATE_ERROR);
00359 }
00360
00361 BOOL LLVoiceChannel::isActive()
00362 {
00363
00364 return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI;
00365 }
00366
00367 BOOL LLVoiceChannel::callStarted()
00368 {
00369 return mState >= STATE_CALL_STARTED;
00370 }
00371
00372 void LLVoiceChannel::deactivate()
00373 {
00374 if (mState >= STATE_RINGING)
00375 {
00376
00377 mIgnoreNextSessionLeave = TRUE;
00378 }
00379
00380 if (callStarted())
00381 {
00382 setState(STATE_HUNG_UP);
00383 }
00384 if (sCurrentVoiceChannel == this)
00385 {
00386
00387 sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
00388 sCurrentVoiceChannel->activate();
00389 }
00390 }
00391
00392 void LLVoiceChannel::activate()
00393 {
00394 if (callStarted())
00395 {
00396 return;
00397 }
00398
00399
00400 if (sCurrentVoiceChannel != this)
00401 {
00402 if (sCurrentVoiceChannel)
00403 {
00404 sCurrentVoiceChannel->deactivate();
00405 }
00406 sCurrentVoiceChannel = this;
00407 }
00408
00409 if (mState == STATE_NO_CHANNEL_INFO)
00410 {
00411
00412 getChannelInfo();
00413 }
00414 else
00415 {
00416 setState(STATE_CALL_STARTED);
00417 }
00418 }
00419
00420 void LLVoiceChannel::getChannelInfo()
00421 {
00422
00423 if (sCurrentVoiceChannel == this)
00424 {
00425 setState(STATE_CALL_STARTED);
00426 }
00427 }
00428
00429
00430 LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id)
00431 {
00432 voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id);
00433 if (found_it == sVoiceChannelMap.end())
00434 {
00435 return NULL;
00436 }
00437 else
00438 {
00439 return found_it->second;
00440 }
00441 }
00442
00443
00444 LLVoiceChannel* LLVoiceChannel::getChannelByURI(LLString uri)
00445 {
00446 voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri);
00447 if (found_it == sVoiceChannelURIMap.end())
00448 {
00449 return NULL;
00450 }
00451 else
00452 {
00453 return found_it->second;
00454 }
00455 }
00456
00457
00458 void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)
00459 {
00460 sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID));
00461 mSessionID = new_session_id;
00462 sVoiceChannelMap.insert(std::make_pair(mSessionID, this));
00463 }
00464
00465 void LLVoiceChannel::setURI(LLString uri)
00466 {
00467 sVoiceChannelURIMap.erase(mURI);
00468 mURI = uri;
00469 sVoiceChannelURIMap.insert(std::make_pair(mURI, this));
00470 }
00471
00472 void LLVoiceChannel::setState(EState state)
00473 {
00474 switch(state)
00475 {
00476 case STATE_RINGING:
00477 gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs);
00478 break;
00479 case STATE_CONNECTED:
00480 gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs);
00481 break;
00482 case STATE_HUNG_UP:
00483 gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs);
00484 break;
00485 default:
00486 break;
00487 }
00488
00489 mState = state;
00490 }
00491
00492
00493
00494 void LLVoiceChannel::initClass()
00495 {
00496 sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
00497 }
00498
00499
00500
00501
00502
00503 LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const LLString& session_name) :
00504 LLVoiceChannel(session_id, session_name)
00505 {
00506 mRetries = DEFAULT_RETRIES_COUNT;
00507 mIsRetrying = FALSE;
00508 }
00509
00510 LLVoiceChannelGroup::~LLVoiceChannelGroup()
00511 {
00512 deactivate();
00513 }
00514
00515 void LLVoiceChannelGroup::deactivate()
00516 {
00517 if (callStarted())
00518 {
00519 LLVoiceClient::getInstance()->leaveNonSpatialChannel();
00520 }
00521 LLVoiceChannel::deactivate();
00522 }
00523
00524 void LLVoiceChannelGroup::activate()
00525 {
00526 if (callStarted()) return;
00527
00528 LLVoiceChannel::activate();
00529
00530 if (callStarted())
00531 {
00532
00533 LLVoiceClient::getInstance()->setNonSpatialChannel(
00534 mURI,
00535 mCredentials);
00536 }
00537 }
00538
00539 void LLVoiceChannelGroup::getChannelInfo()
00540 {
00541 LLViewerRegion* region = gAgent.getRegion();
00542 if (region)
00543 {
00544 std::string url = region->getCapability("ChatSessionRequest");
00545 LLSD data;
00546 data["method"] = "call";
00547 data["session-id"] = mSessionID;
00548 LLHTTPClient::post(url,
00549 data,
00550 new LLVoiceCallCapResponder(mSessionID));
00551 }
00552 }
00553
00554 void LLVoiceChannelGroup::setChannelInfo(
00555 const LLString& uri,
00556 const LLString& credentials)
00557 {
00558 setURI(uri);
00559
00560 mCredentials = credentials;
00561
00562 if (mState == STATE_NO_CHANNEL_INFO)
00563 {
00564 if(!mURI.empty() && !mCredentials.empty())
00565 {
00566 setState(STATE_READY);
00567
00568
00569
00570 if (sCurrentVoiceChannel == this)
00571 {
00572
00573
00574 activate();
00575 }
00576 }
00577 else
00578 {
00579
00580 llwarns << "Received invalid credentials for channel " << mSessionName << llendl;
00581 deactivate();
00582 }
00583 }
00584 else if ( mIsRetrying )
00585 {
00586
00587 LLVoiceClient::getInstance()->setNonSpatialChannel(
00588 mURI,
00589 mCredentials);
00590 }
00591 }
00592
00593 void LLVoiceChannelGroup::handleStatusChange(EStatusType type)
00594 {
00595
00596 switch(type)
00597 {
00598 case STATUS_JOINED:
00599 mRetries = 3;
00600 mIsRetrying = FALSE;
00601 default:
00602 break;
00603 }
00604
00605 LLVoiceChannel::handleStatusChange(type);
00606 }
00607
00608 void LLVoiceChannelGroup::handleError(EStatusType status)
00609 {
00610 std::string notify;
00611 switch(status)
00612 {
00613 case ERROR_CHANNEL_LOCKED:
00614 case ERROR_CHANNEL_FULL:
00615 notify = "VoiceChannelFull";
00616 break;
00617 case ERROR_NOT_AVAILABLE:
00618
00619
00620
00621 if ( mRetries > 0 )
00622 {
00623 mRetries--;
00624 mIsRetrying = TRUE;
00625 mIgnoreNextSessionLeave = TRUE;
00626
00627 getChannelInfo();
00628 return;
00629 }
00630 else
00631 {
00632 notify = "VoiceChannelJoinFailed";
00633 mRetries = DEFAULT_RETRIES_COUNT;
00634 mIsRetrying = FALSE;
00635 }
00636
00637 break;
00638 case ERROR_UNKNOWN:
00639 default:
00640 break;
00641 }
00642
00643
00644 if (!notify.empty())
00645 {
00646 LLNotifyBox::showXml(notify, mNotifyArgs);
00647
00648 gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, LLNotifyBox::getTemplateMessage(notify, mNotifyArgs).c_str());
00649 }
00650
00651 LLVoiceChannel::handleError(status);
00652 }
00653
00654 void LLVoiceChannelGroup::setState(EState state)
00655 {
00656 switch(state)
00657 {
00658 case STATE_RINGING:
00659 if ( !mIsRetrying )
00660 {
00661 gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs);
00662 }
00663
00664 mState = state;
00665 break;
00666 default:
00667 LLVoiceChannel::setState(state);
00668 }
00669 }
00670
00671
00672
00673
00674 LLVoiceChannelProximal::LLVoiceChannelProximal() :
00675 LLVoiceChannel(LLUUID::null, LLString::null)
00676 {
00677 activate();
00678 }
00679
00680 LLVoiceChannelProximal::~LLVoiceChannelProximal()
00681 {
00682
00683 }
00684
00685 BOOL LLVoiceChannelProximal::isActive()
00686 {
00687 return callStarted() && LLVoiceClient::getInstance()->inProximalChannel();
00688 }
00689
00690 void LLVoiceChannelProximal::activate()
00691 {
00692 if (callStarted()) return;
00693
00694 LLVoiceChannel::activate();
00695
00696 if (callStarted())
00697 {
00698
00699 LLVoiceClient::getInstance()->leaveNonSpatialChannel();
00700 }
00701 }
00702
00703 void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal)
00704 {
00705 if (!proximal)
00706 {
00707 return;
00708 }
00709
00710 if (type < BEGIN_ERROR_STATUS)
00711 {
00712 handleStatusChange(type);
00713 }
00714 else
00715 {
00716 handleError(type);
00717 }
00718 }
00719
00720 void LLVoiceChannelProximal::handleStatusChange(EStatusType status)
00721 {
00722
00723 switch(status)
00724 {
00725 case STATUS_LEFT_CHANNEL:
00726
00727 return;
00728 default:
00729 break;
00730 }
00731 LLVoiceChannel::handleStatusChange(status);
00732 }
00733
00734
00735 void LLVoiceChannelProximal::handleError(EStatusType status)
00736 {
00737 std::string notify;
00738 switch(status)
00739 {
00740 case ERROR_CHANNEL_LOCKED:
00741 case ERROR_CHANNEL_FULL:
00742 notify = "ProximalVoiceChannelFull";
00743 break;
00744 default:
00745 break;
00746 }
00747
00748
00749 if (!notify.empty())
00750 {
00751 LLNotifyBox::showXml(notify, mNotifyArgs);
00752 }
00753
00754 LLVoiceChannel::handleError(status);
00755 }
00756
00757 void LLVoiceChannelProximal::deactivate()
00758 {
00759 if (callStarted())
00760 {
00761 setState(STATE_HUNG_UP);
00762 }
00763 }
00764
00765
00766
00767
00768 LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const LLString& session_name, const LLUUID& other_user_id) :
00769 LLVoiceChannelGroup(session_id, session_name),
00770 mOtherUserID(other_user_id)
00771 {
00772
00773 setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));
00774 }
00775
00776 LLVoiceChannelP2P::~LLVoiceChannelP2P()
00777 {
00778 deactivate();
00779 }
00780
00781 void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
00782 {
00783
00784 switch(type)
00785 {
00786 case STATUS_LEFT_CHANNEL:
00787 if (callStarted() && !mIgnoreNextSessionLeave)
00788 {
00789 if (mState == STATE_RINGING)
00790 {
00791
00792 LLNotifyBox::showXml("P2PCallDeclined", mNotifyArgs);
00793 }
00794 else
00795 {
00796
00797 LLNotifyBox::showXml("VoiceChannelDisconnectedP2P", mNotifyArgs);
00798 }
00799 deactivate();
00800 }
00801 mIgnoreNextSessionLeave = FALSE;
00802 return;
00803 default:
00804 break;
00805 }
00806
00807 LLVoiceChannel::handleStatusChange(type);
00808 }
00809
00810 void LLVoiceChannelP2P::handleError(EStatusType type)
00811 {
00812 switch(type)
00813 {
00814 case ERROR_NOT_AVAILABLE:
00815 LLNotifyBox::showXml("P2PCallNoAnswer", mNotifyArgs);
00816 break;
00817 default:
00818 break;
00819 }
00820
00821 LLVoiceChannel::handleError(type);
00822 }
00823
00824 void LLVoiceChannelP2P::activate()
00825 {
00826 if (callStarted()) return;
00827
00828 LLVoiceChannel::activate();
00829
00830 if (callStarted())
00831 {
00832
00833 if (mSessionHandle.empty())
00834 {
00835 LLVoiceClient::getInstance()->callUser(mOtherUserID);
00836 }
00837
00838 else
00839 {
00840 LLVoiceClient::getInstance()->answerInvite(mSessionHandle, mOtherUserID);
00841
00842 mSessionHandle.clear();
00843 }
00844 }
00845 }
00846
00847 void LLVoiceChannelP2P::getChannelInfo()
00848 {
00849
00850 if (sCurrentVoiceChannel == this)
00851 {
00852 setState(STATE_CALL_STARTED);
00853 }
00854 }
00855
00856
00857 void LLVoiceChannelP2P::setSessionHandle(const LLString& handle)
00858 {
00859 BOOL needs_activate = FALSE;
00860 if (callStarted())
00861 {
00862
00863 if (mOtherUserID < gAgent.getID())
00864 {
00865
00866 deactivate();
00867 needs_activate = TRUE;
00868 }
00869 else
00870 {
00871
00872
00873 mSessionHandle.clear();
00874 LLVoiceClient::getInstance()->callUser(mOtherUserID);
00875 return;
00876 }
00877 }
00878
00879 mSessionHandle = handle;
00880
00881 setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID));
00882
00883 if (needs_activate)
00884 {
00885 activate();
00886 }
00887 }
00888
00889
00890
00891
00892 LLFloaterIMPanel::LLFloaterIMPanel(
00893 const std::string& name,
00894 const LLRect& rect,
00895 const std::string& session_label,
00896 const LLUUID& session_id,
00897 const LLUUID& other_participant_id,
00898 EInstantMessage dialog) :
00899 LLFloater(name, rect, session_label),
00900 mInputEditor(NULL),
00901 mHistoryEditor(NULL),
00902 mSessionUUID(session_id),
00903 mVoiceChannel(NULL),
00904 mSessionInitialized(FALSE),
00905
00906 mOtherParticipantUUID(other_participant_id),
00907 mDialog(dialog),
00908 mTyping(FALSE),
00909 mOtherTyping(FALSE),
00910 mTypingLineStartIndex(0),
00911 mSentTypingState(TRUE),
00912 mShowSpeakersOnConnect(TRUE),
00913 mAutoConnect(FALSE),
00914 mSpeakerPanel(NULL),
00915 mFirstKeystrokeTimer(),
00916 mLastKeystrokeTimer()
00917 {
00918 init(session_label);
00919 }
00920
00921 LLFloaterIMPanel::LLFloaterIMPanel(
00922 const std::string& name,
00923 const LLRect& rect,
00924 const std::string& session_label,
00925 const LLUUID& session_id,
00926 const LLUUID& other_participant_id,
00927 const LLDynamicArray<LLUUID>& ids,
00928 EInstantMessage dialog) :
00929 LLFloater(name, rect, session_label),
00930 mInputEditor(NULL),
00931 mHistoryEditor(NULL),
00932 mSessionUUID(session_id),
00933 mVoiceChannel(NULL),
00934 mSessionInitialized(FALSE),
00935 mOtherParticipantUUID(other_participant_id),
00936 mDialog(dialog),
00937 mTyping(FALSE),
00938 mOtherTyping(FALSE),
00939 mTypingLineStartIndex(0),
00940 mSentTypingState(TRUE),
00941 mShowSpeakersOnConnect(TRUE),
00942 mAutoConnect(FALSE),
00943 mSpeakers(NULL),
00944 mSpeakerPanel(NULL),
00945 mFirstKeystrokeTimer(),
00946 mLastKeystrokeTimer()
00947 {
00948 mSessionInitialTargetIDs = ids;
00949 init(session_label);
00950 }
00951
00952
00953 void LLFloaterIMPanel::init(const LLString& session_label)
00954 {
00955 LLString xml_filename;
00956 switch(mDialog)
00957 {
00958 case IM_SESSION_GROUP_START:
00959 mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
00960 xml_filename = "floater_instant_message_group.xml";
00961 mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label);
00962 break;
00963 case IM_SESSION_INVITE:
00964 mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
00965 if (gAgent.isInGroup(mSessionUUID))
00966 {
00967 xml_filename = "floater_instant_message_group.xml";
00968 }
00969 else
00970 {
00971 xml_filename = "floater_instant_message_ad_hoc.xml";
00972 }
00973 mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label);
00974 break;
00975 case IM_SESSION_P2P_INVITE:
00976 xml_filename = "floater_instant_message.xml";
00977 mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID);
00978 break;
00979 case IM_SESSION_CONFERENCE_START:
00980 mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, this);
00981 xml_filename = "floater_instant_message_ad_hoc.xml";
00982 mVoiceChannel = new LLVoiceChannelGroup(mSessionUUID, session_label);
00983 break;
00984
00985 case IM_NOTHING_SPECIAL:
00986 xml_filename = "floater_instant_message.xml";
00987 mVoiceChannel = new LLVoiceChannelP2P(mSessionUUID, session_label, mOtherParticipantUUID);
00988 break;
00989 default:
00990 llwarns << "Unknown session type" << llendl;
00991 xml_filename = "floater_instant_message.xml";
00992 break;
00993 }
00994
00995 mSpeakers = new LLIMSpeakerMgr(mVoiceChannel);
00996
00997 gUICtrlFactory->buildFloater(this,
00998 xml_filename,
00999 &getFactoryMap(),
01000 FALSE);
01001
01002 setLabel(session_label);
01003 setTitle(session_label);
01004 mInputEditor->setMaxTextLength(1023);
01005
01006 mInputEditor->setEnableLineHistory(TRUE);
01007
01008 if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
01009 {
01010 LLLogChat::loadHistory(session_label,
01011 &chatFromLogFile,
01012 (void *)this);
01013 }
01014
01015 if ( !mSessionInitialized )
01016 {
01017 if ( !send_start_session_messages(
01018 mSessionUUID,
01019 mOtherParticipantUUID,
01020 mSessionInitialTargetIDs,
01021 mDialog) )
01022 {
01023
01024
01025 mSessionInitialized = TRUE;
01026 }
01027 else
01028 {
01029
01030 LLUIString session_start = sSessionStartString;
01031
01032 session_start.setArg("[NAME]", getTitle());
01033 mSessionStartMsgPos =
01034 mHistoryEditor->getWText().length();
01035
01036 addHistoryLine(
01037 session_start,
01038 gSavedSettings.getColor4("SystemChatColor"),
01039 false);
01040 }
01041 }
01042 }
01043
01044
01045 LLFloaterIMPanel::~LLFloaterIMPanel()
01046 {
01047 delete mSpeakers;
01048 mSpeakers = NULL;
01049
01050
01051
01052
01053 mVoiceChannel->deactivate();
01054
01055 delete mVoiceChannel;
01056 mVoiceChannel = NULL;
01057 }
01058
01059 BOOL LLFloaterIMPanel::postBuild()
01060 {
01061 requires("chat_editor", WIDGET_TYPE_LINE_EDITOR);
01062 requires("im_history", WIDGET_TYPE_TEXT_EDITOR);
01063 requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX);
01064 requires("title_string", WIDGET_TYPE_TEXT_BOX);
01065 requires("typing_start_string", WIDGET_TYPE_TEXT_BOX);
01066 requires("session_start_string", WIDGET_TYPE_TEXT_BOX);
01067
01068 if (checkRequirements())
01069 {
01070 mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "chat_editor");
01071 mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived );
01072 mInputEditor->setFocusLostCallback( onInputEditorFocusLost );
01073 mInputEditor->setKeystrokeCallback( onInputEditorKeystroke );
01074 mInputEditor->setCommitCallback( onCommitChat );
01075 mInputEditor->setCallbackUserData(this);
01076 mInputEditor->setCommitOnFocusLost( FALSE );
01077 mInputEditor->setRevertOnEsc( FALSE );
01078
01079 childSetAction("profile_callee_btn", onClickProfile, this);
01080 childSetAction("group_info_btn", onClickGroupInfo, this);
01081
01082 childSetAction("start_call_btn", onClickStartCall, this);
01083 childSetAction("end_call_btn", onClickEndCall, this);
01084 childSetAction("send_btn", onClickSend, this);
01085 childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this);
01086
01087
01088
01089
01090 mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history");
01091 mHistoryEditor->setParseHTML(TRUE);
01092
01093 if ( IM_SESSION_GROUP_START == mDialog )
01094 {
01095 childSetEnabled("profile_btn", FALSE);
01096 }
01097 LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string");
01098 sTitleString = title->getText();
01099
01100 LLTextBox* typing_start = LLUICtrlFactory::getTextBoxByName(this, "typing_start_string");
01101
01102 sTypingStartString = typing_start->getText();
01103
01104 LLTextBox* session_start = LLUICtrlFactory::getTextBoxByName(
01105 this,
01106 "session_start_string");
01107 sSessionStartString = session_start->getText();
01108 if (mSpeakerPanel)
01109 {
01110 mSpeakerPanel->refreshSpeakers();
01111 }
01112
01113 if (mDialog == IM_NOTHING_SPECIAL)
01114 {
01115 childSetCommitCallback("mute_btn", onClickMuteVoice, this);
01116 childSetCommitCallback("speaker_volume", onVolumeChange, this);
01117 }
01118
01119 setDefaultBtn("send_btn");
01120 return TRUE;
01121 }
01122
01123 return FALSE;
01124 }
01125
01126 void* LLFloaterIMPanel::createSpeakersPanel(void* data)
01127 {
01128 LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)data;
01129 floaterp->mSpeakerPanel = new LLPanelActiveSpeakers(floaterp->mSpeakers, TRUE);
01130 return floaterp->mSpeakerPanel;
01131 }
01132
01133
01134 void LLFloaterIMPanel::onClickMuteVoice(LLUICtrl* source, void* user_data)
01135 {
01136 LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
01137 if (floaterp)
01138 {
01139 BOOL is_muted = gMuteListp->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat);
01140
01141 LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT);
01142 if (!is_muted)
01143 {
01144 gMuteListp->add(mute, LLMute::flagVoiceChat);
01145 }
01146 else
01147 {
01148 gMuteListp->remove(mute, LLMute::flagVoiceChat);
01149 }
01150 }
01151 }
01152
01153
01154 void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data)
01155 {
01156 LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;
01157 if (floaterp)
01158 {
01159 gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal());
01160 }
01161 }
01162
01163
01164
01165 void LLFloaterIMPanel::draw()
01166 {
01167 LLViewerRegion* region = gAgent.getRegion();
01168
01169 BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "")
01170 && mSessionInitialized
01171 && LLVoiceClient::voiceEnabled();
01172
01173
01174 childSetVisible("end_call_btn", mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED);
01175 childSetVisible("start_call_btn", mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED);
01176 childSetEnabled("start_call_btn", enable_connect);
01177 childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty());
01178
01179 if (mAutoConnect && enable_connect)
01180 {
01181 onClickStartCall(this);
01182 mAutoConnect = FALSE;
01183 }
01184
01185
01186 if (mShowSpeakersOnConnect && mVoiceChannel->isActive())
01187 {
01188 childSetVisible("active_speakers_panel", TRUE);
01189 mShowSpeakersOnConnect = FALSE;
01190 }
01191 childSetValue("toggle_active_speakers_btn", childIsVisible("active_speakers_panel"));
01192
01193 if (mTyping)
01194 {
01195
01196 if (mLastKeystrokeTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
01197 {
01198 setTyping(FALSE);
01199 }
01200
01201
01202
01203 if (!mSentTypingState
01204 && mFirstKeystrokeTimer.getElapsedTimeF32() > 1.f)
01205 {
01206 sendTypingState(TRUE);
01207 mSentTypingState = TRUE;
01208 }
01209 }
01210
01211 if (mSpeakerPanel)
01212 {
01213 mSpeakerPanel->refreshSpeakers();
01214 }
01215 else
01216 {
01217
01218 childSetEnabled("speaker_volume", mVoiceChannel->isActive());
01219 childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID));
01220
01221 childSetValue("mute_btn", gMuteListp->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat));
01222 childSetEnabled("mute_btn", mVoiceChannel->isActive());
01223 }
01224 LLFloater::draw();
01225 }
01226
01227 class LLSessionInviteResponder : public LLHTTPClient::Responder
01228 {
01229 public:
01230 LLSessionInviteResponder(const LLUUID& session_id)
01231 {
01232 mSessionID = session_id;
01233 }
01234
01235 void error(U32 statusNum, const std::string& reason)
01236 {
01237 llinfos << "Error inviting all agents to session" << llendl;
01238
01239
01240 }
01241
01242 private:
01243 LLUUID mSessionID;
01244 };
01245
01246 BOOL LLFloaterIMPanel::inviteToSession(const LLDynamicArray<LLUUID>& ids)
01247 {
01248 LLViewerRegion* region = gAgent.getRegion();
01249 if (!region)
01250 {
01251 return FALSE;
01252 }
01253
01254 S32 count = ids.count();
01255
01256 if( isInviteAllowed() && (count > 0) )
01257 {
01258 llinfos << "LLFloaterIMPanel::inviteToSession() - inviting participants" << llendl;
01259
01260 std::string url = region->getCapability("ChatSessionRequest");
01261
01262 LLSD data;
01263
01264 data["params"] = LLSD::emptyArray();
01265 for (int i = 0; i < count; i++)
01266 {
01267 data["params"].append(ids.get(i));
01268 }
01269
01270 data["method"] = "invite";
01271 data["session-id"] = mSessionUUID;
01272 LLHTTPClient::post(
01273 url,
01274 data,
01275 new LLSessionInviteResponder(mSessionUUID));
01276
01277 }
01278 else
01279 {
01280 llinfos << "LLFloaterIMPanel::inviteToSession -"
01281 << " no need to invite agents for "
01282 << mDialog << llendl;
01283
01284
01285 }
01286
01287 return TRUE;
01288 }
01289
01290 void LLFloaterIMPanel::addHistoryLine(const LLUUID& source, const std::string &utf8msg, const LLColor4& color, bool log_to_file)
01291 {
01292 addHistoryLine(utf8msg, color, log_to_file);
01293 mSpeakers->speakerChatted(source);
01294 mSpeakers->setSpeakerTyping(source, FALSE);
01295 }
01296
01297 void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file)
01298 {
01299 LLMultiFloater* hostp = getHost();
01300 if( !getVisible() && hostp && log_to_file)
01301 {
01302
01303 LLTabContainer* parent = (LLTabContainer*) getParent();
01304 parent->setTabPanelFlashing( this, TRUE );
01305 }
01306
01307
01308
01309
01310 removeTypingIndicator(NULL);
01311
01312
01313 LLString timestring;
01314 bool prepend_newline = true;
01315 if (gSavedSettings.getBOOL("IMShowTimestamps"))
01316 {
01317 timestring = mHistoryEditor->appendTime(prepend_newline);
01318 prepend_newline = false;
01319 }
01320 mHistoryEditor->appendColoredText(utf8msg, false, prepend_newline, color);
01321
01322 if (log_to_file
01323 && gSavedPerAccountSettings.getBOOL("LogInstantMessages") )
01324 {
01325 LLString histstr;
01326 if (gSavedPerAccountSettings.getBOOL("IMLogTimestamp"))
01327 histstr = LLLogChat::timestamp(gSavedPerAccountSettings.getBOOL("LogTimestampDate")) + utf8msg;
01328 else
01329 histstr = utf8msg;
01330
01331 LLLogChat::saveHistory(getTitle(),histstr);
01332 }
01333 }
01334
01335
01336 void LLFloaterIMPanel::setVisible(BOOL b)
01337 {
01338 LLPanel::setVisible(b);
01339
01340 LLMultiFloater* hostp = getHost();
01341 if( b && hostp )
01342 {
01343 LLTabContainer* parent = (LLTabContainer*) getParent();
01344
01345
01346 parent->setTabPanelFlashing( this, FALSE );
01347
01348
01349
01350
01351
01352
01353 }
01354 }
01355
01356
01357 void LLFloaterIMPanel::setInputFocus( BOOL b )
01358 {
01359 mInputEditor->setFocus( b );
01360 }
01361
01362
01363 void LLFloaterIMPanel::selectAll()
01364 {
01365 mInputEditor->selectAll();
01366 }
01367
01368
01369 void LLFloaterIMPanel::selectNone()
01370 {
01371 mInputEditor->deselect();
01372 }
01373
01374
01375 BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
01376 {
01377 BOOL handled = FALSE;
01378 if( getVisible() && mEnabled && !called_from_parent && gFocusMgr.childHasKeyboardFocus(this))
01379 {
01380 if( KEY_RETURN == key && mask == MASK_NONE)
01381 {
01382 sendMsg();
01383 handled = TRUE;
01384
01385
01386
01387 if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) )
01388 {
01389 gIMMgr->toggle(NULL);
01390 }
01391 }
01392 else if ( KEY_ESCAPE == key )
01393 {
01394 handled = TRUE;
01395 gFocusMgr.setKeyboardFocus(NULL, NULL);
01396
01397
01398 if( !gSavedSettings.getBOOL("PinTalkViewOpen") )
01399 {
01400 gIMMgr->toggle(NULL);
01401 }
01402 }
01403 }
01404
01405
01406
01407 return handled;
01408 }
01409
01410 BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
01411 EDragAndDropType cargo_type,
01412 void* cargo_data,
01413 EAcceptance* accept,
01414 LLString& tooltip_msg)
01415 {
01416 BOOL accepted = FALSE;
01417 switch(cargo_type)
01418 {
01419 case DAD_CALLINGCARD:
01420 accepted = dropCallingCard((LLInventoryItem*)cargo_data, drop);
01421 break;
01422 case DAD_CATEGORY:
01423 accepted = dropCategory((LLInventoryCategory*)cargo_data, drop);
01424 break;
01425 default:
01426 break;
01427 }
01428 if (accepted)
01429 {
01430 *accept = ACCEPT_YES_MULTI;
01431 }
01432 else
01433 {
01434 *accept = ACCEPT_NO;
01435 }
01436 return TRUE;
01437 }
01438
01439 BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop)
01440 {
01441 BOOL rv = isInviteAllowed();
01442 if(rv && item && item->getCreatorUUID().notNull())
01443 {
01444 if(drop)
01445 {
01446 LLDynamicArray<LLUUID> ids;
01447 ids.put(item->getCreatorUUID());
01448 inviteToSession(ids);
01449 }
01450 }
01451 else
01452 {
01453
01454 rv = FALSE;
01455 }
01456 return rv;
01457 }
01458
01459 BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop)
01460 {
01461 BOOL rv = isInviteAllowed();
01462 if(rv && category)
01463 {
01464 LLInventoryModel::cat_array_t cats;
01465 LLInventoryModel::item_array_t items;
01466 LLUniqueBuddyCollector buddies;
01467 gInventory.collectDescendentsIf(category->getUUID(),
01468 cats,
01469 items,
01470 LLInventoryModel::EXCLUDE_TRASH,
01471 buddies);
01472 S32 count = items.count();
01473 if(count == 0)
01474 {
01475 rv = FALSE;
01476 }
01477 else if(drop)
01478 {
01479 LLDynamicArray<LLUUID> ids;
01480 for(S32 i = 0; i < count; ++i)
01481 {
01482 ids.put(items.get(i)->getCreatorUUID());
01483 }
01484 inviteToSession(ids);
01485 }
01486 }
01487 return rv;
01488 }
01489
01490 BOOL LLFloaterIMPanel::isInviteAllowed() const
01491 {
01492
01493 return ( (IM_SESSION_CONFERENCE_START == mDialog)
01494 || (IM_SESSION_INVITE == mDialog) );
01495 }
01496
01497
01498
01499 void LLFloaterIMPanel::onTabClick(void* userdata)
01500 {
01501 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01502 self->setInputFocus(TRUE);
01503 }
01504
01505
01506
01507 void LLFloaterIMPanel::onClickProfile( void* userdata )
01508 {
01509
01510 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01511
01512 if (self->mOtherParticipantUUID.notNull())
01513 {
01514 LLFloaterAvatarInfo::showFromDirectory(self->getOtherParticipantID());
01515 }
01516 }
01517
01518
01519 void LLFloaterIMPanel::onClickGroupInfo( void* userdata )
01520 {
01521
01522 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01523
01524 LLFloaterGroupInfo::showFromUUID(self->mSessionUUID);
01525 }
01526
01527
01528 void LLFloaterIMPanel::onClickClose( void* userdata )
01529 {
01530 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01531 if(self)
01532 {
01533 self->close();
01534 }
01535 }
01536
01537
01538 void LLFloaterIMPanel::onClickStartCall(void* userdata)
01539 {
01540 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01541
01542 self->mVoiceChannel->activate();
01543 }
01544
01545
01546 void LLFloaterIMPanel::onClickEndCall(void* userdata)
01547 {
01548 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01549
01550 self->getVoiceChannel()->deactivate();
01551 }
01552
01553
01554 void LLFloaterIMPanel::onClickSend(void* userdata)
01555 {
01556 LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
01557 self->sendMsg();
01558 }
01559
01560
01561 void LLFloaterIMPanel::onClickToggleActiveSpeakers(void* userdata)
01562 {
01563 LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
01564
01565 self->childSetVisible("active_speakers_panel", !self->childIsVisible("active_speakers_panel"));
01566 }
01567
01568
01569 void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata)
01570 {
01571 LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
01572 self->sendMsg();
01573 }
01574
01575
01576 void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata )
01577 {
01578 LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
01579 self->mHistoryEditor->setCursorAndScrollToEnd();
01580 }
01581
01582
01583 void LLFloaterIMPanel::onInputEditorFocusLost(LLUICtrl* caller, void* userdata)
01584 {
01585 LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01586 self->setTyping(FALSE);
01587 }
01588
01589
01590 void LLFloaterIMPanel::onInputEditorKeystroke(LLLineEditor* caller, void* userdata)
01591 {
01592 LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
01593 LLString text = self->mInputEditor->getText();
01594 if (!text.empty())
01595 {
01596 self->setTyping(TRUE);
01597 }
01598 else
01599 {
01600
01601 self->setTyping(FALSE);
01602 }
01603 }
01604
01605 void LLFloaterIMPanel::onClose(bool app_quitting)
01606 {
01607 setTyping(FALSE);
01608
01609 if(mSessionUUID.notNull())
01610 {
01611 std::string name;
01612 gAgent.buildFullname(name);
01613 pack_instant_message(
01614 gMessageSystem,
01615 gAgent.getID(),
01616 FALSE,
01617 gAgent.getSessionID(),
01618 mOtherParticipantUUID,
01619 name.c_str(),
01620 "",
01621 IM_ONLINE,
01622 IM_SESSION_LEAVE,
01623 mSessionUUID);
01624 gAgent.sendReliableMessage();
01625 }
01626 gIMMgr->removeSession(mSessionUUID);
01627
01628 destroy();
01629 }
01630
01631 void deliver_message(const std::string& utf8_text,
01632 const LLUUID& im_session_id,
01633 const LLUUID& other_participant_id,
01634 EInstantMessage dialog)
01635 {
01636 std::string name;
01637 gAgent.buildFullname(name);
01638
01639 const LLRelationship* info = NULL;
01640 info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id);
01641 U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
01642
01643
01644
01645 U8 new_dialog = dialog;
01646
01647 if ( dialog != IM_NOTHING_SPECIAL )
01648 {
01649 new_dialog = IM_SESSION_SEND;
01650 }
01651
01652 pack_instant_message(
01653 gMessageSystem,
01654 gAgent.getID(),
01655 FALSE,
01656 gAgent.getSessionID(),
01657 other_participant_id,
01658 name.c_str(),
01659 utf8_text.c_str(),
01660 offline,
01661 (EInstantMessage)new_dialog,
01662 im_session_id);
01663 gAgent.sendReliableMessage();
01664 }
01665
01666 void LLFloaterIMPanel::sendMsg()
01667 {
01668 LLWString text = mInputEditor->getWText();
01669 LLWString::trim(text);
01670 if (!gAgent.isGodlike()
01671 && (mDialog == IM_NOTHING_SPECIAL)
01672 && mOtherParticipantUUID.isNull())
01673 {
01674 llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
01675 return;
01676 }
01677 if(text.length() > 0)
01678 {
01679
01680 std::string utf8_text = wstring_to_utf8str(text);
01681 utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
01682
01683 if ( mSessionInitialized )
01684 {
01685 deliver_message(utf8_text,
01686 mSessionUUID,
01687 mOtherParticipantUUID,
01688 mDialog);
01689
01690
01691 if((mDialog == IM_NOTHING_SPECIAL) &&
01692 (mOtherParticipantUUID.notNull()))
01693 {
01694 std::string history_echo;
01695 gAgent.buildFullname(history_echo);
01696
01697
01698 char tmpstr[5];
01699 strncpy(tmpstr,
01700 utf8_text.substr(0,4).c_str(),
01701 sizeof(tmpstr) -1);
01702 tmpstr[sizeof(tmpstr) -1] = '\0';
01703 if (!strncmp(tmpstr, "/me ", 4) ||
01704 !strncmp(tmpstr, "/me'", 4))
01705 {
01706 utf8_text.replace(0,3,"");
01707 }
01708 else
01709 {
01710 history_echo += ": ";
01711 }
01712 history_echo += utf8_text;
01713
01714 BOOL other_was_typing = mOtherTyping;
01715
01716 addHistoryLine(gAgent.getID(), history_echo);
01717
01718 if (other_was_typing)
01719 {
01720 addTypingIndicator(mOtherTypingName);
01721 }
01722
01723 }
01724 }
01725 else
01726 {
01727
01728
01729 mQueuedMsgsForInit.append(utf8_text);
01730 }
01731
01732 gViewerStats->incStat(LLViewerStats::ST_IM_COUNT);
01733 }
01734 mInputEditor->setText(LLString::null);
01735
01736
01737
01738 mTyping = FALSE;
01739 mSentTypingState = TRUE;
01740 }
01741
01742 void LLFloaterIMPanel::updateSpeakersList(LLSD speaker_updates)
01743 {
01744 mSpeakers->processSpeakerListUpdate(speaker_updates);
01745 }
01746
01747 void LLFloaterIMPanel::setSpeakersListFromMap(LLSD speaker_map)
01748 {
01749 mSpeakers->processSpeakerMap(speaker_map);
01750 }
01751
01752 void LLFloaterIMPanel::setSpeakersList(LLSD speaker_list)
01753 {
01754 mSpeakers->processSpeakerList(speaker_list);
01755 }
01756
01757 void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id)
01758 {
01759 mSessionUUID = session_id;
01760 mVoiceChannel->updateSessionID(session_id);
01761 mSessionInitialized = TRUE;
01762
01763
01764
01765
01766 S32 chars_to_remove = mHistoryEditor->getWText().length() -
01767 mSessionStartMsgPos;
01768 mHistoryEditor->removeTextFromEnd(chars_to_remove);
01769
01770
01771 LLSD::array_iterator iter;
01772 for ( iter = mQueuedMsgsForInit.beginArray();
01773 iter != mQueuedMsgsForInit.endArray();
01774 ++iter)
01775 {
01776 deliver_message(
01777 iter->asString(),
01778 mSessionUUID,
01779 mOtherParticipantUUID,
01780 mDialog);
01781 }
01782 }
01783
01784 void LLFloaterIMPanel::requestAutoConnect()
01785 {
01786 mAutoConnect = TRUE;
01787 }
01788
01789 void LLFloaterIMPanel::setTyping(BOOL typing)
01790 {
01791 if (typing)
01792 {
01793
01794 mLastKeystrokeTimer.reset();
01795
01796 if (!mTyping)
01797 {
01798
01799 mFirstKeystrokeTimer.reset();
01800
01801
01802 mSentTypingState = FALSE;
01803 }
01804
01805 mSpeakers->setSpeakerTyping(gAgent.getID(), TRUE);
01806 }
01807 else
01808 {
01809 if (mTyping)
01810 {
01811
01812 sendTypingState(FALSE);
01813 mSentTypingState = TRUE;
01814 }
01815 mSpeakers->setSpeakerTyping(gAgent.getID(), FALSE);
01816 }
01817
01818 mTyping = typing;
01819 }
01820
01821 void LLFloaterIMPanel::sendTypingState(BOOL typing)
01822 {
01823
01824
01825 if (mDialog != IM_NOTHING_SPECIAL) return;
01826
01827 std::string name;
01828 gAgent.buildFullname(name);
01829
01830 pack_instant_message(
01831 gMessageSystem,
01832 gAgent.getID(),
01833 FALSE,
01834 gAgent.getSessionID(),
01835 mOtherParticipantUUID,
01836 name.c_str(),
01837 "typing",
01838 IM_ONLINE,
01839 (typing ? IM_TYPING_START : IM_TYPING_STOP),
01840 mSessionUUID);
01841 gAgent.sendReliableMessage();
01842 }
01843
01844 void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing)
01845 {
01846 if (typing)
01847 {
01848
01849 addTypingIndicator(im_info->mName);
01850 }
01851 else
01852 {
01853
01854 removeTypingIndicator(im_info);
01855 }
01856 }
01857
01858
01859 void LLFloaterIMPanel::addTypingIndicator(const std::string &name)
01860 {
01861
01862 if (!mOtherTyping)
01863 {
01864 mTypingLineStartIndex = mHistoryEditor->getWText().length();
01865 LLUIString typing_start = sTypingStartString;
01866 typing_start.setArg("[NAME]", name);
01867 addHistoryLine(typing_start, gSavedSettings.getColor4("SystemChatColor"), false);
01868 mOtherTypingName = name;
01869 mOtherTyping = TRUE;
01870 }
01871
01872
01873
01874 }
01875
01876
01877 void LLFloaterIMPanel::removeTypingIndicator(const LLIMInfo* im_info)
01878 {
01879 if (mOtherTyping)
01880 {
01881
01882 mOtherTyping = FALSE;
01883
01884 S32 chars_to_remove = mHistoryEditor->getWText().length() - mTypingLineStartIndex;
01885 mHistoryEditor->removeTextFromEnd(chars_to_remove);
01886 if (im_info)
01887 {
01888 mSpeakers->setSpeakerTyping(im_info->mFromID, FALSE);
01889 }
01890 }
01891 }
01892
01893
01894 void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata)
01895 {
01896 LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
01897
01898
01899 self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey);
01900
01901 }