llimpanel.cpp

Go to the documentation of this file.
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 // Constants
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 // Statics
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); // no timestamp necessary
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         // *FIX: this could suffer from endian issues
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                 //try an "old school" way.
00188                 if ( statusNum == 400 )
00189                 {
00190                         start_deprecated_conference_chat(
00191                                 mTempSessionID,
00192                                 mCreatorID,
00193                                 mOtherParticipantID,
00194                                 mAgents);
00195                 }
00196 
00197                 //else throw an error back to the client?
00198                 //in theory we should have just have these error strings
00199                 //etc. set up in this file as opposed to the IMMgr,
00200                 //but the error string were unneeded here previously
00201                 //and it is not worth the effort switching over all
00202                 //the possible different language translations
00203         }
00204 
00205 private:
00206         LLUUID mTempSessionID;
00207         LLUUID mCreatorID;
00208         LLUUID mOtherParticipantID;
00209 
00210         LLSD mAgents;
00211 };
00212 
00213 // Returns true if any messages were sent, false otherwise.
00214 // Is sort of equivalent to "does the server need to do anything?"
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                 //we have a new way of starting conference calls now
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);      // called with bad status codes
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                         //403 == no ability
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                 //*TODO: DEBUG SPAM
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 // LLVoiceChannel
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                 // a voice channel already exists for this session id, so this instance will be orphaned
00356                 // the end result should simply be the failure to make voice calls
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         // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed.
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                         // if we are supposed to be active, reconnect
00402                         // this will happen on initial connect, as we request credentials on first use
00403                         if (sCurrentVoiceChannel == this)
00404                         {
00405                                 // just in case we got new channel info while active
00406                                 // should move over to new channel
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         // status updates
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                         // if forceably removed from channel
00453                         // update the UI and revert to default channel
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 // default behavior is to just deactivate channel
00476 // derived classes provide specific error messages
00477 void LLVoiceChannel::handleError(EStatusType type)
00478 {
00479         deactivate();
00480         setState(STATE_ERROR);
00481 }
00482 
00483 BOOL LLVoiceChannel::isActive()
00484 { 
00485         // only considered active when currently bound channel matches what our channel
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                 // ignore session leave event
00499                 mIgnoreNextSessionLeave = TRUE;
00500         }
00501 
00502         if (callStarted())
00503         {
00504                 setState(STATE_HUNG_UP);
00505         }
00506         if (sCurrentVoiceChannel == this)
00507         {
00508                 // default channel is proximal channel
00509                 sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
00510                 sCurrentVoiceChannel->activate();
00511         }
00512 }
00513 
00514 void LLVoiceChannel::activate()
00515 {
00516         if (callStarted())
00517         {
00518                 return;
00519         }
00520 
00521         // deactivate old channel and mark ourselves as the active one
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                 // responsible for setting status to active
00534                 getChannelInfo();
00535         }
00536         else
00537         {
00538                 setState(STATE_CALL_STARTED);
00539         }
00540 }
00541 
00542 void LLVoiceChannel::getChannelInfo()
00543 {
00544         // pretend we have everything we need
00545         if (sCurrentVoiceChannel == this)
00546         {
00547                 setState(STATE_CALL_STARTED);
00548         }
00549 }
00550 
00551 //static 
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 //static 
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 //static
00616 void LLVoiceChannel::initClass()
00617 {
00618         sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();
00619 }
00620 
00621 
00622 //static 
00623 void LLVoiceChannel::suspend()
00624 {
00625         if (!sSuspended)
00626         {
00627                 sSuspendedVoiceChannel = sCurrentVoiceChannel;
00628                 sSuspended = TRUE;
00629         }
00630 }
00631 
00632 //static 
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 // LLVoiceChannelGroup
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                 // we have the channel info, just need to use it now
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                         // if we are supposed to be active, reconnect
00718                         // this will happen on initial connect, as we request credentials on first use
00719                         if (sCurrentVoiceChannel == this)
00720                         {
00721                                 // just in case we got new channel info while active
00722                                 // should move over to new channel
00723                                 activate();
00724                         }
00725                 }
00726                 else
00727                 {
00728                         //*TODO: notify user
00729                         llwarns << "Received invalid credentials for channel " << mSessionName << llendl;
00730                         deactivate();
00731                 }
00732         }
00733         else if ( mIsRetrying )
00734         {
00735                 // we have the channel info, just need to use it now
00736                 LLVoiceClient::getInstance()->setNonSpatialChannel(
00737                         mURI,
00738                         mCredentials);
00739         }
00740 }
00741 
00742 void LLVoiceChannelGroup::handleStatusChange(EStatusType type)
00743 {
00744         // status updates
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                 //clear URI and credentials
00768                 //set the state to be no info
00769                 //and activate
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         // notification
00794         if (!notify.empty())
00795         {
00796                 LLNotifyBox::showXml(notify, mNotifyArgs);
00797                 // echo to im window
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 // LLVoiceChannelProximal
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                 // this implicitly puts you back in the spatial channel
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         // status updates
00868         switch(status)
00869         {
00870         case STATUS_LEFT_CHANNEL:
00871                 // do not notify user when leaving proximal channel
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         // notification
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 // LLVoiceChannelP2P
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         // make sure URI reflects encoded version of other user's agent id
00923         setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id));
00924 }
00925 
00926 void LLVoiceChannelP2P::handleStatusChange(EStatusType type)
00927 {
00928         // status updates
00929         switch(type)
00930         {
00931         case STATUS_LEFT_CHANNEL:
00932                 if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended)
00933                 {
00934                         if (mState == STATE_RINGING)
00935                         {
00936                                 // other user declined call
00937                                 LLNotifyBox::showXml("P2PCallDeclined", mNotifyArgs);
00938                         }
00939                         else
00940                         {
00941                                 // other user hung up
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                 // no session handle yet, we're starting the call
00978                 if (mSessionHandle.empty())
00979                 {
00980                         mReceivedCall = FALSE;
00981                         LLVoiceClient::getInstance()->callUser(mOtherUserID);
00982                 }
00983                 // otherwise answering the call
00984                 else
00985                 {
00986                         LLVoiceClient::getInstance()->answerInvite(mSessionHandle, mOtherUserID);
00987                         // using the session handle invalidates it.  Clear it out here so we can't reuse it by accident.
00988                         mSessionHandle.clear();
00989                 }
00990         }
00991 }
00992 
00993 void LLVoiceChannelP2P::getChannelInfo()
00994 {
00995         // pretend we have everything we need, since P2P doesn't use channel info
00996         if (sCurrentVoiceChannel == this)
00997         {
00998                 setState(STATE_CALL_STARTED);
00999         }
01000 }
01001 
01002 // receiving session from other user who initiated call
01003 void LLVoiceChannelP2P::setSessionHandle(const LLString& handle)
01004 { 
01005         BOOL needs_activate = FALSE;
01006         if (callStarted())
01007         {
01008                 // defer to lower agent id when already active
01009                 if (mOtherUserID < gAgent.getID())
01010                 {
01011                         // pretend we haven't started the call yet, so we can connect to this session instead
01012                         deactivate();
01013                         needs_activate = TRUE;
01014                 }
01015                 else
01016                 {
01017                         // we are active and have priority, invite the other user again
01018                         // under the assumption they will join this new session
01019                         mSessionHandle.clear();
01020                         LLVoiceClient::getInstance()->callUser(mOtherUserID);
01021                         return;
01022                 }
01023         }
01024 
01025         mSessionHandle = handle;
01026         // The URI of a p2p session should always be the other end's SIP URI.
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         // you only "answer" voice invites in p2p mode
01039         // so provide a special purpose message here
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 // LLFloaterIMPanel
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 // must be invite to ad hoc IM
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         // just received text from another user
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         // enable line history support for instant message bar
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                         //we don't need to need to wait for any responses
01184                         //so we're already initialized
01185                         mSessionInitialized = TRUE;
01186                         mSessionStartMsgPos = 0;
01187                 }
01188                 else
01189                 {
01190                         //locally echo a little "starting session" message
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         //kicks you out of the voice channel if it is currently active
01212 
01213         // HAVE to do this here -- if it happens in the LLVoiceChannel destructor it will call the wrong version (since the object's partially deconstructed at that point).
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                 //LLButton* close_btn = getChild<LLButton>("close_btn");
01246                 //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this);
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 //static 
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 //static 
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 // virtual
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         // hide/show start call and end call buttons
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         // show speakers window when voice first connects
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                 // Time out if user hasn't typed for a while.
01360                 if (mLastKeystrokeTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
01361                 {
01362                         setTyping(FALSE);
01363                 }
01364 
01365                 // If we are typing, and it's been a little while, send the
01366                 // typing indicator
01367                 if (!mSentTypingState
01368                         && mFirstKeystrokeTimer.getElapsedTimeF32() > 1.f)
01369                 {
01370                         sendTypingState(TRUE);
01371                         mSentTypingState = TRUE;
01372                 }
01373         }
01374 
01375         // use embedded panel if available
01376         if (mSpeakerPanel)
01377         {
01378                 if (mSpeakerPanel->getVisible())
01379                 {
01380                         mSpeakerPanel->refreshSpeakers();
01381                 }
01382         }
01383         else
01384         {
01385                 // refresh volume and mute checkbox
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                 //throw something back to the viewer here?
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                 // successful add, because everyone that needed to get added
01451                 // was added.
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         // start tab flashing when receiving im for background session from user
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         // Now we're adding the actual line of text, so erase the 
01472         // "Foo is typing..." text segment, and the optional timestamp
01473         // if it was present. JC
01474         removeTypingIndicator(NULL);
01475 
01476         // Actually add the line
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         // 'name' is a sender name that we want to hotlink so that clicking on it opens a profile.
01486         if (name != NULL) // If name exists, then add it to the front of the message.
01487         {
01488                 // Don't hotlink any messages from the system (e.g. "Second Life:"), so just add those in plain text.
01489                 if (!strcmp(name,SYSTEM_FROM))
01490                 {
01491                         mHistoryEditor->appendColoredText(name,false,false,color);
01492                 }
01493                 else
01494                 {
01495                         // Convert the name to a hotlink and add to message.
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                 /* Don't change containing floater title - leave it "Instant Message" JC
01537                 LLUIString title = sTitleString;
01538                 title.setArg("[NAME]", mSessionLabel);
01539                 hostp->setTitle( title );
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                 // Close talk panels on hitting return
01572                 // but not shift-return or control-return
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                 // Close talk panel with escape
01584                 if( !gSavedSettings.getBOOL("PinTalkViewOpen") )
01585                 {
01586                         gIMMgr->toggle(NULL);
01587                 }
01588         }
01589 
01590         // May need to call base class LLPanel::handleKeyHere if not handled
01591         // in order to tab between buttons.  JNC 1.2.2002
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                 // set to false if creator uuid is null.
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 // static
01684 void LLFloaterIMPanel::onTabClick(void* userdata)
01685 {
01686         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01687         self->setInputFocus(TRUE);
01688 }
01689 
01690 
01691 // static
01692 void LLFloaterIMPanel::onClickProfile( void* userdata )
01693 {
01694         //  Bring up the Profile window
01695         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01696         
01697         if (self->mOtherParticipantUUID.notNull())
01698         {
01699                 LLFloaterAvatarInfo::showFromDirectory(self->getOtherParticipantID());
01700         }
01701 }
01702 
01703 // static
01704 void LLFloaterIMPanel::onClickGroupInfo( void* userdata )
01705 {
01706         //  Bring up the Profile window
01707         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01708 
01709         LLFloaterGroupInfo::showFromUUID(self->mSessionUUID);
01710 }
01711 
01712 // static
01713 void LLFloaterIMPanel::onClickClose( void* userdata )
01714 {
01715         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01716         if(self)
01717         {
01718                 self->close();
01719         }
01720 }
01721 
01722 // static
01723 void LLFloaterIMPanel::onClickStartCall(void* userdata)
01724 {
01725         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01726 
01727         self->mVoiceChannel->activate();
01728 }
01729 
01730 // static
01731 void LLFloaterIMPanel::onClickEndCall(void* userdata)
01732 {
01733         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01734 
01735         self->getVoiceChannel()->deactivate();
01736 }
01737 
01738 // static
01739 void LLFloaterIMPanel::onClickSend(void* userdata)
01740 {
01741         LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
01742         self->sendMsg();
01743 }
01744 
01745 // static
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 // static
01754 void LLFloaterIMPanel::onCommitChat(LLUICtrl* caller, void* userdata)
01755 {
01756         LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
01757         self->sendMsg();
01758 }
01759 
01760 // static
01761 void LLFloaterIMPanel::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata )
01762 {
01763         LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
01764         self->mHistoryEditor->setCursorAndScrollToEnd();
01765 }
01766 
01767 // static
01768 void LLFloaterIMPanel::onInputEditorFocusLost(LLFocusableElement* caller, void* userdata)
01769 {
01770         LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
01771         self->setTyping(FALSE);
01772 }
01773 
01774 // static
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                 // Deleting all text counts as stopping typing.
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         // default to IM_SESSION_SEND unless it's nothing special - in
01837         // which case it's probably an IM to everyone.
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                 // Truncate and convert to UTF8 for transport
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                         // local echo
01889                         if((mDialog == IM_NOTHING_SPECIAL) && 
01890                            (mOtherParticipantUUID.notNull()))
01891                         {
01892                                 std::string history_echo;
01893                                 gAgent.buildFullname(history_echo);
01894 
01895                                 // Look for IRC-style emotes here.
01896                                 char tmpstr[5];         /* Flawfinder: ignore */
01897                                 strncpy(tmpstr,
01898                                                 utf8_text.substr(0,4).c_str(),
01899                                                 sizeof(tmpstr) -1);             /* Flawfinder: ignore */
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                         //queue up the message to send once the session is
01926                         //initialized
01927                         mQueuedMsgsForInit.append(utf8_text);
01928                 }
01929 
01930                 LLViewerStats::getInstance()->incStat(LLViewerStats::ST_IM_COUNT);
01931         }
01932         mInputEditor->setText(LLString::null);
01933 
01934         // Don't need to actually send the typing stop message, the other
01935         // client will infer it from receiving the message.
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                 //update the speakers dropdown too
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         //we assume the history editor hasn't moved at all since
01980         //we added the starting session message
01981         //so, we count how many characters to remove
01982         S32 chars_to_remove = mHistoryEditor->getWText().length() -
01983                 mSessionStartMsgPos;
01984         mHistoryEditor->removeTextFromEnd(chars_to_remove);
01985 
01986         //and now, send the queued msg
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                 // Every time you type something, reset this timer
02010                 mLastKeystrokeTimer.reset();
02011 
02012                 if (!mTyping)
02013                 {
02014                         // You just started typing.
02015                         mFirstKeystrokeTimer.reset();
02016 
02017                         // Will send typing state after a short delay.
02018                         mSentTypingState = FALSE;
02019                 }
02020 
02021                 mSpeakers->setSpeakerTyping(gAgent.getID(), TRUE);
02022         }
02023         else
02024         {
02025                 if (mTyping)
02026                 {
02027                         // you just stopped typing, send state immediately
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         // Don't want to send typing indicators to multiple people, potentially too
02040         // much network traffic.  Only send in person-to-person IMs.
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                 // other user started typing
02065                 addTypingIndicator(im_info->mName);
02066         }
02067         else
02068         {
02069                 // other user stopped typing
02070                 removeTypingIndicator(im_info);
02071         }
02072 }
02073 
02074 
02075 void LLFloaterIMPanel::addTypingIndicator(const std::string &name)
02076 {
02077         // we may have lost a "stop-typing" packet, don't add it twice
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         // MBW -- XXX -- merge from release broke this (argument to this function changed from an LLIMInfo to a name)
02088         // Richard will fix.
02089 //      mSpeakers->setSpeakerTyping(im_info->mFromID, TRUE);
02090 }
02091 
02092 
02093 void LLFloaterIMPanel::removeTypingIndicator(const LLIMInfo* im_info)
02094 {
02095         if (mOtherTyping)
02096         {
02097                 // Must do this first, otherwise addHistoryLine calls us again.
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 //static
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                 // add warning log enabled message
02119                 message = LLFloaterChat::getInstance()->getUIString("IM_logging_string");
02120                 break;
02121         case LLLogChat::LOG_END:
02122                 // add log end message
02123                 message = LLFloaterChat::getInstance()->getUIString("IM_logging_string");
02124                 break;
02125         case LLLogChat::LOG_LINE:
02126                 // just add normal lines from file
02127                 break;
02128         default:
02129                 // nothing
02130                 break;
02131         }
02132 
02133         //self->addHistoryLine(line, LLColor4::grey, FALSE);
02134         self->mHistoryEditor->appendColoredText(message, false, true, LLColor4::grey);
02135 }
02136 
02137 void LLFloaterIMPanel::showSessionStartError(
02138         const std::string& error_string)
02139 {
02140         //the error strings etc. should be really be static and local
02141         //to this file instead of in the LLFloaterIM
02142         //but they were in llimview.cpp first and unfortunately
02143         //some translations into non English languages already occurred
02144         //thus making it a tad harder to change over to a
02145         //"correct" solution.  The best solution
02146         //would be to store all of the misc. strings into
02147         //their own XML file which would be read in by any LLIMPanel
02148         //post build function instead of repeating the same info
02149         //in the group, adhoc and normal IM xml files.
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 //static 
02195 void LLFloaterIMPanel::onKickSpeaker(void* user_data)
02196 {
02197 
02198 }
02199 
02200 void LLFloaterIMPanel::onConfirmForceCloseError(S32 option, void* data)
02201 {
02202         //only 1 option really
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 

Generated on Fri May 16 08:33:41 2008 for SecondLife by  doxygen 1.5.5