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

Generated on Thu Jul 1 06:08:44 2010 for Second Life Viewer by  doxygen 1.4.7