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