diff --git a/indra/aistatemachine/aistatemachine.cpp b/indra/aistatemachine/aistatemachine.cpp index 4b7470e26..6903589f2 100644 --- a/indra/aistatemachine/aistatemachine.cpp +++ b/indra/aistatemachine/aistatemachine.cpp @@ -615,7 +615,7 @@ void AIStateMachine::multiplex(event_type event) // If the state is bs_multiplex we only need to run again when need_run was set again in the meantime or when this state machine isn't idle. need_new_run = sub_state_r->need_run || !sub_state_r->idle; // If this fails then the run state didn't change and neither idle() nor yield() was called. - llassert_always(!(need_new_run && !mYieldEngine && sub_state_r->run_state == run_state)); + llassert_always(!(need_new_run && !sub_state_r->skip_idle && !mYieldEngine && sub_state_r->run_state == run_state)); } break; case bs_abort: diff --git a/indra/cmake/Python.cmake b/indra/cmake/Python.cmake index 748c8c2be..83a0690c6 100644 --- a/indra/cmake/Python.cmake +++ b/indra/cmake/Python.cmake @@ -20,10 +20,12 @@ if (WINDOWS) [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.4\\InstallPath] [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\2.3\\InstallPath] ) -elseif (EXISTS /etc/debian_version) - # On Debian and Ubuntu, avoid Python 2.4 if possible. - find_program(PYTHON_EXECUTABLE python2.5 python2.3 python PATHS /usr/bin) + +elseif (EXISTS /etc/arch-release) + # On Archlinux, use Python 2 + + find_program(PYTHON_EXECUTABLE python2 PATHS /usr/bin) if (PYTHON_EXECUTABLE) set(PYTHONINTERP_FOUND ON) diff --git a/indra/cwdebug/debug.h b/indra/cwdebug/debug.h index e14fb18b9..4167813ea 100644 --- a/indra/cwdebug/debug.h +++ b/indra/cwdebug/debug.h @@ -275,7 +275,7 @@ class TeeStream : public std::ostream { }; #if CWDEBUG_LOCATION -class BackTrace { +class LL_COMMON_API BackTrace { private: boost::shared_array M_buffer; int M_frames; diff --git a/indra/llmessage/aicurl.h b/indra/llmessage/aicurl.h index d15c72cd8..6249db171 100644 --- a/indra/llmessage/aicurl.h +++ b/indra/llmessage/aicurl.h @@ -197,6 +197,9 @@ U32 getNumHTTPQueued(void); // Returns the number of curl requests currently added to the multi handle. U32 getNumHTTPAdded(void); +// Return the maximum number of total allowed added curl requests. +U32 getMaxHTTPAdded(void); + // This used to be LLAppViewer::getTextureFetch()->getNumHTTPRequests(). // Returns the number of active curl easy handles (that are actually attempting to download something). U32 getNumHTTPRunning(void); diff --git a/indra/llmessage/aicurlperservice.cpp b/indra/llmessage/aicurlperservice.cpp index ff452c358..a0493f1a3 100644 --- a/indra/llmessage/aicurlperservice.cpp +++ b/indra/llmessage/aicurlperservice.cpp @@ -49,7 +49,7 @@ AIThreadSafeSimpleDC AIPerService::sTotalQueued; namespace AICurlPrivate { // Cached value of CurlConcurrentConnectionsPerService. -U32 CurlConcurrentConnectionsPerService; +U16 CurlConcurrentConnectionsPerService; // Friend functions of RefCountedThreadSafePerService @@ -72,10 +72,11 @@ using namespace AICurlPrivate; AIPerService::AIPerService(void) : mHTTPBandwidth(25), // 25 = 1000 ms / 40 ms. - mConcurrectConnections(CurlConcurrentConnectionsPerService), + mConcurrentConnections(CurlConcurrentConnectionsPerService), + mApprovedRequests(0), mTotalAdded(0), - mApprovedFirst(0), - mUnapprovedFirst(0) + mUsedCT(0), + mCTInUse(0) { } @@ -84,7 +85,9 @@ AIPerService::CapabilityType::CapabilityType(void) : mQueuedCommands(0), mAdded(0), mFlags(0), - mMaxPipelinedRequests(CurlConcurrentConnectionsPerService) + mDownloading(0), + mMaxPipelinedRequests(CurlConcurrentConnectionsPerService), + mConcurrentConnections(CurlConcurrentConnectionsPerService) { } @@ -121,6 +124,20 @@ AIPerService::AIPerService(AIPerService const&) : mHTTPBandwidth(0) // - userinfo does not contain a '@', and if it exists, is always terminated by a '@'. // - port does not contain a ':', and if it exists is always prepended by a ':'. // +// This function also needs to deal with full paths, in which case it should return +// an empty string. +// +// Full paths can have the form: "/something..." +// or "C:\something..." +// and maybe even "C:/something..." +// +// The first form leads to an empty string being returned because the '/' signals the +// end of the authority and we'll return immediately. +// The second one will abort when hitting the backslash because that is an illegal +// character in an url (before the first '/' anyway). +// The third will abort because "C:" would be the hostname and a colon in the hostname +// is not legal. +// //static std::string AIPerService::extract_canonical_servicename(std::string const& url) { @@ -158,8 +175,13 @@ std::string AIPerService::extract_canonical_servicename(std::string const& url) } else { - // Found slash that is not part of the "sheme://" string. Signals end of authority. + // Found a slash that is not part of the "sheme://" string. Signals end of authority. // We're done. + if (hostname < sheme_colon) + { + // This happens when windows filenames are passed to this function of the form "C:/..." + servicename.clear(); + } break; } } @@ -172,6 +194,12 @@ std::string AIPerService::extract_canonical_servicename(std::string const& url) servicename.clear(); // Remove the "userinfo@" } } + else if (c == '\\') + { + // Found a backslash, which is an illegal character for an URL. This is a windows path... reject it. + servicename.clear(); + break; + } if (p >= hostname) { // Convert hostname to lowercase in a way that we compare two hostnames equal iff libcurl does. @@ -252,9 +280,57 @@ void AIPerService::release(AIPerServicePtr& instance) instance.reset(); } -bool AIPerService::throttled() const +void AIPerService::redivide_connections(void) { - return mTotalAdded >= mConcurrectConnections; + // Priority order. + static AICapabilityType order[number_of_capability_types] = { cap_inventory, cap_texture, cap_mesh, cap_other }; + // Count the number of capability types that are currently in use and store the types in an array. + AICapabilityType used_order[number_of_capability_types]; + int number_of_capability_types_in_use = 0; + for (int i = 0; i < number_of_capability_types; ++i) + { + U32 const mask = CT2mask(order[i]); + if ((mCTInUse & mask)) + { + used_order[number_of_capability_types_in_use++] = order[i]; + } + else + { + // Give every other type (that is not in use) one connection, so they can be used (at which point they'll get more). + mCapabilityType[order[i]].mConcurrentConnections = 1; + } + } + // Keep one connection in reserve for currently unused capability types (that have been used before). + int reserve = (mUsedCT != mCTInUse) ? 1 : 0; + // Distribute (mConcurrentConnections - reserve) over number_of_capability_types_in_use. + U16 max_connections_per_CT = (mConcurrentConnections - reserve) / number_of_capability_types_in_use + 1; + // The first count CTs get max_connections_per_CT connections. + int count = (mConcurrentConnections - reserve) % number_of_capability_types_in_use; + for(int i = 1, j = 0;; --i) + { + while (j < count) + { + mCapabilityType[used_order[j++]].mConcurrentConnections = max_connections_per_CT; + } + if (i == 0) + { + break; + } + // Finish the loop till all used CTs are assigned. + count = number_of_capability_types_in_use; + // Never assign 0 as maximum. + if (max_connections_per_CT > 1) + { + // The remaining CTs get one connection less so that the sum of all assigned connections is mConcurrentConnections - reserve. + --max_connections_per_CT; + } + } +} + +bool AIPerService::throttled(AICapabilityType capability_type) const +{ + return mTotalAdded >= mConcurrentConnections || + mCapabilityType[capability_type].mAdded >= mCapabilityType[capability_type].mConcurrentConnections; } void AIPerService::added_to_multi_handle(AICapabilityType capability_type) @@ -263,17 +339,37 @@ void AIPerService::added_to_multi_handle(AICapabilityType capability_type) ++mTotalAdded; } -void AIPerService::removed_from_multi_handle(AICapabilityType capability_type) +void AIPerService::removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something) { - --mCapabilityType[capability_type].mAdded; + CapabilityType& ct(mCapabilityType[capability_type]); + llassert(mTotalAdded > 0 && ct.mAdded > 0); + bool done = --ct.mAdded == 0; + if (downloaded_something) + { + llassert(ct.mDownloading > 0); + --ct.mDownloading; + } --mTotalAdded; - llassert(mTotalAdded >= 0 && mCapabilityType[capability_type].mAdded >= 0); + if (done && ct.pipelined_requests() == 0) + { + mark_unused(capability_type); + } } -void AIPerService::queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type) +// Returns true if the request was queued. +bool AIPerService::queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type, bool force_queuing) { - mCapabilityType[capability_type].mQueuedRequests.push_back(easy_request.get_ptr()); - TotalQueued_wat(sTotalQueued)->count++; + CapabilityType::queued_request_type& queued_requests(mCapabilityType[capability_type].mQueuedRequests); + bool needs_queuing = force_queuing || !queued_requests.empty(); + if (needs_queuing) + { + queued_requests.push_back(easy_request.get_ptr()); + if (is_approved(capability_type)) + { + TotalQueued_wat(sTotalQueued)->approved++; + } + } + return needs_queuing; } bool AIPerService::cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type) @@ -297,105 +393,139 @@ bool AIPerService::cancel(AICurlEasyRequest const& easy_request, AICapabilityTyp prev = cur; } mCapabilityType[capability_type].mQueuedRequests.pop_back(); // if this is safe. - TotalQueued_wat total_queued_w(sTotalQueued); - total_queued_w->count--; - llassert(total_queued_w->count >= 0); + if (is_approved(capability_type)) + { + TotalQueued_wat total_queued_w(sTotalQueued); + llassert(total_queued_w->approved > 0); + total_queued_w->approved--; + } return true; } -void AIPerService::add_queued_to(curlthread::MultiHandle* multi_handle, bool recursive) +void AIPerService::add_queued_to(curlthread::MultiHandle* multi_handle, bool only_this_service) { - int order[number_of_capability_types]; - // The first two types are approved types, they should be the first to try. - // Try the one that has the largest queue first, if they the queues have equal size, try mApprovedFirst first. - size_t s0 = mCapabilityType[0].mQueuedRequests.size(); - size_t s1 = mCapabilityType[1].mQueuedRequests.size(); - if (s0 == s1) + U32 success = 0; // The CTs that we successfully added a request for from the queue. + bool success_this_pass = false; + int i = 0; + // The first pass we only look at CTs with 0 requests added to the multi handle. Subsequent passes only non-zero ones. + for (int pass = 0;; ++i) { - order[0] = mApprovedFirst; - mApprovedFirst = 1 - mApprovedFirst; - order[1] = mApprovedFirst; - } - else if (s0 > s1) - { - order[0] = 0; - order[1] = 1; - } - else - { - order[0] = 1; - order[1] = 0; - } - // The next two types are unapproved types. Here, try them alternating regardless of queue size. - int n = mUnapprovedFirst; - for (int i = 2; i < number_of_capability_types; ++i, n = (n + 1) % (number_of_capability_types - 2)) - { - order[i] = 2 + n; - } - mUnapprovedFirst = (mUnapprovedFirst + 1) % (number_of_capability_types - 2); - - for (int i = 0; i < number_of_capability_types; ++i) - { - CapabilityType& ct(mCapabilityType[order[i]]); - if (!ct.mQueuedRequests.empty()) + if (i == number_of_capability_types) { - if (!multi_handle->add_easy_request(ct.mQueuedRequests.front(), true)) + i = 0; + // Keep trying until we couldn't add anything anymore. + if (pass++ && !success_this_pass) { - // Throttled. If this failed then every capability type will fail: we either are using too much bandwidth, or too many total connections. - // However, it MAY be that this service was thottled for using too much bandwidth by itself. Look if other services can be added. + // Done. break; } - // Request was added, remove it from the queue. - ct.mQueuedRequests.pop_front(); - if (ct.mQueuedRequests.empty()) - { - // We obtained a request from the queue, and after that there we no more request in the queue of this service. - ct.mFlags |= ctf_empty; - } - else - { - // We obtained a request from the queue, and even after that there was at least one more request in the queue of this service. - ct.mFlags |= ctf_full; - } - TotalQueued_wat total_queued_w(sTotalQueued); - llassert(total_queued_w->count > 0); - if (!--(total_queued_w->count)) - { - // We obtained a request from the queue, and after that there we no more request in any queue. - total_queued_w->empty = true; - } - else - { - // We obtained a request from the queue, and even after that there was at least one more request in some queue. - total_queued_w->full = true; - } - // We added something from a queue, so we're done. - return; + success_this_pass = false; } - else + CapabilityType& ct(mCapabilityType[i]); + if (!pass != !ct.mAdded) // Does mAdded match what we're looking for (first mAdded == 0, then mAdded != 0)? + { + continue; + } + if (multi_handle->added_maximum()) + { + // We hit the maximum number of global connections. Abort every attempt to add anything. + only_this_service = true; + break; + } + if (mTotalAdded >= mConcurrentConnections) + { + // We hit the maximum number of connections for this service. Abort any attempt to add anything to this service. + break; + } + if (ct.mAdded >= ct.mConcurrentConnections) + { + // We hit the maximum number of connections for this capability type. Try the next one. + continue; + } + U32 mask = CT2mask((AICapabilityType)i); + if (ct.mQueuedRequests.empty()) // Is there anything in the queue (left) at all? { // We could add a new request, but there is none in the queue! // Note that if this service does not serve this capability type, // then obviously this queue was empty; however, in that case // this variable will never be looked at, so it's ok to set it. - ct.mFlags |= ctf_starvation; + ct.mFlags |= ((success & mask) ? ctf_empty : ctf_starvation); } - if (i == number_of_capability_types - 1) + else { - // Last entry also empty. All queues of this service were empty. Check total connections. - TotalQueued_wat total_queued_w(sTotalQueued); - if (total_queued_w->count == 0) + // Attempt to add the front of the queue. + if (!multi_handle->add_easy_request(ct.mQueuedRequests.front(), true)) { - // The queue of every service is empty! - total_queued_w->starvation = true; - return; + // If that failed then we got throttled on bandwidth because the maximum number of connections were not reached yet. + // Therefore this will keep failing for this service, we abort any additional attempt to add something for this service. + break; + } + // Request was added, remove it from the queue. + ct.mQueuedRequests.pop_front(); + // Mark that at least one request of this CT was successfully added. + success |= mask; + success_this_pass = true; + // Update approved count. + if (is_approved((AICapabilityType)i)) + { + TotalQueued_wat total_queued_w(sTotalQueued); + llassert(total_queued_w->approved > 0); + total_queued_w->approved--; } } } - if (recursive) + + size_t queuedapproved_size = 0; + for (int i = 0; i < number_of_capability_types; ++i) + { + CapabilityType& ct(mCapabilityType[i]); + U32 mask = CT2mask((AICapabilityType)i); + // Add up the size of all queues with approved requests. + if ((approved_mask & mask)) + { + queuedapproved_size += ct.mQueuedRequests.size(); + } + // Skip CTs that we didn't add anything for. + if (!(success & mask)) + { + continue; + } + if (!ct.mQueuedRequests.empty()) + { + // We obtained one or more requests from the queue, and even after that there was at least one more request in the queue of this CT. + ct.mFlags |= ctf_full; + } + } + + // Update the flags of sTotalQueued. + { + TotalQueued_wat total_queued_w(sTotalQueued); + if (total_queued_w->approved == 0) + { + if ((success & approved_mask)) + { + // We obtained an approved request from the queue, and after that there were no more requests in any (approved) queue. + total_queued_w->empty = true; + } + else + { + // Every queue of every approved CT is empty! + total_queued_w->starvation = true; + } + } + else if ((success & approved_mask)) + { + // We obtained an approved request from the queue, and even after that there was at least one more request in some (approved) queue. + total_queued_w->full = true; + } + } + + // Don't try other services if anything was added successfully. + if (success || only_this_service) { return; } + // Nothing from this service could be added, try other services. instance_map_wat instance_map_w(sInstanceMap); for (iterator service = instance_map_w->begin(); service != instance_map_w->end(); ++service) @@ -422,8 +552,11 @@ void AIPerService::purge(void) { size_t s = per_service_w->mCapabilityType[i].mQueuedRequests.size(); per_service_w->mCapabilityType[i].mQueuedRequests.clear(); - total_queued_w->count -= s; - llassert(total_queued_w->count >= 0); + if (is_approved((AICapabilityType)i)) + { + llassert(total_queued_w->approved >= s); + total_queued_w->approved -= s; + } } } } @@ -435,24 +568,34 @@ void AIPerService::adjust_concurrent_connections(int increment) for (AIPerService::iterator iter = instance_map_w->begin(); iter != instance_map_w->end(); ++iter) { PerService_wat per_service_w(*iter->second); - U32 old_concurrent_connections = per_service_w->mConcurrectConnections; - per_service_w->mConcurrectConnections = llclamp(old_concurrent_connections + increment, (U32)1, CurlConcurrentConnectionsPerService); - increment = per_service_w->mConcurrectConnections - old_concurrent_connections; + U16 old_concurrent_connections = per_service_w->mConcurrentConnections; + int new_concurrent_connections = llclamp(old_concurrent_connections + increment, 1, (int)CurlConcurrentConnectionsPerService); + per_service_w->mConcurrentConnections = (U16)new_concurrent_connections; + increment = per_service_w->mConcurrentConnections - old_concurrent_connections; for (int i = 0; i < number_of_capability_types; ++i) { - per_service_w->mCapabilityType[i].mMaxPipelinedRequests = llmax(per_service_w->mCapabilityType[i].mMaxPipelinedRequests + increment, (U32)0); + per_service_w->mCapabilityType[i].mMaxPipelinedRequests = llmax(per_service_w->mCapabilityType[i].mMaxPipelinedRequests + increment, 0); + int new_concurrent_connections_per_capability_type = + llclamp((new_concurrent_connections * per_service_w->mCapabilityType[i].mConcurrentConnections + old_concurrent_connections / 2) / old_concurrent_connections, 1, new_concurrent_connections); + per_service_w->mCapabilityType[i].mConcurrentConnections = (U16)new_concurrent_connections_per_capability_type; } } } +void AIPerService::ResetUsed::operator()(AIPerService::instance_map_type::value_type const& service) const +{ + PerService_wat(*service.second)->resetUsedCt(); +} + void AIPerService::Approvement::honored(void) { if (!mHonored) { mHonored = true; - AICurlPrivate::PerService_wat per_service_w(*mPerServicePtr); - llassert(per_service_w->mCapabilityType[mCapabilityType].mApprovedRequests > 0); + PerService_wat per_service_w(*mPerServicePtr); + llassert(per_service_w->mCapabilityType[mCapabilityType].mApprovedRequests > 0 && per_service_w->mApprovedRequests > 0); per_service_w->mCapabilityType[mCapabilityType].mApprovedRequests--; + per_service_w->mApprovedRequests--; } } diff --git a/indra/llmessage/aicurlperservice.h b/indra/llmessage/aicurlperservice.h index f50c1f393..0509dfd95 100644 --- a/indra/llmessage/aicurlperservice.h +++ b/indra/llmessage/aicurlperservice.h @@ -43,12 +43,15 @@ #include #include #include +#include +#include #include #include "aithreadsafe.h" #include "aiaverage.h" class AICurlEasyRequest; class AIPerService; +class AIServiceBar; namespace AICurlPrivate { namespace curlthread { class MultiHandle; } @@ -59,6 +62,8 @@ class ThreadSafeBufferedCurlEasyRequest; // Forward declaration of BufferedCurlEasyRequestPtr (see aicurlprivate.h). typedef boost::intrusive_ptr BufferedCurlEasyRequestPtr; +} // namespace AICurlPrivate + // AIPerService objects are created by the curl thread and destructed by the main thread. // We need locking. typedef AIThreadSafeSimpleDC threadsafe_PerService; @@ -66,8 +71,6 @@ typedef AIAccessConst PerService_crat; typedef AIAccess PerService_rat; typedef AIAccess PerService_wat; -} // namespace AICurlPrivate - // We can't put threadsafe_PerService in a std::map because you can't copy a mutex. // Therefore, use an intrusive pointer for the threadsafe type. typedef boost::intrusive_ptr AIPerServicePtr; @@ -84,6 +87,8 @@ enum AICapabilityType { // {Capabilities} [Responders] number_of_capability_types = 4 }; +static U32 const approved_mask = 3; // The mask of cap_texture OR-ed with the mask of cap_inventory. + //----------------------------------------------------------------------------- // AIPerService @@ -93,13 +98,14 @@ enum AICapabilityType { // {Capabilities} [Responders] // for that service already have been reached. And to keep track of the bandwidth usage, and the // number of queued requests in the pipeline, for this service. class AIPerService { - private: + public: typedef std::map instance_map_type; typedef AIThreadSafeSimpleDC threadsafe_instance_map_type; typedef AIAccess instance_map_rat; typedef AIAccess instance_map_wat; - static threadsafe_instance_map_type sInstanceMap; // Map of AIPerService instances with the hostname as key. + private: + static threadsafe_instance_map_type sInstanceMap; // Map of AIPerService instances with the canonical hostname:port as key. friend class AIThreadSafeSimpleDC; // threadsafe_PerService AIPerService(void); @@ -111,7 +117,7 @@ class AIPerService { // Utility function; extract canonical (lowercase) hostname and port from url. static std::string extract_canonical_servicename(std::string const& url); - // Return (possibly create) a unique instance for the given hostname. + // Return (possibly create) a unique instance for the given hostname:port combination. static AIPerServicePtr instance(std::string const& servicename); // Release instance (object will be deleted if this was the last instance). @@ -120,6 +126,10 @@ class AIPerService { // Remove everything. Called upon viewer exit. static void purge(void); + // Make a copy of the instanceMap and then run 'action(per_service)' on each AIPerService object. + template + static void copy_forEach(Action const& action); + private: static U16 const ctf_empty = 1; static U16 const ctf_full = 2; @@ -129,13 +139,15 @@ class AIPerService { typedef std::deque queued_request_type; queued_request_type mQueuedRequests; // Waiting (throttled) requests. - U16 mApprovedRequests; // The number of approved requests by approveHTTPRequestFor that were not added to the command queue yet. + U16 mApprovedRequests; // The number of approved requests for this CT by approveHTTPRequestFor that were not added to the command queue yet. U16 mQueuedCommands; // Number of add commands (minus remove commands), for this service, in the command queue. U16 mAdded; // Number of active easy handles with this service. U16 mFlags; // ctf_empty: Set to true when the queue becomes precisely empty. // ctf_full : Set to true when the queue is popped and then still isn't empty; // ctf_starvation: Set to true when the queue was about to be popped but was already empty. - U32 mMaxPipelinedRequests; // The maximum number of accepted requests for this service and (approved) capability type, that didn't finish yet. + U32 mDownloading; // The number of active easy handles with this service for which data was received. + U16 mMaxPipelinedRequests; // The maximum number of accepted requests for this service and (approved) capability type, that didn't finish yet. + U16 mConcurrentConnections; // The maximum number of allowed concurrent connections to the service of this capability type. // Declare, not define, constructor and destructor - in order to avoid instantiation of queued_request_type from header. CapabilityType(void); @@ -144,45 +156,88 @@ class AIPerService { S32 pipelined_requests(void) const { return mApprovedRequests + mQueuedCommands + mQueuedRequests.size() + mAdded; } }; + friend class AIServiceBar; CapabilityType mCapabilityType[number_of_capability_types]; AIAverage mHTTPBandwidth; // Keeps track on number of bytes received for this service in the past second. - int mConcurrectConnections; // The maximum number of allowed concurrent connections to this service. - int mTotalAdded; // Number of active easy handles with this host. - int mApprovedFirst; // First capability type to try. - int mUnapprovedFirst; // First capability type to try after all approved types were tried. + int mConcurrentConnections; // The maximum number of allowed concurrent connections to this service. + int mApprovedRequests; // The number of approved requests for this service by approveHTTPRequestFor that were not added to the command queue yet. + int mTotalAdded; // Number of active easy handles with this service. + + U32 mUsedCT; // Bit mask with one bit per capability type. A '1' means the capability was in use since the last resetUsedCT(). + U32 mCTInUse; // Bit mask with one bit per capability type. A '1' means the capability is in use right now. + + // Helper struct, used in the static resetUsed. + struct ResetUsed { void operator()(instance_map_type::value_type const& service) const; }; + + void redivide_connections(void); + void mark_inuse(AICapabilityType capability_type) + { + U32 bit = CT2mask(capability_type); + if ((mCTInUse & bit) == 0) // If this CT went from unused to used + { + mCTInUse |= bit; + mUsedCT |= bit; + if (mUsedCT != bit) // and more than one CT use this service. + { + redivide_connections(); + } + } + } + void mark_unused(AICapabilityType capability_type) + { + U32 bit = CT2mask(capability_type); + if ((mCTInUse & bit) != 0) // If this CT went from used to unused + { + mCTInUse &= ~bit; + if (mCTInUse && mUsedCT != bit) // and more than one CT use this service, and at least one is in use. + { + redivide_connections(); + } + } + } + public: + static bool is_approved(AICapabilityType capability_type) { return (((U32)1 << capability_type) & approved_mask); } + static U32 CT2mask(AICapabilityType capability_type) { return (U32)1 << capability_type; } + void resetUsedCt(void) { mUsedCT = mCTInUse; } + bool is_used(AICapabilityType capability_type) const { return (mUsedCT & CT2mask(capability_type)); } + bool is_inuse(AICapabilityType capability_type) const { return (mCTInUse & CT2mask(capability_type)); } + + static void resetUsed(void) { copy_forEach(ResetUsed()); } + U32 is_used(void) const { return mUsedCT; } // Non-zero if this service was used for any capability type. + U32 is_inuse(void) const { return mCTInUse; } // Non-zero if this service is in use for any capability type. // Global administration of the total number of queued requests of all services combined. private: struct TotalQueued { - S32 count; // The sum of mQueuedRequests.size() of all AIPerService objects together. - bool empty; // Set to true when count becomes precisely zero as the result of popping any queue. - bool full; // Set to true when count is still larger than zero after popping any queue. - bool starvation; // Set to true when any queue was about to be popped when count was already zero. - TotalQueued(void) : count(0), empty(false), full(false), starvation(false) { } + S32 approved; // The sum of mQueuedRequests.size() of all AIPerService::CapabilityType objects of approved types. + bool empty; // Set to true when approved becomes precisely zero as the result of popping any queue. + bool full; // Set to true when approved is still larger than zero after popping any queue. + bool starvation; // Set to true when any queue was about to be popped when approved was already zero. + TotalQueued(void) : approved(0), empty(false), full(false), starvation(false) { } }; static AIThreadSafeSimpleDC sTotalQueued; typedef AIAccessConst TotalQueued_crat; typedef AIAccess TotalQueued_rat; typedef AIAccess TotalQueued_wat; public: - static S32 total_queued_size(void) { return TotalQueued_rat(sTotalQueued)->count; } + static S32 total_approved_queue_size(void) { return TotalQueued_rat(sTotalQueued)->approved; } // Global administration of the maximum number of pipelined requests of all services combined. private: struct MaxPipelinedRequests { - S32 count; // The maximum total number of accepted requests that didn't finish yet. + S32 threshold; // The maximum total number of accepted requests that didn't finish yet. U64 last_increment; // Last time that sMaxPipelinedRequests was incremented. U64 last_decrement; // Last time that sMaxPipelinedRequests was decremented. - MaxPipelinedRequests(void) : count(32), last_increment(0), last_decrement(0) { } + MaxPipelinedRequests(void) : threshold(32), last_increment(0), last_decrement(0) { } }; static AIThreadSafeSimpleDC sMaxPipelinedRequests; typedef AIAccessConst MaxPipelinedRequests_crat; typedef AIAccess MaxPipelinedRequests_rat; typedef AIAccess MaxPipelinedRequests_wat; public: - static void setMaxPipelinedRequests(S32 count) { MaxPipelinedRequests_wat(sMaxPipelinedRequests)->count = count; } - static void incrementMaxPipelinedRequests(S32 increment) { MaxPipelinedRequests_wat(sMaxPipelinedRequests)->count += increment; } + static void setMaxPipelinedRequests(S32 threshold) { MaxPipelinedRequests_wat(sMaxPipelinedRequests)->threshold = threshold; } + static void incrementMaxPipelinedRequests(S32 increment) { MaxPipelinedRequests_wat(sMaxPipelinedRequests)->threshold += increment; } // Global administration of throttle fraction (which is the same for all services). private: @@ -201,16 +256,18 @@ class AIPerService { static bool sNoHTTPBandwidthThrottling; // Global override to disable bandwidth throttling. public: - void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; } + void added_to_command_queue(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mQueuedCommands; mark_inuse(capability_type); } void removed_from_command_queue(AICapabilityType capability_type) { --mCapabilityType[capability_type].mQueuedCommands; llassert(mCapabilityType[capability_type].mQueuedCommands >= 0); } - void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this host has been added to the multi handle. - void removed_from_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this host is removed again from the multi handle. - bool throttled(void) const; // Returns true if the maximum number of allowed requests for this host have been added to the multi handle. + void added_to_multi_handle(AICapabilityType capability_type); // Called when an easy handle for this service has been added to the multi handle. + void removed_from_multi_handle(AICapabilityType capability_type, bool downloaded_something); // Called when an easy handle for this service is removed again from the multi handle. + void download_started(AICapabilityType capability_type) { ++mCapabilityType[capability_type].mDownloading; } + bool throttled(AICapabilityType capability_type) const; // Returns true if the maximum number of allowed requests for this service/capability type have been added to the multi handle. + bool nothing_added(AICapabilityType capability_type) const { return mCapabilityType[capability_type].mAdded == 0; } - void queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Add easy_request to the queue. - bool cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Remove easy_request from the queue (if it's there). + bool queue(AICurlEasyRequest const& easy_request, AICapabilityType capability_type, bool force_queuing = true); // Add easy_request to the queue if queue is empty or force_queuing. + bool cancel(AICurlEasyRequest const& easy_request, AICapabilityType capability_type); // Remove easy_request from the queue (if it's there). - void add_queued_to(AICurlPrivate::curlthread::MultiHandle* mh, bool recursive = false); + void add_queued_to(AICurlPrivate::curlthread::MultiHandle* mh, bool only_this_service = false); // Add queued easy handle (if any) to the multi handle. The request is removed from the queue, // followed by either a call to added_to_multi_handle() or to queue() to add it back. @@ -222,6 +279,7 @@ class AIPerService { static void setNoHTTPBandwidthThrottling(bool nb) { sNoHTTPBandwidthThrottling = nb; } static void setHTTPThrottleBandwidth(F32 max_kbps) { sHTTPThrottleBandwidth125 = 125.f * max_kbps; } static size_t getHTTPThrottleBandwidth125(void) { return sHTTPThrottleBandwidth125; } + static F32 throttleFraction(void) { return ThrottleFraction_wat(sThrottleFraction)->fraction / 1024.f; } // Called when CurlConcurrentConnectionsPerService changes. static void adjust_concurrent_connections(int increment); @@ -244,7 +302,7 @@ class AIPerService { // the AIPerService object locked for the whole duration of the call. // The functions only lock it when access is required. - // Returns approvement if curl can handle another request for this host. + // Returns approvement if curl can handle another request for this service. // Should return NULL if the maximum allowed HTTP bandwidth is reached, or when // the latency between request and actual delivery becomes too large. static Approvement* approveHTTPRequestFor(AIPerServicePtr const& per_service, AICapabilityType capability_type); @@ -271,8 +329,21 @@ class RefCountedThreadSafePerService : public threadsafe_PerService { friend void intrusive_ptr_release(RefCountedThreadSafePerService* p); }; -extern U32 CurlConcurrentConnectionsPerService; +extern U16 CurlConcurrentConnectionsPerService; } // namespace AICurlPrivate +template +void AIPerService::copy_forEach(Action const& action) +{ + // Make a copy so we don't need to keep the lock on sInstanceMap for too long. + std::vector > current_services; + { + instance_map_rat instance_map_r(sInstanceMap); + std::copy(instance_map_r->begin(), instance_map_r->end(), std::back_inserter(current_services)); + } + // Apply the functor on each of the services. + std::for_each(current_services.begin(), current_services.end(), action); +} + #endif // AICURLPERSERVICE_H diff --git a/indra/llmessage/aicurlprivate.h b/indra/llmessage/aicurlprivate.h index aa5a1ba7b..af8b6b267 100644 --- a/indra/llmessage/aicurlprivate.h +++ b/indra/llmessage/aicurlprivate.h @@ -463,6 +463,9 @@ class BufferedCurlEasyRequest : public CurlEasyRequest { // Return the capability type of this request. AICapabilityType capability_type(void) const { llassert(mCapabilityType != number_of_capability_types); return mCapabilityType; } + + // Return true if any data was received. + bool received_data(void) const { return mTotalRawBytes > 0; } }; inline ThreadSafeBufferedCurlEasyRequest* CurlEasyRequest::get_lockobj(void) diff --git a/indra/llmessage/aicurlthread.cpp b/indra/llmessage/aicurlthread.cpp index d27e755c6..a29859048 100644 --- a/indra/llmessage/aicurlthread.cpp +++ b/indra/llmessage/aicurlthread.cpp @@ -1710,7 +1710,7 @@ CURLMsg const* MultiHandle::info_read(int* msgs_in_queue) const return ret; } -static U32 curl_max_total_concurrent_connections = 32; // Initialized on start up by startCurlThread(). +U32 curl_max_total_concurrent_connections = 32; // Initialized on start up by startCurlThread(). bool MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request, bool from_queue) { @@ -1721,10 +1721,32 @@ bool MultiHandle::add_easy_request(AICurlEasyRequest const& easy_request, bool f AICurlEasyRequest_wat curl_easy_request_w(*easy_request); capability_type = curl_easy_request_w->capability_type(); per_service = curl_easy_request_w->getPerServicePtr(); - // Never throttle on bandwidth if there are no handles running (sTotalAdded == 1, the long poll connection). - bool too_much_bandwidth = sTotalAdded > 1 && !curl_easy_request_w->approved() && AIPerService::checkBandwidthUsage(per_service, get_clock_count() * HTTPTimeout::sClockWidth_40ms); + if (!from_queue) + { + // Add the request to the back of a non-empty queue. + PerService_wat per_service_w(*per_service); + if (per_service_w->queue(easy_request, capability_type, false)) + { + // The queue was not empty, therefore the request was queued. +#ifdef SHOW_ASSERT + // Not active yet, but it's no longer an error if next we try to remove the request. + curl_easy_request_w->mRemovedPerCommand = false; +#endif + // This is a fail-safe. Normally, if there is anything in the queue then things should + // be running (normally an attempt is made to add from the queue whenever a request + // finishes). However, it CAN happen on occassion that things get 'stuck' with + // nothing running, so nothing will ever finish and therefore the queue would never + // be checked. Only do this when there is indeed nothing running (added) though. + if (per_service_w->nothing_added(capability_type)) + { + per_service_w->add_queued_to(this); + } + return true; + } + } + bool too_much_bandwidth = !curl_easy_request_w->approved() && AIPerService::checkBandwidthUsage(per_service, get_clock_count() * HTTPTimeout::sClockWidth_40ms); PerService_wat per_service_w(*per_service); - if (!too_much_bandwidth && sTotalAdded < curl_max_total_concurrent_connections && !per_service_w->throttled()) + if (!too_much_bandwidth && sTotalAdded < curl_max_total_concurrent_connections && !per_service_w->throttled(capability_type)) { curl_easy_request_w->set_timeout_opts(); if (curl_easy_request_w->add_handle_to_multi(curl_easy_request_w, mMultiHandle) == CURLM_OK) @@ -1791,10 +1813,11 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons AIPerServicePtr per_service; { AICurlEasyRequest_wat curl_easy_request_w(**iter); + bool downloaded_something = curl_easy_request_w->received_data(); res = curl_easy_request_w->remove_handle_from_multi(curl_easy_request_w, mMultiHandle); capability_type = curl_easy_request_w->capability_type(); per_service = curl_easy_request_w->getPerServicePtr(); - PerService_wat(*per_service)->removed_from_multi_handle(capability_type); // (About to be) removed from mAddedEasyRequests. + PerService_wat(*per_service)->removed_from_multi_handle(capability_type, downloaded_something); // (About to be) removed from mAddedEasyRequests. #ifdef SHOW_ASSERT curl_easy_request_w->mRemovedPerCommand = as_per_command; #endif @@ -2137,6 +2160,12 @@ void BufferedCurlEasyRequest::update_body_bandwidth(void) getinfo(CURLINFO_SIZE_DOWNLOAD, &size_download); size_t total_raw_bytes = size_download; size_t raw_bytes = total_raw_bytes - mTotalRawBytes; + if (mTotalRawBytes == 0 && total_raw_bytes > 0) + { + // Update service/capability type administration for the HTTP Debug Console. + PerService_wat per_service_w(*mPerServicePtr); + per_service_w->download_started(mCapabilityType); + } mTotalRawBytes = total_raw_bytes; // Note that in some cases (like HTTP_PARTIAL_CONTENT), the output of CURLINFO_SIZE_DOWNLOAD lags // behind and will return 0 the first time, and the value of the previous chunk the next time. @@ -2190,6 +2219,7 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size return header_len; } std::string header(header_line, header_len); + bool being_redirected = false; bool done = false; if (!LLStringUtil::_isASCII(header)) { @@ -2234,7 +2264,7 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size if (status >= 300 && status < 400) { // Timeout administration needs to know if we're being redirected. - self_w->httptimeout()->being_redirected(); + being_redirected = true; } } // Update HTTP bandwidth. @@ -2248,6 +2278,14 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size // Transfer timed out. Return 0 which will abort with error CURLE_WRITE_ERROR. return 0; } + if (being_redirected) + { + // Call this after data_received(), because that might reset mBeingRedirected if it causes a late- upload_finished! + // Ie, when the upload finished was not detected and this is a redirect header then the call to data_received() + // will call upload_finished() which sets HTTPTimeout::mUploadFinished (and resets HTTPTimeout::mBeingRedirected), + // after which we set HTTPTimeout::mBeingRedirected here because we ARE being redirected. + self_w->httptimeout()->being_redirected(); + } if (done) { return header_len; @@ -2533,7 +2571,7 @@ void startCurlThread(LLControlGroup* control_group) // Cache Debug Settings. sConfigGroup = control_group; curl_max_total_concurrent_connections = sConfigGroup->getU32("CurlMaxTotalConcurrentConnections"); - CurlConcurrentConnectionsPerService = sConfigGroup->getU32("CurlConcurrentConnectionsPerService"); + CurlConcurrentConnectionsPerService = (U16)sConfigGroup->getU32("CurlConcurrentConnectionsPerService"); gNoVerifySSLCert = sConfigGroup->getBOOL("NoVerifySSLCert"); AIPerService::setMaxPipelinedRequests(curl_max_total_concurrent_connections); AIPerService::setHTTPThrottleBandwidth(sConfigGroup->getF32("HTTPThrottleBandwidth")); @@ -2558,10 +2596,19 @@ bool handleCurlConcurrentConnectionsPerService(LLSD const& newvalue) { using namespace AICurlPrivate; - U32 new_concurrent_connections = newvalue.asInteger(); - AIPerService::adjust_concurrent_connections(new_concurrent_connections - CurlConcurrentConnectionsPerService); - CurlConcurrentConnectionsPerService = new_concurrent_connections; - llinfos << "CurlConcurrentConnectionsPerService set to " << CurlConcurrentConnectionsPerService << llendl; + U16 new_concurrent_connections = (U16)newvalue.asInteger(); + U16 const maxCurlConcurrentConnectionsPerService = 32; + if (new_concurrent_connections < 1 || new_concurrent_connections > maxCurlConcurrentConnectionsPerService) + { + sConfigGroup->setU32("CurlConcurrentConnectionsPerService", static_cast((new_concurrent_connections < 1) ? 1 : maxCurlConcurrentConnectionsPerService)); + } + else + { + int increment = new_concurrent_connections - CurlConcurrentConnectionsPerService; + CurlConcurrentConnectionsPerService = new_concurrent_connections; + AIPerService::adjust_concurrent_connections(increment); + llinfos << "CurlConcurrentConnectionsPerService set to " << CurlConcurrentConnectionsPerService << llendl; + } return true; } @@ -2581,7 +2628,7 @@ U32 getNumHTTPCommands(void) U32 getNumHTTPQueued(void) { - return AIPerService::total_queued_size(); + return AIPerService::total_approved_queue_size(); } U32 getNumHTTPAdded(void) @@ -2589,6 +2636,11 @@ U32 getNumHTTPAdded(void) return AICurlPrivate::curlthread::MultiHandle::total_added_size(); } +U32 getMaxHTTPAdded(void) +{ + return AICurlPrivate::curlthread::curl_max_total_concurrent_connections; +} + size_t getHTTPBandwidth(void) { using namespace AICurlPrivate; @@ -2605,29 +2657,69 @@ AIThreadSafeSimpleDC AIPerService::sThrottleFrac LLAtomicU32 AIPerService::sHTTPThrottleBandwidth125(250000); bool AIPerService::sNoHTTPBandwidthThrottling; -// Return true if we want at least one more HTTP request for this host. +// Return Approvement if we want at least one more HTTP request for this service. // // It's OK if this function is a bit fuzzy, but we don't want it to return -// true a hundred times on a row when it is called fast in a loop. +// approvement a hundred times on a row when it is called in a tight loop. // Hence the following consideration: // -// This function is called only from LLTextureFetchWorker::doWork, and when it returns true -// then doWork will call LLHTTPClient::request with a NULL default engine (signaling that -// it is OK to run in any thread). -// -// At the end, LLHTTPClient::request calls AIStateMachine::run, which in turn calls -// AIStateMachine::reset at the end. Because NULL is passed as default_engine, reset will -// call AIStateMachine::multiplex to immediately start running the state machine. This -// causes it to go through the states bs_reset, bs_initialize and then bs_multiplex with -// run state AICurlEasyRequestStateMachine_addRequest. Finally, in this state, multiplex -// calls AICurlEasyRequestStateMachine::multiplex_impl which then calls AICurlEasyRequest::addRequest -// which causes an increment of command_queue_w->size and AIPerService::mQueuedCommands. +// If this function returns non-NULL, a Approvement object was created and +// the corresponding AIPerService::CapabilityType::mApprovedRequests was +// incremented. The Approvement object is passed to LLHTTPClient::request, +// and once the request is added to the command queue, used to update the counters. // // It is therefore guaranteed that in one loop of LLTextureFetchWorker::doWork, -// this size is incremented; stopping this function from returning true once we reached the -// threshold of "pipelines" requests (the sum of requests in the command queue, the ones -// throttled and queued in AIPerService::mQueuedRequests and the already -// running requests (in MultiHandle::mAddedEasyRequests)). +// or LLInventoryModelBackgroundFetch::bulkFetch (the two functions currently +// calling this function) this function will stop returning aprovement once we +// reached the threshold of "pipelined" requests (the sum of approved requests, +// requests in the command queue, the ones throttled and queued in +// AIPerService::mQueuedRequests and the already running requests +// (in MultiHandle::mAddedEasyRequests)). +// +// A request has two types of reasons why it can be throttled: +// 1) The number of connections. +// 2) Bandwidth usage. +// And three levels where each can occur: +// a) Global +// b) Service +// c) Capability Type (CT) +// Currently, not all of those are in use. The ones that are used are: +// +// | Global | Service | CT +// +--------+---------+-------- +// 1) The number of connections | X | X | X +// 2) Bandwidth usage | X | | +// +// Pre-approved requests have the bandwidth tested here, and the +// connections tested in the curl thread, right before they are +// added to the multi handle. +// +// The "pipeline" is as follows: +// +// // If the number of requests in the pipeline is less than a threshold +// | // and the global bandwidth usage is not too large. +// V +// +// | +// V +// +// | +// V +// // If the number of connections at all three levels allow it. +// | +// V +// +// +// Every time this function is called, but not more often than once every 40 ms, the state +// of the CT queue is checked to be starvation, empty or full. If it is starvation +// then the threshold for allowed number of connections is incremented by one, +// if it is empty then nother is done and when it is full then the threshold is +// decremented by one. +// +// Starvation means that we could add a request from the queue to the multi handle, +// but the queue was empty. Empty means that after adding one or more requests to the +// multi handle the queue became empty, and full means that after adding one of more +// requests to the multi handle the queue still wasn't empty (see AIPerService::add_queued_to). // //static AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr const& per_service, AICapabilityType capability_type) @@ -2640,13 +2732,13 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c // Cache all sTotalQueued info. bool starvation, decrement_threshold; - S32 total_queued_or_added = MultiHandle::total_added_size(); + S32 total_approved_queuedapproved_or_added = MultiHandle::total_added_size(); { TotalQueued_wat total_queued_w(sTotalQueued); - total_queued_or_added += total_queued_w->count; + total_approved_queuedapproved_or_added += total_queued_w->approved; starvation = total_queued_w->starvation; decrement_threshold = total_queued_w->full && !total_queued_w->empty; - total_queued_w->empty = total_queued_w->full = false; // Reset flags. + total_queued_w->starvation = total_queued_w->empty = total_queued_w->full = false; // Reset flags. } // Whether or not we're going to approve a new request, decrement the global threshold first, when appropriate. @@ -2654,13 +2746,13 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c if (decrement_threshold) { MaxPipelinedRequests_wat max_pipelined_requests_w(sMaxPipelinedRequests); - if (max_pipelined_requests_w->count > (S32)curl_max_total_concurrent_connections && + if (max_pipelined_requests_w->threshold > (S32)curl_max_total_concurrent_connections && sTime_40ms > max_pipelined_requests_w->last_decrement) { // Decrement the threshold because since the last call to this function at least one curl request finished // and was replaced with another request from the queue, but the queue never ran empty: we have too many // queued requests. - max_pipelined_requests_w->count--; + max_pipelined_requests_w->threshold--; // Do this at most once every 40 ms. max_pipelined_requests_w->last_decrement = sTime_40ms; } @@ -2670,7 +2762,7 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c bool reject, equal, increment_threshold; { - PerService_wat per_service_w(*per_service); + PerService_wat per_service_w(*per_service); // Keep this lock for the duration of accesses to ct. CapabilityType& ct(per_service_w->mCapabilityType[capability_type]); S32 const pipelined_requests_per_capability_type = ct.pipelined_requests(); reject = pipelined_requests_per_capability_type >= (S32)ct.mMaxPipelinedRequests; @@ -2680,14 +2772,14 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c ct.mFlags = 0; if (decrement_threshold) { - if ((int)ct.mMaxPipelinedRequests > per_service_w->mConcurrectConnections) + if ((int)ct.mMaxPipelinedRequests > ct.mConcurrentConnections) { ct.mMaxPipelinedRequests--; } } else if (increment_threshold && reject) { - if ((int)ct.mMaxPipelinedRequests < 2 * per_service_w->mConcurrectConnections) + if ((int)ct.mMaxPipelinedRequests < 2 * ct.mConcurrentConnections) { ct.mMaxPipelinedRequests++; // Immediately take the new threshold into account. @@ -2699,7 +2791,9 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c // Before releasing the lock on per_service, stop other threads from getting a // too small value from pipelined_requests() and approving too many requests. ct.mApprovedRequests++; + per_service_w->mApprovedRequests++; } + total_approved_queuedapproved_or_added += per_service_w->mApprovedRequests; } if (reject) { @@ -2711,13 +2805,15 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c if (checkBandwidthUsage(per_service, sTime_40ms)) { // Too much bandwidth is being used, either in total or for this service. - PerService_wat(*per_service)->mCapabilityType[capability_type].mApprovedRequests--; // Not approved after all. + PerService_wat per_service_w(*per_service); + per_service_w->mCapabilityType[capability_type].mApprovedRequests--; // Not approved after all. + per_service_w->mApprovedRequests--; return NULL; } // Check if it's ok to get a new request based on the total number of requests and increment the threshold if appropriate. - S32 const pipelined_requests = command_queue_rat(command_queue)->size + total_queued_or_added; + S32 const pipelined_requests = command_queue_rat(command_queue)->size + total_approved_queuedapproved_or_added; // We can't take the command being processed (command_being_processed) into account without // introducing relatively long waiting times for some mutex (namely between when the command // is moved from command_queue to command_being_processed, till it's actually being added to @@ -2726,18 +2822,19 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c // here instead. // The maximum number of requests that may be queued in command_queue is equal to the total number of requests - // that may exist in the pipeline minus the number of requests queued in AIPerService objects, minus - // the number of already running requests. + // that may exist in the pipeline minus the number approved requests not yet added to the command queue, minus the + // number of requests queued in AIPerService objects, minus the number of already running requests + // (excluding non-approved requests queued in their CT queue). MaxPipelinedRequests_wat max_pipelined_requests_w(sMaxPipelinedRequests); - reject = pipelined_requests >= max_pipelined_requests_w->count; - equal = pipelined_requests == max_pipelined_requests_w->count; + reject = pipelined_requests >= max_pipelined_requests_w->threshold; + equal = pipelined_requests == max_pipelined_requests_w->threshold; increment_threshold = starvation; if (increment_threshold && reject) { - if (max_pipelined_requests_w->count < 2 * (S32)curl_max_total_concurrent_connections && + if (max_pipelined_requests_w->threshold < 2 * (S32)curl_max_total_concurrent_connections && sTime_40ms > max_pipelined_requests_w->last_increment) { - max_pipelined_requests_w->count++; + max_pipelined_requests_w->threshold++; max_pipelined_requests_w->last_increment = sTime_40ms; // Immediately take the new threshold into account. reject = !equal; @@ -2745,7 +2842,9 @@ AIPerService::Approvement* AIPerService::approveHTTPRequestFor(AIPerServicePtr c } if (reject) { - PerService_wat(*per_service)->mCapabilityType[capability_type].mApprovedRequests--; // Not approved after all. + PerService_wat per_service_w(*per_service); + per_service_w->mCapabilityType[capability_type].mApprovedRequests--; // Not approved after all. + per_service_w->mApprovedRequests--; return NULL; } return new Approvement(per_service, capability_type); diff --git a/indra/llmessage/aicurlthread.h b/indra/llmessage/aicurlthread.h index 9a4c2b487..080456652 100644 --- a/indra/llmessage/aicurlthread.h +++ b/indra/llmessage/aicurlthread.h @@ -39,6 +39,8 @@ namespace AICurlPrivate { namespace curlthread { +extern U32 curl_max_total_concurrent_connections; + class PollSet; // For ordering a std::set with AICurlEasyRequest objects. @@ -100,6 +102,9 @@ class MultiHandle : public CurlMultiHandle // Return the total number of added curl requests. static U32 total_added_size(void) { return sTotalAdded; } + // Return true if we reached the global maximum number of connections. + static bool added_maximum(void) { return sTotalAdded >= curl_max_total_concurrent_connections; } + public: //----------------------------------------------------------------------------- // Curl socket administration: diff --git a/indra/llmessage/aihttptimeout.cpp b/indra/llmessage/aihttptimeout.cpp index 22dbb1cfd..bfb0f767b 100644 --- a/indra/llmessage/aihttptimeout.cpp +++ b/indra/llmessage/aihttptimeout.cpp @@ -157,7 +157,10 @@ void HTTPTimeout::upload_starting(void) // | void HTTPTimeout::upload_finished(void) { + // Disable this assert when there isn't enough debug output to do anything with it. +#if defined(CWDEBUG) || defined(DEBUG_CURLIO) llassert(!mUploadFinished); // If we get here twice, then the 'upload finished' detection failed. +#endif mUploadFinished = true; // Only accept a call to upload_starting() if being_redirected() is called after this point. mBeingRedirected = false; diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index 62eb07fed..502cabf37 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -925,9 +925,7 @@ P(meshLODResponder); P(meshPhysicsShapeResponder); P(meshSkinInfoResponder); P(mimeDiscoveryResponder); -P(moderationModeResponder); -P(muteTextResponder); -P(muteVoiceResponder); +P(moderationResponder); P(navMeshRebakeResponder); P(navMeshResponder); P(navMeshStatusResponder); @@ -955,9 +953,9 @@ P(viewerChatterBoxInvitationAcceptResponder); P(viewerMediaOpenIDResponder); P(viewerMediaWebProfileResponder); P(viewerStatsResponder); -P(viewerVoiceAccountProvisionResponder); +P(vivoxVoiceAccountProvisionResponder); +P(vivoxVoiceClientCapResponder); P(voiceCallCapResponder); -P(voiceClientCapResponder); P(webProfileResponders); P(wholeModelFeeResponder); P(wholeModelUploadResponder); diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 1cb832983..5aa1ebdc3 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -37,13 +37,11 @@ #pragma warning (disable : 4263) #pragma warning (disable : 4264) #endif -#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverloaded-virtual" #include "dae.h" #include "dae/daeErrorHandler.h" #include "dom/domConstants.h" #include "dom/domMesh.h" -#pragma GCC diagnostic pop #if LL_MSVC #pragma warning (pop) #endif diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h index 1c03b108d..f061865b8 100644 --- a/indra/llui/llcombobox.h +++ b/indra/llui/llcombobox.h @@ -208,7 +208,6 @@ protected: LLColor4 mListColor; private: - S32 mButtonPadding; BOOL mAllowTextEntry; S32 mMaxChars; BOOL mTextEntryTentative; diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index 04ede8942..f9ee54e9f 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -51,6 +51,7 @@ LLContainerView::LLContainerView(const LLContainerView::Params& p) { mCollapsible = TRUE; mScrollContainer = NULL; + mRectAlpha = 0.25; } LLContainerView::~LLContainerView() @@ -100,7 +101,7 @@ void LLContainerView::draw() { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f)); + gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4(0.f, 0.f, 0.f, mRectAlpha)); } // Draw the label diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index be340581d..70d994f87 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -75,17 +75,17 @@ public: void setDisplayChildren(const BOOL displayChildren); BOOL getDisplayChildren() { return mDisplayChildren; } void setScrollContainer(LLScrollContainer* scroll); + void setRectAlpha(F32 alpha) { mRectAlpha = alpha; } private: LLScrollContainer* mScrollContainer; void arrange(S32 width, S32 height, BOOL called_from_parent = TRUE); BOOL mShowLabel; - + F32 mRectAlpha; protected: BOOL mDisplayChildren; std::string mLabel; public: BOOL mCollapsible; - }; #endif // LL_CONTAINERVIEW_ diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 5c9e2162f..16fb57c25 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -1075,7 +1075,8 @@ void LLFloater::setForeground(BOOL front) releaseFocus(); } - setBackgroundOpaque( front ); + if (front || !LLUI::sConfigGroup->getBOOL("FloaterUnfocusedBackgroundOpaque")) // Singu Note: This can be removed when InactiveFloaterTransparency is added + setBackgroundOpaque( front ); } } diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 972209859..e3a2795fb 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -367,7 +367,7 @@ void LLKeywords::findSegments(std::vector* seg_list, const LLW return; } - S32 text_len = wtext.size() + 1; + S32 text_len = wtext.size(); seg_list->push_back( new LLTextSegment( LLColor3(defaultColor), 0, text_len ) ); @@ -584,6 +584,7 @@ void LLKeywords::insertSegment(std::vector& seg_list, LLTextSe { LLTextSegmentPtr last = seg_list.back(); S32 new_seg_end = new_segment->getEnd(); + llassert(new_seg_end <= text_len); if( new_segment->getStart() == last->getStart() ) { diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 1870b2615..c501d865c 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3750,9 +3750,9 @@ void LLPieMenu::draw() F32 center_y = height/2; S32 steps = 100; - gGL.pushMatrix(); + gGL.pushUIMatrix(); { - gGL.translatef(center_x, center_y, 0.f); + gGL.translateUI(center_x, center_y, 0.f); F32 line_width = LLUI::sConfigGroup->getF32("PieMenuLineWidth"); LLColor4 line_color = LLUI::sColorsGroup->getColor("PieMenuLineColor"); @@ -3800,7 +3800,7 @@ void LLPieMenu::draw() LLUI::setLineWidth(1.0f); } - gGL.popMatrix(); + gGL.popUIMatrix(); mHoverThisFrame = FALSE; @@ -3816,9 +3816,9 @@ void LLPieMenu::drawBackground(LLMenuItemGL* itemp, LLColor4& color) S32 steps = 100; gGL.color4fv( color.mV ); - gGL.pushMatrix(); + gGL.pushUIMatrix(); { - gGL.translatef(center_x - itemp->getRect().mLeft, center_y - itemp->getRect().mBottom, 0.f); + gGL.translateUI(center_x - itemp->getRect().mLeft, center_y - itemp->getRect().mBottom, 0.f); item_list_t::iterator item_iter; S32 i = 0; @@ -3838,7 +3838,7 @@ void LLPieMenu::drawBackground(LLMenuItemGL* itemp, LLColor4& color) i++; } } - gGL.popMatrix(); + gGL.popUIMatrix(); } // virtual diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 06de6f937..50da07787 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -291,9 +291,8 @@ void LLMultiSliderCtrl::onEditorCommit(const LLSD& value) val = (F32) atof( text.c_str() ); if( mMultiSlider->getMinValue() <= val && val <= mMultiSlider->getMaxValue() ) { - setCurSliderValue( val ); - if( (!mValidateCallback || mValidateCallback( this, mCallbackUserData )) && - (!mValidateSignal || (*(mValidateSignal))(this, val))) + setCurSliderValue( val ); // set the value temporarily so that the callback can retrieve it. + if( !mValidateSignal || (*(mValidateSignal))( this, val ) ) { success = TRUE; } @@ -322,8 +321,7 @@ void LLMultiSliderCtrl::onSliderCommit(const LLSD& value) F32 new_val = mMultiSlider->getCurSliderValue(); mCurValue = new_val; // set the value temporarily so that the callback can retrieve it. - if( (!mValidateCallback || mValidateCallback( this, mCallbackUserData )) && - (!mValidateSignal || (*mValidateSignal)(this, new_val ) )) + if( !mValidateSignal || (*(mValidateSignal))( this, new_val ) ) { success = TRUE; } diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index daebe75e8..9435f65bb 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -773,34 +773,28 @@ BOOL LLPanel::childHasFocus(const std::string& id) } } -void LLPanel::childSetCommitCallback(const std::string& id, void (*cb)(LLUICtrl*, void*), void *userdata ) +// *TODO: Deprecate; for backwards compatability only: +// Prefer getChild("foo")->setCommitCallback(boost:bind(...)), +// which takes a generic slot. Or use mCommitCallbackRegistrar.add() with +// a named callback and reference it in XML. +void LLPanel::childSetCommitCallback(const std::string& id, boost::function cb, void* data) { - LLUICtrl* child = getChild(id, true); + LLUICtrl* child = findChild(id); if (child) { - child->setCommitCallback(cb); - child->setCallbackUserData(userdata); + child->setCommitCallback(boost::bind(cb, child, data)); } } -void LLPanel::childSetValidate(const std::string& id, BOOL (*cb)(LLUICtrl*, void*)) +void LLPanel::childSetValidate(const std::string& id, boost::function cb) { - LLUICtrl* child = getChild(id, true); + LLUICtrl* child = findChild(id); if (child) { child->setValidateBeforeCommit(cb); } } -void LLPanel::childSetUserData(const std::string& id, void* userdata) -{ - LLUICtrl* child = getChild(id, true); - if (child) - { - child->setCallbackUserData(userdata); - } -} - void LLPanel::childSetColor(const std::string& id, const LLColor4& color) { LLUICtrl* child = getChild(id, true); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 490b27311..d2ca151e5 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -172,9 +172,13 @@ public: void childSetFocus(const std::string& id, BOOL focus = TRUE); BOOL childHasFocus(const std::string& id); - void childSetCommitCallback(const std::string& id, void (*cb)(LLUICtrl*, void*), void* userdata = NULL ); - void childSetValidate(const std::string& id, BOOL (*cb)(LLUICtrl*, void*) ); - void childSetUserData(const std::string& id, void* userdata); + // *TODO: Deprecate; for backwards compatability only: + // Prefer getChild("foo")->setCommitCallback(boost:bind(...)), + // which takes a generic slot. Or use mCommitCallbackRegistrar.add() with + // a named callback and reference it in XML. + void childSetCommitCallback(const std::string& id, boost::function cb, void* data = NULL); + + void childSetValidate(const std::string& id, boost::function cb ); void childSetColor(const std::string& id, const LLColor4& color); void childSetAlpha(const std::string& id, F32 alpha); diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 5a5bc1d41..ee9b62a9a 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -3866,16 +3866,14 @@ LLScrollColumnHeader::LLScrollColumnHeader(const std::string& label, const LLRec mHasResizableElement(FALSE) { mListPosition = LLComboBox::ABOVE; - setCommitCallback(onSelectSort); - setCallbackUserData(this); + setCommitCallback(boost::bind(&LLScrollColumnHeader::onSelectSort, this)); mButton->setTabStop(FALSE); // require at least two frames between mouse down and mouse up event to capture intentional "hold" not just bad framerate mButton->setHeldDownDelay(LLUI::sConfigGroup->getF32("ColumnHeaderDropDownDelay"), 2); - mButton->setHeldDownCallback(boost::bind(&LLScrollColumnHeader::onHeldDown, this)); + mButton->setHeldDownCallback(boost::bind(&LLScrollColumnHeader::showList, this)); mButton->setClickedCallback(boost::bind(&LLScrollColumnHeader::onClick, this)); mButton->setMouseDownCallback(boost::bind(&LLScrollColumnHeader::onMouseDown, this)); - mButton->setCallbackUserData(this); mButton->setToolTip(label); mAscendingText = std::string("[LOW]...[HIGH](Ascending)"); // *TODO: Translate @@ -4034,7 +4032,7 @@ BOOL LLScrollColumnHeader::handleDoubleClick(S32 x, S32 y, MASK mask) } else { - onClick(this); + onClick(); } return TRUE; } @@ -4062,42 +4060,29 @@ void LLScrollColumnHeader::setImageOverlay(const std::string &image_name, LLFont } } -//static -void LLScrollColumnHeader::onClick(void* user_data) +void LLScrollColumnHeader::onClick() { - LLScrollColumnHeader* headerp = (LLScrollColumnHeader*)user_data; - if (!headerp) return; + if (!mColumn) return; - LLScrollListColumn* column = headerp->mColumn; - if (!column) return; - - if (headerp->mList->getVisible()) + if (mList->getVisible()) { - headerp->hideList(); + hideList(); } - LLScrollListCtrl::onClickColumn(column); + LLScrollListCtrl::onClickColumn(mColumn); // propagate new sort order to sort order list - headerp->mList->selectNthItem(column->mParentCtrl->getSortAscending() ? 0 : 1); + mList->selectNthItem(mColumn->mParentCtrl->getSortAscending() ? 0 : 1); - headerp->mList->setFocus(TRUE); + mList->setFocus(TRUE); } -//static -void LLScrollColumnHeader::onMouseDown(void* user_data) +void LLScrollColumnHeader::onMouseDown() { // for now, do nothing but block the normal showList() behavior return; } -//static -void LLScrollColumnHeader::onHeldDown(void* user_data) -{ - LLScrollColumnHeader* headerp = (LLScrollColumnHeader*)user_data; - headerp->showList(); -} - void LLScrollColumnHeader::showList() { if (mShowSortOptions) @@ -4180,30 +4165,25 @@ void LLScrollColumnHeader::showList() } } -//static -void LLScrollColumnHeader::onSelectSort(LLUICtrl* ctrl, void* user_data) +void LLScrollColumnHeader::onSelectSort() { - LLScrollColumnHeader* headerp = (LLScrollColumnHeader*)user_data; - if (!headerp) return; - - LLScrollListColumn* column = headerp->mColumn; - if (!column) return; - LLScrollListCtrl *parent = column->mParentCtrl; + if (!mColumn) return; + LLScrollListCtrl* parent = mColumn->mParentCtrl; if (!parent) return; - if (headerp->getCurrentIndex() == 0) + if (getCurrentIndex() == 0) { // ascending - parent->sortByColumn(column->mSortingColumn, TRUE); + parent->sortByColumn(mColumn->mSortingColumn, TRUE); } else { // descending - parent->sortByColumn(column->mSortingColumn, FALSE); + parent->sortByColumn(mColumn->mSortingColumn, FALSE); } // restore original column header - headerp->setLabel(headerp->mOrigLabel); + setLabel(mOrigLabel); } LLView* LLScrollColumnHeader::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding) diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 70f490dcc..528f413ed 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -241,10 +241,9 @@ public: void enableResizeBar(BOOL enable); std::string getLabel() { return mOrigLabel; } - static void onSelectSort(LLUICtrl* ctrl, void* user_data); - static void onClick(void* user_data); - static void onMouseDown(void* user_data); - static void onHeldDown(void* user_data); + void onSelectSort(); + void onClick(); + void onMouseDown(); private: LLScrollListColumn* mColumn; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 42c72a2f5..594a2d890 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -227,9 +227,8 @@ void LLSliderCtrl::onEditorCommit( LLUICtrl* ctrl, const LLSD& userdata ) val = (F32) atof( text.c_str() ); if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() ) { - self->setValue( val ); - if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && - (!self->mValidateSignal || (*(self->mValidateSignal))( self, val ))) + self->setValue( val ); // set the value temporarily so that the callback can retrieve it. + if( !self->mValidateSignal || (*(self->mValidateSignal))( self, val ) ) { success = TRUE; } @@ -263,8 +262,7 @@ void LLSliderCtrl::onSliderCommit( LLUICtrl* ctrl, const LLSD& userdata ) F32 new_val = self->mSlider->getValueF32(); self->mValue = new_val; // set the value temporarily so that the callback can retrieve it. - if( (!self->mValidateCallback || self->mValidateCallback( self, self->mCallbackUserData )) && - (!self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ))) + if( !self->mValidateSignal || (*(self->mValidateSignal))( self, new_val ) ) { success = TRUE; } diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 37244e640..489f71282 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -198,8 +198,7 @@ void LLSpinCtrl::onUpBtn( const LLSD& data ) F32 saved_val = (F32)getValue().asReal(); setValue(val); - if( (mValidateCallback && !mValidateCallback( this, mCallbackUserData ) ) || - (mValidateSignal && !(*mValidateSignal)( this, val ) )) + if( mValidateSignal && !(*mValidateSignal)( this, val ) ) { setValue( saved_val ); reportInvalidData(); @@ -227,8 +226,7 @@ void LLSpinCtrl::onDownBtn( const LLSD& data ) F32 saved_val = (F32)getValue().asReal(); setValue(val); - if( (mValidateCallback && !mValidateCallback( this, mCallbackUserData ) ) || - (mValidateSignal && !(*mValidateSignal)( this, val ) )) + if( mValidateSignal && !(*mValidateSignal)( this, val ) ) { setValue( saved_val ); reportInvalidData(); @@ -317,9 +315,7 @@ void LLSpinCtrl::onEditorCommit( const LLSD& data ) F32 saved_val = mValue; mValue = val; - - if( (!mValidateCallback || mValidateCallback( this, mCallbackUserData )) && - (!mValidateSignal || (*mValidateSignal)(this, val) )) + if( !mValidateSignal || (*mValidateSignal)( this, val ) ) { success = TRUE; onCommit(); diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index ce9f71aff..738ec3b06 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -140,7 +140,6 @@ private: std::vector mLineLengthList; callback_t mClickedCallback; - void* mCallbackUserData; }; #endif diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 803cb7f0f..7dbca795d 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -45,9 +45,6 @@ LLUICtrl::LLUICtrl() : mViewModel(LLViewModelPtr(new LLViewModel)), mCommitSignal(NULL), mValidateSignal(NULL), - mCommitCallback(NULL), - mValidateCallback(NULL), - mCallbackUserData(NULL), mMouseEnterSignal(NULL), mMouseLeaveSignal(NULL), mTentative(FALSE), @@ -64,10 +61,7 @@ LLUICtrl::LLUICtrl(const std::string& name, const LLRect rect, BOOL mouse_opaque LLView( name, rect, mouse_opaque, reshape ), mCommitSignal(NULL), mValidateSignal(NULL), - mCommitCallback(NULL), mViewModel(LLViewModelPtr(new LLViewModel)), - mValidateCallback( NULL ), - mCallbackUserData( NULL ), mMouseEnterSignal(NULL), mMouseLeaveSignal(NULL), mTentative( FALSE ), @@ -113,10 +107,6 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask) void LLUICtrl::onCommit() { - if( mCommitCallback ) - { - mCommitCallback( this, mCallbackUserData ); - } if (mCommitSignal) (*mCommitSignal)(this, getValue()); } @@ -641,4 +631,4 @@ boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal { if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t(); return mMouseLeaveSignal->connect(cb); -} \ No newline at end of file +} diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 6109214cd..79d8fd8fc 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -52,9 +52,6 @@ public: typedef boost::function enable_callback_t; typedef boost::signals2::signal enable_signal_t; - typedef void (*LLUICtrlCallback)(LLUICtrl* ctrl, void* userdata); - typedef BOOL (*LLUICtrlValidate)(LLUICtrl* ctrl, void* userdata); - LLUICtrl(); LLUICtrl( const std::string& name, const LLRect rect = LLRect(), BOOL mouse_opaque = TRUE, commit_callback_t commit_callback = NULL, @@ -133,13 +130,6 @@ public: boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb ); // *TODO: Deprecate; for backwards compatability only: - //Keeping userdata around with legacy setCommitCallback because it's used ALL OVER THE PLACE. - void* getCallbackUserData() const { return mCallbackUserData; } - void setCallbackUserData( void* data ) { mCallbackUserData = data; } - - void setCommitCallback( void (*cb)(LLUICtrl*, void*) ) { mCommitCallback = cb; } - void setValidateBeforeCommit( BOOL(*cb)(LLUICtrl*, void*) ) { mValidateCallback = cb; } - // *TODO: Deprecate; for backwards compatability only: boost::signals2::connection setCommitCallback( boost::function cb, void* data); boost::signals2::connection setValidateBeforeCommit( boost::function cb ); @@ -171,10 +161,6 @@ protected: commit_signal_t* mMouseLeaveSignal; LLViewModelPtr mViewModel; - void (*mCommitCallback)( LLUICtrl* ctrl, void* userdata ); - BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata ); - - void* mCallbackUserData; private: diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 60e95f11f..e1fa498fa 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -2105,7 +2105,7 @@ void LLWindowSDL::initCursors() mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14); mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16); mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8); - mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15); + mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",0,0); mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13); mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6); mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fca9817c1..5f1f6311b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -79,6 +79,7 @@ include_directories( set(viewer_SOURCE_FILES NACLantispam.cpp + aihttpview.cpp aoremotectrl.cpp ascentfloatercontactgroups.cpp ascentkeyword.cpp @@ -91,6 +92,7 @@ set(viewer_SOURCE_FILES floatervoicelicense.cpp generichandlers.cpp hbfloatergrouptitles.cpp + groupchatlistener.cpp hippofloaterxml.cpp hippogridmanager.cpp hippolimits.cpp @@ -113,11 +115,11 @@ set(viewer_SOURCE_FILES llanimstatelabels.cpp llappearancemgr.cpp llappviewer.cpp - llassetconverter.cpp llassetuploadqueue.cpp llassetuploadresponders.cpp llattachmentsmgr.cpp llaudiosourcevo.cpp + llavataractions.cpp llavatarpropertiesprocessor.cpp llbox.cpp llcallbacklist.cpp @@ -169,7 +171,6 @@ set(viewer_SOURCE_FILES llflexibleobject.cpp llfloaterabout.cpp llfloateractivespeakers.cpp - llfloateranimpreview.cpp llfloaterauction.cpp llfloateravatarinfo.cpp llfloateravatarlist.cpp @@ -258,7 +259,7 @@ set(viewer_SOURCE_FILES llfloatertos.cpp llfloaterurldisplay.cpp llfloaterurlentry.cpp - llfloatervoicedevicesettings.cpp + llfloatervoiceeffect.cpp llfloaterwater.cpp llfloaterwebcontent.cpp llfloaterwhitelistentry.cpp @@ -273,6 +274,7 @@ set(viewer_SOURCE_FILES llgiveinventory.cpp llgivemoney.cpp llglsandbox.cpp + llgroupactions.cpp llgroupmgr.cpp llgroupnotify.cpp llhomelocationresponder.cpp @@ -303,7 +305,6 @@ set(viewer_SOURCE_FILES llinventorypanel.cpp lljoystickbutton.cpp lllandmarklist.cpp - lllocalinventory.cpp lllogchat.cpp llloginhandler.cpp llmainlooprepeater.cpp @@ -382,9 +383,12 @@ set(viewer_SOURCE_FILES llpanelprimmediacontrols.cpp llpanelprofile.cpp llpanelskins.cpp + llpanelvoicedevicesettings.cpp + llpanelvoiceeffect.cpp llpanelvolume.cpp llpanelweb.cpp llparcelselection.cpp + llparticipantlist.cpp llpatchvertexarray.cpp llpathfindingcharacter.cpp llpathfindingcharacterlist.cpp @@ -421,6 +425,7 @@ set(viewer_SOURCE_FILES llsky.cpp llslurl.cpp llspatialpartition.cpp + llspeakers.cpp llsprite.cpp llstartup.cpp llstatusbar.cpp @@ -521,9 +526,12 @@ set(viewer_SOURCE_FILES llvoclouds.cpp llvograss.cpp llvoground.cpp + llvoicecallhandler.cpp + llvoicechannel.cpp llvoiceclient.cpp llvoiceremotectrl.cpp llvoicevisualizer.cpp + llvoicevivox.cpp llvoinventorylistener.cpp llvopartgroup.cpp llvosky.cpp @@ -580,6 +588,7 @@ set(viewer_HEADER_FILES ViewerInstall.cmake NACLantispam.h + aihttpview.h aoremotectrl.h ascentfloatercontactgroups.h ascentkeyword.h @@ -591,6 +600,7 @@ set(viewer_HEADER_FILES floaterlocalassetbrowse.h floatervoicelicense.h generichandlers.h + groupchatlistener.h hbfloatergrouptitles.h hippofloaterxml.h hippogridmanager.h @@ -615,11 +625,11 @@ set(viewer_HEADER_FILES llappearance.h llappearancemgr.h llappviewer.h - llassetconverter.h llassetuploadqueue.h llassetuploadresponders.h llattachmentsmgr.h llaudiosourcevo.h + llavataractions.h llavatarpropertiesprocessor.h llbox.h llcallbacklist.h @@ -671,7 +681,6 @@ set(viewer_HEADER_FILES llflexibleobject.h llfloaterabout.h llfloateractivespeakers.h - llfloateranimpreview.h llfloaterauction.h llfloateravatarinfo.h llfloateravatarlist.h @@ -760,7 +769,7 @@ set(viewer_HEADER_FILES llfloatertos.h llfloaterurldisplay.h llfloaterurlentry.h - llfloatervoicedevicesettings.h + llfloatervoiceeffect.h llfloaterwater.h llfloaterwebcontent.h llfloaterwhitelistentry.h @@ -775,6 +784,7 @@ set(viewer_HEADER_FILES llgesturemgr.h llgiveinventory.h llgivemoney.h + llgroupactions.h llgroupmgr.h llgroupnotify.h llhomelocationresponder.h @@ -805,7 +815,6 @@ set(viewer_HEADER_FILES lljoystickbutton.h lllandmarklist.h lllightconstants.h - lllocalinventory.h lllogchat.h llloginhandler.h llmainlooprepeater.h @@ -884,9 +893,12 @@ set(viewer_HEADER_FILES llpanelprimmediacontrols.h llpanelprofile.h llpanelskins.h + llpanelvoicedevicesettings.h + llpanelvoiceeffect.h llpanelvolume.h llpanelweb.h llparcelselection.h + llparticipantlist.h llpatchvertexarray.h llpathfindingcharacter.h llpathfindingcharacterlist.h @@ -925,6 +937,7 @@ set(viewer_HEADER_FILES llsky.h llslurl.h llspatialpartition.h + llspeakers.h llsprite.h llstartup.h llstatusbar.h @@ -1028,9 +1041,11 @@ set(viewer_HEADER_FILES llvoclouds.h llvograss.h llvoground.h + llvoicechannel.h llvoiceclient.h llvoiceremotectrl.h llvoicevisualizer.h + llvoicevivox.h llvoinventorylistener.h llvopartgroup.h llvosky.h @@ -1064,7 +1079,6 @@ set(viewer_HEADER_FILES noise.h pipeline.h qtoolalign.h - randgauss.h rlvcommon.h rlvdefines.h rlvextensions.h diff --git a/indra/newview/aihttpview.cpp b/indra/newview/aihttpview.cpp new file mode 100644 index 000000000..86849575f --- /dev/null +++ b/indra/newview/aihttpview.cpp @@ -0,0 +1,348 @@ +/** + * @file aihttpview.cpp + * @brief Definition of class AIHTTPView. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/05/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#include "llviewerprecompiledheaders.h" +#include "aihttpview.h" +#include "llrect.h" +#include "llerror.h" +#include "aicurlperservice.h" +#include "llviewerstats.h" +#include "llfontgl.h" +#include "aihttptimeout.h" + +AIHTTPView* gHttpView = NULL; +static S32 sLineHeight; + +// Forward declaration. +namespace AICurlInterface { + size_t getHTTPBandwidth(void); + U32 getNumHTTPAdded(void); + U32 getMaxHTTPAdded(void); +} // namespace AICurlInterface + +//============================================================================= + + //PerService_crat per_service_r(*service.second); +class AIServiceBar : public LLView +{ + private: + AIHTTPView* mHTTPView; + std::string mName; + AIPerServicePtr mPerService; + + public: + AIServiceBar(AIHTTPView* httpview, AIPerService::instance_map_type::value_type const& service) + : LLView("aiservice bar", LLRect(), FALSE), mHTTPView(httpview), mName(service.first), mPerService(service.second) { } + + /*virtual*/ void draw(void); + /*virtual*/ LLRect getRequiredRect(void); +}; + +int const mc_col = number_of_capability_types; // Maximum connections column. +int const bw_col = number_of_capability_types + 1; // Bandwidth column. + +void AIServiceBar::draw() +{ + LLColor4 text_color = LLColor4::white; + F32 height = getRect().getHeight(); + U32 start = 4; + LLFontGL::getFontMonospace()->renderUTF8(mName, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(mName); + std::string text; + AIPerService::CapabilityType const* cts; + U32 is_used; + U32 is_inuse; + int total_added; + int concurrent_connections; + size_t bandwidth; + { + PerService_rat per_service_r(*mPerService); + is_used = per_service_r->is_used(); + is_inuse = per_service_r->is_inuse(); + total_added = per_service_r->mTotalAdded; + concurrent_connections = per_service_r->mConcurrentConnections; + bandwidth = per_service_r->bandwidth().truncateData(AIHTTPView::getTime_40ms()); + cts = per_service_r->mCapabilityType; // Not thread-safe, but we're only reading from it and only using the results to show in a debug console. + } + for (int col = 0; col < number_of_capability_types; ++col) + { + AICapabilityType capability_type = static_cast(col); + AIPerService::CapabilityType const& ct(cts[capability_type]); + start = mHTTPView->updateColumn(col, start); + U32 mask = AIPerService::CT2mask(capability_type); + if (!(is_used & mask)) + { + text = " | "; + } + else if (col < 2) + { + text = llformat(" | %hu-%hu-%lu,{%hu/%hu,%u}/%u", ct.mApprovedRequests, ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrentConnections, ct.mDownloading, ct.mMaxPipelinedRequests); + } + else + { + text = llformat(" | --%hu-%lu,{%hu/%hu,%u}", ct.mQueuedCommands, ct.mQueuedRequests.size(), ct.mAdded, ct.mConcurrentConnections, ct.mDownloading); + } + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, ((is_inuse & mask) == 0) ? LLColor4::grey2 : text_color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + } + start = mHTTPView->updateColumn(mc_col, start); + text = llformat(" | %d/%d", total_added, concurrent_connections); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + start = mHTTPView->updateColumn(bw_col, start); + size_t max_bandwidth = mHTTPView->mMaxBandwidthPerService; + text = " | "; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + text = llformat("%lu", bandwidth / 125); + if (bandwidth == 0) + { + text_color = LLColor4::grey2; + } + LLColor4 color = (bandwidth > max_bandwidth) ? LLColor4::red : ((bandwidth > max_bandwidth * .75f) ? LLColor4::yellow : text_color); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + text = llformat("/%lu", max_bandwidth / 125); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); +} + +LLRect AIServiceBar::getRequiredRect(void) +{ + LLRect rect; + rect.mTop = sLineHeight; + return rect; +} + +//============================================================================= + +static int const number_of_header_lines = 2; + +class AIGLHTTPHeaderBar : public LLView +{ + public: + AIGLHTTPHeaderBar(std::string const& name, AIHTTPView* httpview) : + LLView(name, FALSE), mHTTPView(httpview) + { + sLineHeight = llround(LLFontGL::getFontMonospace()->getLineHeight()); + setRect(LLRect(0, 0, 200, sLineHeight * number_of_header_lines)); + } + + /*virtual*/ void draw(void); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ LLRect getRequiredRect(void); + + private: + AIHTTPView* mHTTPView; +}; + +void AIGLHTTPHeaderBar::draw(void) +{ + S32 const v_offset = -1; // Offset from the bottom. Move header one pixel down. + S32 const h_offset = 4; + + LLGLSUIDefault gls_ui; + + LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); + std::string text; + + // First header line. + F32 height = v_offset + sLineHeight * number_of_header_lines; + text = "HTTP console -- [approved]-commandQ-curlQ,{added/max,downloading}[/max]"; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, h_offset, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + text = " | Added/Max"; + U32 start = mHTTPView->updateColumn(mc_col, 100); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, LLColor4::green, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + text = " | Tot/Max BW (kbit/s)"; + start = mHTTPView->updateColumn(bw_col, start); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, LLColor4::green, LLFontGL::LEFT, LLFontGL::TOP); + mHTTPView->setWidth(start + LLFontGL::getFontMonospace()->getWidth(text) + h_offset); + + // Second header line. + height -= sLineHeight; + start = h_offset; + text = "Service (host:port)"; + static char const* caption[number_of_capability_types] = { + " | Textures", " | Inventory", " | Mesh", " | Other" + }; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, LLColor4::green, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + for (int col = 0; col < number_of_capability_types; ++col) + { + start = mHTTPView->updateColumn(col, start); + text = caption[col]; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, LLColor4::green, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + } + start = mHTTPView->updateColumn(mc_col, start); + text = llformat(" | %u/%u", AICurlInterface::getNumHTTPAdded(), AICurlInterface::getMaxHTTPAdded()); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + + // This bandwidth is averaged over 1 seconds (in bytes/s). + size_t const bandwidth = AICurlInterface::getHTTPBandwidth(); + size_t const max_bandwidth = AIPerService::getHTTPThrottleBandwidth125(); + mHTTPView->mMaxBandwidthPerService = max_bandwidth * AIPerService::throttleFraction(); + LLColor4 color = (bandwidth > max_bandwidth) ? LLColor4::red : ((bandwidth > max_bandwidth * .75f) ? LLColor4::yellow : text_color); + color[VALPHA] = text_color[VALPHA]; + start = mHTTPView->updateColumn(bw_col, start); + text = " | "; + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, LLColor4::green, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + text = llformat("%lu", bandwidth / 125); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, color, LLFontGL::LEFT, LLFontGL::TOP); + start += LLFontGL::getFontMonospace()->getWidth(text); + text = llformat("/%lu", max_bandwidth / 125); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, start, height, text_color, LLFontGL::LEFT, LLFontGL::TOP); +} + +BOOL AIGLHTTPHeaderBar::handleMouseDown(S32 x, S32 y, MASK mask) +{ + return FALSE; +} + +LLRect AIGLHTTPHeaderBar::getRequiredRect() +{ + LLRect rect; + rect.mTop = sLineHeight * number_of_header_lines; + return rect; +} + +//============================================================================= + +AIHTTPView::AIHTTPView(AIHTTPView::Params const& p) : + LLContainerView(p), mGLHTTPHeaderBar(NULL), mWidth(200) +{ + setVisible(FALSE); + setRectAlpha(0.5); +} + +AIHTTPView::~AIHTTPView() +{ + delete mGLHTTPHeaderBar; + mGLHTTPHeaderBar = NULL; +} + +U32 AIHTTPView::updateColumn(int col, U32 start) +{ + llassert(col <= mStartColumn.size()); + if (col == mStartColumn.size()) + { + mStartColumn.push_back(start); + } + else if (mStartColumn[col] < start) + { + mStartColumn[col] = start; + } + return mStartColumn[col]; +} + +//static +void AIHTTPView::toggle_visibility(void* user_data) +{ + LLView* viewp = (LLView*)user_data; + bool visible = !viewp->getVisible(); + if (visible) + { + AIPerService::resetUsed(); + } + viewp->setVisible(visible); +} + +U64 AIHTTPView::sTime_40ms; + +struct KillView +{ + void operator()(LLView* viewp) + { + viewp->getParent()->removeChild(viewp); + viewp->die(); + } +}; + +struct CreateServiceBar +{ + AIHTTPView* mHTTPView; + + CreateServiceBar(AIHTTPView* httpview) : mHTTPView(httpview) { } + + void operator()(AIPerService::instance_map_type::value_type const& service) + { + if (!PerService_rat(*service.second)->is_used()) + return; + AIServiceBar* service_bar = new AIServiceBar(mHTTPView, service); + mHTTPView->addChild(service_bar); + mHTTPView->mServiceBars.push_back(service_bar); + } +}; + +void AIHTTPView::draw() +{ + for_each(mServiceBars.begin(), mServiceBars.end(), KillView()); + mServiceBars.clear(); + + if (mGLHTTPHeaderBar) + { + removeChild(mGLHTTPHeaderBar); + mGLHTTPHeaderBar->die(); + } + + CreateServiceBar functor(this); + AIPerService::copy_forEach(functor); + + sTime_40ms = get_clock_count() * AICurlPrivate::curlthread::HTTPTimeout::sClockWidth_40ms; + + mGLHTTPHeaderBar = new AIGLHTTPHeaderBar("gl httpheader bar", this); + addChild(mGLHTTPHeaderBar); + + reshape(mWidth, getRect().getHeight(), TRUE); + + for (child_list_const_iter_t child_iter = getChildList()->begin(); child_iter != getChildList()->end(); ++child_iter) + { + LLView* viewp = *child_iter; + if (viewp->getRect().mBottom < 0) + { + viewp->setVisible(FALSE); + } + } + + LLContainerView::draw(); +} + +BOOL AIHTTPView::handleMouseUp(S32 x, S32 y, MASK mask) +{ + return FALSE; +} + +BOOL AIHTTPView::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + return FALSE; +} + diff --git a/indra/newview/aihttpview.h b/indra/newview/aihttpview.h new file mode 100644 index 000000000..767d37e54 --- /dev/null +++ b/indra/newview/aihttpview.h @@ -0,0 +1,74 @@ +/** + * @file aihttpview.h + * @brief Declaration for AIHTTPView. + * + * Copyright (c) 2013, Aleric Inglewood. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution. + * + * CHANGELOG + * and additional copyright holders. + * + * 28/05/2013 + * Initial version, written by Aleric Inglewood @ SL + */ + +#ifndef AIHTTPVIEW_H +#define AIHTTPVIEW_H + +#include "llcontainerview.h" +#include + +class AIGLHTTPHeaderBar; +class AIServiceBar; +struct CreateServiceBar; + +class AIHTTPView : public LLContainerView +{ + friend class AIGLHTTPHeaderBar; + friend class AIServiceBar; + friend struct CreateServiceBar; + + public: + AIHTTPView(AIHTTPView::Params const& p); + ~AIHTTPView(); + + /*virtual*/ void draw(void); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); + + U32 updateColumn(int col, U32 start); + void setWidth(S32 width) { mWidth = width; } + + private: + AIGLHTTPHeaderBar* mGLHTTPHeaderBar; + std::vector mServiceBars; + std::vector mStartColumn; + size_t mMaxBandwidthPerService; + S32 mWidth; + + static U64 sTime_40ms; + + public: + static U64 getTime_40ms(void) { return sTime_40ms; } + static void toggle_visibility(void* user_data); +}; + +extern AIHTTPView *gHttpView; + +#endif // AIHTTPVIEW_H diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7894e27a2..8df4ae10d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -696,6 +696,17 @@ Value 0 + LiruLegacyOutfitStoreObjChanges + + Comment + While true, automatic detach is performed upon all copyable attachments just before legacy outfit creation to preserve any modifications to them. As a last resort, setting this false allows creating a legacy outfit that won't save changes made to copyable attachments since wearing them or login. + Persist + 1 + Type + Boolean + Value + 1 + LiruNoTransactionClutter Comment @@ -707,6 +718,17 @@ Value 0 + LiruScriptErrorsStealFocus + + Comment + When false, scripts that have errors won't steal focus once they've been failed to compile + Persist + 1 + Type + Boolean + Value + 1 + LiruSensibleARC Comment @@ -741,6 +763,17 @@ Found in Advanced->Rendering->Info Displays Value 0 + FloaterUnfocusedBackgroundOpaque + + Comment + Disables floaters going transparent when not in focus, may conflict with some skins, though. + Persist + 1 + Type + Boolean + Value + 0 + InstantMessageLogPathAnyAccount Comment @@ -1141,7 +1174,7 @@ This should be as low as possible, but too low may break functionality Type Boolean Value - 1 + 0 WoLfVerticalIMTabs @@ -7428,6 +7461,22 @@ This should be as low as possible, but too low may break functionality Value -1 + FloaterVoiceEffectRect + + Comment + Rectangle for voice morpher floater + Persist + 1 + Type + Rect + Value + + 0 + 300 + 360 + 0 + + FloaterWorldMapRect2 Comment @@ -14386,6 +14435,17 @@ This should be as low as possible, but too low may break functionality Value 0 + ShowVoiceVisualizersInCalls + + Comment + Enables in-world voice visualizers, voice gestures and lip-sync while in group or P2P calls. + Persist + 1 + Type + Boolean + Value + 0 + ShowVolumeSettingsPopup Comment @@ -15677,6 +15737,28 @@ This should be as low as possible, but too low may break functionality Value 0 + SpeakerParticipantDefaultOrder + + Comment + Order for displaying speakers in voice controls. 0 = alphabetical. 1 = recent. + Persist + 1 + Type + U32 + Value + 1 + + SpeakerParticipantRemoveDelay + + Comment + Timeout to remove participants who is not in channel before removed from list of active speakers (text/voice chat) + Persist + 1 + Type + F32 + Value + 10.0 + UseNewWalkRun Comment @@ -15907,6 +15989,50 @@ This should be as low as possible, but too low may break functionality Value 0 + VoiceCallsRejectGroup + + Comment + Silently reject all incoming group voice calls. + Persist + 1 + Type + Boolean + Value + 0 + + VoiceDisableMic + + Comment + Completely disable the ability to open the mic. + Persist + 1 + Type + Boolean + Value + 0 + + VoiceEffectExpiryWarningTime + + Comment + How much notice to give of Voice Morph subscriptions expiry, in seconds. + Persist + 1 + Type + S32 + Value + 259200 + + VoiceMorphingEnabled + + Comment + Whether or not to enable Voice Morphs and show the UI. + Persist + 0 + Type + Boolean + Value + 1 + AutoDisengageMic Comment @@ -15918,6 +16044,17 @@ This should be as low as possible, but too low may break functionality Value 1 + ShowDeviceSettings + + Comment + Show device settings + Persist + 0 + Type + Boolean + Value + 0 + VoiceEarLocation Comment @@ -16028,6 +16165,17 @@ This should be as low as possible, but too low may break functionality Value Default + VoiceLogFile + + Comment + Log file to use when launching the voice daemon + Persist + 1 + Type + String + Value + + VoiceOutputAudioDevice Comment @@ -16061,6 +16209,17 @@ This should be as low as possible, but too low may break functionality Value 0 + VoiceServerType + + Comment + The type of voice server to connect to. + Persist + 0 + Type + String + Value + vivox + WLSkyDetail Comment diff --git a/indra/newview/app_settings/settings_ascent.xml b/indra/newview/app_settings/settings_ascent.xml index dd8d4e606..e3d288568 100644 --- a/indra/newview/app_settings/settings_ascent.xml +++ b/indra/newview/app_settings/settings_ascent.xml @@ -235,28 +235,6 @@ Value 0.0 - AscentUseSystemFolder - - Comment - Enables the System folder for setting and non-permanent asset storage. - Persist - 1 - Type - Boolean - Value - 0 - - AscentSystemTemporary - - Comment - When enabled, temporary uploads are put in the System Asset folder (if System Folder exists). - Persist - 1 - Type - Boolean - Value - 0 - AscentShowFriendsTag Comment @@ -620,5 +598,16 @@ Value /away + SinguCompleteNameProfiles + + Comment + Use the complete name "Display Name (legacy.name)" in profiles, instead of following the choice set by PhoenixNameSystem. + Persist + 1 + Type + Boolean + Value + 0 + diff --git a/indra/newview/app_settings/settings_crash_behavior.xml b/indra/newview/app_settings/settings_crash_behavior.xml index 433583bee..0665d86f2 100644 --- a/indra/newview/app_settings/settings_crash_behavior.xml +++ b/indra/newview/app_settings/settings_crash_behavior.xml @@ -31,7 +31,7 @@ Type U32 Value - 16 + 8 NoVerifySSLCert diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml index d389c3b8d..d3521c5f0 100644 --- a/indra/newview/app_settings/settings_per_account.xml +++ b/indra/newview/app_settings/settings_per_account.xml @@ -573,6 +573,17 @@ Boolean Value 1 + + VoiceEffectDefault + + Comment + Selected Voice Morph + Persist + 1 + Type + String + Value + 00000000-0000-0000-0000-000000000000 LogFileNamewithDate diff --git a/indra/newview/ascentprefschat.cpp b/indra/newview/ascentprefschat.cpp index cf60bb715..242865bff 100644 --- a/indra/newview/ascentprefschat.cpp +++ b/indra/newview/ascentprefschat.cpp @@ -51,14 +51,14 @@ LLPrefsAscentChat::LLPrefsAscentChat() { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_ascent_chat.xml"); - childSetCommitCallback("SpellBase", onSpellBaseComboBoxCommit, this); - childSetAction("EmSpell_EditCustom", onSpellEditCustom, this); - childSetAction("EmSpell_GetMore", onSpellGetMore, this); - childSetAction("EmSpell_Add", onSpellAdd, this); - childSetAction("EmSpell_Remove", onSpellRemove, this); + getChild("SpellBase")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onSpellBaseComboBoxCommit, this, _2)); + getChild("EmSpell_EditCustom")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onSpellEditCustom, this)); + getChild("EmSpell_GetMore")->setCommitCallback(boost::bind(&lggHunSpell_Wrapper::getMoreButton, glggHunSpell, this)); + getChild("EmSpell_Add")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onSpellAdd, this)); + getChild("EmSpell_Remove")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onSpellRemove, this)); - childSetCommitCallback("time_format_combobox", onCommitTimeDate, this); - childSetCommitCallback("date_format_combobox", onCommitTimeDate, this); + getChild("time_format_combobox")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitTimeDate, this, _1)); + getChild("date_format_combobox")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitTimeDate, this, _1)); bool started = (LLStartUp::getStartupState() == STATE_STARTED); if (!started) // Disable autoresponse when not logged in @@ -87,19 +87,19 @@ LLPrefsAscentChat::LLPrefsAscentChat() childSetValue("BusyModeResponseShow", gSavedPerAccountSettings.getBOOL("BusyModeResponseShow")); childSetEnabled("reset_antispam", started); - childSetCommitCallback("reset_antispam", onCommitResetAS, this); - childSetCommitCallback("enable_as", onCommitEnableAS, this); - childSetCommitCallback("antispam_checkbox", onCommitDialogBlock, this); - childSetCommitCallback("Group Invites", onCommitDialogBlock, this); + getChild("reset_antispam")->setCommitCallback(boost::bind(NACLAntiSpamRegistry::purgeAllQueues)); + getChild("enable_as")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitEnableAS, this, _2)); + getChild("antispam_checkbox")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitDialogBlock, this, _1, _2)); + getChild("Group Invites")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitDialogBlock, this, _1, _2)); - childSetCommitCallback("KeywordsOn", onCommitKeywords, this); - childSetCommitCallback("KeywordsList", onCommitKeywords, this); - childSetCommitCallback("KeywordsSound", onCommitKeywords, this); - childSetCommitCallback("KeywordsInChat", onCommitKeywords, this); - childSetCommitCallback("KeywordsInIM", onCommitKeywords, this); - childSetCommitCallback("KeywordsChangeColor", onCommitKeywords, this); - childSetCommitCallback("KeywordsColor", onCommitKeywords, this); - childSetCommitCallback("KeywordsPlaySound", onCommitKeywords, this); + getChild("KeywordsOn")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsList")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsSound")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsInChat")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsInIM")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsChangeColor")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsColor")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); + getChild("KeywordsPlaySound")->setCommitCallback(boost::bind(&LLPrefsAscentChat::onCommitKeywords, this, _1)); refreshValues(); refresh(); @@ -109,73 +109,43 @@ LLPrefsAscentChat::~LLPrefsAscentChat() { } -//static -void LLPrefsAscentChat::onSpellAdd(void* data) +void LLPrefsAscentChat::onSpellAdd() { - LLPrefsAscentChat* self = (LLPrefsAscentChat*)data; - - if(self) - { - glggHunSpell->addButton(self->childGetValue("EmSpell_Avail").asString()); - } - - self->refresh(); + glggHunSpell->addButton(childGetValue("EmSpell_Avail").asString()); + refresh(); } -//static -void LLPrefsAscentChat::onSpellRemove(void* data) +void LLPrefsAscentChat::onSpellRemove() { - LLPrefsAscentChat* self = (LLPrefsAscentChat*)data; - - if(self) - { - glggHunSpell->removeButton(self->childGetValue("EmSpell_Installed").asString()); - } - - self->refresh(); + glggHunSpell->removeButton(childGetValue("EmSpell_Installed").asString()); + refresh(); } -//static -void LLPrefsAscentChat::onSpellGetMore(void* data) +void LLPrefsAscentChat::onSpellEditCustom() { - glggHunSpell->getMoreButton(data); + glggHunSpell->editCustomButton(); } -//static -void LLPrefsAscentChat::onSpellEditCustom(void* data) +void LLPrefsAscentChat::onSpellBaseComboBoxCommit(const LLSD& value) { - glggHunSpell->editCustomButton(); + glggHunSpell->newDictSelection(value.asString()); } -//static -void LLPrefsAscentChat::onSpellBaseComboBoxCommit(LLUICtrl* ctrl, void* userdata) +void LLPrefsAscentChat::onCommitTimeDate(LLUICtrl* ctrl) { - LLComboBox* box = (LLComboBox*)ctrl; - - if (box) - { - glggHunSpell->newDictSelection(box->getValue().asString()); - } -} - -//static -void LLPrefsAscentChat::onCommitTimeDate(LLUICtrl* ctrl, void* userdata) -{ - LLPrefsAscentChat* self = (LLPrefsAscentChat*)userdata; - - LLComboBox* combo = (LLComboBox*)ctrl; + LLComboBox* combo = static_cast(ctrl); if (ctrl->getName() == "time_format_combobox") { - self->tempTimeFormat = combo->getCurrentIndex(); + tempTimeFormat = combo->getCurrentIndex(); } else if (ctrl->getName() == "date_format_combobox") { - self->tempDateFormat = combo->getCurrentIndex(); + tempDateFormat = combo->getCurrentIndex(); } std::string short_date, long_date, short_time, long_time, timestamp; - if (self->tempTimeFormat == 0) + if (tempTimeFormat == 0) { short_time = "%H:%M"; long_time = "%H:%M:%S"; @@ -188,13 +158,13 @@ void LLPrefsAscentChat::onCommitTimeDate(LLUICtrl* ctrl, void* userdata) timestamp = " %I:%M %p"; } - if (self->tempDateFormat == 0) + if (tempDateFormat == 0) { short_date = "%Y-%m-%d"; long_date = "%A %d %B %Y"; timestamp = "%a %d %b %Y" + timestamp; } - else if (self->tempDateFormat == 1) + else if (tempDateFormat == 1) { short_date = "%d/%m/%Y"; long_date = "%A %d %B %Y"; @@ -214,70 +184,57 @@ void LLPrefsAscentChat::onCommitTimeDate(LLUICtrl* ctrl, void* userdata) gSavedSettings.setString("TimestampFormat", timestamp); } -//static -void LLPrefsAscentChat::onCommitResetAS(LLUICtrl*, void*) +void LLPrefsAscentChat::onCommitEnableAS(const LLSD& value) { - NACLAntiSpamRegistry::purgeAllQueues(); + bool enabled = value.asBoolean(); + childSetEnabled("spammsg_checkbox", enabled); + childSetEnabled("antispamtime", enabled); + childSetEnabled("antispamamount", enabled); + childSetEnabled("antispamsoundmulti", enabled); + childSetEnabled("antispamsoundpreloadmulti", enabled); + childSetEnabled("antispamnewlines", enabled); + childSetEnabled("Notify On Spam", enabled); } -//static -void LLPrefsAscentChat::onCommitEnableAS(LLUICtrl* ctrl, void* user_data) +void LLPrefsAscentChat::onCommitDialogBlock(LLUICtrl* ctrl, const LLSD& value) { - LLPrefsAscentChat* self = (LLPrefsAscentChat*)user_data; - bool enabled = ctrl->getValue().asBoolean(); - self->childSetEnabled("spammsg_checkbox", enabled); - self->childSetEnabled("antispamtime", enabled); - self->childSetEnabled("antispamamount", enabled); - self->childSetEnabled("antispamsoundmulti", enabled); - self->childSetEnabled("antispamsoundpreloadmulti", enabled); - self->childSetEnabled("antispamnewlines", enabled); - self->childSetEnabled("Notify On Spam", enabled); -} - -//static -void LLPrefsAscentChat::onCommitDialogBlock(LLUICtrl* ctrl, void* user_data) -{ - LLPrefsAscentChat* self = (LLPrefsAscentChat*)user_data; - self->childSetEnabled("Group Fee Invites", !self->childGetValue("antispam_checkbox").asBoolean() && !self->childGetValue("Group Invites").asBoolean()); - bool enabled = ctrl->getValue().asBoolean(); + childSetEnabled("Group Fee Invites", !childGetValue("antispam_checkbox").asBoolean() && !childGetValue("Group Invites").asBoolean()); + bool enabled = value.asBoolean(); if (ctrl->getName() == "antispam_checkbox") { - self->childSetEnabled("Block All Dialogs From", !enabled); - self->childSetEnabled("Alerts", !enabled); - self->childSetEnabled("Friendship Offers", !enabled); - self->childSetEnabled("Group Invites", !enabled); - self->childSetEnabled("Group Notices", !enabled); - self->childSetEnabled("Item Offers", !enabled); - self->childSetEnabled("Scripts", !enabled); - self->childSetEnabled("Teleport Offers", !enabled); + childSetEnabled("Block All Dialogs From", !enabled); + childSetEnabled("Alerts", !enabled); + childSetEnabled("Friendship Offers", !enabled); + childSetEnabled("Group Invites", !enabled); + childSetEnabled("Group Notices", !enabled); + childSetEnabled("Item Offers", !enabled); + childSetEnabled("Scripts", !enabled); + childSetEnabled("Teleport Offers", !enabled); } } -//static -void LLPrefsAscentChat::onCommitKeywords(LLUICtrl* ctrl, void* user_data) +void LLPrefsAscentChat::onCommitKeywords(LLUICtrl* ctrl) { - LLPrefsAscentChat* self = (LLPrefsAscentChat*)user_data; - if (ctrl->getName() == "KeywordsOn") { - bool enabled = self->childGetValue("KeywordsOn").asBoolean(); - self->childSetEnabled("KeywordsList", enabled); - self->childSetEnabled("KeywordsInChat", enabled); - self->childSetEnabled("KeywordsInIM", enabled); - self->childSetEnabled("KeywordsChangeColor", enabled); - self->childSetEnabled("KeywordsColor", enabled); - self->childSetEnabled("KeywordsPlaySound", enabled); - self->childSetEnabled("KeywordsSound", enabled); + bool enabled = childGetValue("KeywordsOn").asBoolean(); + childSetEnabled("KeywordsList", enabled); + childSetEnabled("KeywordsInChat", enabled); + childSetEnabled("KeywordsInIM", enabled); + childSetEnabled("KeywordsChangeColor", enabled); + childSetEnabled("KeywordsColor", enabled); + childSetEnabled("KeywordsPlaySound", enabled); + childSetEnabled("KeywordsSound", enabled); } - gSavedPerAccountSettings.setBOOL("KeywordsOn", self->childGetValue("KeywordsOn")); - gSavedPerAccountSettings.setString("KeywordsList", self->childGetValue("KeywordsList")); - gSavedPerAccountSettings.setBOOL("KeywordsInChat", self->childGetValue("KeywordsInChat")); - gSavedPerAccountSettings.setBOOL("KeywordsInIM", self->childGetValue("KeywordsInIM")); - gSavedPerAccountSettings.setBOOL("KeywordsChangeColor", self->childGetValue("KeywordsChangeColor")); - gSavedPerAccountSettings.setColor4("KeywordsColor", self->childGetValue("KeywordsColor")); - gSavedPerAccountSettings.setBOOL("KeywordsPlaySound", self->childGetValue("KeywordsPlaySound")); - gSavedPerAccountSettings.setString("KeywordsSound", self->childGetValue("KeywordsSound")); + gSavedPerAccountSettings.setBOOL("KeywordsOn", childGetValue("KeywordsOn")); + gSavedPerAccountSettings.setString("KeywordsList", childGetValue("KeywordsList")); + gSavedPerAccountSettings.setBOOL("KeywordsInChat", childGetValue("KeywordsInChat")); + gSavedPerAccountSettings.setBOOL("KeywordsInIM", childGetValue("KeywordsInIM")); + gSavedPerAccountSettings.setBOOL("KeywordsChangeColor", childGetValue("KeywordsChangeColor")); + gSavedPerAccountSettings.setColor4("KeywordsColor", childGetValue("KeywordsColor")); + gSavedPerAccountSettings.setBOOL("KeywordsPlaySound", childGetValue("KeywordsPlaySound")); + gSavedPerAccountSettings.setString("KeywordsSound", childGetValue("KeywordsSound")); } // Store current settings for cancel diff --git a/indra/newview/ascentprefschat.h b/indra/newview/ascentprefschat.h index aa0b91d9d..ee067bbaa 100644 --- a/indra/newview/ascentprefschat.h +++ b/indra/newview/ascentprefschat.h @@ -47,16 +47,14 @@ public: void refreshValues(); protected: - static void onSpellAdd(void* data); - static void onSpellRemove(void* data); - static void onSpellGetMore(void* data); - static void onSpellEditCustom(void* data); - static void onSpellBaseComboBoxCommit(LLUICtrl* ctrl, void* userdata); - static void onCommitTimeDate(LLUICtrl* ctrl, void *userdata); - static void onCommitResetAS(LLUICtrl*,void*); - static void onCommitEnableAS(LLUICtrl*, void*); - static void onCommitDialogBlock(LLUICtrl*, void*); - static void onCommitKeywords(LLUICtrl* ctrl, void* user_data); + void onSpellAdd(); + void onSpellRemove(); + void onSpellEditCustom(); + void onSpellBaseComboBoxCommit(const LLSD& value); + void onCommitTimeDate(LLUICtrl* ctrl); + void onCommitEnableAS(const LLSD& value); + void onCommitDialogBlock(LLUICtrl* ctrl, const LLSD& value); + void onCommitKeywords(LLUICtrl* ctrl); //Chat/IM ----------------------------------------------------------------------------- BOOL mIMAnnounceIncoming; diff --git a/indra/newview/ascentprefssys.cpp b/indra/newview/ascentprefssys.cpp index fc2bc1a62..0dba5124c 100644 --- a/indra/newview/ascentprefssys.cpp +++ b/indra/newview/ascentprefssys.cpp @@ -51,41 +51,41 @@ LLPrefsAscentSys::LLPrefsAscentSys() LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_ascent_system.xml"); //General ----------------------------------------------------------------------------- - childSetCommitCallback("speed_rez_check", onCommitCheckBox, this); - childSetCommitCallback("double_click_teleport_check", onCommitCheckBox, this); - childSetCommitCallback("system_folder_check", onCommitCheckBox, this); - childSetCommitCallback("show_look_at_check", onCommitCheckBox, this); - childSetCommitCallback("enable_clouds", onCommitCheckBox, this); - childSetCommitCallback("power_user_check", onCommitCheckBox, this); - childSetCommitCallback("power_user_confirm_check", onCommitCheckBox, this); + getChild("speed_rez_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); + getChild("double_click_teleport_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); + getChild("system_folder_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); + getChild("show_look_at_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); + getChild("enable_clouds")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); + getChild("power_user_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); + getChild("power_user_confirm_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); //Command Line ------------------------------------------------------------------------ - childSetCommitCallback("chat_cmd_toggle", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLinePos", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineGround", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineHeight", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineTeleportHome", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineRezPlatform", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineCalc", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineClearChat", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineDrawDistance", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdTeleportToCam", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineKeyToName", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineOfferTp", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineMapTo", onCommitCmdLine, this); - childSetCommitCallback("AscentCmdLineTP2", onCommitCmdLine, this); - childSetCommitCallback("SinguCmdLineAway", onCommitCmdLine, this); + getChild("chat_cmd_toggle")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLinePos")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineGround")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineHeight")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineTeleportHome")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineRezPlatform")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineCalc")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineClearChat")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineDrawDistance")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdTeleportToCam")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineKeyToName")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineOfferTp")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineMapTo")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("AscentCmdLineTP2")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); + getChild("SinguCmdLineAway")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCmdLine, this, _1, _2)); //Security ---------------------------------------------------------------------------- - childSetCommitCallback("disable_click_sit_check", onCommitCheckBox, this); + getChild("disable_click_sit_check")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); //Build ------------------------------------------------------------------------------- - childSetCommitCallback("next_owner_copy", onCommitCheckBox, this); + getChild("next_owner_copy")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitCheckBox, this, _1, _2)); childSetEnabled("next_owner_transfer", gSavedSettings.getBOOL("NextOwnerCopy")); - childSetCommitCallback("material", onCommitComboBox, this); - childSetCommitCallback("combobox shininess", onCommitComboBox, this); + getChild("material")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitComboBox, this, _1, _2)); + getChild("combobox shininess")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitComboBox, this, _1, _2)); getChild("texture control")->setDefaultImageAssetID(LLUUID(gSavedSettings.getString("EmeraldBuildPrefs_Texture"))); - childSetCommitCallback("texture control", onCommitTexturePicker, this); + getChild("texture control")->setCommitCallback(boost::bind(&LLPrefsAscentSys::onCommitTexturePicker, this, _1)); refreshValues(); refresh(); @@ -95,37 +95,34 @@ LLPrefsAscentSys::~LLPrefsAscentSys() { } -//static -void LLPrefsAscentSys::onCommitCheckBox(LLUICtrl* ctrl, void* user_data) +void LLPrefsAscentSys::onCommitCheckBox(LLUICtrl* ctrl, const LLSD& value) { - LLPrefsAscentSys* self = static_cast(user_data); - // llinfos << "Change to " << ctrl->getControlName() << " aka " << ctrl->getName() << llendl; const std::string name = ctrl->getName(); - bool enabled = ctrl->getValue().asBoolean(); + bool enabled = value.asBoolean(); if (name == "speed_rez_check") { - self->childSetEnabled("speed_rez_interval", enabled); - self->childSetEnabled("speed_rez_seconds", enabled); + childSetEnabled("speed_rez_interval", enabled); + childSetEnabled("speed_rez_seconds", enabled); } else if (name == "double_click_teleport_check") { - self->childSetEnabled("center_after_teleport_check", enabled); - self->childSetEnabled("offset_teleport_check", enabled); + childSetEnabled("center_after_teleport_check", enabled); + childSetEnabled("offset_teleport_check", enabled); } else if (name == "system_folder_check") { - self->childSetEnabled("temp_in_system_check", enabled); + childSetEnabled("temp_in_system_check", enabled); } else if (name == "enable_clouds") { - self->childSetEnabled("enable_classic_clouds", enabled); + childSetEnabled("enable_classic_clouds", enabled); } else if (name == "power_user_check") { - self->childSetEnabled("power_user_confirm_check", enabled); - self->childSetValue("power_user_confirm_check", false); + childSetEnabled("power_user_confirm_check", enabled); + childSetValue("power_user_confirm_check", false); } else if (name == "power_user_confirm_check") { @@ -143,79 +140,65 @@ void LLPrefsAscentSys::onCommitCheckBox(LLUICtrl* ctrl, void* user_data) } else if (name == "disable_click_sit_check") { - self->childSetEnabled("disable_click_sit_own_check", !enabled); + childSetEnabled("disable_click_sit_own_check", !enabled); } else if (name == "next_owner_copy") { if (!enabled) gSavedSettings.setBOOL("NextOwnerTransfer", true); - self->childSetEnabled("next_owner_transfer", enabled); + childSetEnabled("next_owner_transfer", enabled); } } -//static -void LLPrefsAscentSys::onCommitCmdLine(LLUICtrl* ctrl, void* user_data) +void LLPrefsAscentSys::onCommitCmdLine(LLUICtrl* ctrl, const LLSD& value) { - LLPrefsAscentSys* self = (LLPrefsAscentSys*)user_data; - - if (ctrl->getName() == "chat_cmd_toggle") + const std::string& name = ctrl->getName(); + if (name == "chat_cmd_toggle") { - bool enabled = self->childGetValue("chat_cmd_toggle").asBoolean(); - self->childSetEnabled("cmd_line_text_2", enabled); - self->childSetEnabled("cmd_line_text_3", enabled); - self->childSetEnabled("cmd_line_text_4", enabled); - self->childSetEnabled("cmd_line_text_5", enabled); - self->childSetEnabled("cmd_line_text_6", enabled); - self->childSetEnabled("cmd_line_text_7", enabled); - self->childSetEnabled("cmd_line_text_8", enabled); - self->childSetEnabled("cmd_line_text_9", enabled); - self->childSetEnabled("cmd_line_text_10", enabled); - self->childSetEnabled("cmd_line_text_11", enabled); - self->childSetEnabled("cmd_line_text_12", enabled); - self->childSetEnabled("cmd_line_text_13", enabled); - self->childSetEnabled("cmd_line_text_15", enabled); - self->childSetEnabled("AscentCmdLinePos", enabled); - self->childSetEnabled("AscentCmdLineGround", enabled); - self->childSetEnabled("AscentCmdLineHeight", enabled); - self->childSetEnabled("AscentCmdLineTeleportHome", enabled); - self->childSetEnabled("AscentCmdLineRezPlatform", enabled); - self->childSetEnabled("AscentPlatformSize", enabled); - self->childSetEnabled("AscentCmdLineCalc", enabled); - self->childSetEnabled("AscentCmdLineClearChat", enabled); - self->childSetEnabled("AscentCmdLineDrawDistance", enabled); - self->childSetEnabled("AscentCmdTeleportToCam", enabled); - self->childSetEnabled("AscentCmdLineKeyToName", enabled); - self->childSetEnabled("AscentCmdLineOfferTp", enabled); - self->childSetEnabled("AscentCmdLineMapTo", enabled); - self->childSetEnabled("map_to_keep_pos", enabled); - self->childSetEnabled("AscentCmdLineTP2", enabled); - self->childSetEnabled("SinguCmdLineAway", enabled); + bool enabled = value.asBoolean(); + childSetEnabled("cmd_line_text_2", enabled); + childSetEnabled("cmd_line_text_3", enabled); + childSetEnabled("cmd_line_text_4", enabled); + childSetEnabled("cmd_line_text_5", enabled); + childSetEnabled("cmd_line_text_6", enabled); + childSetEnabled("cmd_line_text_7", enabled); + childSetEnabled("cmd_line_text_8", enabled); + childSetEnabled("cmd_line_text_9", enabled); + childSetEnabled("cmd_line_text_10", enabled); + childSetEnabled("cmd_line_text_11", enabled); + childSetEnabled("cmd_line_text_12", enabled); + childSetEnabled("cmd_line_text_13", enabled); + childSetEnabled("cmd_line_text_15", enabled); + childSetEnabled("AscentCmdLinePos", enabled); + childSetEnabled("AscentCmdLineGround", enabled); + childSetEnabled("AscentCmdLineHeight", enabled); + childSetEnabled("AscentCmdLineTeleportHome", enabled); + childSetEnabled("AscentCmdLineRezPlatform", enabled); + childSetEnabled("AscentPlatformSize", enabled); + childSetEnabled("AscentCmdLineCalc", enabled); + childSetEnabled("AscentCmdLineClearChat", enabled); + childSetEnabled("AscentCmdLineDrawDistance", enabled); + childSetEnabled("AscentCmdTeleportToCam", enabled); + childSetEnabled("AscentCmdLineKeyToName", enabled); + childSetEnabled("AscentCmdLineOfferTp", enabled); + childSetEnabled("AscentCmdLineMapTo", enabled); + childSetEnabled("map_to_keep_pos", enabled); + childSetEnabled("AscentCmdLineTP2", enabled); + childSetEnabled("SinguCmdLineAway", enabled); } - - gSavedSettings.setString("AscentCmdLinePos", self->childGetValue("AscentCmdLinePos")); - gSavedSettings.setString("AscentCmdLineGround", self->childGetValue("AscentCmdLineGround")); - gSavedSettings.setString("AscentCmdLineHeight", self->childGetValue("AscentCmdLineHeight")); - gSavedSettings.setString("AscentCmdLineTeleportHome", self->childGetValue("AscentCmdLineTeleportHome")); - gSavedSettings.setString("AscentCmdLineRezPlatform", self->childGetValue("AscentCmdLineRezPlatform")); - gSavedSettings.setString("AscentCmdLineCalc", self->childGetValue("AscentCmdLineCalc")); - gSavedSettings.setString("AscentCmdLineClearChat", self->childGetValue("AscentCmdLineClearChat")); - gSavedSettings.setString("AscentCmdLineDrawDistance", self->childGetValue("AscentCmdLineDrawDistance")); - gSavedSettings.setString("AscentCmdTeleportToCam", self->childGetValue("AscentCmdTeleportToCam")); - gSavedSettings.setString("AscentCmdLineKeyToName", self->childGetValue("AscentCmdLineKeyToName")); - gSavedSettings.setString("AscentCmdLineOfferTp", self->childGetValue("AscentCmdLineOfferTp")); - gSavedSettings.setString("AscentCmdLineMapTo", self->childGetValue("AscentCmdLineMapTo")); - gSavedSettings.setString("AscentCmdLineTP2", self->childGetValue("AscentCmdLineTP2")); - gSavedSettings.setString("SinguCmdLineAway", self->childGetValue("SinguCmdLineAway")); + else + { + gSavedSettings.setString(name, value); // Singu Note: Keep commandline settings using the same name as their settings + } } -void LLPrefsAscentSys::onCommitComboBox(LLUICtrl* ctrl, void* userdata) +void LLPrefsAscentSys::onCommitComboBox(LLUICtrl* ctrl, const LLSD& value) { - LLComboBox* box = (LLComboBox*)ctrl; - if(box) gSavedSettings.setString(box->getControlName(), box->getValue().asString()); + gSavedSettings.setString(ctrl->getControlName(), value.asString()); } -void LLPrefsAscentSys::onCommitTexturePicker(LLUICtrl* ctrl, void* userdata) +void LLPrefsAscentSys::onCommitTexturePicker(LLUICtrl* ctrl) { - LLTextureCtrl* image_ctrl = (LLTextureCtrl*)ctrl; + LLTextureCtrl* image_ctrl = static_cast(ctrl); if(image_ctrl) gSavedSettings.setString("EmeraldBuildPrefs_Texture", image_ctrl->getImageAssetID().asString()); } @@ -235,8 +218,6 @@ void LLPrefsAscentSys::refreshValues() mAlwaysShowFly = gSavedSettings.getBOOL("AscentFlyAlwaysEnabled"); mDisableMinZoom = gSavedSettings.getBOOL("AscentDisableMinZoomDist"); mPowerUser = gSavedSettings.getBOOL("AscentPowerfulWizard"); - mUseSystemFolder = gSavedSettings.getBOOL("AscentUseSystemFolder"); - mUploadToSystem = gSavedSettings.getBOOL("AscentSystemTemporary"); mFetchInventoryOnLogin = gSavedSettings.getBOOL("FetchInventoryOnLogin"); mEnableLLWind = gSavedSettings.getBOOL("WindEnabled"); mEnableClouds = gSavedSettings.getBOOL("CloudsEnabled"); @@ -303,7 +284,6 @@ void LLPrefsAscentSys::refresh() childSetEnabled("offset_teleport_check", mDoubleClickTeleport); childSetValue("power_user_check", mPowerUser); childSetValue("power_user_confirm_check", mPowerUser); - childSetEnabled("temp_in_system_check", mUseSystemFolder); childSetEnabled("speed_rez_interval", mSpeedRez); childSetEnabled("speed_rez_seconds", mSpeedRez); @@ -388,8 +368,6 @@ void LLPrefsAscentSys::cancel() gSavedSettings.setBOOL("AscentBuildAlwaysEnabled", mBuildAlwaysEnabled); gSavedSettings.setBOOL("AscentFlyAlwaysEnabled", mAlwaysShowFly); gSavedSettings.setBOOL("AscentDisableMinZoomDist", mDisableMinZoom); - gSavedSettings.setBOOL("AscentUseSystemFolder", mUseSystemFolder); - gSavedSettings.setBOOL("AscentSystemTemporary", mUploadToSystem); gSavedSettings.setBOOL("FetchInventoryOnLogin", mFetchInventoryOnLogin); gSavedSettings.setBOOL("WindEnabled", mEnableLLWind); gSavedSettings.setBOOL("CloudsEnabled", mEnableClouds); diff --git a/indra/newview/ascentprefssys.h b/indra/newview/ascentprefssys.h index 2ecacb43e..8a7341481 100644 --- a/indra/newview/ascentprefssys.h +++ b/indra/newview/ascentprefssys.h @@ -47,10 +47,10 @@ public: void refreshValues(); protected: - static void onCommitCheckBox(LLUICtrl* ctrl, void* user_data); - static void onCommitCmdLine(LLUICtrl* ctrl, void* user_data); - static void onCommitComboBox(LLUICtrl* ctrl, void* user_data); - static void onCommitTexturePicker(LLUICtrl* ctrl, void* user_data); + void onCommitCheckBox(LLUICtrl* ctrl, const LLSD& value); + void onCommitCmdLine(LLUICtrl* ctrl, const LLSD& value); + void onCommitComboBox(LLUICtrl* ctrl, const LLSD& value); + void onCommitTexturePicker(LLUICtrl* ctrl); //General ----------------------------------------------------------------------------- BOOL mDoubleClickTeleport; @@ -66,8 +66,6 @@ protected: BOOL mAlwaysShowFly; BOOL mDisableMinZoom; BOOL mPowerUser; - BOOL mUseSystemFolder; - BOOL mUploadToSystem; BOOL mFetchInventoryOnLogin; BOOL mEnableLLWind; BOOL mEnableClouds; diff --git a/indra/newview/ascentprefsvan.cpp b/indra/newview/ascentprefsvan.cpp index 34273a963..869b4026a 100644 --- a/indra/newview/ascentprefsvan.cpp +++ b/indra/newview/ascentprefsvan.cpp @@ -59,22 +59,18 @@ LLPrefsAscentVan::LLPrefsAscentVan() childSetVisible("announce_stream_metadata", gAudiop && gAudiop->getStreamingAudioImpl() && gAudiop->getStreamingAudioImpl()->supportsMetaData()); - childSetCommitCallback("tag_spoofing_combobox", onCommitClientTag, this); + getChild("tag_spoofing_combobox")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitClientTag, this, _1)); - childSetCommitCallback("show_my_tag_check", onCommitCheckBox, this); - childSetCommitCallback("show_self_tag_check", onCommitCheckBox, this); - childSetCommitCallback("show_self_tag_color_check", onCommitCheckBox, this); - childSetCommitCallback("customize_own_tag_check", onCommitCheckBox, this); - childSetCommitCallback("show_friend_tag_check", onCommitCheckBox, this); - childSetCommitCallback("use_status_check", onCommitCheckBox, this); + getChild("show_my_tag_check")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitCheckBox, this, _1, _2)); + getChild("show_self_tag_check")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitCheckBox, this, _1, _2)); + getChild("show_self_tag_color_check")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitCheckBox, this, _1, _2)); + getChild("customize_own_tag_check")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitCheckBox, this, _1, _2)); + getChild("show_friend_tag_check")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitCheckBox, this, _1, _2)); + getChild("use_status_check")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitCheckBox, this, _1, _2)); - childSetCommitCallback("custom_tag_label_box", onCommitTextModified, this); + getChild("custom_tag_label_box")->setCommitCallback(boost::bind(&LLPrefsAscentVan::onCommitTextModified, this, _1, _2)); - childSetCommitCallback("X Modifier", onCommitUpdateAvatarOffsets); - childSetCommitCallback("Y Modifier", onCommitUpdateAvatarOffsets); - childSetCommitCallback("Z Modifier", onCommitUpdateAvatarOffsets); - - childSetAction("update_clientdefs", onManualClientUpdate, this); + getChild("update_clientdefs")->setCommitCallback(boost::bind(LLPrefsAscentVan::onManualClientUpdate)); refreshValues(); refresh(); @@ -84,57 +80,40 @@ LLPrefsAscentVan::~LLPrefsAscentVan() { } -//static -void LLPrefsAscentVan::onCommitClientTag(LLUICtrl* ctrl, void* userdata) +void LLPrefsAscentVan::onCommitClientTag(LLUICtrl* ctrl) { std::string client_uuid; U32 client_index; - LLPrefsAscentVan* self = (LLPrefsAscentVan*)userdata; - LLComboBox* combo = (LLComboBox*)ctrl; + LLComboBox* combo = static_cast(ctrl); - if (combo) - { - client_index = combo->getCurrentIndex(); - //Don't rebake if it's not neccesary. - if (client_index != self->mSelectedClient) - { - client_uuid = combo->getSelectedValue().asString(); - gSavedSettings.setString("AscentReportClientUUID", client_uuid); - gSavedSettings.setU32("AscentReportClientIndex", client_index); + client_index = combo->getCurrentIndex(); + //Don't rebake if it's not neccesary. + if (client_index != mSelectedClient) + { + client_uuid = combo->getSelectedValue().asString(); + gSavedSettings.setString("AscentReportClientUUID", client_uuid); + gSavedSettings.setU32("AscentReportClientIndex", client_index); - if (gAgentAvatarp) - { - // Slam pending upload count to "unstick" things - bool slam_for_debug = true; - gAgentAvatarp->forceBakeAllTextures(slam_for_debug); - } - } - } + if (gAgentAvatarp) + { + // Slam pending upload count to "unstick" things + bool slam_for_debug = true; + gAgentAvatarp->forceBakeAllTextures(slam_for_debug); + } + } } -//static -void LLPrefsAscentVan::onCommitUpdateAvatarOffsets(LLUICtrl* ctrl, void* userdata) +void LLPrefsAscentVan::onCommitTextModified(LLUICtrl* ctrl, const LLSD& value) { - //if (!gAgent.getID().isNull()) - //{ - // gAgent.sendAgentSetAppearance(); - //} -} - -//static -void LLPrefsAscentVan::onCommitTextModified(LLUICtrl* ctrl, void* userdata) -{ - LLPrefsAscentVan* self = (LLPrefsAscentVan*)userdata; - if (ctrl->getName() == "custom_tag_label_box") { - gSavedSettings.setString("AscentCustomTagLabel", self->childGetValue("custom_tag_label_box")); + gSavedSettings.setString("AscentCustomTagLabel", value); } } //static -void LLPrefsAscentVan::onManualClientUpdate(void* data) +void LLPrefsAscentVan::onManualClientUpdate() { LLChat chat("Definitions already up-to-date."); chat.mSourceType = CHAT_SOURCE_SYSTEM; @@ -148,33 +127,26 @@ void LLPrefsAscentVan::onManualClientUpdate(void* data) LLFloaterChat::addChat(chat); } -//static -void LLPrefsAscentVan::onCommitCheckBox(LLUICtrl* ctrl, void* user_data) +void LLPrefsAscentVan::onCommitCheckBox(LLUICtrl* ctrl, const LLSD& value) { - LLPrefsAscentVan* self = (LLPrefsAscentVan*)user_data; - // llinfos << "Control named " << ctrl->getControlName() << llendl; if (ctrl->getName() == "use_status_check") { - bool showCustomColors = gSavedSettings.getBOOL("AscentUseStatusColors"); - self->childSetEnabled("friends_color_textbox", showCustomColors); - bool frColors = gSavedSettings.getBOOL("ColorFriendChat"); - self->childSetEnabled("friend_color_swatch", showCustomColors || frColors); - bool eoColors = gSavedSettings.getBOOL("ColorEstateOwnerChat"); - self->childSetEnabled("estate_owner_color_swatch", showCustomColors || eoColors); - bool lindColors = gSavedSettings.getBOOL("ColorLindenChat"); - self->childSetEnabled("linden_color_swatch", showCustomColors || lindColors); - bool muteColors = gSavedSettings.getBOOL("ColorMutedChat"); - self->childSetEnabled("muted_color_swatch", showCustomColors || muteColors); + bool showCustomColors = value.asBoolean(); + childSetEnabled("friends_color_textbox", showCustomColors); + childSetEnabled("friend_color_swatch", showCustomColors || gSavedSettings.getBOOL("ColorFriendChat")); + childSetEnabled("estate_owner_color_swatch", showCustomColors || gSavedSettings.getBOOL("ColorEstateOwnerChat")); + childSetEnabled("linden_color_swatch", showCustomColors || gSavedSettings.getBOOL("ColorLindenChat")); + childSetEnabled("muted_color_swatch", showCustomColors || gSavedSettings.getBOOL("ColorMutedChat")); } else if (ctrl->getName() == "customize_own_tag_check") { - BOOL showCustomOptions = gSavedSettings.getBOOL("AscentUseCustomTag"); - self->childSetEnabled("custom_tag_label_text", showCustomOptions); - self->childSetEnabled("custom_tag_label_box", showCustomOptions); - self->childSetEnabled("custom_tag_color_text", showCustomOptions); - self->childSetEnabled("custom_tag_color_swatch", showCustomOptions); + bool showCustomOptions = value.asBoolean(); + childSetEnabled("custom_tag_label_text", showCustomOptions); + childSetEnabled("custom_tag_label_box", showCustomOptions); + childSetEnabled("custom_tag_color_text", showCustomOptions); + childSetEnabled("custom_tag_color_swatch", showCustomOptions); } } @@ -191,6 +163,9 @@ void LLPrefsAscentVan::refreshValues() mTurnAround = gSavedSettings.getBOOL("TurnAroundWhenWalkingBackwards"); mAnnounceSnapshots = gSavedSettings.getBOOL("AnnounceSnapshots"); mAnnounceStreamMetadata = gSavedSettings.getBOOL("AnnounceStreamMetadata"); + mUnfocusedFloatersOpaque = gSavedSettings.getBOOL("FloaterUnfocusedBackgroundOpaque"); + mCompleteNameProfiles = gSavedSettings.getBOOL("SinguCompleteNameProfiles"); + mScriptErrorsStealFocus = gSavedSettings.getBOOL("LiruScriptErrorsStealFocus"); //Tags\Colors ---------------------------------------------------------------------------- mAscentBroadcastTag = gSavedSettings.getBOOL("AscentBroadcastTag"); @@ -259,6 +234,9 @@ void LLPrefsAscentVan::cancel() gSavedSettings.setBOOL("TurnAroundWhenWalkingBackwards", mTurnAround); gSavedSettings.setBOOL("AnnounceSnapshots", mAnnounceSnapshots); gSavedSettings.setBOOL("AnnounceStreamMetadata", mAnnounceStreamMetadata); + gSavedSettings.setBOOL("FloaterUnfocusedBackgroundOpaque", mUnfocusedFloatersOpaque); + gSavedSettings.setBOOL("SinguCompleteNameProfiles", mCompleteNameProfiles); + gSavedSettings.setBOOL("LiruScriptErrorsStealFocus", mScriptErrorsStealFocus); //Tags\Colors ---------------------------------------------------------------------------- gSavedSettings.setBOOL("AscentBroadcastTag", mAscentBroadcastTag); diff --git a/indra/newview/ascentprefsvan.h b/indra/newview/ascentprefsvan.h index 25e789697..5ec08c605 100644 --- a/indra/newview/ascentprefsvan.h +++ b/indra/newview/ascentprefsvan.h @@ -47,11 +47,10 @@ public: void refreshValues(); protected: - static void onCommitClientTag(LLUICtrl* ctrl, void* userdata); - static void onCommitUpdateAvatarOffsets(LLUICtrl* ctrl, void* userdata); - static void onCommitCheckBox(LLUICtrl* ctrl, void* user_data); - static void onCommitTextModified(LLUICtrl* ctrl, void* userdata); - static void onManualClientUpdate(void* data); + void onCommitClientTag(LLUICtrl* ctrl); + void onCommitCheckBox(LLUICtrl* ctrl, const LLSD& value); + void onCommitTextModified(LLUICtrl* ctrl, const LLSD& value); + static void onManualClientUpdate(); //Main BOOL mUseAccountSettings; BOOL mShowTPScreen; @@ -62,6 +61,9 @@ protected: bool mTurnAround; bool mAnnounceSnapshots; bool mAnnounceStreamMetadata; + bool mUnfocusedFloatersOpaque; + bool mCompleteNameProfiles; + bool mScriptErrorsStealFocus; //Tags\Colors BOOL mAscentBroadcastTag; std::string mReportClientUUID; diff --git a/indra/newview/groupchatlistener.cpp b/indra/newview/groupchatlistener.cpp new file mode 100644 index 000000000..543032737 --- /dev/null +++ b/indra/newview/groupchatlistener.cpp @@ -0,0 +1,81 @@ +/** + * @file groupchatlistener.cpp + * @author Nat Goodspeed + * @date 2011-04-11 + * @brief Implementation for groupchatlistener. + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +// Precompiled header +#include "llviewerprecompiledheaders.h" +// associated header +#include "groupchatlistener.h" +// STL headers +// std headers +// external library headers +// other Linden headers +#include "llgroupactions.h" +//#include "llimview.h" + + +namespace { + void startIm_wrapper(LLSD const & event) + { + LLUUID session_id = LLGroupActions::startIM(event["id"].asUUID()); + sendReply(LLSDMap("session_id", LLSD(session_id)), event); + } + + /* Singu TODO + void send_message_wrapper(const std::string& text, const LLUUID& session_id, const LLUUID& group_id) + { + LLIMModel::sendMessage(text, session_id, group_id, IM_SESSION_GROUP_START); + } + */ +} + + +GroupChatListener::GroupChatListener(): + LLEventAPI("GroupChat", + "API to enter, leave, send and intercept group chat messages") +{ + add("startIM", + "Enter a group chat in group with UUID [\"id\"]\n" + "Assumes the logged-in agent is already a member of this group.", + &startIm_wrapper); + add("endIM", + "Leave a group chat in group with UUID [\"id\"]\n" + "Assumes a prior successful startIM request.", + &LLGroupActions::endIM, + LLSDArray("id")); + /* Singu TODO + add("sendIM", + "send a groupchat IM", + &send_message_wrapper, + LLSDArray("text")("session_id")("group_id")); + */ +} +/* + static void sendMessage(const std::string& utf8_text, const LLUUID& im_session_id, + const LLUUID& other_participant_id, EInstantMessage dialog); +*/ + diff --git a/indra/newview/groupchatlistener.h b/indra/newview/groupchatlistener.h new file mode 100644 index 000000000..42d6a6adc --- /dev/null +++ b/indra/newview/groupchatlistener.h @@ -0,0 +1,41 @@ +/** + * @file groupchatlistener.h + * @author Nat Goodspeed + * @date 2011-04-11 + * @brief + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#if ! defined(LL_GROUPCHATLISTENER_H) +#define LL_GROUPCHATLISTENER_H + +#include "lleventapi.h" + +class GroupChatListener: public LLEventAPI +{ +public: + GroupChatListener(); +}; + +#endif /* ! defined(LL_GROUPCHATLISTENER_H) */ + diff --git a/indra/newview/hbfloatergrouptitles.cpp b/indra/newview/hbfloatergrouptitles.cpp index 7ef401146..f75211730 100644 --- a/indra/newview/hbfloatergrouptitles.cpp +++ b/indra/newview/hbfloatergrouptitles.cpp @@ -40,6 +40,7 @@ #include "hbfloatergrouptitles.h" #include "llagent.h" +#include "llgroupactions.h" #include "lluictrlfactory.h" #include "llviewercontrol.h" @@ -145,13 +146,7 @@ void HBFloaterGroupTitles::onActivate(void* userdata) LLUUID group_id = item->getColumn(LIST_GROUP_ID)->getValue().asUUID(); if (group_id != old_group_id) { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ActivateGroup); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_GroupID, group_id); - gAgent.sendReliableMessage(); + LLGroupActions::activate(group_id); } // Set the title diff --git a/indra/newview/hippofloaterxml.cpp b/indra/newview/hippofloaterxml.cpp index 76aa9abcf..210094508 100644 --- a/indra/newview/hippofloaterxml.cpp +++ b/indra/newview/hippofloaterxml.cpp @@ -363,18 +363,24 @@ bool HippoFloaterXmlImpl::execute(LLFloater *floater, LLUICtrl *ctrl, if (HippoFloaterXmlImpl *floaterp = dynamic_cast(ctrl)) { floaterp->mIsNotifyOnClose = set; } else { - if (set) - ctrl->setCommitCallback(boost::bind(¬ifyCallback, _1, floater), ctrl); - else - ctrl->setCommitCallback(0); + HippoFloaterXmlImpl *thisFloater = static_cast(floater); + if (set) { + notice_ptr_t connptr(new notice_connection_t(ctrl->setCommitCallback(boost::bind(¬ifyCallback, _1, floater), ctrl))); + thisFloater->mNotices[ctrl] = connptr; + } else { + thisFloater->mNotices.erase(ctrl); + } } } else if (key == "picker") { bool set = (value != "0"); if (!dynamic_cast(ctrl)) { - if (set) - ctrl->setCommitCallback(boost::bind(&pickerCallback, _1, floater), ctrl); - else - ctrl->setCommitCallback(0); + HippoFloaterXmlImpl *thisFloater = static_cast(floater); + if (set) { + notice_ptr_t connptr(new notice_connection_t(ctrl->setCommitCallback(boost::bind(&pickerCallback, _1, floater), ctrl))); + thisFloater->mNotices[ctrl] = connptr; + } else { + thisFloater->mNotices.erase(ctrl); + } } } } diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 15765e321..25e984503 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -43,6 +43,7 @@ #include "llfirstuse.h" #include "llfloatercamera.h" #include "llfloatertools.h" +#include "llgroupactions.h" #include "llgroupmgr.h" #include "llhomelocationresponder.h" #include "llhudmanager.h" @@ -58,6 +59,7 @@ #include "llsky.h" #include "llslurl.h" #include "llsmoothstep.h" +#include "llspeakers.h" #include "llstartup.h" #include "llstatusbar.h" #include "lltool.h" @@ -78,6 +80,7 @@ #include "llviewerstats.h" #include "llviewerwindow.h" #include "llvoavatarself.h" +#include "llvoiceclient.h" #include "llworld.h" #include "llworldmap.h" #include "llworldmapmessage.h" @@ -86,11 +89,9 @@ #include "llurldispatcher.h" #include "llimview.h" //For gIMMgr //Floaters -#include "llfloateractivespeakers.h" #include "llfloateravatarinfo.h" #include "llfloaterchat.h" #include "llfloaterdirectory.h" -#include "llfloatergroupinfo.h" #include "llfloatergroups.h" #include "llfloaterland.h" #include "llfloatermap.h" @@ -262,6 +263,56 @@ void LLAgent::parcelChangedCallback() gAgent.mCanEditParcel = can_edit; } +// static +bool LLAgent::isActionAllowed(const LLSD& sdname) +{ + bool retval = false; + + const std::string& param = sdname.asString(); + + if (param == "speak") + { + if ( gAgent.isVoiceConnected() && + LLViewerParcelMgr::getInstance()->allowAgentVoice() && + ! LLVoiceClient::getInstance()->inTuningMode() ) + { + retval = true; + } + else + { + retval = false; + } + } + + return retval; +} + +// static +void LLAgent::pressMicrophone(const LLSD& name) +{ + //LLFirstUse::speak(false); + + LLVoiceClient::getInstance()->inputUserControlState(true); +} + +// static +void LLAgent::releaseMicrophone(const LLSD& name) +{ + LLVoiceClient::getInstance()->inputUserControlState(false); +} + +// static +void LLAgent::toggleMicrophone(const LLSD& name) +{ + LLVoiceClient::getInstance()->toggleUserPTTState(); +} + +// static +bool LLAgent::isMicrophoneOn(const LLSD& sdname) +{ + return LLVoiceClient::getInstance()->getUserPTTState(); +} + // ************************************************************ // Enabled this definition to compile a 'hacked' viewer that // locally believes the end user has godlike powers. @@ -3235,7 +3286,7 @@ BOOL LLAgent::downGrabbed() const void update_group_floaters(const LLUUID& group_id) { - LLFloaterGroupInfo::refreshGroup(group_id); + LLGroupActions::refresh(group_id); // update avatar info LLFloaterAvatarInfo* fa = LLFloaterAvatarInfo::getInstance(gAgent.getID()); @@ -3288,7 +3339,7 @@ void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **) LLGroupMgr::getInstance()->clearGroupData(group_id); // close the floater for this group, if any. - LLFloaterGroupInfo::closeGroup(group_id); + LLGroupActions::closeGroup(group_id); // refresh the group panel of the search window, if necessary. LLFloaterDirectory::refreshGroup(group_id); } @@ -3367,7 +3418,7 @@ class LLAgentDropGroupViewerNode : public LLHTTPNode LLGroupMgr::getInstance()->clearGroupData(group_id); // close the floater for this group, if any. - LLFloaterGroupInfo::closeGroup(group_id); + LLGroupActions::closeGroup(group_id); // refresh the group panel of the search window, //if necessary. LLFloaterDirectory::refreshGroup(group_id); @@ -3896,7 +3947,7 @@ bool LLAgent::teleportCore(bool is_local) // MBW -- Let the voice client know a teleport has begun so it can leave the existing channel. // This was breaking the case of teleporting within a single sim. Backing it out for now. -// gVoiceClient->leaveChannel(); +// LLVoiceClient::getInstance()->leaveChannel(); return true; } diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 1f5bf92f9..8a01093bf 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -312,6 +312,22 @@ public: static bool enableFlying(); BOOL canFly(); // Does this parcel allow you to fly? + //-------------------------------------------------------------------- + // Voice + //-------------------------------------------------------------------- +public: + bool isVoiceConnected() const { return mVoiceConnected; } + void setVoiceConnected(const bool b) { mVoiceConnected = b; } + + static void pressMicrophone(const LLSD& name); + static void releaseMicrophone(const LLSD& name); + static void toggleMicrophone(const LLSD& name); + static bool isMicrophoneOn(const LLSD& sdname); + static bool isActionAllowed(const LLSD& sdname); + +private: + bool mVoiceConnected; + //-------------------------------------------------------------------- // Chat //-------------------------------------------------------------------- diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 66d8b81ba..2d43a6e18 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -3664,6 +3664,7 @@ public: if (!LLApp::isRunning() || mFailed) return; + /* Singu Note: This wasn't working when we detached copyable attachments early, changeOutfit instead LLInventoryModel::item_array_t body_items, wear_items, obj_items, gest_items; for(std::set::const_iterator it = mWearItems.begin(); it != mWearItems.end(); ++it) { @@ -3692,6 +3693,8 @@ public: if(!body_items.empty() || !wear_items.empty() || !obj_items.empty() || !gest_items.empty()) LLAppearanceMgr::instance().updateCOF(body_items, wear_items, obj_items, gest_items, false); + */ + LLAppearanceMgr::instance().changeOutfit(true, mFolderID, false); } private: class LLCreateBase : public LLInventoryCallback @@ -3880,6 +3883,7 @@ LLUUID LLAppearanceMgr::makeNewOutfitLegacy(const std::string& new_folder_name, LLInventoryModel::item_array_t remove_items; LLPointer cb = new LLCreateLegacyOutfit(folder_id,boost::bind(&scroll_to_folder,folder_id),boost::bind(&show_created_outfit,folder_id,true)); + uuid_vec_t obj_ids; // Collect the uuids of copyable objects, in order to keep any changes made in the copies for (LLInventoryModel::item_array_t::const_iterator iter = items.begin(); iter != items.end(); @@ -3888,8 +3892,9 @@ LLUUID LLAppearanceMgr::makeNewOutfitLegacy(const std::string& new_folder_name, LLViewerInventoryItem* item = (*iter); LLViewerInventoryItem* base_item = item->getLinkedItem() ? item->getLinkedItem() : item; bool is_copy = base_item->getPermissions().allowCopyBy(gAgent.getID()); + bool is_obj = base_item->getInventoryType() == LLInventoryType::IT_OBJECT; //Just treat 'object' type as modifiable... permission slam screws them up pretty well. - bool is_mod = base_item->getInventoryType() == LLInventoryType::IT_OBJECT || base_item->getPermissions().allowModifyBy(gAgent.getID()); + bool is_mod = is_obj || base_item->getPermissions().allowModifyBy(gAgent.getID()); //If it's multi-worn we want to create a copy of the item if possible AND create a new link to that new copy with the same desc as the old link. bool is_multi = base_item->isWearableType() && gAgentWearables.getWearableCount(base_item->getWearableType()) > 1 ; @@ -3899,9 +3904,12 @@ LLUUID LLAppearanceMgr::makeNewOutfitLegacy(const std::string& new_folder_name, } else if( is_copy ) { + if (is_obj) obj_ids.push_back(base_item->getUUID()); // If it's a copyable object, store it for later cb->makeCopy(item,is_multi && use_links); } } + if (gSavedSettings.getBOOL("LiruLegacyOutfitStoreObjChanges")) // As a last resort, someone may create a legacy outfit to undo attachment changes + LLAppearanceMgr::instance().removeItemsFromAvatar(obj_ids); // The avatar will have to go without these for now cb->dispatch(); return folder_id; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 68e048e3a..29a339eca 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -79,7 +79,7 @@ #include "llfirstuse.h" #include "llrender.h" #include "llvector4a.h" -#include "llimpanel.h" // For LLVoiceClient and LLVoiceChannel +#include "llvoicechannel.h" #include "llvoavatarself.h" #include "llurlmatch.h" #include "llprogressview.h" @@ -266,12 +266,6 @@ LLTimer gLogoutTimer; static const F32 LOGOUT_REQUEST_TIME = 6.f; // this will be cut short by the LogoutReply msg. F32 gLogoutMaxTime = LOGOUT_REQUEST_TIME; -// -LLUUID gSystemFolderRoot; -LLUUID gSystemFolderSettings; -LLUUID gSystemFolderAssets; -// - BOOL gDisconnected = FALSE; // used to restore texture state after a mode switch @@ -650,7 +644,7 @@ bool LLAppViewer::init() AIHTTPTimeoutPolicy policy_tmp( "CurlTimeout* Debug Settings", gSavedSettings.getU32("CurlTimeoutDNSLookup"), - gSavedSettings.getU32("CurlTimeoutConnect"), + /*gSavedSettings.getU32("CurlTimeoutConnect") Temporary HACK: 30 is the current max*/ 30, gSavedSettings.getU32("CurlTimeoutReplyDelay"), gSavedSettings.getU32("CurlTimeoutLowSpeedTime"), gSavedSettings.getU32("CurlTimeoutLowSpeedLimit"), @@ -1102,7 +1096,7 @@ bool LLAppViewer::mainLoop() // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated. LLVoiceChannel::initClass(); - LLVoiceClient::init(gServicePump); + LLVoiceClient::getInstance()->init(gServicePump); LLTimer frameTimer,idleTimer,periodicRenderingTimer; LLTimer debugTime; @@ -1459,7 +1453,7 @@ bool LLAppViewer::cleanup() // to ensure shutdown order LLMortician::setZealous(TRUE); - LLVoiceClient::terminate(); + LLVoiceClient::getInstance()->terminate(); disconnectViewer(); @@ -4255,8 +4249,10 @@ void LLAppViewer::sendLogoutRequest() gLogoutMaxTime = LOGOUT_REQUEST_TIME; mLogoutRequestSent = TRUE; - if(gVoiceClient) - gVoiceClient->leaveChannel(); + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->leaveChannel(); + } //Set internal status variables and marker files gLogoutInProgress = TRUE; diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index b894ad17e..1f9b5025c 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -321,12 +321,6 @@ extern LLTimer gLogoutTimer; extern F32 gSimLastTime; extern F32 gSimFrames; -// -extern LLUUID gSystemFolderRoot; -extern LLUUID gSystemFolderSettings; -extern LLUUID gSystemFolderAssets; -// - extern BOOL gDisconnected; extern LLFrameTimer gRestoreGLTimer; diff --git a/indra/newview/llassetconverter.cpp b/indra/newview/llassetconverter.cpp deleted file mode 100644 index 19d636dc3..000000000 --- a/indra/newview/llassetconverter.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -#include "llviewerprecompiledheaders.h" -#include "llvfs.h" -#include "llapr.h" -#include "llvfile.h" -#include "llassetconverter.h" -#include "llviewertexturelist.h" -#include "llvorbisencode.h" -#include "llwearable.h" -#include "llbvhloader.h" -#include -struct conversion_s -{ - std::string extension; - LLAssetType::EType type; - boost::function conv_fn; -}conversion_list[] = -{ - { "bmp", LLAssetType::AT_TEXTURE, boost::bind(&LLViewerTextureList::createUploadFile,_1,_2,IMG_CODEC_BMP) }, - { "tga", LLAssetType::AT_TEXTURE, boost::bind(&LLViewerTextureList::createUploadFile,_1,_2,IMG_CODEC_TGA) }, - { "jpg", LLAssetType::AT_TEXTURE, boost::bind(&LLViewerTextureList::createUploadFile,_1,_2,IMG_CODEC_JPEG)}, - { "jpeg", LLAssetType::AT_TEXTURE, boost::bind(&LLViewerTextureList::createUploadFile,_1,_2,IMG_CODEC_JPEG)}, - { "png", LLAssetType::AT_TEXTURE, boost::bind(&LLViewerTextureList::createUploadFile,_1,_2,IMG_CODEC_PNG) }, - { "jp2", LLAssetType::AT_TEXTURE, &LLAssetConverter::copyFile }, - { "j2k", LLAssetType::AT_TEXTURE, &LLAssetConverter::copyFile }, - { "j2c", LLAssetType::AT_TEXTURE, &LLAssetConverter::copyFile }, - { "wav", LLAssetType::AT_SOUND, &encode_vorbis_file }, - { "ogg", LLAssetType::AT_SOUND, &LLAssetConverter::copyFile }, - { "bvh", LLAssetType::AT_ANIMATION, &LLAssetConverter::copyBVH }, - { "animatn", LLAssetType::AT_ANIMATION, &LLAssetConverter::copyFile }, - { "gesture", LLAssetType::AT_GESTURE, &LLAssetConverter::copyFile }, - { "notecard", LLAssetType::AT_NOTECARD, &LLAssetConverter::copyFile }, - { "lsl", LLAssetType::AT_LSL_TEXT, &LLAssetConverter::copyFile }, - //tmp ? -}; - -// static -extern std::string STATUS[]; -LLAssetType::EType LLAssetConverter::convert(const std::string &src_filename, const std::string &filename) -{ - std::string exten = gDirUtilp->getExtension(src_filename); - for(U32 i = 0;i < sizeof(conversion_list) / sizeof(conversion_list[0]); ++i) - { - if(conversion_list[i].extension == exten) - { - return conversion_list[i].conv_fn(src_filename, filename) ? - conversion_list[i].type : LLAssetType::AT_NONE; - } - } - LLWearableType::EType wear_type = LLWearableType::typeNameToType(exten); - if(wear_type != LLWearableType::WT_NONE && copyFile(src_filename, filename)) - { - return LLWearableType::getAssetType(wear_type); - } - - llwarns << "Unhandled extension" << llendl; - return LLAssetType::AT_NONE; -} -bool LLAssetConverter::copyFile(const std::string &src_filename, const std::string &dst_filename) -{ - S32 src_size; - - LLAPRFile src_fp(src_filename, LL_APR_RB, &src_size); - if(!src_fp.getFileHandle()) return false; - - LLAPRFile dst_fp(dst_filename, LL_APR_WB); - if(!dst_fp.getFileHandle()) return false; - - std::vector buffer(src_size + 1); - - src_fp.read(&buffer[0], src_size); - dst_fp.write(&buffer[0], src_size); - - return true; -} -bool LLAssetConverter::copyBVH(const std::string &src_filename, const std::string &dst_filename) -{ - S32 file_size; - - LLAPRFile fp(src_filename, LL_APR_RB, &file_size); - if(!fp.getFileHandle()) return false; - - std::vector buffer(file_size + 1); - - ELoadStatus load_status = E_ST_OK; - S32 line_number = 0; - - LLBVHLoader* loaderp = new LLBVHLoader(&buffer[0], load_status, line_number); - - if(load_status == E_ST_NO_XLT_FILE) - { - llwarns << "NOTE: No translation table found." << llendl; - } - else if(load_status != E_ST_OK) - { - llwarns << "ERROR: [line: " << line_number << "] " << STATUS[load_status].c_str() << llendl; - } - - buffer.resize(loaderp->getOutputSize()); - - LLDataPackerBinaryBuffer dp((U8*)&buffer[0], buffer.size()); - loaderp->serialize(dp); - - delete loaderp; - - LLAPRFile apr_file(dst_filename, LL_APR_RB, &file_size); - if(!apr_file.getFileHandle()) return false; - - apr_file.write(&buffer[0], buffer.size()); - return true; -} -// diff --git a/indra/newview/llassetconverter.h b/indra/newview/llassetconverter.h deleted file mode 100644 index 618468617..000000000 --- a/indra/newview/llassetconverter.h +++ /dev/null @@ -1,16 +0,0 @@ -// -#ifndef LL_LLASSETCONVERTER_H -#define LL_LLASSETCONVERTER_H - -#include "llcommon.h" -#include "llassettype.h" - -class LLAssetConverter -{ -public: - static LLAssetType::EType convert(const std::string &src_filename, const std::string &filename); - static bool copyFile(const std::string &src_filename, const std::string &dest_filename); - static bool copyBVH(const std::string &src_filename, const std::string &dst_filename); -}; -#endif -// diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp new file mode 100644 index 000000000..a242a029b --- /dev/null +++ b/indra/newview/llavataractions.cpp @@ -0,0 +1,760 @@ +/** + * @file llavataractions.cpp + * @brief Friend-related actions (add, remove, offer teleport, etc) + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" + +#include "llavataractions.h" + +#include "llavatarnamecache.h" // IDEVO +#include "llnotifications.h" +#include "llnotificationsutil.h" // for LLNotificationsUtil +#include "roles_constants.h" // for GP_MEMBER_INVITE + +#include "llagent.h" +#include "llcallingcard.h" // LLAvatarTracker +#include "llfloateravatarinfo.h" +#include "llfloatergroupinvite.h" +#include "llfloatergroups.h" +#include "llfloaterworldmap.h" +#include "llgivemoney.h" +#include "llimview.h" // for gIMMgr +#include "llinventoryobserver.h" +#include "llmutelist.h" +#include "lltrans.h" +#include "llvoiceclient.h" +#include "llweb.h" +#include "llslurl.h" // IDEVO +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h +#include "rlvhandler.h" +// [/RLVa:KB] + +extern const S32 TRANS_GIFT; +void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group = FALSE, S32 trx_type = TRANS_GIFT, const std::string& desc = LLStringUtil::null); +void handle_lure(const uuid_vec_t& ids); +void send_improved_im(const LLUUID& to_id, const std::string& name, const std::string& message, U8 offline, EInstantMessage dialog, const LLUUID& id, U32 timestamp = NO_TIMESTAMP, const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET, S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE); + +// static +void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name) +{ + if(id == gAgentID) + { + LLNotificationsUtil::add("AddSelfFriend"); + return; + } + + LLSD args; + args["NAME"] = name; + LLSD payload; + payload["id"] = id; + payload["name"] = name; + + LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage); +} + +void on_avatar_name_friendship(const LLUUID& id, const LLAvatarName av_name) +{ + LLAvatarActions::requestFriendshipDialog(id, av_name.getCompleteName()); +} + +// static +void LLAvatarActions::requestFriendshipDialog(const LLUUID& id) +{ + if(id.isNull()) + { + return; + } + + LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_friendship, _1, _2)); +} + +// static +void LLAvatarActions::removeFriendDialog(const LLUUID& id) +{ + if (id.isNull()) + return; + + uuid_vec_t ids; + ids.push_back(id); + removeFriendsDialog(ids); +} + +// static +void LLAvatarActions::removeFriendsDialog(const uuid_vec_t& ids) +{ + if(ids.size() == 0) + return; + + LLSD args; + std::string msgType; + if(ids.size() == 1) + { + LLUUID agent_id = ids[0]; + std::string av_name; + if(LLAvatarNameCache::getPNSName(agent_id, av_name)) + { + args["NAME"] = av_name; + } + + msgType = "RemoveFromFriends"; + } + else + { + msgType = "RemoveMultipleFromFriends"; + } + + LLSD payload; + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + payload["ids"].append(*it); + } + + LLNotificationsUtil::add(msgType, + args, + payload, + &handleRemove); +} + +// static +void LLAvatarActions::offerTeleport(const LLUUID& invitee) +{ + if (invitee.isNull()) + return; + + LLDynamicArray ids; + ids.push_back(invitee); + offerTeleport(ids); +} + +// static +void LLAvatarActions::offerTeleport(const uuid_vec_t& ids) +{ + if (ids.size() == 0) + return; + + handle_lure(ids); +} + +static void on_avatar_name_cache_start_im(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + static LLCachedControl tear_off("OtherChatsTornOff"); + if (!tear_off) gIMMgr->setFloaterOpen(true); + gIMMgr->addSession(LLCacheName::cleanFullName(av_name.getLegacyName()), IM_NOTHING_SPECIAL, agent_id); + make_ui_sound("UISndStartIM"); +} + +// static +void LLAvatarActions::startIM(const LLUUID& id) +{ + if (id.isNull() || gAgentID == id) + return; + +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(id)) ) + { + LLUUID idSession = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); + if ( (idSession.notNull()) && (!gIMMgr->hasSession(idSession)) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); + return; + } + } +// [/RLVa:KB] + + LLAvatarName av_name; + if (LLAvatarNameCache::get(id, &av_name)) // Bypass expiration, open NOW! + on_avatar_name_cache_start_im(id, av_name); + else + LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_cache_start_im, _1, _2)); +} + +// static +void LLAvatarActions::endIM(const LLUUID& id) +{ + if (id.isNull()) + return; + + LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); + if (session_id.notNull()) + { + gIMMgr->removeSession(session_id); + } +} + +static void on_avatar_name_cache_start_call(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + LLUUID session_id = gIMMgr->addSession(LLCacheName::cleanFullName(av_name.getLegacyName()), IM_NOTHING_SPECIAL, agent_id); + if (session_id.notNull()) + { + gIMMgr->startCall(session_id); + } + make_ui_sound("UISndStartIM"); +} + + +// static +void LLAvatarActions::startCall(const LLUUID& id) +{ + if (id.isNull()) + { + return; + } + +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(id)) ) + { + LLUUID idSession = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); + if ( (idSession.notNull()) && (!gIMMgr->hasSession(idSession)) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); + return; + } + } +// [/RLVa:KB] + + LLAvatarNameCache::get(id, + boost::bind(&on_avatar_name_cache_start_call, _1, _2)); +} + +// static +void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) +{ + if (ids.size() == 0) + { + return; + } + + // convert vector into LLDynamicArray for addSession + LLDynamicArray id_array; + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + const LLUUID& idAgent = *it; + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(idAgent)) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", LLSLURL("agent", idAgent, "completename").getSLURLString())); + return; + } + id_array.push_back(idAgent); +// [/RLVa:KB] +// id_array.push_back(*it); + } + + // create the new ad hoc voice session + const std::string title = LLTrans::getString("conference-title"); + LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, + ids[0], id_array); + if (session_id.isNull()) + { + return; + } + + gIMMgr->autoStartCallOnStartup(session_id); + + make_ui_sound("UISndStartIM"); +} + +/* AD *TODO: Is this function needed any more? + I fixed it a bit(added check for canCall), but it appears that it is not used + anywhere. Maybe it should be removed? +// static +bool LLAvatarActions::isCalling(const LLUUID &id) +{ + if (id.isNull() || !canCall()) + { + return false; + } + + LLUUID session_id = gIMMgr->computeSessionID(IM_NOTHING_SPECIAL, id); + return (LLIMModel::getInstance()->findIMSession(session_id) != NULL); +}*/ + +//static +bool LLAvatarActions::canCall() +{ + return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); +} + +// static +void LLAvatarActions::startConference(const uuid_vec_t& ids) +{ + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + const LLUUID& idAgent = *it; + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(idAgent)) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", LLSLURL("agent", idAgent, "completename").getSLURLString())); + return; + } +// [/RLVa:KB] + } + static LLCachedControl tear_off("OtherChatsTornOff"); + if (!tear_off) gIMMgr->setFloaterOpen(true); + const std::string title = LLTrans::getString("conference-title"); + gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, ids[0], ids); + make_ui_sound("UISndStartIM"); +} + +/* Singu TODO: Web Profiles +static const char* get_profile_floater_name(const LLUUID& avatar_id) +{ + // Use different floater XML for our profile to be able to save its rect. + return avatar_id == gAgentID ? "my_profile" : "profile"; +} +*/ + +static void on_avatar_name_show_profile(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + //if (!gSavedSettings.getBOOL("UseWebProfiles") + //{ + LLFloaterAvatarInfo* floater = LLFloaterAvatarInfo::getInstance(agent_id); + if(!floater) + { + floater = new LLFloaterAvatarInfo(av_name.getCompleteName()+" - "+LLTrans::getString("Command_Profile_Label"), agent_id); + floater->center(); + } + + // ...bring that window to front + floater->open(); /*Flawfinder: ignore*/ + //} + /* + else + { + std::string username = av_name.mUsername; + if (username.empty()) + { + username = LLCacheName::buildUsername(av_name.mDisplayName); + } + + llinfos << "opening web profile for " << username << llendl; + std::string url = getProfileURL(username); + + // PROFILES: open in webkit window + LLFloaterWebContent::Params p; + p.url(url). + id(agent_id.asString()); + LLFloaterReg::showInstance(get_profile_floater_name(agent_id), p); + } + */ +} + +// static +void LLAvatarActions::showProfile(const LLUUID& id) +{ + if (id.notNull()) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(id, &av_name)) // Bypass expiration, open NOW! + on_avatar_name_show_profile(id, av_name); + else + LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_show_profile, _1, _2)); + } +} + +//static +bool LLAvatarActions::profileVisible(const LLUUID& id) +{ + LLFloater* browser = getProfileFloater(id); + return browser && browser->getVisible(); +} + +//static +LLFloater* LLAvatarActions::getProfileFloater(const LLUUID& id) +{ + LLFloater* browser; + //if (!gSavedSettings.getBOOL("UseWebProfiles") + browser = LLFloaterAvatarInfo::getInstance(id); + /*else + browser = dynamic_cast + (LLFloaterReg::findInstance(get_profile_floater_name(id), LLSD().with("id", id))); + */ + return browser; +} + +//static +void LLAvatarActions::hideProfile(const LLUUID& id) +{ + LLFloater* browser = getProfileFloater(id); + if (browser) + { + browser->close(); + } +} + +// static +void LLAvatarActions::showOnMap(const LLUUID& id) +{ + std::string av_name; + if (!LLAvatarNameCache::getPNSName(id, av_name)) + { + LLAvatarNameCache::get(id, boost::bind(&LLAvatarActions::showOnMap, id)); + return; + } + + gFloaterWorldMap->trackAvatar(id, av_name); + LLFloaterWorldMap::show(true); +} + +// static +void LLAvatarActions::pay(const LLUUID& id) +{ + LLNotification::Params params("BusyModePay"); + params.functor(boost::bind(&LLAvatarActions::handlePay, _1, _2, id)); + + if (gAgent.getBusy()) + { + // warn users of being in busy mode during a transaction + LLNotifications::instance().add(params); + } + else + { + LLNotifications::instance().forceResponse(params, 1); + } +} + +// static +void LLAvatarActions::kick(const LLUUID& id) +{ + LLSD payload; + payload["avatar_id"] = id; + LLNotifications::instance().add("KickUser", LLSD(), payload, handleKick); +} + +// static +void LLAvatarActions::freeze(const LLUUID& id) +{ + LLSD payload; + payload["avatar_id"] = id; + LLNotifications::instance().add("FreezeUser", LLSD(), payload, handleFreeze); +} + +// static +void LLAvatarActions::unfreeze(const LLUUID& id) +{ + LLSD payload; + payload["avatar_id"] = id; + LLNotifications::instance().add("UnFreezeUser", LLSD(), payload, handleUnfreeze); +} + +//static +void LLAvatarActions::csr(const LLUUID& id) +{ + std::string name; + if (!gCacheName->getFullName(id, name)) return; + + std::string url = "http://csr.lindenlab.com/agent/"; + + // slow and stupid, but it's late + S32 len = name.length(); + for (S32 i = 0; i < len; i++) + { + if (name[i] == ' ') + { + url += "%20"; + } + else + { + url += name[i]; + } + } + + LLWeb::loadURL(url); +} + +// Singu TODO: Share inventory code block should live here + +// static +void LLAvatarActions::toggleBlock(const LLUUID& id) +{ + std::string name; + + gCacheName->getFullName(id, name); // needed for mute + LLMute mute(id, name, LLMute::AGENT); + + if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) + { + LLMuteList::getInstance()->remove(mute); + } + else + { + LLMuteList::getInstance()->add(mute); + } +} + +// static +void LLAvatarActions::toggleMuteVoice(const LLUUID& id) +{ + std::string name; + gCacheName->getFullName(id, name); // needed for mute + + LLMuteList* mute_list = LLMuteList::getInstance(); + bool is_muted = mute_list->isMuted(id, LLMute::flagVoiceChat); + + LLMute mute(id, name, LLMute::AGENT); + if (!is_muted) + { + mute_list->add(mute, LLMute::flagVoiceChat); + } + else + { + mute_list->remove(mute, LLMute::flagVoiceChat); + } +} + +// static +bool LLAvatarActions::canOfferTeleport(const LLUUID& id) +{ + // First use LLAvatarTracker::isBuddy() + // If LLAvatarTracker::instance().isBuddyOnline function only is used + // then for avatars that are online and not a friend it will return false. + // But we should give an ability to offer a teleport for such avatars. + if(LLAvatarTracker::instance().isBuddy(id)) + { + return LLAvatarTracker::instance().isBuddyOnline(id); + } + + return true; +} + +// static +bool LLAvatarActions::canOfferTeleport(const uuid_vec_t& ids) +{ + // We can't send more than 250 lures in a single message, so disable this + // button when there are too many id's selected. + if(ids.size() > 250) return false; + + bool result = true; + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + if(!canOfferTeleport(*it)) + { + result = false; + break; + } + } + return result; +} + +void LLAvatarActions::inviteToGroup(const LLUUID& id) +{ + LLFloaterGroupPicker* widget = LLFloaterGroupPicker::showInstance(LLSD(id)); + if (widget) + { + widget->center(); + widget->setPowersMask(GP_MEMBER_INVITE); + //widget->removeNoneOption(); + widget->setSelectCallback(callback_invite_to_group, (void*)&id); + } +} + +//== private methods ======================================================================================== + +// static +bool LLAvatarActions::handleRemove(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + const LLSD& ids = notification["payload"]["ids"]; + for (LLSD::array_const_iterator itr = ids.beginArray(); itr != ids.endArray(); ++itr) + { + LLUUID id = itr->asUUID(); + const LLRelationship* ip = LLAvatarTracker::instance().getBuddyInfo(id); + if (ip) + { + switch (option) + { + case 0: // YES + if( ip->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) + { + LLAvatarTracker::instance().empower(id, FALSE); + LLAvatarTracker::instance().notifyObservers(); + } + LLAvatarTracker::instance().terminateBuddy(id); + LLAvatarTracker::instance().notifyObservers(); + gInventory.addChangedMask(LLInventoryObserver::LABEL | LLInventoryObserver::CALLING_CARD, LLUUID::null); + gInventory.notifyObservers(); + break; + + case 1: // NO + default: + llinfos << "No removal performed." << llendl; + break; + } + } + } + return false; +} + +// static +bool LLAvatarActions::handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + gAgent.clearBusy(); + } + + LLFloaterPay::payDirectly(&give_money, avatar_id, /*is_group=*/false); + return false; +} + +// static +void LLAvatarActions::callback_invite_to_group(LLUUID group_id, void* id) +{ + uuid_vec_t agent_ids; + agent_ids.push_back(*static_cast(id)); + + LLFloaterGroupInvite::showForGroup(group_id, &agent_ids); +} + + +// static +bool LLAvatarActions::callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + requestFriendship(notification["payload"]["id"].asUUID(), + notification["payload"]["name"].asString(), + response["message"].asString()); + } + return false; +} + +// static +bool LLAvatarActions::handleKick(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + + if (option == 0) + { + LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_GodKickUser); + msg->nextBlockFast(_PREHASH_UserInfo); + msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_AgentID, avatar_id ); + msg->addU32("KickFlags", KICK_FLAGS_DEFAULT ); + msg->addStringFast(_PREHASH_Reason, response["message"].asString() ); + gAgent.sendReliableMessage(); + } + return false; +} +bool LLAvatarActions::handleFreeze(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + + if (option == 0) + { + LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_GodKickUser); + msg->nextBlockFast(_PREHASH_UserInfo); + msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_AgentID, avatar_id ); + msg->addU32("KickFlags", KICK_FLAGS_FREEZE ); + msg->addStringFast(_PREHASH_Reason, response["message"].asString() ); + gAgent.sendReliableMessage(); + } + return false; +} +bool LLAvatarActions::handleUnfreeze(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + std::string text = response["message"].asString(); + if (option == 0) + { + LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); + LLMessageSystem* msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_GodKickUser); + msg->nextBlockFast(_PREHASH_UserInfo); + msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() ); + msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_AgentID, avatar_id ); + msg->addU32("KickFlags", KICK_FLAGS_UNFREEZE ); + msg->addStringFast(_PREHASH_Reason, text ); + gAgent.sendReliableMessage(); + } + return false; +} + +// static +void LLAvatarActions::requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message) +{ + const LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + send_improved_im(target_id, + target_name, + message, + IM_ONLINE, + IM_FRIENDSHIP_OFFERED, + calling_card_folder_id); + + LLSD args; + args["TO_NAME"] = target_name; + + LLSD payload; + payload["from_id"] = target_id; + //payload["SUPPRESS_TOAST"] = true; + LLNotificationsUtil::add("FriendshipOffered", args, payload); +} + +//static +bool LLAvatarActions::isFriend(const LLUUID& id) +{ + return ( NULL != LLAvatarTracker::instance().getBuddyInfo(id) ); +} + +// static +bool LLAvatarActions::isBlocked(const LLUUID& id) +{ + return LLMuteList::getInstance()->isMuted(id); +} + +// static +bool LLAvatarActions::isVoiceMuted(const LLUUID& id) +{ + return LLMuteList::getInstance()->isMuted(id, LLMute::flagVoiceChat); +} + +// static +bool LLAvatarActions::canBlock(const LLUUID& id) +{ + bool is_linden = LLMuteList::getInstance()->isLinden(id); + bool is_self = id == gAgentID; + return !is_self && !is_linden; +} + diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h new file mode 100644 index 000000000..64511e60e --- /dev/null +++ b/indra/newview/llavataractions.h @@ -0,0 +1,197 @@ +/** + * @file llavataractions.h + * @brief Friend-related actions (add, remove, offer teleport, etc) + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLAVATARACTIONS_H +#define LL_LLAVATARACTIONS_H + +class LLFloater; + +/** + * Friend-related actions (add, remove, offer teleport, etc) + */ +class LLAvatarActions +{ +public: + /** + * Show a dialog explaining what friendship entails, then request friendship. + */ + static void requestFriendshipDialog(const LLUUID& id, const std::string& name); + + /** + * Show a dialog explaining what friendship entails, then request friendship. + */ + static void requestFriendshipDialog(const LLUUID& id); + + /** + * Show a friend removal dialog. + */ + static void removeFriendDialog(const LLUUID& id); + static void removeFriendsDialog(const uuid_vec_t& ids); + + /** + * Show teleport offer dialog. + */ + static void offerTeleport(const LLUUID& invitee); + static void offerTeleport(const uuid_vec_t& ids); + + /** + * Start instant messaging session. + */ + static void startIM(const LLUUID& id); + + /** + * End instant messaging session. + */ + static void endIM(const LLUUID& id); + + /** + * Start an avatar-to-avatar voice call with another user + */ + static void startCall(const LLUUID& id); + + /** + * Start an ad-hoc conference voice call with multiple users + */ + static void startAdhocCall(const uuid_vec_t& ids); + + /** + * Start conference chat with the given avatars. + */ + static void startConference(const uuid_vec_t& ids); + + /** + * Show avatar profile. + */ + static void showProfile(const LLUUID& id); + static void hideProfile(const LLUUID& id); + static bool profileVisible(const LLUUID& id); + static LLFloater* getProfileFloater(const LLUUID& id); + + /** + * Show avatar on world map. + */ + static void showOnMap(const LLUUID& id); + + /** + * Give money to the avatar. + */ + static void pay(const LLUUID& id); + /** + * Block/unblock the avatar. + */ + static void toggleBlock(const LLUUID& id); + + /** + * Block/unblock the avatar voice. + */ + static void toggleMuteVoice(const LLUUID& id); + + /** + * Return true if avatar with "id" is a friend + */ + static bool isFriend(const LLUUID& id); + + /** + * @return true if the avatar is blocked + */ + static bool isBlocked(const LLUUID& id); + + /** + * @return true if the avatar voice is blocked + */ + static bool isVoiceMuted(const LLUUID& id); + + /** + * @return true if you can block the avatar + */ + static bool canBlock(const LLUUID& id); + + /** + * Return true if the avatar is in a P2P voice call with a given user + */ + /* AD *TODO: Is this function needed any more? + I fixed it a bit(added check for canCall), but it appears that it is not used + anywhere. Maybe it should be removed? + static bool isCalling(const LLUUID &id);*/ + + /** + * @return true if call to the resident can be made + */ + + static bool canCall(); + /** + * Invite avatar to a group. + */ + static void inviteToGroup(const LLUUID& id); + + /** + * Kick avatar off grid + */ + static void kick(const LLUUID& id); + + /** + * Freeze avatar + */ + static void freeze(const LLUUID& id); + + /** + * Unfreeze avatar + */ + static void unfreeze(const LLUUID& id); + + /** + * Open csr page for avatar + */ + static void csr(const LLUUID& id); + + /** + * Checks whether we can offer a teleport to the avatar, only offline friends + * cannot be offered a teleport. + * + * @return false if avatar is a friend and not visibly online + */ + static bool canOfferTeleport(const LLUUID& id); + + /** + * @return false if any one of the specified avatars a friend and not visibly online + */ + static bool canOfferTeleport(const uuid_vec_t& ids); + +private: + static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response); + static bool handleRemove(const LLSD& notification, const LLSD& response); + static bool handlePay(const LLSD& notification, const LLSD& response, LLUUID avatar_id); + static bool handleKick(const LLSD& notification, const LLSD& response); + static bool handleFreeze(const LLSD& notification, const LLSD& response); + static bool handleUnfreeze(const LLSD& notification, const LLSD& response); + static void callback_invite_to_group(LLUUID group_id, void* id); + +public: + // Just request friendship, no dialog. + static void requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message); +}; + +#endif // LL_LLAVATARACTIONS_H diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index b5bc95088..61fcd5d7b 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -3,10 +3,9 @@ * @brief LLColorSwatch class implementation * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -277,11 +276,11 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op ) if (pick_op == COLOR_CANCEL && subject->mOnCancelCallback) { - subject->mOnCancelCallback(subject, subject->mCallbackUserData); + subject->mOnCancelCallback(subject, LLSD()); } else if (pick_op == COLOR_SELECT && subject->mOnSelectCallback) { - subject->mOnSelectCallback(subject, subject->mCallbackUserData); + subject->mOnSelectCallback(subject, LLSD()); } else { diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h index d406e6a9c..449b74db1 100644 --- a/indra/newview/llcolorswatch.h +++ b/indra/newview/llcolorswatch.h @@ -3,10 +3,9 @@ * @brief LLColorSwatch class definition * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -70,8 +69,8 @@ public: void setValid(BOOL valid); void setLabel(const std::string& label); void setCanApplyImmediately(BOOL apply) { mCanApplyImmediately = apply; } - void setOnCancelCallback(LLUICtrlCallback cb) { mOnCancelCallback = cb; } - void setOnSelectCallback(LLUICtrlCallback cb) { mOnSelectCallback = cb; } + void setOnCancelCallback(commit_callback_t cb) { mOnCancelCallback = cb; } + void setOnSelectCallback(commit_callback_t cb) { mOnSelectCallback = cb; } void setFallbackImageName(const std::string& name) { mFallbackImageName = name; } void showPicker(BOOL take_focus); @@ -96,8 +95,8 @@ protected: LLHandle mPickerHandle; LLViewBorder* mBorder; BOOL mCanApplyImmediately; - LLUICtrlCallback mOnCancelCallback; - LLUICtrlCallback mOnSelectCallback; + commit_callback_t mOnCancelCallback; + commit_callback_t mOnSelectCallback; LLPointer mAlphaGradientImage; std::string mFallbackImageName; diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp index 92dc5dffa..e1451a411 100644 --- a/indra/newview/lldebugview.cpp +++ b/indra/newview/lldebugview.cpp @@ -39,6 +39,7 @@ #include "llfasttimerview.h" #include "llconsole.h" #include "lltextureview.h" +#include "aihttpview.h" #include "llresmgr.h" #include "imageids.h" #include "llvelocitybar.h" @@ -93,6 +94,16 @@ LLDebugView::LLDebugView(const std::string& name, const LLRect &rect) addChild(gTextureView); //gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE); + r.set(150, rect.getHeight() - 50, 870, 100); + AIHTTPView::Params hvp; + hvp.name("gHttpView"); + hvp.rect(r); + hvp.visible(false); + gHttpView = LLUICtrlFactory::create(hvp); + //gHttpView->setFollowsBottom(); + //gHttpView->setFollowsLeft(); + addChild(gHttpView); + if(gAuditTexture) { r.set(150, rect.getHeight() - 50, 900 + LLImageGL::sTextureLoadedCounter.size() * 30, 100); @@ -129,6 +140,7 @@ LLDebugView::~LLDebugView() // These have already been deleted. Fix the globals appropriately. gDebugView = NULL; gTextureView = NULL; + gHttpView = NULL; gTextureSizeView = NULL; gTextureCategoryView = NULL; } diff --git a/indra/newview/lldroptarget.cpp b/indra/newview/lldroptarget.cpp index 610ce2068..c86e1f18c 100644 --- a/indra/newview/lldroptarget.cpp +++ b/indra/newview/lldroptarget.cpp @@ -9,31 +9,27 @@ * Altered to support a callback so it can return the item * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Pulled into its own file for more widespread use + * Rewritten by Liru Færs to act as its own ui element and use a control * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code * Copyright (c) 2004-2009, Linden Research, Inc. * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/newview/lldroptarget.h b/indra/newview/lldroptarget.h index 1a08484ce..e064c1b02 100644 --- a/indra/newview/lldroptarget.h +++ b/indra/newview/lldroptarget.h @@ -9,31 +9,27 @@ * Altered to support a callback so it can return the item * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Pulled into its own file for more widespread use + * Rewritten by Liru Færs to act as its own ui element and use a control * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * Second Life Viewer Source Code * Copyright (c) 2004-2009, Linden Research, Inc. * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/newview/llfloateractivespeakers.cpp b/indra/newview/llfloateractivespeakers.cpp index 839769e27..077951e3e 100644 --- a/indra/newview/llfloateractivespeakers.cpp +++ b/indra/newview/llfloateractivespeakers.cpp @@ -3,10 +3,9 @@ * @brief Management interface for muting and controlling volume of residents currently speaking * * $LicenseInfo:firstyear=2005&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2005-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -34,164 +33,28 @@ #include "llfloateractivespeakers.h" -#include "llagent.h" -#include "llappviewer.h" -#include "llimview.h" -#include "llsdutil.h" -#include "llfloateravatarinfo.h" +#include "llparticipantlist.h" +#include "llpanelvoiceeffect.h" +#include "llspeakers.h" #include "lluictrlfactory.h" -#include "llviewercontrol.h" -#include "llscrolllistctrl.h" -#include "llbutton.h" -#include "lltextbox.h" -#include "llmutelist.h" -#include "llviewerobjectlist.h" -#include "llvoavatar.h" -#include "llimpanel.h" // LLVoiceChannel -#include "llviewerwindow.h" -#include "llworld.h" -// [RLVa:KB] -#include "rlvhandler.h" -// [/RLVa:KB] - -#include "llavatarname.h" - -class AIHTTPTimeoutPolicy; -extern AIHTTPTimeoutPolicy muteVoiceResponder_timeout; -extern AIHTTPTimeoutPolicy muteTextResponder_timeout; -extern AIHTTPTimeoutPolicy moderationModeResponder_timeout; - -using namespace LLOldEvents; - -const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers -const F32 RESORT_TIMEOUT = 5.f; // seconds of mouse inactivity before it's ok to sort regardless of mouse-in-view. -const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); -const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); -const F32 TYPING_ANIMATION_FPS = 2.5f; - -LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) : - mStatus(LLSpeaker::STATUS_TEXT_ONLY), - mLastSpokeTime(0.f), - mSpeechVolume(0.f), - mHasSpoken(FALSE), - mDotColor(LLColor4::white), - mID(id), - mTyping(FALSE), - mSortIndex(0), - mType(type), - mIsModerator(FALSE), - mModeratorMutedVoice(FALSE), - mModeratorMutedText(FALSE), - mNameRequested(false) +namespace { - // Make sure we also get the display name if SLIM or some other external - // voice client is used and not whatever is provided. - if ((name.empty() && type == SPEAKER_AGENT) || type == SPEAKER_EXTERNAL) + void* createEffectPanel(void*) { - lookupName(); - } - else - { - mDisplayName = name; - mLegacyName = name; - } - gVoiceClient->setUserVolume(id, LLMuteList::getInstance()->getSavedResidentVolume(id)); - mActivityTimer.reset(SPEAKER_TIMEOUT); -} - - -void LLSpeaker::lookupName() -{ - if(!mNameRequested) - { - mNameRequested = true; - LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _2)); + return new LLPanelVoiceEffect; } } -void LLSpeaker::onNameCache(const LLAvatarName& avatar_name) -{ - LLAvatarNameCache::getPNSName(avatar_name, mDisplayName); -// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g - // TODO-RLVa: this seems to get called per frame which is very likely an LL bug that will eventually get fixed - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - mDisplayName = RlvStrings::getAnonym(mDisplayName); -// [/RLVa:KB] - - // Also set the legacy name. We will need it to initiate a new - // IM session. - mLegacyName = LLCacheName::cleanFullName(avatar_name.getLegacyName()); -} - -LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source) -: LLEvent(source, "Speaker text moderation event") -{ -} - -LLSD LLSpeakerTextModerationEvent::getValue() -{ - return std::string("text"); -} - - -LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source) -: LLEvent(source, "Speaker voice moderation event") -{ -} - -LLSD LLSpeakerVoiceModerationEvent::getValue() -{ - return std::string("voice"); -} - -LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id) -: LLEvent(source, "Speaker added/removed from speaker mgr"), - mSpeakerID(speaker_id) -{ -} - -LLSD LLSpeakerListChangeEvent::getValue() -{ - return mSpeakerID; -} - -// helper sort class -struct LLSortRecentSpeakers -{ - bool operator()(const LLPointer lhs, const LLPointer rhs) const; -}; - -bool LLSortRecentSpeakers::operator()(const LLPointer lhs, const LLPointer rhs) const -{ - // Sort first on status - if (lhs->mStatus != rhs->mStatus) - { - return (lhs->mStatus < rhs->mStatus); - } - - // and then on last speaking time - if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) - { - return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); - } - - // and finally (only if those are both equal), on name. - return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); -} - -// -// LLFloaterActiveSpeakers -// - LLFloaterActiveSpeakers::LLFloaterActiveSpeakers(const LLSD& seed) : mPanel(NULL) { mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL); + mFactoryMap["panel_voice_effect"] = LLCallbackMap(createEffectPanel, NULL); // do not automatically open singleton floaters (as result of getInstance()) BOOL no_open = FALSE; LLUICtrlFactory::getInstance()->buildFloater(this, "floater_active_speakers.xml", &getFactoryMap(), no_open); //RN: for now, we poll voice client every frame to get voice amplitude feedback - //gVoiceClient->addObserver(this); + //LLVoiceClient::getInstance()->addObserver(this); mPanel->refreshSpeakers(); } @@ -222,11 +85,11 @@ void LLFloaterActiveSpeakers::draw() BOOL LLFloaterActiveSpeakers::postBuild() { - mPanel = getChild("active_speakers_panel"); + mPanel = getChild("active_speakers_panel"); return TRUE; } -void LLFloaterActiveSpeakers::onChange() +void LLFloaterActiveSpeakers::onParticipantsChanged() { //refresh(); } @@ -234,1247 +97,6 @@ void LLFloaterActiveSpeakers::onChange() //static void* LLFloaterActiveSpeakers::createSpeakersPanel(void* data) { - // don't show text only speakers - return new LLPanelActiveSpeakers(LLActiveSpeakerMgr::getInstance(), FALSE); + return new LLParticipantList(LLActiveSpeakerMgr::getInstance(), /*show_text_chatters=*/ false); } -// -// LLPanelActiveSpeakers::SpeakerMuteListener -// -bool LLPanelActiveSpeakers::SpeakerMuteListener::handleEvent(LLPointer event, const LLSD& userdata) -{ - LLPointer speakerp = (LLSpeaker*)event->getSource(); - if (speakerp.isNull()) return false; - - // update UI on confirmation of moderator mutes - if (event->getValue().asString() == "voice") - { - mPanel->childSetValue("moderator_allow_voice", !speakerp->mModeratorMutedVoice); - } - if (event->getValue().asString() == "text") - { - mPanel->childSetValue("moderator_allow_text", !speakerp->mModeratorMutedText); - } - return true; -} - - -// -// LLPanelActiveSpeakers::SpeakerAddListener -// -bool LLPanelActiveSpeakers::SpeakerAddListener::handleEvent(LLPointer event, const LLSD& userdata) -{ - mPanel->addSpeaker(event->getValue().asUUID()); - return true; -} - - -// -// LLPanelActiveSpeakers::SpeakerRemoveListener -// -bool LLPanelActiveSpeakers::SpeakerRemoveListener::handleEvent(LLPointer event, const LLSD& userdata) -{ - mPanel->removeSpeaker(event->getValue().asUUID()); - return true; -} - -// -// LLPanelActiveSpeakers::SpeakerClearListener -// -bool LLPanelActiveSpeakers::SpeakerClearListener::handleEvent(LLPointer event, const LLSD& userdata) -{ - mPanel->mSpeakerList->clearRows(); - return true; -} - - -// -// LLPanelActiveSpeakers -// -LLPanelActiveSpeakers::LLPanelActiveSpeakers(LLSpeakerMgr* data_source, BOOL show_text_chatters) : - mSpeakerList(NULL), - mMuteVoiceCtrl(NULL), - mMuteTextCtrl(NULL), - mNameText(NULL), - mProfileBtn(NULL), - mShowTextChatters(show_text_chatters), - mSpeakerMgr(data_source) -{ - setMouseOpaque(FALSE); - mSpeakerMuteListener = new SpeakerMuteListener(this); - mSpeakerAddListener = new SpeakerAddListener(this); - mSpeakerRemoveListener = new SpeakerRemoveListener(this); - mSpeakerClearListener = new SpeakerClearListener(this); - - mSpeakerMgr->addListener(mSpeakerAddListener, "add"); - mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); - mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); -} - -BOOL LLPanelActiveSpeakers::postBuild() -{ - std::string sort_column = gSavedSettings.getString(std::string("FloaterActiveSpeakersSortColumn")); - BOOL sort_ascending = gSavedSettings.getBOOL( std::string("FloaterActiveSpeakersSortAscending")); - - mSpeakerList = getChild("speakers_list"); - mSpeakerList->sortByColumn(sort_column, sort_ascending); - mSpeakerList->setDoubleClickCallback(boost::bind(&onDoubleClickSpeaker,this)); - mSpeakerList->setCommitOnSelectionChange(TRUE); - mSpeakerList->setCommitCallback(boost::bind(&LLPanelActiveSpeakers::handleSpeakerSelect,this)); - mSpeakerList->setSortChangedCallback(boost::bind(&LLPanelActiveSpeakers::onSortChanged,this)); - mSpeakerList->setCallbackUserData(this); - - if ((mMuteTextCtrl = findChild("mute_text_btn"))) - childSetCommitCallback("mute_text_btn", onClickMuteTextCommit, this); - - mMuteVoiceCtrl = getChild("mute_btn"); - childSetCommitCallback("mute_btn", onClickMuteVoiceCommit, this); - - childSetCommitCallback("speaker_volume", onVolumeChange, this); - - mNameText = findChild("resident_name"); - - if ((mProfileBtn = findChild("profile_btn"))) - childSetAction("profile_btn", onClickProfile, this); - - if (findChild("moderator_allow_voice")) - childSetCommitCallback("moderator_allow_voice", onModeratorMuteVoice, this); - if (findChild("moderator_allow_text")) - childSetCommitCallback("moderator_allow_text", onModeratorMuteText, this); - if (findChild("moderator_mode")) - childSetCommitCallback("moderation_mode", onChangeModerationMode, this); - - mVolumeSlider.connect(this,"speaker_volume"); - - // update speaker UI - handleSpeakerSelect(); - - return TRUE; -} - -void LLPanelActiveSpeakers::addSpeaker(const LLUUID& speaker_id) -{ - if (mSpeakerList->getItemIndex(speaker_id) >= 0) - { - // already have this speaker - return; - } - - LLPointer speakerp = mSpeakerMgr->findSpeaker(speaker_id); - if (speakerp) - { - // since we are forced to sort by text, encode sort order as string - std::string speaking_order_sort_string = llformat("%010d", speakerp->mSortIndex); - - LLSD row; - row["id"] = speaker_id; - - LLSD& columns = row["columns"]; - - columns[0]["column"] = "icon_speaking_status"; - columns[0]["type"] = "icon"; - columns[0]["value"] = "icn_active-speakers-dot-lvl0.tga"; - - std::string speaker_name; - if (speakerp->mDisplayName.empty()) - { - speaker_name = LLCacheName::getDefaultName(); - } - else - { - speaker_name = speakerp->mDisplayName; - } - columns[1]["column"] = "speaker_name"; - columns[1]["type"] = "text"; - columns[1]["value"] = speaker_name; - - columns[2]["column"] = "speaking_status"; - columns[2]["type"] = "text"; - - // print speaking ordinal in a text-sorting friendly manner - columns[2]["value"] = speaking_order_sort_string; - - mSpeakerList->addElement(row); - } -} - -void LLPanelActiveSpeakers::removeSpeaker(const LLUUID& speaker_id) -{ - mSpeakerList->deleteSingleItem(mSpeakerList->getItemIndex(speaker_id)); -} - -void LLPanelActiveSpeakers::handleSpeakerSelect() -{ - LLUUID speaker_id = mSpeakerList->getValue().asUUID(); - LLPointer selected_speakerp = mSpeakerMgr->findSpeaker(speaker_id); - - if (selected_speakerp.notNull()) - { - // since setting these values is delayed by a round trip to the Vivox servers - // update them only when selecting a new speaker or - // asynchronously when an update arrives - childSetValue("moderator_allow_voice", selected_speakerp ? !selected_speakerp->mModeratorMutedVoice : TRUE); - childSetValue("moderator_allow_text", selected_speakerp ? !selected_speakerp->mModeratorMutedText : TRUE); - - mSpeakerMuteListener->clearDispatchers(); - selected_speakerp->addListener(mSpeakerMuteListener); - } -} - -void LLPanelActiveSpeakers::refreshSpeakers() -{ - // store off current selection and scroll state to preserve across list rebuilds - LLUUID selected_id = mSpeakerList->getSelectedValue().asUUID(); - S32 scroll_pos = mSpeakerList->getScrollInterface()->getScrollPos(); - - // decide whether it's ok to resort the list then update the speaker manager appropriately. - // rapid resorting by activity makes it hard to interact with speakers in the list - // so we freeze the sorting while the user appears to be interacting with the control. - // we assume this is the case whenever the mouse pointer is within the active speaker - // panel and hasn't been motionless for more than a few seconds. see DEV-6655 -MG - LLRect screen_rect; - localRectToScreen(getLocalRect(), &screen_rect); - BOOL mouse_in_view = screen_rect.pointInRect(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY()); - F32 mouses_last_movement = gMouseIdleTimer.getElapsedTimeF32(); - BOOL sort_ok = ! (mouse_in_view && mouses_last_movementupdate(sort_ok); - - const std::string icon_image_0 = "icn_active-speakers-dot-lvl0.tga"; - const std::string icon_image_1 = "icn_active-speakers-dot-lvl1.tga"; - const std::string icon_image_2 = "icn_active-speakers-dot-lvl2.tga"; - - std::vector items = mSpeakerList->getAllData(); - - std::string mute_icon_image = "mute_icon.tga"; - - LLSpeakerMgr::speaker_list_t speaker_list; - mSpeakerMgr->getSpeakerList(&speaker_list, mShowTextChatters); - for (std::vector::iterator item_it = items.begin(); - item_it != items.end(); - ++item_it) - { - LLScrollListItem* itemp = (*item_it); - LLUUID speaker_id = itemp->getUUID(); - - LLPointer speakerp = mSpeakerMgr->findSpeaker(speaker_id); - if (!speakerp) - { - continue; - } - - // since we are forced to sort by text, encode sort order as string - std::string speaking_order_sort_string = llformat("%010d", speakerp->mSortIndex); - - LLScrollListCell* icon_cell = itemp->getColumn(0); - if (icon_cell) - { - - std::string icon_image_id; - - S32 icon_image_idx = llmin(2, llfloor((speakerp->mSpeechVolume / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); - switch(icon_image_idx) - { - case 0: - icon_image_id = icon_image_0; - break; - case 1: - icon_image_id = icon_image_1; - break; - case 2: - icon_image_id = icon_image_2; - break; - } - - LLColor4 icon_color; - - if (speakerp->mStatus == LLSpeaker::STATUS_MUTED) - { - icon_cell->setValue(mute_icon_image); - if(speakerp->mModeratorMutedVoice) - { - icon_color.setVec(0.5f, 0.5f, 0.5f, 1.f); - } - else - { - icon_color.setVec(1.f, 71.f / 255.f, 71.f / 255.f, 1.f); - } - } - else - { - icon_cell->setValue(icon_image_id); - icon_color = speakerp->mDotColor; - - if (speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE) // if voice is disabled for this speaker - { - // non voice speakers have hidden icons, render as transparent - icon_color.setVec(0.f, 0.f, 0.f, 0.f); - } - } - - icon_cell->setColor(icon_color); - - if (speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE && speakerp->mStatus != LLSpeaker::STATUS_MUTED) // if voice is disabled for this speaker - { - // non voice speakers have hidden icons, render as transparent - icon_cell->setColor(LLColor4::transparent); - } - } - - // update name column - LLScrollListCell* name_cell = itemp->getColumn(1); - if (name_cell) - { - if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL) - { - // draw inactive speakers in different color - static LLCachedControl sSpeakersInactive(gColors, "SpeakersInactive"); - - name_cell->setColor(sSpeakersInactive); - } - else - { - static LLCachedControl sDefaultListText(gColors, "DefaultListText"); - - name_cell->setColor(sDefaultListText); - } - // - if(!mShowTextChatters && !(speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL) && speakerp->mID != gAgent.getID()) - { - bool found = false; - for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); - iter != LLWorld::getInstance()->getRegionList().end(); ++iter) - { - LLViewerRegion* regionp = *iter; - // let us check to see if they are actually in the sim - if(regionp) - { - if(regionp->mMapAvatarIDs.find(speakerp->mID) != -1) - { - found = true; - break; - } - } - } - if(!found) - { - static LLCachedControl sSpeakersGhost(gColors, "SpeakersGhost"); - - name_cell->setColor(sSpeakersGhost); - } - } - // - - std::string speaker_name; - if (speakerp->mDisplayName.empty()) - { - speaker_name = LLCacheName::getDefaultName(); - } - else - { - speaker_name = speakerp->mDisplayName; - } - - if (speakerp->mIsModerator) - { - speaker_name += std::string(" ") + getString("moderator_label"); - } - - name_cell->setValue(speaker_name); - ((LLScrollListText*)name_cell)->setFontStyle(speakerp->mIsModerator ? LLFontGL::BOLD : LLFontGL::NORMAL); - } - - // update speaking order column - LLScrollListCell* speaking_status_cell = itemp->getColumn(2); - if (speaking_status_cell) - { - // print speaking ordinal in a text-sorting friendly manner - speaking_status_cell->setValue(speaking_order_sort_string); - } - } - - // we potentially modified the sort order by touching the list items - mSpeakerList->setNeedsSort(); - - LLPointer selected_speakerp = mSpeakerMgr->findSpeaker(selected_id); - // update UI for selected participant - if (mMuteVoiceCtrl) - { - mMuteVoiceCtrl->setValue(LLMuteList::getInstance()->isMuted(selected_id, LLMute::flagVoiceChat)); - mMuteVoiceCtrl->setEnabled(LLVoiceClient::voiceEnabled() - && gVoiceClient->getVoiceEnabled(selected_id) - && selected_id.notNull() - && selected_id != gAgent.getID() - && (selected_speakerp.notNull() && (selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT || selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL))); - - } - if (mMuteTextCtrl) - { - mMuteTextCtrl->setValue(LLMuteList::getInstance()->isMuted(selected_id, LLMute::flagTextChat)); - mMuteTextCtrl->setEnabled(selected_id.notNull() - && selected_id != gAgent.getID() - && selected_speakerp.notNull() - && selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL - // Ansariel: No, we don't want to mute Lindens with display names - //&& !LLMuteList::getInstance()->isLinden(selected_speakerp->mDisplayName)); - && !selected_speakerp->mLegacyName.empty() - && !LLMuteList::getInstance()->isLinden(selected_speakerp->mLegacyName)); - } - mVolumeSlider->setValue(gVoiceClient->getUserVolume(selected_id)); - mVolumeSlider->setEnabled(LLVoiceClient::voiceEnabled() - && gVoiceClient->getVoiceEnabled(selected_id) - && selected_id.notNull() - && selected_id != gAgent.getID() - && (selected_speakerp.notNull() && (selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT || selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL))); - - if (LLView* view = findChild("moderator_controls_label")) - view->setEnabled(selected_id.notNull()); - - if (LLView* view = findChild("moderator_allow_voice")) - view->setEnabled(selected_id.notNull() && mSpeakerMgr->isVoiceActive() && gVoiceClient->getVoiceEnabled(selected_id)); - - if (LLView* view = findChild("moderator_allow_text")) - view->setEnabled(selected_id.notNull()); - - if (mProfileBtn) - { - mProfileBtn->setEnabled(selected_id.notNull() && (selected_speakerp.notNull() && selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL) ); - } - - // show selected user name in large font - if (mNameText) - { - if (selected_speakerp) - { - mNameText->setValue(selected_speakerp->mDisplayName); - } - else - { - mNameText->setValue(LLStringUtil::null); - } - } - - //update moderator capabilities - LLPointer self_speakerp = mSpeakerMgr->findSpeaker(gAgent.getID()); - if(self_speakerp) - { - if (LLView* view = findChild("moderation_mode_panel")) - view->setVisible(self_speakerp->mIsModerator && mSpeakerMgr->isVoiceActive()); - if (LLView* view = findChild("moderator_controls")) - view->setVisible(self_speakerp->mIsModerator); - } - - // keep scroll value stable - mSpeakerList->getScrollInterface()->setScrollPos(scroll_pos); -} - -void LLPanelActiveSpeakers::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) -{ - mSpeakerMgr->setSpeaker(id, name, status, type); -} - -void LLPanelActiveSpeakers::setVoiceModerationCtrlMode( - const BOOL& moderated_voice) -{ - LLUICtrl* voice_moderation_ctrl = getChild("moderation_mode"); - - if ( voice_moderation_ctrl ) - { - std::string value; - - value = moderated_voice ? "moderated" : "unmoderated"; - voice_moderation_ctrl->setValue(value); - } -} - -//static -void LLPanelActiveSpeakers::onClickMuteTextCommit(LLUICtrl* ctrl, void* user_data) -{ - LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; - LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); - BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, LLMute::flagTextChat); - std::string name; - - //fill in name using voice client's copy of name cache - LLPointer speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id); - if (speakerp.isNull()) - { - return; - } - - name = speakerp->mDisplayName; - - LLMute mute(speaker_id, name, speakerp->mType == LLSpeaker::SPEAKER_AGENT ? LLMute::AGENT : LLMute::OBJECT); - - if (!is_muted) - { - LLMuteList::getInstance()->add(mute, LLMute::flagTextChat); - } - else - { - LLMuteList::getInstance()->remove(mute, LLMute::flagTextChat); - } -} - - -//static -void LLPanelActiveSpeakers::onClickMuteVoiceCommit(LLUICtrl* ctrl, void* user_data) -{ - LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; - LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); - BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, LLMute::flagVoiceChat); - std::string name; - - LLPointer speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id); - if (speakerp.isNull()) - { - return; - } - - name = speakerp->mDisplayName; - - // muting voice means we're dealing with an agent - LLMute mute(speaker_id, name, LLMute::AGENT); - - if (!is_muted) - { - LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); - } - else - { - LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); - } -} - - -//static -void LLPanelActiveSpeakers::onVolumeChange(LLUICtrl* source, void* user_data) -{ - LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; - LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); - - F32 new_volume = (F32)panelp->childGetValue("speaker_volume").asReal(); - gVoiceClient->setUserVolume(speaker_id, new_volume); - - // store this volume setting for future sessions - LLMuteList::getInstance()->setSavedResidentVolume(speaker_id, new_volume); -} - -//static -void LLPanelActiveSpeakers::onClickProfile(void* user_data) -{ -// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - { - return; - } -// [/RLVa:KB] - - LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; - LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); - - LLFloaterAvatarInfo::showFromDirectory(speaker_id); -} - -//static -void LLPanelActiveSpeakers::onDoubleClickSpeaker(void* user_data) -{ -// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - { - return; - } -// [/RLVa:KB] - - LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; - LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); - - LLPointer speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id); - - if (speaker_id != gAgent.getID() && speakerp.notNull() && !speakerp->mLegacyName.empty()) - { - // Changed for display name support - //gIMMgr->addSession(speakerp->mDisplayName, IM_NOTHING_SPECIAL, speaker_id); - gIMMgr->addSession(speakerp->mLegacyName, IM_NOTHING_SPECIAL, speaker_id); - } -} - -//static -void LLPanelActiveSpeakers::onSortChanged(void* user_data) -{ - LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; - std::string sort_column = panelp->mSpeakerList->getSortColumnName(); - BOOL sort_ascending = panelp->mSpeakerList->getSortAscending(); - gSavedSettings.setString(std::string("FloaterActiveSpeakersSortColumn"), sort_column); - gSavedSettings.setBOOL( std::string("FloaterActiveSpeakersSortAscending"), sort_ascending); -} - - -//static -void LLPanelActiveSpeakers::onModeratorMuteVoice(LLUICtrl* ctrl, void* user_data) -{ - LLPanelActiveSpeakers* self = (LLPanelActiveSpeakers*)user_data; - LLUICtrl* speakers_list = self->getChild("speakers_list"); - if (!speakers_list || !gAgent.getRegion()) return; - - std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = self->mSpeakerMgr->getSessionID(); - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = speakers_list->getValue(); - data["params"]["mute_info"] = LLSD::emptyMap(); - // ctrl value represents ability to type, so invert - data["params"]["mute_info"]["voice"] = !ctrl->getValue(); - - class MuteVoiceResponder : public LLHTTPClient::ResponderIgnoreBody - { - public: - MuteVoiceResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - /*virtual*/ void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - - if ( gIMMgr ) - { - LLFloaterIMPanel* floaterp; - - floaterp = gIMMgr->findFloaterBySession(mSessionID); - - if ( floaterp ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( 403 == status ) - { - floaterp->showSessionEventError( - "mute", - "not_a_mod_error"); - } - else - { - floaterp->showSessionEventError( - "mute", - "generic_request_error"); - } - } - } - } - - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return muteVoiceResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "MuteVoiceResponder"; } - - private: - LLUUID mSessionID; - }; - - LLHTTPClient::post( - url, - data, - new MuteVoiceResponder(self->mSpeakerMgr->getSessionID())); -} - -//static -void LLPanelActiveSpeakers::onModeratorMuteText(LLUICtrl* ctrl, void* user_data) -{ - LLPanelActiveSpeakers* self = (LLPanelActiveSpeakers*)user_data; - LLUICtrl* speakers_list = self->getChild("speakers_list"); - if (!speakers_list || !gAgent.getRegion()) return; - - std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "mute update"; - data["session-id"] = self->mSpeakerMgr->getSessionID(); - data["params"] = LLSD::emptyMap(); - data["params"]["agent_id"] = speakers_list->getValue(); - data["params"]["mute_info"] = LLSD::emptyMap(); - // ctrl value represents ability to type, so invert - data["params"]["mute_info"]["text"] = !ctrl->getValue(); - - class MuteTextResponder : public LLHTTPClient::ResponderIgnoreBody - { - public: - MuteTextResponder(const LLUUID& session_id) - { - mSessionID = session_id; - } - - /*virtual*/ void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - - if ( gIMMgr ) - { - LLFloaterIMPanel* floaterp; - - floaterp = gIMMgr->findFloaterBySession(mSessionID); - - if ( floaterp ) - { - //403 == you're not a mod - //should be disabled if you're not a moderator - if ( 403 == status ) - { - floaterp->showSessionEventError( - "mute", - "not_a_mod_error"); - } - else - { - floaterp->showSessionEventError( - "mute", - "generic_request_error"); - } - } - } - } - - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return muteTextResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "MuteTextResponder"; } - - private: - LLUUID mSessionID; - }; - - LLHTTPClient::post( - url, - data, - new MuteTextResponder(self->mSpeakerMgr->getSessionID())); -} - -//static -void LLPanelActiveSpeakers::onChangeModerationMode(LLUICtrl* ctrl, void* user_data) -{ - LLPanelActiveSpeakers* self = (LLPanelActiveSpeakers*)user_data; - if (!gAgent.getRegion()) return; - - std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "session update"; - data["session-id"] = self->mSpeakerMgr->getSessionID(); - data["params"] = LLSD::emptyMap(); - - data["params"]["update_info"] = LLSD::emptyMap(); - - data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap(); - if (ctrl->getValue().asString() == "unmoderated") - { - data["params"]["update_info"]["moderated_mode"]["voice"] = false; - } - else if (ctrl->getValue().asString() == "moderated") - { - data["params"]["update_info"]["moderated_mode"]["voice"] = true; - } - - struct ModerationModeResponder : public LLHTTPClient::ResponderIgnoreBody - { - /*virtual*/ void error(U32 status, const std::string& reason) - { - llwarns << status << ": " << reason << llendl; - } - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return moderationModeResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "ModerationModeResponder"; } - }; - - LLHTTPClient::post(url, data, new ModerationModeResponder()); -} - -// -// LLSpeakerMgr -// - -LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : - mVoiceChannel(channelp) -{ -} - -LLSpeakerMgr::~LLSpeakerMgr() -{ -} - -LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) -{ - if (id.isNull()) return NULL; - - LLPointer speakerp; - if (mSpeakers.find(id) == mSpeakers.end()) - { - speakerp = new LLSpeaker(id, name, type); - speakerp->mStatus = status; - mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); - mSpeakersSorted.push_back(speakerp); - fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); - } - else - { - speakerp = findSpeaker(id); - if (speakerp.notNull()) - { - // keep highest priority status (lowest value) instead of overriding current value - speakerp->mStatus = llmin(speakerp->mStatus, status); - speakerp->mActivityTimer.reset(SPEAKER_TIMEOUT); - // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id - // we need to override speakers that we think are objects when we find out they are really - // residents - if (type == LLSpeaker::SPEAKER_AGENT) - { - speakerp->mType = LLSpeaker::SPEAKER_AGENT; - speakerp->lookupName(); - } - } - } - - return speakerp; -} - -void LLSpeakerMgr::update(BOOL resort_ok) -{ - if (!gVoiceClient) - { - return; - } - - LLColor4 speaking_color = gSavedSettings.getColor4("SpeakingColor"); - LLColor4 overdriven_color = gSavedSettings.getColor4("OverdrivenColor"); - - if(resort_ok) // only allow list changes when user is not interacting with it - { - updateSpeakerList(); - } - - // update status of all current speakers - BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();) - { - LLUUID speaker_id = speaker_it->first; - LLSpeaker* speakerp = speaker_it->second; - - speaker_it++; - - if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id)) - { - speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id); - BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id); - if (moderator_muted_voice != speakerp->mModeratorMutedVoice) - { - speakerp->mModeratorMutedVoice = moderator_muted_voice; - speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); - } - - if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) - { - speakerp->mStatus = LLSpeaker::STATUS_MUTED; - } - else if (gVoiceClient->getIsSpeaking(speaker_id)) - { - // reset inactivity expiration - if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) - { - speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); - speakerp->mHasSpoken = TRUE; - } - speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; - // interpolate between active color and full speaking color based on power of speech output - speakerp->mDotColor = speaking_color; - if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) - { - speakerp->mDotColor = overdriven_color; - } - } - else - { - speakerp->mSpeechVolume = 0.f; - speakerp->mDotColor = ACTIVE_COLOR; - - if (speakerp->mHasSpoken) - { - // have spoken once, not currently speaking - speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; - } - else - { - // default state for being in voice channel - speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; - } - } - } - // speaker no longer registered in voice channel, demote to text only - else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) - { - if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL) - { - // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice) - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - } - else - { - speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; - speakerp->mSpeechVolume = 0.f; - speakerp->mDotColor = ACTIVE_COLOR; - } - } - } - - if(resort_ok) // only allow list changes when user is not interacting with it - { - // sort by status then time last spoken - std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); - } - - // for recent speakers who are not currently speaking, show "recent" color dot for most recent - // fading to "active" color - - S32 recent_speaker_count = 0; - S32 sort_index = 0; - speaker_list_t::iterator sorted_speaker_it; - for(sorted_speaker_it = mSpeakersSorted.begin(); - sorted_speaker_it != mSpeakersSorted.end(); ) - { - LLPointer speakerp = *sorted_speaker_it; - - // color code recent speakers who are not currently speaking - if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) - { - speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); - recent_speaker_count++; - } - - // stuff sort ordinal into speaker so the ui can sort by this value - speakerp->mSortIndex = sort_index++; - - // remove speakers that have been gone too long - if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired()) - { - fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "remove"); - - mSpeakers.erase(speakerp->mID); - sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it); - } - else - { - ++sorted_speaker_it; - } - } -} - -void LLSpeakerMgr::updateSpeakerList() -{ - // are we bound to the currently active voice channel? - if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) - { - LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList(); - if(participants) - { - LLVoiceClient::participantMap::iterator participant_it; - - // add new participants to our list of known speakers - for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it) - { - LLVoiceClient::participantState* participantp = participant_it->second; - setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); - } - } - } -} - -const LLPointer LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) -{ - //In some conditions map causes crash if it is empty(Windows only), adding check (EK) - if (mSpeakers.size() == 0) - return NULL; - speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); - if (found_it == mSpeakers.end()) - { - return NULL; - } - return found_it->second; -} - -void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text) -{ - speaker_list->clear(); - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) - { - LLPointer speakerp = speaker_it->second; - // what about text only muted or inactive? - if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) - { - speaker_list->push_back(speakerp); - } - } -} - -const LLUUID LLSpeakerMgr::getSessionID() -{ - return mVoiceChannel->getSessionID(); -} - - -void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing) -{ - LLPointer speakerp = findSpeaker(speaker_id); - if (speakerp.notNull()) - { - speakerp->mTyping = typing; - } -} - -// speaker has chatted via either text or voice -void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) -{ - LLPointer speakerp = findSpeaker(speaker_id); - if (speakerp.notNull()) - { - speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); - speakerp->mHasSpoken = TRUE; - } -} - -BOOL LLSpeakerMgr::isVoiceActive() -{ - // mVoiceChannel = NULL means current voice channel, whatever it is - return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); -} - - -// -// LLIMSpeakerMgr -// -LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) -{ -} - -void LLIMSpeakerMgr::updateSpeakerList() -{ - // don't do normal updates which are pulled from voice channel - // rely on user list reported by sim - - // We need to do this to allow PSTN callers into group chats to show in the list. - LLSpeakerMgr::updateSpeakerList(); - - return; -} - -void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) -{ - if ( !speakers.isMap() ) return; - - if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) - { - LLSD::map_const_iterator speaker_it; - for(speaker_it = speakers["agent_info"].beginMap(); - speaker_it != speakers["agent_info"].endMap(); - ++speaker_it) - { - LLUUID agent_id(speaker_it->first); - - LLPointer speakerp = setSpeaker( - agent_id, - LLStringUtil::null, - LLSpeaker::STATUS_TEXT_ONLY); - - if ( speaker_it->second.isMap() ) - { - speakerp->mIsModerator = speaker_it->second["is_moderator"]; - speakerp->mModeratorMutedText = - speaker_it->second["mutes"]["text"]; - } - } - } - else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) - { - //older, more decprecated way. Need here for - //using older version of servers - LLSD::array_const_iterator speaker_it; - for(speaker_it = speakers["agents"].beginArray(); - speaker_it != speakers["agents"].endArray(); - ++speaker_it) - { - const LLUUID agent_id = (*speaker_it).asUUID(); - - LLPointer speakerp = setSpeaker( - agent_id, - LLStringUtil::null, - LLSpeaker::STATUS_TEXT_ONLY); - } - } -} - -void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) -{ - if ( !update.isMap() ) return; - - if ( update.has("agent_updates") && update["agent_updates"].isMap() ) - { - LLSD::map_const_iterator update_it; - for( - update_it = update["agent_updates"].beginMap(); - update_it != update["agent_updates"].endMap(); - ++update_it) - { - LLUUID agent_id(update_it->first); - LLPointer speakerp = findSpeaker(agent_id); - - LLSD agent_data = update_it->second; - - if (agent_data.isMap() && agent_data.has("transition")) - { - if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull()) - { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - speakerp->mActivityTimer.reset(SPEAKER_TIMEOUT); - } - else if (agent_data["transition"].asString() == "ENTER") - { - // add or update speaker - speakerp = setSpeaker(agent_id); - } - else - { - llwarns << "bad membership list update " << ll_print_sd(agent_data["transition"]) << llendl; - } - } - - if (speakerp.isNull()) continue; - - // should have a valid speaker from this point on - if (agent_data.isMap() && agent_data.has("info")) - { - LLSD agent_info = agent_data["info"]; - - if (agent_info.has("is_moderator")) - { - speakerp->mIsModerator = agent_info["is_moderator"]; - } - - if (agent_info.has("mutes")) - { - speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; - } - } - } - } - else if ( update.has("updates") && update["updates"].isMap() ) - { - LLSD::map_const_iterator update_it; - for ( - update_it = update["updates"].beginMap(); - update_it != update["updates"].endMap(); - ++update_it) - { - LLUUID agent_id(update_it->first); - LLPointer speakerp = findSpeaker(agent_id); - - std::string agent_transition = update_it->second.asString(); - if (agent_transition == "LEAVE" && speakerp.notNull()) - { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - speakerp->mActivityTimer.reset(SPEAKER_TIMEOUT); - } - else if ( agent_transition == "ENTER") - { - // add or update speaker - speakerp = setSpeaker(agent_id); - } - else - { - llwarns << "bad membership list update " - << agent_transition << llendl; - } - } - } -} - - -// -// LLActiveSpeakerMgr -// - -LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) -{ -} - -void LLActiveSpeakerMgr::updateSpeakerList() -{ - // point to whatever the current voice channel is - mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); - - // always populate from active voice channel - if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) - { - fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); - mSpeakers.clear(); - mSpeakersSorted.clear(); - mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); - } - LLSpeakerMgr::updateSpeakerList(); - - // clean up text only speakers - for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) - { - LLUUID speaker_id = speaker_it->first; - LLSpeaker* speakerp = speaker_it->second; - if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) - { - // automatically flag text only speakers for removal - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - } - } - -} - - - -// -// LLLocalSpeakerMgr -// - -LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) -{ -} - -LLLocalSpeakerMgr::~LLLocalSpeakerMgr () -{ -} - -void LLLocalSpeakerMgr::updateSpeakerList() -{ - // pull speakers from voice channel - LLSpeakerMgr::updateSpeakerList(); - - if (gDisconnected)//the world is cleared. - { - return ; - } - - // pick up non-voice speakers in chat range - std::vector avatar_ids; - std::vector positions; - LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); - for(U32 i=0; ifirst; - LLSpeaker* speakerp = speaker_it->second; - if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) - { - LLVOAvatar* avatarp = gObjectList.findAvatar(speaker_id); - if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS) - { - speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; - speakerp->mDotColor = INACTIVE_COLOR; - speakerp->mActivityTimer.reset(SPEAKER_TIMEOUT); - } - } - } -} diff --git a/indra/newview/llfloateractivespeakers.h b/indra/newview/llfloateractivespeakers.h index 31bd3faa8..c8657f09a 100644 --- a/indra/newview/llfloateractivespeakers.h +++ b/indra/newview/llfloateractivespeakers.h @@ -3,10 +3,9 @@ * @brief Management interface for muting and controlling volume of residents currently speaking * * $LicenseInfo:firstyear=2005&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2005-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -33,156 +32,10 @@ #ifndef LL_LLFLOATERACTIVESPEAKERS_H #define LL_LLFLOATERACTIVESPEAKERS_H -#include "llavatarnamecache.h" #include "llfloater.h" -#include "llmemory.h" #include "llvoiceclient.h" -#include "llframetimer.h" -#include "llevent.h" -#include "lllayoutstack.h" - -#include -#include - -class LLScrollListCtrl; -class LLButton; -class LLPanelActiveSpeakers; -class LLSpeakerMgr; -class LLVoiceChannel; -class LLSlider; -class LLTextBox; -class LLCheckBoxCtrl; - - -// data for a given participant in a voice channel -class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider, public boost::signals2::trackable -{ -public: - typedef enum e_speaker_type - { - SPEAKER_AGENT, - SPEAKER_OBJECT, - SPEAKER_EXTERNAL // Speaker that doesn't map to an avatar or object (i.e. PSTN caller in a group) - } ESpeakerType; - - typedef enum e_speaker_status - { - STATUS_SPEAKING, - STATUS_HAS_SPOKEN, - STATUS_VOICE_ACTIVE, - STATUS_TEXT_ONLY, - STATUS_NOT_IN_CHANNEL, - STATUS_MUTED - } ESpeakerStatus; - - - LLSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, const ESpeakerType type = SPEAKER_AGENT); - ~LLSpeaker() {}; - void lookupName(); - void onNameCache(const LLAvatarName& avatar_name); - - ESpeakerStatus mStatus; // current activity status in speech group - F32 mLastSpokeTime; // timestamp when this speaker last spoke - F32 mSpeechVolume; // current speech amplitude (timea average rms amplitude?) - std::string mDisplayName; // cache user name for this speaker - LLFrameTimer mActivityTimer; // time out speakers when they are not part of current voice channel - BOOL mHasSpoken; // has this speaker said anything this session? - LLColor4 mDotColor; - LLUUID mID; - BOOL mTyping; - S32 mSortIndex; - ESpeakerType mType; - BOOL mIsModerator; - BOOL mModeratorMutedVoice; - BOOL mModeratorMutedText; - std::string mLegacyName; - bool mNameRequested; -}; - -class LLSpeakerTextModerationEvent : public LLOldEvents::LLEvent -{ -public: - LLSpeakerTextModerationEvent(LLSpeaker* source); - /*virtual*/ LLSD getValue(); -}; - -class LLSpeakerVoiceModerationEvent : public LLOldEvents::LLEvent -{ -public: - LLSpeakerVoiceModerationEvent(LLSpeaker* source); - /*virtual*/ LLSD getValue(); -}; - -class LLSpeakerListChangeEvent : public LLOldEvents::LLEvent -{ -public: - LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id); - /*virtual*/ LLSD getValue(); - -private: - const LLUUID& mSpeakerID; -}; - -class LLSpeakerMgr : public LLOldEvents::LLObservable -{ -public: - LLSpeakerMgr(LLVoiceChannel* channelp); - virtual ~LLSpeakerMgr(); - - const LLPointer findSpeaker(const LLUUID& avatar_id); - void update(BOOL resort_ok); - void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing); - void speakerChatted(const LLUUID& speaker_id); - LLPointer setSpeaker(const LLUUID& id, - const std::string& name = LLStringUtil::null, - LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, - LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT); - - BOOL isVoiceActive(); - - typedef std::vector > speaker_list_t; - void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text); - const LLUUID getSessionID(); - -protected: - virtual void updateSpeakerList(); - - typedef std::map > speaker_map_t; - speaker_map_t mSpeakers; - - speaker_list_t mSpeakersSorted; - LLFrameTimer mSpeechTimer; - LLVoiceChannel* mVoiceChannel; -}; - -class LLIMSpeakerMgr : public LLSpeakerMgr -{ -public: - LLIMSpeakerMgr(LLVoiceChannel* channel); - - void updateSpeakers(const LLSD& update); - void setSpeakers(const LLSD& speakers); -protected: - virtual void updateSpeakerList(); -}; - -class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton -{ -public: - LLActiveSpeakerMgr(); -protected: - virtual void updateSpeakerList(); -}; - -class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton -{ -public: - LLLocalSpeakerMgr(); - ~LLLocalSpeakerMgr (); -protected: - virtual void updateSpeakerList(); -}; +class LLParticipantList; class LLFloaterActiveSpeakers : public LLFloaterSingleton, @@ -200,107 +53,14 @@ public: /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void draw(); - /*virtual*/ void onChange(); + /*virtual*/ void onParticipantsChanged(); static void* createSpeakersPanel(void* data); protected: LLFloaterActiveSpeakers(const LLSD& seed); - LLPanelActiveSpeakers* mPanel; + LLParticipantList* mPanel; }; -class LLPanelActiveSpeakers : public LLLayoutPanel -{ -public: - LLPanelActiveSpeakers(LLSpeakerMgr* data_source, BOOL show_text_chatters); - - /*virtual*/ BOOL postBuild(); - - void handleSpeakerSelect(); - void refreshSpeakers(); - - void setSpeaker(const LLUUID& id, - const std::string& name = LLStringUtil::null, - LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, - LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT); - - void setVoiceModerationCtrlMode(const BOOL& moderated_voice); - - static void onClickMuteVoiceCommit(LLUICtrl* ctrl, void* user_data); - static void onClickMuteTextCommit(LLUICtrl* ctrl, void* user_data); - static void onVolumeChange(LLUICtrl* source, void* user_data); - static void onClickProfile(void* user_data); - static void onDoubleClickSpeaker(void* user_data); - static void onSortChanged(void* user_data); - static void onModeratorMuteVoice(LLUICtrl* ctrl, void* user_data); - static void onModeratorMuteText(LLUICtrl* ctrl, void* user_data); - static void onChangeModerationMode(LLUICtrl* ctrl, void* user_data); - -protected: - class SpeakerMuteListener : public LLOldEvents::LLSimpleListener - { - public: - SpeakerMuteListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {} - - /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); - - LLPanelActiveSpeakers* mPanel; - }; - - friend class SpeakerAddListener; - class SpeakerAddListener : public LLOldEvents::LLSimpleListener - { - public: - SpeakerAddListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {} - - /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); - - LLPanelActiveSpeakers* mPanel; - }; - - friend class SpeakerRemoveListener; - class SpeakerRemoveListener : public LLOldEvents::LLSimpleListener - { - public: - SpeakerRemoveListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {} - - /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); - - LLPanelActiveSpeakers* mPanel; - }; - - - friend class SpeakerClearListener; - class SpeakerClearListener : public LLOldEvents::LLSimpleListener - { - public: - SpeakerClearListener(LLPanelActiveSpeakers* panel) : mPanel(panel) {} - - /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); - - LLPanelActiveSpeakers* mPanel; - }; - - void addSpeaker(const LLUUID& id); - void removeSpeaker(const LLUUID& id); - - - LLScrollListCtrl* mSpeakerList; - LLUICtrl* mMuteVoiceCtrl; - LLUICtrl* mMuteTextCtrl; - LLTextBox* mNameText; - LLButton* mProfileBtn; - BOOL mShowTextChatters; - LLSpeakerMgr* mSpeakerMgr; - LLFrameTimer mIconAnimationTimer; - LLPointer mSpeakerMuteListener; - LLPointer mSpeakerAddListener; - LLPointer mSpeakerRemoveListener; - LLPointer mSpeakerClearListener; - - CachedUICtrl mVolumeSlider; -}; - - #endif // LL_LLFLOATERACTIVESPEAKERS_H diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp deleted file mode 100644 index 8fdfc1a93..000000000 --- a/indra/newview/llfloateranimpreview.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/** - * @file llfloateranimpreview.cpp - * @brief LLFloaterAnimPreview class implementation - * - * Copyright (c) 2012, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llfloateranimpreview.h" - -LLFloaterAnimPreview::LLFloaterAnimPreview(LLSD const& filename) : LLFloaterNameDesc(filename) -{ -} - -BOOL LLFloaterAnimPreview::postBuild() -{ - if (!LLFloaterNameDesc::postBuild()) - { - return FALSE; - } - getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); - return TRUE; -} diff --git a/indra/newview/llfloateranimpreview.h b/indra/newview/llfloateranimpreview.h deleted file mode 100644 index 7603286e7..000000000 --- a/indra/newview/llfloateranimpreview.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file llfloateranimpreview.h - * @brief LLFloaterAnimPreview class definition - * - * Copyright (c) 2012, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLFLOATERANIMPREVIEW_H -#define LL_LLFLOATERANIMPREVIEW_H - -#include "llfloaternamedesc.h" - -class LLFloaterAnimPreview : public LLFloaterNameDesc { - public: - LLFloaterAnimPreview(LLSD const& filename); - virtual BOOL postBuild(void); -}; - -#endif // LL_LLFLOATERANIMPREVIEW_H diff --git a/indra/newview/llfloateravatarinfo.cpp b/indra/newview/llfloateravatarinfo.cpp index 642da1d4a..fe13d15fd 100644 --- a/indra/newview/llfloateravatarinfo.cpp +++ b/indra/newview/llfloateravatarinfo.cpp @@ -35,168 +35,27 @@ #include "llviewerprecompiledheaders.h" #include "llfloateravatarinfo.h" -#include "llavatarnamecache.h" - -// viewer project includes -#include "llagentdata.h" -#include "llcommandhandler.h" -#include "llimview.h" -#include "llfloaterfriends.h" -#include "llfloatermute.h" -#include "llmenucommands.h" #include "llpanelavatar.h" -#include "llviewermessage.h" #include "lluictrlfactory.h" -#include "llweb.h" - -// linden library includes -#include "llinventory.h" -#include "lluuid.h" -#include "message.h" - - -const char FLOATER_TITLE[] = "Profile"; -const LLRect FAI_RECT(0, 530, 420, 0); - -//----------------------------------------------------------------------------- -// Globals -//----------------------------------------------------------------------------- - -class LLAgentHandler : public LLCommandHandler -{ -public: - void verbCallback(const std::string& verb, LLUUID agent_id, const LLAvatarName& avatar_name) - { - if (verb == "im") - { - gIMMgr->setFloaterOpen(TRUE); - gIMMgr->addSession( avatar_name.getCompleteName(), IM_NOTHING_SPECIAL, agent_id); - return; - } - - if (verb == "requestfriend") - { - LLPanelFriends::requestFriendshipDialog( agent_id, avatar_name.getCompleteName() ); - return; - } - - if (verb == "mute") - { - LLFloaterMute::getInstance()->open(); - LLMute mute(agent_id, avatar_name.getCompleteName(), LLMute::AGENT); - LLMuteList::getInstance()->add(mute); - return; - } - - if (verb == "unmute") - { - LLMute mute(agent_id, avatar_name.getCompleteName(), LLMute::AGENT); - LLMuteList::getInstance()->remove(mute); - return; - } - } - - // requires trusted browser to trigger - LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { } - - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) - { - if (params.size() < 2) - { - return false; - } - LLUUID agent_id; - if (!agent_id.set(params[0], FALSE)) - { - return false; - } - - const std::string verb = params[1].asString(); - if (verb == "about") - { - LLFloaterAvatarInfo::show(agent_id); - return true; - } - - if (verb == "pay") - { - handle_pay_by_id(agent_id); - return true; - } - - if (verb == "offerteleport") - { - handle_lure(agent_id); - return true; - } - - if ((verb == "im") || (verb == "requestfriend") || (verb == "unmute")) - { - LLAvatarNameCache::get(agent_id, boost::bind(&LLAgentHandler::verbCallback, this, verb, _1, _2)); - return true; - } - - if (verb == "mute") - { - if (LLMuteList::getInstance()->isMuted(agent_id)) - { - LLFloaterMute::getInstance()->open(); - LLFloaterMute::getInstance()->selectMute(agent_id); - } - else - { - LLAvatarNameCache::get(agent_id, boost::bind(&LLAgentHandler::verbCallback, this, verb, _1, _2)); - } - return true; - } - - return false; - } -}; -LLAgentHandler gAgentHandler; - -//----------------------------------------------------------------------------- -// Member functions -//----------------------------------------------------------------------------- - -//---------------------------------------------------------------------------- void* LLFloaterAvatarInfo::createPanelAvatar(void* data) { LLFloaterAvatarInfo* self = (LLFloaterAvatarInfo*)data; self->mPanelAvatarp = new LLPanelAvatar("PanelAv", LLRect(), TRUE); // allow edit self + self->mPanelAvatarp->setAvatarID(self->mAvatarID); return self->mPanelAvatarp; } -//---------------------------------------------------------------------------- - - -BOOL LLFloaterAvatarInfo::postBuild() +LLFloaterAvatarInfo::LLFloaterAvatarInfo(const std::string& name, const LLUUID &avatar_id) +: LLFloater(name), LLInstanceTracker(avatar_id), + mAvatarID(avatar_id) { - return TRUE; -} - -LLFloaterAvatarInfo::LLFloaterAvatarInfo(const std::string& name, const LLRect &rect, const LLUUID &avatar_id) -: LLPreview(name, rect, FLOATER_TITLE, LLUUID::null, LLUUID::null), LLInstanceTracker(avatar_id), - mAvatarID( avatar_id ), - mSuggestedOnlineStatus(ONLINE_STATUS_NO) -{ - setAutoFocus(TRUE); + setAutoFocus(true); LLCallbackMap::map_t factory_map; - factory_map["Panel Avatar"] = LLCallbackMap(createPanelAvatar, this); - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_profile.xml", &factory_map); - - if(mPanelAvatarp) - { - mPanelAvatarp->selectTab(0); - } - - //gCacheName->get(avatar_id, FALSE, callbackLoadAvatarName); - LLAvatarNameCache::get(avatar_id, boost::bind(&LLFloaterAvatarInfo::callbackLoadAvatarName, this, _1, _2)); + setTitle(name); } // virtual @@ -206,131 +65,11 @@ LLFloaterAvatarInfo::~LLFloaterAvatarInfo() void LLFloaterAvatarInfo::resetGroupList() { - // only get these updates asynchronously via the group floater, which works on the agent only - if (mAvatarID != gAgentID) - { - return; - } - mPanelAvatarp->resetGroupList(); } -// static -LLFloaterAvatarInfo* LLFloaterAvatarInfo::show(const LLUUID &avatar_id) -{ - if (avatar_id.isNull()) - { - return NULL; - } - - LLFloaterAvatarInfo *floater = LLFloaterAvatarInfo::getInstance(avatar_id); - if(!floater) - { - floater = new LLFloaterAvatarInfo("avatarinfo", FAI_RECT, avatar_id ); - floater->center(); - } - - // ...bring that window to front - floater->open(); /*Flawfinder: ignore*/ - return floater; -} - -// Open profile to a certain tab. -// static -void LLFloaterAvatarInfo::showFromObject(const LLUUID& avatar_id,std::string tab_name) -{ - LLFloaterAvatarInfo *floater = show(avatar_id); - if (floater) - { - floater->mPanelAvatarp->setAvatarID(avatar_id, LLStringUtil::null, ONLINE_STATUS_NO); - floater->mPanelAvatarp->selectTabByName(tab_name); - } -} - -// static -void LLFloaterAvatarInfo::showFromDirectory(const LLUUID &avatar_id) -{ - LLFloaterAvatarInfo *floater = show(avatar_id); - if (floater) - { - floater->mPanelAvatarp->setAvatarID(avatar_id, LLStringUtil::null, ONLINE_STATUS_NO); - } -} - - -// static -void LLFloaterAvatarInfo::showFromFriend(const LLUUID& agent_id, BOOL online) -{ - LLFloaterAvatarInfo *floater = show(agent_id); - if (floater) - { - floater->mSuggestedOnlineStatus = online ? ONLINE_STATUS_YES : ONLINE_STATUS_NO; - } -} - - -// static -void LLFloaterAvatarInfo::showFromProfile(const LLUUID &avatar_id, LLRect rect) -{ - if (avatar_id.isNull()) - { - return; - } - - LLFloaterAvatarInfo *floater = LLFloaterAvatarInfo::getInstance(avatar_id); - if(!floater) - { - floater = new LLFloaterAvatarInfo("avatarinfo", FAI_RECT, avatar_id); - floater->translate(rect.mLeft - floater->getRect().mLeft + 16, - rect.mTop - floater->getRect().mTop - 16); - floater->mPanelAvatarp->setAvatarID(avatar_id, LLStringUtil::null, ONLINE_STATUS_NO); - } - floater->open(); -} - -void LLFloaterAvatarInfo::showProfileCallback(S32 option, void *userdata) -{ - if (option == 0) - { - showFromObject(gAgentID); - } -} - -void LLFloaterAvatarInfo::callbackLoadAvatarName(const LLUUID& id, const LLAvatarName& av_name) -{ - // Build a new title including the avatar name. - std::ostringstream title; - //title << first << " " << last << " - " << floater->getTitle(); - title << av_name.getCompleteName()<< " - " << getTitle(); - setTitle(title.str()); -} - -//// virtual -void LLFloaterAvatarInfo::draw() -{ - // skip LLPreview::draw() - LLFloater::draw(); -} - // virtual BOOL LLFloaterAvatarInfo::canClose() { return mPanelAvatarp && mPanelAvatarp->canClose(); } - -void LLFloaterAvatarInfo::loadAsset() -{ - if (mPanelAvatarp) { - mPanelAvatarp->setAvatarID(mAvatarID, LLStringUtil::null, mSuggestedOnlineStatus); - mAssetStatus = PREVIEW_ASSET_LOADING; - } -} - -LLPreview::EAssetStatus LLFloaterAvatarInfo::getAssetStatus() -{ - if (mPanelAvatarp && mPanelAvatarp->haveData()) - { - mAssetStatus = PREVIEW_ASSET_LOADED; - } - return mAssetStatus; -} diff --git a/indra/newview/llfloateravatarinfo.h b/indra/newview/llfloateravatarinfo.h index b9682b308..b95bd14d1 100644 --- a/indra/newview/llfloateravatarinfo.h +++ b/indra/newview/llfloateravatarinfo.h @@ -40,66 +40,24 @@ #define LL_LLFLOATERAVATARINFO_H #include "llfloater.h" -#include "llpreview.h" -#include "lluuid.h" -#include "llpanelavatar.h" #include "llinstancetracker.h" -class LLAvatarName; -class LLButton; -class LLCheckBoxCtrl; -class LLInventoryItem; -class LLLineEditor; -class LLMessageSystem; -class LLScrollListCtrl; -class LLTabContainer; -class LLTextBox; -class LLTextEditor; -class LLTextureCtrl; -class LLUICtrl; -class LLViewerTexture; -class LLViewerObject; +class LLPanelAvatar; class LLFloaterAvatarInfo -: public LLPreview, public LLInstanceTracker +: public LLFloater, public LLInstanceTracker { public: static void* createPanelAvatar(void* data); - virtual BOOL postBuild(); - - LLFloaterAvatarInfo(const std::string& name, const LLRect &rect, const LLUUID &avatar_id ); + LLFloaterAvatarInfo(const std::string& name, const LLUUID &avatar_id); /*virtual*/ ~LLFloaterAvatarInfo(); - - /*virtual*/ void draw(); - /*virtual*/ BOOL canClose(); - - /*virtual*/ void loadAsset(); - /*virtual*/ EAssetStatus getAssetStatus(); - - static LLFloaterAvatarInfo* show(const LLUUID& avatar_id); - // Core method, doesn't do anything funny with online status or - // tab selection. - - static void showFromObject(const LLUUID &avatar_id, std::string tab_name = std::string()); - - static void showFromDirectory(const LLUUID &avatar_id); - - static void showFromFriend(const LLUUID &agent_id, BOOL online); - - static void showFromProfile(const LLUUID &avatar_id, LLRect rect); - - static void showProfileCallback(S32 option, void *userdata); - void callbackLoadAvatarName(const LLUUID& agent_id, const LLAvatarName& av_name); void resetGroupList(); private: LLUUID mAvatarID; // for which avatar is this window? LLPanelAvatar* mPanelAvatarp; - EOnlineStatus mSuggestedOnlineStatus; }; -std::string getProfileURL(const std::string& agent_name); - #endif diff --git a/indra/newview/llfloateravatarlist.cpp b/indra/newview/llfloateravatarlist.cpp index 4a5640a27..c41e7f1db 100644 --- a/indra/newview/llfloateravatarlist.cpp +++ b/indra/newview/llfloateravatarlist.cpp @@ -25,21 +25,17 @@ #include "llwindow.h" #include "llscrolllistctrl.h" #include "llradiogroup.h" -#include "llviewercontrol.h" #include "llnotificationsutil.h" #include "llvoavatar.h" #include "llimview.h" -#include "llfloateravatarinfo.h" -#include "llregionflags.h" #include "llfloaterreporter.h" #include "llagent.h" #include "llagentcamera.h" +#include "llavataractions.h" #include "llfloaterregioninfo.h" #include "llviewerregion.h" #include "lltracker.h" -#include "llviewerstats.h" -#include "llerror.h" #include "llchat.h" #include "llfloaterchat.h" #include "llviewermessage.h" @@ -882,7 +878,7 @@ void LLFloaterAvatarList::refreshAvatarList() name_color = ascent_estate_owner_color; } //without these dots, SL would suck. - else if(is_agent_friend(av_id)) + else if(LLAvatarActions::isFriend(av_id)) { static const LLCachedControl ascent_friend_color("AscentFriendColor",LLColor4(1.f,1.f,0.f,1.f)); name_color = ascent_friend_color; @@ -1142,22 +1138,12 @@ void LLFloaterAvatarList::onClickIM() if (ids.size() == 1) { // Single avatar - LLUUID agent_id = ids[0]; - - std::string avatar_name; - if (gCacheName->getFullName(agent_id, avatar_name)) - { - gIMMgr->setFloaterOpen(TRUE); - gIMMgr->addSession(avatar_name,IM_NOTHING_SPECIAL,agent_id); - } + LLAvatarActions::startIM(ids[0]); } else { // Group IM - LLUUID session_id; - session_id.generate(); - gIMMgr->setFloaterOpen(TRUE); - gIMMgr->addSession("Avatars Conference", IM_SESSION_CONFERENCE_START, ids[0], ids); + LLAvatarActions::startConference(ids); } } } @@ -1265,30 +1251,7 @@ BOOL LLFloaterAvatarList::handleKeyHere(KEY key, MASK mask) if (( KEY_RETURN == key ) && (MASK_SHIFT == mask)) { - uuid_vec_t ids = mAvatarList->getSelectedIDs(); - if (ids.size() > 0) - { - if (ids.size() == 1) - { - // Single avatar - LLUUID agent_id = ids[0]; - - std::string avatar_name; - if (gCacheName->getFullName(agent_id, avatar_name)) - { - gIMMgr->setFloaterOpen(TRUE); - gIMMgr->addSession(avatar_name,IM_NOTHING_SPECIAL,agent_id); - } - } - else - { - // Group IM - LLUUID session_id; - session_id.generate(); - gIMMgr->setFloaterOpen(TRUE); - gIMMgr->addSession("Avatars Conference", IM_SESSION_CONFERENCE_START, ids[0], ids); - } - } + onClickIM(); } return LLPanel::handleKeyHere(key, mask); } @@ -1553,7 +1516,7 @@ static void cmd_append_names(const LLAvatarListEntry* entry, std::string &str, s { if(!str.empty())str.append(sep);str.append(entry->getName()); } static void cmd_toggle_mark(LLAvatarListEntry* entry) { entry->toggleMark(); } static void cmd_ar(const LLAvatarListEntry* entry) { LLFloaterReporter::showFromObject(entry->getID()); } -static void cmd_profile(const LLAvatarListEntry* entry) { LLFloaterAvatarInfo::showFromDirectory(entry->getID()); } +static void cmd_profile(const LLAvatarListEntry* entry) { LLAvatarActions::showProfile(entry->getID()); } static void cmd_teleport(const LLAvatarListEntry* entry) { gAgent.teleportViaLocation(entry->getPosition()); } static void cmd_freeze(const LLAvatarListEntry* entry) { send_freeze(entry->getID(), true); } static void cmd_unfreeze(const LLAvatarListEntry* entry) { send_freeze(entry->getID(), false); } diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index 6bf1d9a9e..a9e6bf4b6 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -557,7 +557,10 @@ BOOL LLFloaterAvatarPicker::handleDragAndDrop(S32 x, S32 y, MASK mask, std::string avatar_name = selection->getColumn(0)->getValue().asString(); if (dest_agent_id.notNull() && dest_agent_id != gAgentID) { - if (drop) +// if (drop) +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + if ( (drop) && ( (!rlv_handler_t::isEnabled()) || (gRlvHandler.canStartIM(dest_agent_id)) ) ) +// [/RLVa:KB] { // Start up IM before give the item session_id = gIMMgr->addSession(avatar_name, IM_NOTHING_SPECIAL, dest_agent_id); diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index a5eb65b01..2544f6788 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -3,10 +3,9 @@ * @brief LLFloaterBvhPreview class implementation * * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2004-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -37,6 +36,7 @@ #include "llbvhloader.h" #include "lldatapacker.h" #include "lldir.h" +#include "lleconomy.h" #include "llnotificationsutil.h" #include "llvfile.h" #include "llapr.h" @@ -68,7 +68,6 @@ #include "llvoavatarself.h" #include "pipeline.h" #include "lluictrlfactory.h" -#include "llviewercontrol.h" #include "hippogridmanager.h" @@ -76,8 +75,6 @@ #include "llinventorymodel.h" // gInventoryModel // -S32 LLFloaterBvhPreview::sUploadAmount = 10; - const S32 PREVIEW_BORDER_WIDTH = 2; const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; @@ -143,17 +140,15 @@ std::string STATUS[] = "E_ST_BAD_ROOT" }; - //----------------------------------------------------------------------------- // LLFloaterBvhPreview() //----------------------------------------------------------------------------- LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename, void* item) : - LLFloaterNameDesc(filename) + LLFloaterNameDesc(filename, item) { // mItem = item; // - mLastMouseX = 0; mLastMouseY = 0; @@ -189,27 +184,27 @@ LLFloaterBvhPreview::LLFloaterBvhPreview(const std::string& filename, void* item //----------------------------------------------------------------------------- void LLFloaterBvhPreview::setAnimCallbacks() { - childSetCommitCallback("playback_slider", onSliderMove, this); + getChild("playback_slider")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onSliderMove, this)); - childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); - childSetValue("preview_base_anim", "Standing"); + getChild("preview_base_anim")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitBaseAnim, this)); + getChild("preview_base_anim")->setValue("Standing"); - childSetCommitCallback("priority", onCommitPriority, this); - childSetCommitCallback("loop_check", onCommitLoop, this); - childSetCommitCallback("loop_in_point", onCommitLoopIn, this); - childSetValidate("loop_in_point", validateLoopIn); - childSetCommitCallback("loop_out_point", onCommitLoopOut, this); - childSetValidate("loop_out_point", validateLoopOut); + getChild("priority")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitPriority, this)); + getChild("loop_check")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitLoop, this)); + getChild("loop_in_point")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitLoopIn, this)); + getChild("loop_in_point")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateLoopIn, this, _1)); + getChild("loop_out_point")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitLoopOut, this)); + getChild("loop_out_point")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateLoopOut, this, _1)); - childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); + getChild("hand_pose_combo")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitHandPose, this)); - childSetCommitCallback("emote_combo", onCommitEmote, this); - childSetValue("emote_combo", "[None]"); + getChild("emote_combo")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitEmote, this)); + getChild("emote_combo")->setValue("[None]"); - childSetCommitCallback("ease_in_time", onCommitEaseIn, this); - childSetValidate("ease_in_time", validateEaseIn); - childSetCommitCallback("ease_out_time", onCommitEaseOut, this); - childSetValidate("ease_out_time", validateEaseOut); + getChild("ease_in_time")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitEaseIn, this)); + getChild("ease_in_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseIn, this, _1)); + getChild("ease_out_time")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitEaseOut, this)); + getChild("ease_out_time")->setValidateBeforeCommit( boost::bind(&LLFloaterBvhPreview::validateEaseOut, this, _1)); } //----------------------------------------------------------------------------- @@ -228,11 +223,11 @@ BOOL LLFloaterBvhPreview::postBuild() mInWorld = gSavedSettings.getBOOL("PreviewAnimInWorld"); - childSetCommitCallback("name_form", onCommitName, this); + getChild("name_form")->setCommitCallback(boost::bind(&LLFloaterBvhPreview::onCommitName, this)); if (gSavedSettings.getBOOL("AscentPowerfulWizard")) { - childSetMaxValue("priority", 7); + getChild("priority")->setMaxValue(7); } childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); @@ -244,12 +239,12 @@ BOOL LLFloaterBvhPreview::postBuild() r = getRect(); translate(0, 230); reshape(r.getWidth(), r.getHeight() - 230); - childSetValue("bad_animation_text", getString("in_world")); - childShow("bad_animation_text"); + getChild("bad_animation_text")->setValue(getString("in_world")); + getChildView("bad_animation_text")->setVisible(TRUE); } else { - childHide("bad_animation_text"); + getChildView("bad_animation_text")->setVisible(FALSE); } mPreviewRect.set(PREVIEW_HPAD, @@ -263,10 +258,6 @@ BOOL LLFloaterBvhPreview::postBuild() r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT ); mPlayButton = getChild( "play_btn"); - if (!mPlayButton) - { - mPlayButton = new LLButton("play_btn"); - } mPlayButton->setClickedCallback(boost::bind(&LLFloaterBvhPreview::onBtnPlay,this)); mPlayButton->setImages(std::string("button_anim_play.tga"), @@ -278,10 +269,6 @@ BOOL LLFloaterBvhPreview::postBuild() mPlayButton->setScaleImage(TRUE); mStopButton = getChild( "stop_btn"); - if (!mStopButton) - { - mStopButton = new LLButton("stop_btn"); - } mStopButton->setClickedCallback(boost::bind(&LLFloaterBvhPreview::onBtnStop, this)); mStopButton->setImages(std::string("button_anim_stop.tga"), @@ -293,27 +280,6 @@ BOOL LLFloaterBvhPreview::postBuild() mStopButton->setScaleImage(TRUE); r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT); - //childSetCommitCallback("playback_slider", onSliderMove, this); - - //childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this); - //childSetValue("preview_base_anim", "Standing"); - - //childSetCommitCallback("priority", onCommitPriority, this); - //childSetCommitCallback("loop_check", onCommitLoop, this); - //childSetCommitCallback("loop_in_point", onCommitLoopIn, this); - //childSetValidate("loop_in_point", validateLoopIn); - //childSetCommitCallback("loop_out_point", onCommitLoopOut, this); - //childSetValidate("loop_out_point", validateLoopOut); - - //childSetCommitCallback("hand_pose_combo", onCommitHandPose, this); - - //childSetCommitCallback("emote_combo", onCommitEmote, this); - //childSetValue("emote_combo", "[None]"); - - //childSetCommitCallback("ease_in_time", onCommitEaseIn, this); - //childSetValidate("ease_in_time", validateEaseIn); - //childSetCommitCallback("ease_out_time", onCommitEaseOut, this); - //childSetValidate("ease_out_time", validateEaseOut); // moved declaration from below BOOL success = false; @@ -364,6 +330,7 @@ BOOL LLFloaterBvhPreview::postBuild() // moved everything bvh from below if(loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION) { + // generate unique id for this motion mTransactionID.generate(); mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID()); @@ -373,13 +340,9 @@ BOOL LLFloaterBvhPreview::postBuild() // this motion will not request an asset transfer until next update, so we have a chance to // load the keyframe data locally if (mInWorld) - { motionp = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID); - } else - { motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID); - } // create data buffer for keyframe initialization S32 buffer_size = loaderp->getOutputSize(); @@ -476,21 +439,20 @@ BOOL LLFloaterBvhPreview::postBuild() mAnimPreview->setZoom(camera_zoom); } - motionp->setName(childGetValue("name_form").asString()); + motionp->setName(getChild("name_form")->getValue().asString()); if (!mInWorld) - { mAnimPreview->getDummyAvatar()->startMotion(mMotionID); - } - childSetMinValue("playback_slider", 0.0); - childSetMaxValue("playback_slider", 1.0); - childSetValue("loop_check", LLSD(motionp->getLoop())); - childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f)); - childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f)); - childSetValue("priority", LLSD((F32)motionp->getPriority())); - childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose())); - childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration())); - childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration())); + getChild("playback_slider")->setMinValue(0.0); + getChild("playback_slider")->setMaxValue(1.0); + + getChild("loop_check")->setValue(LLSD(motionp->getLoop())); + getChild("loop_in_point")->setValue(LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f)); + getChild("loop_out_point")->setValue(LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f)); + getChild("priority")->setValue(LLSD((F32)motionp->getPriority())); + getChild("hand_pose_combo")->setValue(LLHandMotion::getHandPoseName(motionp->getHandPose())); + getChild("ease_in_time")->setValue(LLSD(motionp->getEaseInDuration())); + getChild("ease_out_time")->setValue(LLSD(motionp->getEaseOutDuration())); setEnabled(TRUE); std::string seconds_string; seconds_string = llformat(" - %.2f seconds", motionp->getDuration()); @@ -501,7 +463,7 @@ BOOL LLFloaterBvhPreview::postBuild() { mAnimPreview = NULL; mMotionID.setNull(); - childSetValue("bad_animation_text", getString("failed_to_initialize")); + getChild("bad_animation_text")->setValue(getString("failed_to_initialize")); } @@ -587,38 +549,37 @@ void LLFloaterBvhPreview::draw() void LLFloaterBvhPreview::resetMotion() { LLVOAvatar* avatarp; - if (mInWorld) - { + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } - else - { + else if (mAnimPreview) avatarp = mAnimPreview->getDummyAvatar(); - } - if (!avatarp) - { + else return; - } BOOL paused = avatarp->areAnimationsPaused(); - // *TODO: Fix awful casting hack - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + LLKeyframeMotion* motionp = dynamic_cast(avatarp->findMotion(mMotionID)); + if( motionp ) + { + // Set emotion + std::string emote = getChild("emote_combo")->getValue().asString(); + motionp->setEmote(mIDList[emote]); + } - // Set emotion - std::string emote = childGetValue("emote_combo").asString(); - motionp->setEmote(mIDList[emote]); - - LLUUID base_id = mIDList[childGetValue("preview_base_anim").asString()]; + LLUUID base_id = mIDList[getChild("preview_base_anim")->getValue().asString()]; avatarp->deactivateAllMotions(); - avatarp->startMotion(base_id, BASE_ANIM_TIME_OFFSET); avatarp->startMotion(mMotionID, 0.0f); - childSetValue("playback_slider", 0.0f); + avatarp->startMotion(base_id, BASE_ANIM_TIME_OFFSET); + getChild("playback_slider")->setValue(0.0f); // Set pose - std::string handpose = childGetValue("hand_pose_combo").asString(); + std::string handpose = getChild("hand_pose_combo")->getValue().asString(); avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); - motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + + if( motionp ) + { + motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + } if (paused) { @@ -667,9 +628,7 @@ BOOL LLFloaterBvhPreview::handleMouseUp(S32 x, S32 y, MASK mask) BOOL LLFloaterBvhPreview::handleHover(S32 x, S32 y, MASK mask) { if (mInWorld) - { return TRUE; - } MASK local_mask = mask & ~MASK_ALT; @@ -726,11 +685,12 @@ BOOL LLFloaterBvhPreview::handleHover(S32 x, S32 y, MASK mask) //----------------------------------------------------------------------------- BOOL LLFloaterBvhPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if (!mInWorld) - { - mAnimPreview->zoom((F32)clicks * -0.2f); - mAnimPreview->requestUpdate(); - } + if (mInWorld || !mAnimPreview) + return false; + + mAnimPreview->zoom((F32)clicks * -0.2f); + mAnimPreview->requestUpdate(); + return TRUE; } @@ -740,9 +700,7 @@ BOOL LLFloaterBvhPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLFloaterBvhPreview::onMouseCaptureLost() { if (!mInWorld) - { gViewerWindow->showCursor(); - } } //----------------------------------------------------------------------------- @@ -756,38 +714,25 @@ void LLFloaterBvhPreview::onBtnPlay() if (mMotionID.notNull()) { LLVOAvatar* avatarp; - if (mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } - else - { - if (!mAnimPreview) - { - return; - } + else if (mAnimPreview) avatarp = mAnimPreview->getDummyAvatar(); - } + else + return; if(!avatarp->isMotionActive(mMotionID)) { resetMotion(); mPauseRequest = NULL; } - else + else if (avatarp->areAnimationsPaused()) { - if (avatarp->areAnimationsPaused()) - { - mPauseRequest = NULL; - } - else - { - mPauseRequest = avatarp->requestPause(); - } + mPauseRequest = NULL; + } + else //onBtnPause() does this in v3 + { + mPauseRequest = avatarp->requestPause(); } } } @@ -803,22 +748,12 @@ void LLFloaterBvhPreview::onBtnStop() if (mMotionID.notNull()) { LLVOAvatar* avatarp; - if (mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } - else - { - if (!mAnimPreview) - { - return; - } + else if (mAnimPreview) avatarp = mAnimPreview->getDummyAvatar(); - } + else + return; resetMotion(); mPauseRequest = avatarp->requestPause(); } @@ -827,435 +762,320 @@ void LLFloaterBvhPreview::onBtnStop() //----------------------------------------------------------------------------- // onSliderMove() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onSliderMove(LLUICtrl* ctrl, void*user_data) +void LLFloaterBvhPreview::onSliderMove() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)user_data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - F32 slider_value = (F32)previewp->childGetValue("playback_slider").asReal(); - LLUUID base_id = previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]; - LLMotion* motionp = avatarp->findMotion(previewp->mMotionID); + return; + + F32 slider_value = (F32)getChild("playback_slider")->getValue().asReal(); + LLUUID base_id = mIDList[getChild("preview_base_anim")->getValue().asString()]; + LLMotion* motionp = avatarp->findMotion(mMotionID); F32 duration = motionp->getDuration();// + motionp->getEaseOutDuration(); F32 delta_time = duration * slider_value; avatarp->deactivateAllMotions(); avatarp->startMotion(base_id, delta_time + BASE_ANIM_TIME_OFFSET); - avatarp->startMotion(previewp->mMotionID, delta_time); - previewp->mPauseRequest = avatarp->requestPause(); - previewp->refresh(); + avatarp->startMotion(mMotionID, delta_time); + mPauseRequest = avatarp->requestPause(); + refresh(); } //----------------------------------------------------------------------------- // onCommitBaseAnim() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitBaseAnim() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } + return; BOOL paused = avatarp->areAnimationsPaused(); // stop all other possible base motions - avatarp->stopMotion(ANIM_AGENT_STAND, TRUE); - avatarp->stopMotion(ANIM_AGENT_WALK, TRUE); - avatarp->stopMotion(ANIM_AGENT_SIT, TRUE); - avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE); + avatarp->stopMotion(mIDList["Standing"], TRUE); + avatarp->stopMotion(mIDList["Walking"], TRUE); + avatarp->stopMotion(mIDList["Sitting"], TRUE); + avatarp->stopMotion(mIDList["Flying"], TRUE); - previewp->resetMotion(); + resetMotion(); if (!paused) { - previewp->mPauseRequest = NULL; + mPauseRequest = NULL; } } //----------------------------------------------------------------------------- // onCommitLoop() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitLoop(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitLoop() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; - + LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; + + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); if (motionp) { - motionp->setLoop(previewp->childGetValue("loop_check").asBoolean()); - motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration()); - motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); + motionp->setLoop(getChild("loop_check")->getValue().asBoolean()); + motionp->setLoopIn((F32)getChild("loop_in_point")->getValue().asReal() * 0.01f * motionp->getDuration()); + motionp->setLoopOut((F32)getChild("loop_out_point")->getValue().asReal() * 0.01f * motionp->getDuration()); } } //----------------------------------------------------------------------------- // onCommitLoopIn() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitLoopIn() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; + + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); if (motionp) { - motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f); - previewp->resetMotion(); - previewp->childSetValue("loop_check", LLSD(TRUE)); - onCommitLoop(ctrl, data); + motionp->setLoopIn((F32)getChild("loop_in_point")->getValue().asReal() / 100.f); + resetMotion(); + getChild("loop_check")->setValue(LLSD(TRUE)); + onCommitLoop(); } } //----------------------------------------------------------------------------- // onCommitLoopOut() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitLoopOut() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; + + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); if (motionp) { - motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration()); - previewp->resetMotion(); - previewp->childSetValue("loop_check", LLSD(TRUE)); - onCommitLoop(ctrl, data); + motionp->setLoopOut((F32)getChild("loop_out_point")->getValue().asReal() * 0.01f * motionp->getDuration()); + resetMotion(); + getChild("loop_check")->setValue(LLSD(TRUE)); + onCommitLoop(); } } //----------------------------------------------------------------------------- // onCommitName() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitName(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitName() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; + + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); if (motionp) { - motionp->setName(previewp->childGetValue("name_form").asString()); + motionp->setName(getChild("name_form")->getValue().asString()); } - LLFloaterNameDesc::doCommit(ctrl, data); + doCommit(); } //----------------------------------------------------------------------------- // onCommitHandPose() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitHandPose(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitHandPose() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; - previewp->resetMotion(); // sets hand pose + resetMotion(); // sets hand pose } //----------------------------------------------------------------------------- // onCommitEmote() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitEmote(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitEmote() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; - previewp->resetMotion(); // ssts emote + resetMotion(); // ssts emote } //----------------------------------------------------------------------------- // onCommitPriority() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitPriority(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitPriority() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; - motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal())); + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + + motionp->setPriority(llfloor((F32)getChild("priority")->getValue().asReal())); } //----------------------------------------------------------------------------- // onCommitEaseIn() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitEaseIn() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; - motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal()); - previewp->resetMotion(); + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + + motionp->setEaseIn((F32)getChild("ease_in_time")->getValue().asReal()); + resetMotion(); } //----------------------------------------------------------------------------- // onCommitEaseOut() //----------------------------------------------------------------------------- -void LLFloaterBvhPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data) +void LLFloaterBvhPreview::onCommitEaseOut() { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) + if (!getEnabled()) return; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return; - motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal()); - previewp->resetMotion(); + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); + + motionp->setEaseOut((F32)getChild("ease_out_time")->getValue().asReal()); + resetMotion(); } //----------------------------------------------------------------------------- // validateEaseIn() //----------------------------------------------------------------------------- -BOOL LLFloaterBvhPreview::validateEaseIn(LLUICtrl* spin, void* data) +bool LLFloaterBvhPreview::validateEaseIn(const LLSD& data) { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) - return FALSE; + if (!getEnabled()) + return false; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return FALSE; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return FALSE; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return false; + + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); if (!motionp->getLoop()) { - F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration()); - previewp->childSetValue("ease_in_time", LLSD(new_ease_in)); + F32 new_ease_in = llclamp((F32)getChild("ease_in_time")->getValue().asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration()); + getChild("ease_in_time")->setValue(LLSD(new_ease_in)); } - return TRUE; + return true; } //----------------------------------------------------------------------------- // validateEaseOut() //----------------------------------------------------------------------------- -BOOL LLFloaterBvhPreview::validateEaseOut(LLUICtrl* spin, void* data) +bool LLFloaterBvhPreview::validateEaseOut(const LLSD& data) { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - - if (!previewp->getEnabled()) - return FALSE; + if (!getEnabled()) + return false; LLVOAvatar* avatarp; - if (previewp->mInWorld) - { - if (!gAgentAvatarp) - { - return FALSE; - } + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } + else if (mAnimPreview) + avatarp = mAnimPreview->getDummyAvatar(); else - { - if (!previewp->mAnimPreview) - { - return FALSE; - } - avatarp = previewp->mAnimPreview->getDummyAvatar(); - } - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID); + return false; + + LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); if (!motionp->getLoop()) { - F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration()); - previewp->childSetValue("ease_out_time", LLSD(new_ease_out)); + F32 new_ease_out = llclamp((F32)getChild("ease_out_time")->getValue().asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration()); + getChild("ease_out_time")->setValue(LLSD(new_ease_out)); } - return TRUE; + return true; } //----------------------------------------------------------------------------- // validateLoopIn() //----------------------------------------------------------------------------- -BOOL LLFloaterBvhPreview::validateLoopIn(LLUICtrl* ctrl, void* data) +bool LLFloaterBvhPreview::validateLoopIn(const LLSD& data) { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) - return FALSE; + if (!getEnabled()) + return false; - F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); - F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); + F32 loop_in_value = (F32)getChild("loop_in_point")->getValue().asReal(); + F32 loop_out_value = (F32)getChild("loop_out_point")->getValue().asReal(); if (loop_in_value < 0.f) { @@ -1270,21 +1090,20 @@ BOOL LLFloaterBvhPreview::validateLoopIn(LLUICtrl* ctrl, void* data) loop_in_value = loop_out_value; } - previewp->childSetValue("loop_in_point", LLSD(loop_in_value)); - return TRUE; + getChild("loop_in_point")->setValue(LLSD(loop_in_value)); + return true; } //----------------------------------------------------------------------------- // validateLoopOut() //----------------------------------------------------------------------------- -BOOL LLFloaterBvhPreview::validateLoopOut(LLUICtrl* spin, void* data) +bool LLFloaterBvhPreview::validateLoopOut(const LLSD& data) { - LLFloaterBvhPreview* previewp = (LLFloaterBvhPreview*)data; - if (!previewp->getEnabled()) - return FALSE; + if (!getEnabled()) + return false; - F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal(); - F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal(); + F32 loop_out_value = (F32)getChild("loop_out_point")->getValue().asReal(); + F32 loop_in_value = (F32)getChild("loop_in_point")->getValue().asReal(); if (loop_out_value < 0.f) { @@ -1299,8 +1118,8 @@ BOOL LLFloaterBvhPreview::validateLoopOut(LLUICtrl* spin, void* data) loop_out_value = loop_in_value; } - previewp->childSetValue("loop_out_point", LLSD(loop_out_value)); - return TRUE; + getChild("loop_out_point")->setValue(LLSD(loop_out_value)); + return true; } @@ -1309,66 +1128,59 @@ BOOL LLFloaterBvhPreview::validateLoopOut(LLUICtrl* spin, void* data) //----------------------------------------------------------------------------- void LLFloaterBvhPreview::refresh() { + // Are we showing the play button (default) or the pause button? + bool show_play = true; if (!mAnimPreview) { - childShow("bad_animation_text"); + getChildView("bad_animation_text")->setVisible(TRUE); + // play button visible but disabled mPlayButton->setEnabled(FALSE); mStopButton->setEnabled(FALSE); - childDisable("ok_btn"); + getChildView("ok_btn")->setEnabled(FALSE); } else { if (!mInWorld) - { - childHide("bad_animation_text"); - } + getChildView("bad_animation_text")->setVisible(FALSE); + // re-enabled in case previous animation was bad mPlayButton->setEnabled(TRUE); + mStopButton->setEnabled(TRUE); LLVOAvatar* avatarp; - if (mInWorld) - { + if (mInWorld && gAgentAvatarp) avatarp = gAgentAvatarp; - } - else - { + else if (mAnimPreview) avatarp = mAnimPreview->getDummyAvatar(); - } + else + return; if (avatarp->isMotionActive(mMotionID)) { mStopButton->setEnabled(TRUE); LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - if (avatarp->areAnimationsPaused()) - { - - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - } - else + if (!avatarp->areAnimationsPaused()) { + // animation is playing if (motionp) { F32 fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration(); - childSetValue("playback_slider", fraction_complete); + getChild("playback_slider")->setValue(fraction_complete); } - mPlayButton->setImages(std::string("button_anim_pause.tga"), - std::string("button_anim_pause_selected.tga")); - + show_play = false; } } else { + // Motion just finished playing mPauseRequest = avatarp->requestPause(); - mPlayButton->setImages(std::string("button_anim_play.tga"), - std::string("button_anim_play_selected.tga")); - - mStopButton->setEnabled(TRUE); // stop also resets, leave enabled. } - childEnable("ok_btn"); + getChildView("ok_btn")->setEnabled(TRUE); if (!mInWorld) - { mAnimPreview->requestUpdate(); - } } + if (show_play) + mPlayButton->setImages(std::string("button_anim_play.tga"), std::string("button_anim_play_selected.tga")); + else + mPlayButton->setImages(std::string("button_anim_pause.tga"), std::string("button_anim_pause_selected.tga")); + } //----------------------------------------------------------------------------- @@ -1403,10 +1215,10 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata) file.setMaxSize(size); if (file.write((U8*)buffer, size)) { - std::string name = floaterp->childGetValue("name_form").asString(); - std::string desc = floaterp->childGetValue("description_form").asString(); + std::string name = floaterp->getChild("name_form")->getValue().asString(); + std::string desc = floaterp->getChild("description_form")->getValue().asString(); LLAssetStorage::LLStoreAssetCallback callback = NULL; - S32 expected_upload_cost = sUploadAmount; + S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); void *userdata = NULL; // @@ -1572,9 +1384,13 @@ BOOL LLPreviewAnimation::render() LLVertexBuffer::unbind(); LLGLDepthTest gls_depth(GL_TRUE); - LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool(); - avatarp->dirtyMesh(); - avatarPoolp->renderAvatars(avatarp); // renders only one avatar + LLFace* face = avatarp->mDrawable->getFace(0); + if (face) + { + LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)face->getPool(); + avatarp->dirtyMesh(); + avatarPoolp->renderAvatars(avatarp); // renders only one avatar + } } gGL.color4f(1,1,1,1); diff --git a/indra/newview/llfloaterbvhpreview.h b/indra/newview/llfloaterbvhpreview.h index 78b9db6a0..7f31054c0 100644 --- a/indra/newview/llfloaterbvhpreview.h +++ b/indra/newview/llfloaterbvhpreview.h @@ -3,10 +3,9 @@ * @brief LLFloaterBvhPreview class definition * * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2004-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -92,22 +91,21 @@ public: void onBtnPlay(); void onBtnStop(); - static void setUploadAmount(S32 amount) { sUploadAmount = amount; } - static void onSliderMove(LLUICtrl*, void*); - static void onCommitBaseAnim(LLUICtrl*, void*); - static void onCommitLoop(LLUICtrl*, void*); - static void onCommitLoopIn(LLUICtrl*, void*); - static void onCommitLoopOut(LLUICtrl*, void*); - static BOOL validateLoopIn(LLUICtrl*, void*); - static BOOL validateLoopOut(LLUICtrl*, void*); - static void onCommitName(LLUICtrl*, void*); - static void onCommitHandPose(LLUICtrl*, void*); - static void onCommitEmote(LLUICtrl*, void*); - static void onCommitPriority(LLUICtrl*, void*); - static void onCommitEaseIn(LLUICtrl*, void*); - static void onCommitEaseOut(LLUICtrl*, void*); - static BOOL validateEaseIn(LLUICtrl*, void*); - static BOOL validateEaseOut(LLUICtrl*, void*); + void onSliderMove(); + void onCommitBaseAnim(); + void onCommitLoop(); + void onCommitLoopIn(); + void onCommitLoopOut(); + bool validateLoopIn(const LLSD& data); + bool validateLoopOut(const LLSD& data); + void onCommitName(); + void onCommitHandPose(); + void onCommitEmote(); + void onCommitPriority(); + void onCommitEaseIn(); + void onCommitEaseOut(); + bool validateEaseIn(const LLSD& data); + bool validateEaseOut(const LLSD& data); static void onBtnOK(void*); static void onSaveComplete(const LLUUID& asset_uuid, LLAssetType::EType type, @@ -129,14 +127,10 @@ protected: LLRectf mPreviewImageRect; LLAssetID mMotionID; LLTransactionID mTransactionID; - BOOL mEnabled; BOOL mInWorld; LLAnimPauseRequest mPauseRequest; std::map mIDList; - - static S32 sUploadAmount; - // void* mItem; // diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index bf92422a1..6eb9dbe0f 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -52,12 +52,13 @@ #include "llagent.h" #include "llchatbar.h" #include "llconsole.h" -#include "llfloateractivespeakers.h" #include "llfloaterchatterbox.h" #include "llfloatermute.h" #include "llfloaterscriptdebug.h" #include "lllogchat.h" #include "llmutelist.h" +#include "llparticipantlist.h" +#include "llspeakers.h" #include "llstylemap.h" #include "lluictrlfactory.h" #include "llviewermessage.h" @@ -126,7 +127,7 @@ void LLFloaterChat::draw() BOOL LLFloaterChat::postBuild() { - mPanel = (LLPanelActiveSpeakers*)getChild("active_speakers_panel"); + mPanel = getChild("active_speakers_panel"); LLChatBar* chat_barp = getChild("chat_panel", TRUE); if (chat_barp) @@ -324,7 +325,7 @@ void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file) // add objects as transient speakers that can be muted if (chat.mSourceType == CHAT_SOURCE_OBJECT) { - chat_floater->mPanel->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT); + LLLocalSpeakerMgr::getInstance()->setSpeaker(chat.mFromID, chat.mFromName, LLSpeaker::STATUS_NOT_IN_CHANNEL, LLSpeaker::SPEAKER_OBJECT); } // start tab flashing on incoming text from other users (ignoring system text, etc) @@ -627,7 +628,7 @@ void LLFloaterChat::chatFromLogFile(LLLogChat::ELogLineType type , std::string l //static void* LLFloaterChat::createSpeakersPanel(void* data) { - return new LLPanelActiveSpeakers(LLLocalSpeakerMgr::getInstance(), TRUE); + return new LLParticipantList(LLLocalSpeakerMgr::getInstance(), true); } //static diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h index 3c7cbc4c5..7eaab1b16 100644 --- a/indra/newview/llfloaterchat.h +++ b/indra/newview/llfloaterchat.h @@ -48,7 +48,7 @@ class LLViewerTextEditor; class LLMessageSystem; class LLUUID; class LLCheckBoxCtrl; -class LLPanelActiveSpeakers; +class LLParticipantList; class LLLogChat; class LLChatBar; @@ -95,7 +95,7 @@ public: static void show(LLFloater* instance, const LLSD& key); static void hide(LLFloater* instance, const LLSD& key); - LLPanelActiveSpeakers* mPanel; + LLParticipantList* mPanel; BOOL mScrolledToEnd; BOOL focusFirstItem(BOOL prefer_text_fields = FALSE, BOOL focus_flash = TRUE ); diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index 9a56acfec..c491f07fe 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -40,7 +40,7 @@ #include "llfloaterchat.h" #include "llfloaterfriends.h" #include "llfloatergroups.h" -#include "llviewercontrol.h" +#include "llvoicechannel.h" #include "llimview.h" #include "llimpanel.h" #include "llstring.h" @@ -315,7 +315,7 @@ void LLFloaterChatterBox::addFloater(LLFloater* floaterp, //static LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater() { - if (!LLVoiceClient::voiceEnabled()) + if (!LLVoiceClient::getInstance()->voiceEnabled()) { return NULL; } diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 657ac452a..fb2ab184c 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -76,8 +76,6 @@ const F32 CONTEXT_FADE_TIME = 0.08f; // ////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -// default ctor LLFloaterColorPicker::LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show_apply_immediate ) : LLFloater (std::string("Color Picker Floater")), mComponents ( 3 ), @@ -113,6 +111,10 @@ LLFloaterColorPicker::LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show mCanApplyImmediately ( show_apply_immediate ), mContextConeOpacity ( 0.f ) { + // build the majority of the gui using the factory builder + LLUICtrlFactory::getInstance()->buildFloater ( this, "floater_color_picker.xml" ); + setVisible ( FALSE ); + // create user interface for this picker createUI (); @@ -133,10 +135,6 @@ LLFloaterColorPicker::~LLFloaterColorPicker() // void LLFloaterColorPicker::createUI () { - // build the majority of the gui using the factory builder - LLUICtrlFactory::getInstance()->buildFloater ( this, "floater_color_picker.xml" ); - setVisible ( FALSE ); - // create RGB type area (not really RGB but it's got R,G & B in it.,.. LLPointer raw = new LLImageRaw ( mRGBViewerImageWidth, mRGBViewerImageHeight, mComponents ); @@ -184,7 +182,6 @@ void LLFloaterColorPicker::showUI () setVisible ( TRUE ); setFocus ( TRUE ); - // HACK: if system color picker is required - close the SL one we made and use default system dialog if ( gSavedSettings.getBOOL ( "UseDefaultColorPicker" ) ) { @@ -237,7 +234,7 @@ BOOL LLFloaterColorPicker::postBuild() childSetCommitCallback("hspin", onTextCommit, (void*)this ); childSetCommitCallback("sspin", onTextCommit, (void*)this ); childSetCommitCallback("lspin", onTextCommit, (void*)this ); - childSetCommitCallback("hexval", onHexCommit, (void*)this ); + getChild("hexval")->setCommitCallback(boost::bind(&LLFloaterColorPicker::onHexCommit, this, _2) ); LLToolPipette::getInstance()->setToolSelectCallback(boost::bind(&LLFloaterColorPicker::onColorSelect, this, _1)); @@ -248,9 +245,6 @@ BOOL LLFloaterColorPicker::postBuild() // void LLFloaterColorPicker::initUI ( F32 rValIn, F32 gValIn, F32 bValIn ) { - // start catching lose-focus events from entry widgets - enableTextCallbacks ( TRUE ); - // under some circumstances, we get rogue values that can be calmed by clamping... rValIn = llclamp ( rValIn, 0.0f, 1.0f ); gValIn = llclamp ( gValIn, 0.0f, 1.0f ); @@ -360,12 +354,8 @@ void LLFloaterColorPicker::setCurRgb ( F32 curRIn, F32 curGIn, F32 curBIn ) // update corresponding HSL values and LLColor3(curRIn, curGIn, curBIn).calcHSL(&curH, &curS, &curL); - // color changed so update text fields (fixes SL-16968) - // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion - // CP: this was required when I first wrote the code but this may not be necessary anymore - leaving it there just in case - enableTextCallbacks( FALSE ); + // color changed so update text fields updateTextEntry(); - enableTextCallbacks( TRUE ); } ////////////////////////////////////////////////////////////////////////////// @@ -497,7 +487,7 @@ void LLFloaterColorPicker::draw() mSwatch->localRectToOtherView(mSwatch->getLocalRect(), &swatch_rect, this); // draw context cone connecting color picker with color swatch in parent floater LLRect local_rect = getLocalRect(); - if (gFocusMgr.childHasKeyboardFocus(this) && mSwatch->isInVisibleChain() && mContextConeOpacity > 0.001f) + if (hasFocus() && mSwatch->isInVisibleChain() && mContextConeOpacity > 0.001f) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLGLEnable(GL_CULL_FACE); @@ -716,77 +706,36 @@ std::string RGBToHex(int rNum, int gNum, int bNum) } //Called when a hex value is entered into the Hex field - Convert and set values. -void LLFloaterColorPicker::onHexCommit ( LLUICtrl* ctrl, void* data ) +void LLFloaterColorPicker::onHexCommit(const LLSD& value) { - if ( data ) - { - LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data; - if ( self ) - { - char* pStop; - int num = strtol(ctrl->getValue().asString().c_str(), &pStop, 16); - int r = (num & 0xFF0000) >> 16; - int g = (num & 0xFF00) >> 8; - int b = num & 0xFF; - self->setCurRgb (r / 255.0f, g / 255.0f, b / 255.0f); + char* pStop; + int num = strtol(value.asString().c_str(), &pStop, 16); + int r = (num & 0xFF0000) >> 16; + int g = (num & 0xFF00) >> 8; + int b = num & 0xFF; + setCurRgb (r / 255.0f, g / 255.0f, b / 255.0f); - // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion - self->enableTextCallbacks ( FALSE ); - self->updateTextEntry (); - self->enableTextCallbacks ( TRUE ); - if (self->mApplyImmediateCheck->get()) - { - LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE ); - } - } + updateTextEntry(); + if (mApplyImmediateCheck->get()) + { + LLColorSwatchCtrl::onColorChanged(getSwatch(), LLColorSwatchCtrl::COLOR_CHANGE); } } ////////////////////////////////////////////////////////////////////////////// // update text entry values for RGB/HSL (can't be done in ::draw () since this overwrites input -void -LLFloaterColorPicker:: -updateTextEntry () +void LLFloaterColorPicker::updateTextEntry () { // set values in spinners - childSetValue("rspin", ( getCurR () * 255.0f ) ); - childSetValue("gspin", ( getCurG () * 255.0f ) ); - childSetValue("bspin", ( getCurB () * 255.0f ) ); - childSetValue("hspin", ( getCurH () * 360.0f ) ); - childSetValue("sspin", ( getCurS () * 100.0f ) ); - childSetValue("lspin", ( getCurL () * 100.0f ) ); - childSetValue("hexval", RGBToHex(getCurR() * 255, getCurG() * 255, getCurB() * 255)); + getChild("rspin")->setValue(( getCurR () * 255.0f ) ); + getChild("gspin")->setValue(( getCurG () * 255.0f ) ); + getChild("bspin")->setValue(( getCurB () * 255.0f ) ); + getChild("hspin")->setValue(( getCurH () * 360.0f ) ); + getChild("sspin")->setValue(( getCurS () * 100.0f ) ); + getChild("lspin")->setValue(( getCurL () * 100.0f ) ); + getChild("hexval")->setValue(RGBToHex(getCurR() * 255, getCurG() * 255, getCurB() * 255)); } -////////////////////////////////////////////////////////////////////////////// -// turns on or off text entry commit call backs -void -LLFloaterColorPicker:: -enableTextCallbacks ( BOOL stateIn ) -{ - if ( stateIn ) - { - childSetCommitCallback("rspin", onTextCommit, (void*)this ); - childSetCommitCallback("gspin", onTextCommit, (void*)this ); - childSetCommitCallback("bspin", onTextCommit, (void*)this ); - childSetCommitCallback("hspin", onTextCommit, (void*)this ); - childSetCommitCallback("sspin", onTextCommit, (void*)this ); - childSetCommitCallback("lspin", onTextCommit, (void*)this ); - childSetCommitCallback("hexval", onHexCommit, (void*)this ); - } - else - { - childSetCommitCallback("rspin", 0, (void*)this ); - childSetCommitCallback("gspin", 0, (void*)this ); - childSetCommitCallback("bspin", 0, (void*)this ); - childSetCommitCallback("hspin", 0, (void*)this ); - childSetCommitCallback("sspin", 0, (void*)this ); - childSetCommitCallback("lspin", 0, (void*)this ); - childSetCommitCallback("hexval", 0, (void*)this ); - } -} - - ////////////////////////////////////////////////////////////////////////////// // void LLFloaterColorPicker::onTextEntryChanged ( LLUICtrl* ctrl ) @@ -818,10 +767,7 @@ void LLFloaterColorPicker::onTextEntryChanged ( LLUICtrl* ctrl ) // update current RGB (and implicitly HSL) setCurRgb ( rVal, gVal, bVal ); - // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion - enableTextCallbacks ( FALSE ); updateTextEntry (); - enableTextCallbacks ( TRUE ); } else // value in HSL boxes changed @@ -844,10 +790,7 @@ void LLFloaterColorPicker::onTextEntryChanged ( LLUICtrl* ctrl ) // update current HSL (and implicitly RGB) setCurHsl ( hVal, sVal, lVal ); - // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion - enableTextCallbacks ( FALSE ); updateTextEntry (); - enableTextCallbacks ( TRUE ); } if (mApplyImmediateCheck->get()) @@ -980,10 +923,7 @@ BOOL LLFloaterColorPicker::handleMouseDown ( S32 x, S32 y, MASK mask ) LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE ); } - // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion - enableTextCallbacks ( FALSE ); updateTextEntry (); - enableTextCallbacks ( TRUE ); } return TRUE; @@ -1152,9 +1092,6 @@ void LLFloaterColorPicker::cancelSelection () // restore the previous color selection setCurRgb ( getOrigR (), getOrigG (), getOrigB () ); - // we're going away and when we do and the entry widgets lose focus, they do bad things so turn them off - enableTextCallbacks ( FALSE ); - // update in world item with original color via current swatch LLColorSwatchCtrl::onColorChanged( getSwatch(), LLColorSwatchCtrl::COLOR_CANCEL ); diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h index 185b2a400..1d0535ca8 100644 --- a/indra/newview/llfloatercolorpicker.h +++ b/indra/newview/llfloatercolorpicker.h @@ -126,12 +126,9 @@ class LLFloaterColorPicker void onClickPipette ( ); static void onTextCommit ( LLUICtrl* ctrl, void* data ); static void onImmediateCheck ( LLUICtrl* ctrl, void* data ); - static void onHexCommit ( LLUICtrl* ctrl, void* data ); + void onHexCommit(const LLSD& value); void onColorSelect( const LLTextureEntry& te ); private: - // turns on or off text entry commit call backs - void enableTextCallbacks ( BOOL stateIn ); - // draws color selection palette void drawPalette (); diff --git a/indra/newview/llfloaterdirectory.cpp b/indra/newview/llfloaterdirectory.cpp index 72cfc97dc..a696e61ff 100644 --- a/indra/newview/llfloaterdirectory.cpp +++ b/indra/newview/llfloaterdirectory.cpp @@ -391,10 +391,8 @@ void* LLFloaterDirectory::createEventDetail(void* userdata) void* LLFloaterDirectory::createGroupDetail(void* userdata) { LLFloaterDirectory *self = (LLFloaterDirectory*)userdata; - self->mPanelGroupp = new LLPanelGroup("panel_group.xml", - "PanelGroup", - gAgent.getGroupID()); - self->mPanelGroupp->setAllowEdit(FALSE || gAgent.isGodlike()); // Gods can always edit panels + self->mPanelGroupp = new LLPanelGroup(gAgent.getGroupID()); + self->mPanelGroupp->setAllowEdit(false); // Singu Note: This setting actually just tells the panel whether or not it is in search self->mPanelGroupp->setVisible(FALSE); return self->mPanelGroupp; } diff --git a/indra/newview/llfloaterexploreanimations.cpp b/indra/newview/llfloaterexploreanimations.cpp index 6267b31c5..5ffe000b6 100644 --- a/indra/newview/llfloaterexploreanimations.cpp +++ b/indra/newview/llfloaterexploreanimations.cpp @@ -6,7 +6,6 @@ #include "llscrolllistctrl.h" #include "llfloaterbvhpreview.h" #include "llvoavatar.h" -#include "lllocalinventory.h" #include "llviewercamera.h" #include "llviewerwindow.h" #include "lltoolmgr.h" diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp index 2ea281745..c2c958f98 100644 --- a/indra/newview/llfloaterfriends.cpp +++ b/indra/newview/llfloaterfriends.cpp @@ -37,46 +37,31 @@ #include "llfloaterfriends.h" -#include - -#include "lldir.h" #include "llagent.h" -#include "llappviewer.h" // for gLastVersionChannel - +#include "llavataractions.h" #include "llavatarnamecache.h" - -#include "llfloateravatarpicker.h" -#include "llviewerwindow.h" #include "llbutton.h" +#include "lldir.h" +#include "lleventtimer.h" #include "llfiltereditor.h" -#include "llfloateravatarinfo.h" -#include "llinventorymodel.h" +#include "llfloateravatarpicker.h" #include "llnamelistctrl.h" #include "llnotificationsutil.h" -#include "llresmgr.h" -#include "llimview.h" -#include "lluictrlfactory.h" -#include "llmenucommands.h" -#include "llviewercontrol.h" -#include "llviewermessage.h" -#include "lleventtimer.h" +#include "llsdserialize.h" #include "lltextbox.h" +#include "lluictrlfactory.h" +#include "llviewerwindow.h" #include "llvoiceclient.h" -#include "llsdserialize.h" #include "statemachine/aifilepicker.h" -#include "llviewermenufile.h" -#include "llviewermenu.h" -#include "llviewernetwork.h" #include "hippogridmanager.h" -#include "llchat.h" -#include "llfloaterchat.h" - // stuff for Contact groups //#include "ascentfloatercontactgroups.h" +//#include "llchat.h" +//#include "llfloaterchat.h" #define DEFAULT_PERIOD 5.0 #define RIGHTS_CHANGE_TIMEOUT 5.0 @@ -399,10 +384,10 @@ BOOL LLPanelFriends::postBuild() //childSetAction("assign_btn", onClickAssign, this); childSetAction("expand_collapse_btn", onClickExpand, this); childSetAction("profile_btn", onClickProfile, this); - childSetAction("offer_teleport_btn", onClickOfferTeleport, this); + getChild("offer_teleport_btn")->setCommitCallback(boost::bind(static_cast(LLAvatarActions::offerTeleport), boost::bind(&LLScrollListCtrl::getSelectedIDs, mFriendsList))); childSetAction("pay_btn", onClickPay, this); childSetAction("add_btn", onClickAddFriend, this); - childSetAction("remove_btn", onClickRemove, this); + getChild("remove_btn")->setCommitCallback(boost::bind(LLAvatarActions::removeFriendsDialog, boost::bind(&LLScrollListCtrl::getSelectedIDs, mFriendsList))); //childSetAction("export_btn", onClickExport, this); Making Dummy View -HgB //childSetAction("import_btn", onClickImport, this); Making Dummy View -HgB @@ -685,7 +670,6 @@ BOOL LLPanelFriends::refreshNamesSync(const LLAvatarTracker::buddy_map_t & all_b BOOL LLPanelFriends::refreshNamesPresence(const LLAvatarTracker::buddy_map_t & all_buddies) { - std::vector items = mFriendsList->getAllData(); std::sort(items.begin(), items.end(), SortFriendsByID()); @@ -784,10 +768,7 @@ void LLPanelFriends::onClickProfile(void* user_data) const uuid_vec_t ids = panelp->mFriendsList->getSelectedIDs(); if(!ids.empty()) { - LLUUID agent_id = ids[0]; - BOOL online; - online = LLAvatarTracker::instance().isBuddyOnline(agent_id); - LLFloaterAvatarInfo::showFromFriend(agent_id, online); + LLAvatarActions::showProfile(ids[0]); } } @@ -851,103 +832,24 @@ void LLPanelFriends::onClickIM(void* user_data) const uuid_vec_t ids = panelp->mFriendsList->getSelectedIDs(); if(!ids.empty()) { - static LLCachedControl tear_off("OtherChatsTornOff"); - if(!tear_off) gIMMgr->setFloaterOpen(TRUE); if(ids.size() == 1) { - LLUUID agent_id = ids[0]; - const LLRelationship* info = LLAvatarTracker::instance().getBuddyInfo(agent_id); - std::string fullname; - if(info && gCacheName->getFullName(agent_id, fullname)) - { - gIMMgr->addSession(fullname, IM_NOTHING_SPECIAL, agent_id); - } + LLAvatarActions::startIM(ids[0]); } else { - gIMMgr->addSession("Friends Conference", IM_SESSION_CONFERENCE_START, ids[0], ids); + LLAvatarActions::startConference(ids); } - make_ui_sound("UISndStartIM"); } } -// static -void LLPanelFriends::requestFriendship(const LLUUID& target_id, const std::string& target_name, const std::string& message) -{ - LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - send_improved_im(target_id, - target_name, - message, - IM_ONLINE, - IM_FRIENDSHIP_OFFERED, - calling_card_folder_id); -} - -// static -bool LLPanelFriends::callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option == 0) - { - requestFriendship(notification["payload"]["id"].asUUID(), - notification["payload"]["name"].asString(), - response["message"].asString()); - } - return false; -} - -bool LLPanelFriends::callbackAddFriend(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - if (option == 0) - { - // Servers older than 1.25 require the text of the message to be the - // calling card folder ID for the offering user. JC - LLUUID calling_card_folder_id = - gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - std::string message = calling_card_folder_id.asString(); - requestFriendship(notification["payload"]["id"].asUUID(), - notification["payload"]["name"].asString(), - message); - } - return false; -} - // static void LLPanelFriends::onPickAvatar( const uuid_vec_t& ids, const std::vector& names ) { if (names.empty()) return; if (ids.empty()) return; - requestFriendshipDialog(ids[0], names[0].getCompleteName()); -} - -// static -void LLPanelFriends::requestFriendshipDialog(const LLUUID& id, - const std::string& name) -{ - if(id == gAgentID) - { - LLNotificationsUtil::add("AddSelfFriend"); - return; - } - - LLSD args; - args["NAME"] = name; - LLSD payload; - payload["id"] = id; - payload["name"] = name; - // Look for server versions like: Second Life Server 1.24.4.95600 - if (gLastVersionChannel.find(" 1.24.") != std::string::npos) - { - // Old and busted server version, doesn't support friend - // requests with messages. - LLNotificationsUtil::add("AddFriend", args, payload, &callbackAddFriend); - } - else - { - LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage); - } + LLAvatarActions::requestFriendshipDialog(ids[0], names[0].getCompleteName()); } // static @@ -962,44 +864,6 @@ void LLPanelFriends::onClickAddFriend(void* user_data) } } -// static -void LLPanelFriends::onClickRemove(void* user_data) -{ - LLPanelFriends* panelp = (LLPanelFriends*)user_data; - - //llinfos << "LLPanelFriends::onClickRemove()" << llendl; - const uuid_vec_t ids = panelp->mFriendsList->getSelectedIDs(); - LLSD args; - if(!ids.empty()) - { - std::string msgType = "RemoveFromFriends"; - if(ids.size() == 1) - { - LLUUID agent_id = ids[0]; - std::string fullname; - if (LLAvatarNameCache::getPNSName(agent_id, fullname)) - args["NAME"] = fullname; - } - else - { - msgType = "RemoveMultipleFromFriends"; - } - LLSD payload; - - for (uuid_vec_t::const_iterator it = ids.begin(); - it != ids.end(); - ++it) - { - payload["ids"].append(*it); - } - - LLNotifications::instance().add(msgType, - args, - payload, - &handleRemove); - } -} - void LLPanelFriends::onClickExport(void* user_data) { std::string agn; @@ -1050,7 +914,6 @@ void LLPanelFriends::onClickExport_continued(void* user_data, AIFilePicker* file export_file.close(); } -bool LLPanelFriends::merging; void LLPanelFriends::onClickImport(void* user_data) { @@ -1091,6 +954,7 @@ void LLPanelFriends::onClickImport_filepicker_continued(AIFilePicker* filepicker LLSD importstatellsd; LLSDSerialize::fromXMLDocument(importstatellsd, stateload); + static bool merging; //LLMessageSystem* msg = gMessageSystem; LLSD newdata; @@ -1108,10 +972,10 @@ void LLPanelFriends::onClickImport_filepicker_continued(AIFilePicker* filepicker if(merging && importstatellsd.has(agent_id.asString()))continue;//dont need to request what we've already requested from another list import and have not got a reply yet std::string agent_name = content["name"]; - if(!is_agent_friend(agent_id))//dont need to request what we have + if(!LLAvatarActions::isFriend(agent_id))//dont need to request what we have { if(merging)importstatellsd[agent_id.asString()] = content;//MERGEEEE - requestFriendship(agent_id, agent_name, "Imported from "+file); + LLAvatarActions::requestFriendship(agent_id, agent_name, "Imported from "+file); newdata[iter->first] = iter->second; }else { @@ -1155,7 +1019,7 @@ void LLPanelFriends::FriendImportState(LLUUID id, bool accepted) if(can_map)rights |= LLRelationship::GRANT_MAP_LOCATION; if(can_mod)rights |= LLRelationship::GRANT_MODIFY_OBJECTS; if(see_online)rights |= LLRelationship::GRANT_ONLINE_STATUS; - if(is_agent_friend(id))//is this legit shit yo + if(LLAvatarActions::isFriend(id))//is this legit shit yo { const LLRelationship* friend_status = LLAvatarTracker::instance().getBuddyInfo(id); if(friend_status) @@ -1185,18 +1049,6 @@ void LLPanelFriends::FriendImportState(LLUUID id, bool accepted) } } -// static -void LLPanelFriends::onClickOfferTeleport(void* user_data) -{ - LLPanelFriends* panelp = (LLPanelFriends*)user_data; - - const uuid_vec_t ids = panelp->mFriendsList->getSelectedIDs(); - if(!ids.empty()) - { - handle_lure(ids); - } -} - // static void LLPanelFriends::onClickPay(void* user_data) { @@ -1205,58 +1057,56 @@ void LLPanelFriends::onClickPay(void* user_data) const uuid_vec_t ids = panelp->mFriendsList->getSelectedIDs(); if(!ids.empty()) { - handle_pay_by_id(ids[0]); + LLAvatarActions::pay(ids[0]); } } -void LLPanelFriends::confirmModifyRights(rights_map_t& ids, EGrantRevoke command) +void LLPanelFriends::confirmModifyRights(rights_map_t& rights, EGrantRevoke command) { - if (ids.empty()) return; - - LLSD args; - if(!ids.empty()) + if (rights.empty()) return; + + // Make a copy on the heap: rights is allocated on the stack. + // This copy will be deleted in LLPanelFriends::modifyRightsConfirmation. + rights_map_t* heap_rights = new rights_map_t(rights); + + // for single friend, show their name + if (rights.size() == 1) { - rights_map_t* rights = new rights_map_t(ids); + LLSD args; + std::string fullname; + if (LLAvatarNameCache::getPNSName(rights.begin()->first, fullname)) + args["NAME"] = fullname; - // for single friend, show their name - if(ids.size() == 1) + if (command == GRANT) { - LLUUID agent_id = ids.begin()->first; - std::string fullname; - if (LLAvatarNameCache::getPNSName(agent_id, fullname)) - args["NAME"] = fullname; - - if (command == GRANT) - { - LLNotificationsUtil::add("GrantModifyRights", - args, - LLSD(), - boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights)); - } - else - { - LLNotificationsUtil::add("RevokeModifyRights", - args, - LLSD(), - boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights)); - } + LLNotificationsUtil::add("GrantModifyRights", + args, + LLSD(), + boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, heap_rights)); } else { - if (command == GRANT) - { - LLNotificationsUtil::add("GrantModifyRightsMultiple", - args, - LLSD(), - boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights)); - } - else - { - LLNotificationsUtil::add("RevokeModifyRightsMultiple", - args, - LLSD(), - boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, rights)); - } + LLNotificationsUtil::add("RevokeModifyRights", + args, + LLSD(), + boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, heap_rights)); + } + } + else + { + if (command == GRANT) + { + LLNotificationsUtil::add("GrantModifyRightsMultiple", + LLSD(), + LLSD(), + boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, heap_rights)); + } + else + { + LLNotificationsUtil::add("RevokeModifyRightsMultiple", + LLSD(), + LLSD(), + boost::bind(&LLPanelFriends::modifyRightsConfirmation, this, _1, _2, heap_rights)); } } } @@ -1403,42 +1253,3 @@ void LLPanelFriends::sendRightsGrant(rights_map_t& ids) mNumRightsChanged = ids.size(); gAgent.sendReliableMessage(); } - - - -// static -bool LLPanelFriends::handleRemove(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - const LLSD& ids = notification["payload"]["ids"]; - for(LLSD::array_const_iterator itr = ids.beginArray(); itr != ids.endArray(); ++itr) - { - LLUUID id = itr->asUUID(); - const LLRelationship* ip = LLAvatarTracker::instance().getBuddyInfo(id); - if(ip) - { - switch(option) - { - case 0: // YES - if( ip->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) - { - LLAvatarTracker::instance().empower(id, FALSE); - LLAvatarTracker::instance().notifyObservers(); - } - LLAvatarTracker::instance().terminateBuddy(id); - LLAvatarTracker::instance().notifyObservers(); - gInventory.addChangedMask(LLInventoryObserver::LABEL | LLInventoryObserver::CALLING_CARD, LLUUID::null); - gInventory.notifyObservers(); - break; - - case 1: // NO - default: - llinfos << "No removal performed." << llendl; - break; - } - } - - } - return false; -} diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h index 5fa23fe52..c82db7886 100644 --- a/indra/newview/llfloaterfriends.h +++ b/indra/newview/llfloaterfriends.h @@ -76,15 +76,6 @@ public: virtual BOOL postBuild(); - // Show a dialog explaining what friendship entails, then request - // friendship. JC - static void requestFriendshipDialog(const LLUUID& target_id, - const std::string& target_name); - - // Just request friendship, no dialog. - static void requestFriendship(const LLUUID& target_id, - const std::string& target_name, const std::string& message); - void populateContactGroupSelect(); private: @@ -130,8 +121,6 @@ private: // callback methods static void onSelectName(LLUICtrl* ctrl, void* user_data); static void onChangeContactGroup(LLUICtrl* ctrl, void* user_data); - static bool callbackAddFriend(const LLSD& notification, const LLSD& response); - static bool callbackAddFriendWithMessage(const LLSD& notification, const LLSD& response); static void onPickAvatar(const uuid_vec_t& ids, const std::vector& names ); void onContactFilterEdit(const std::string& search_string); static void onClickIM(void* user_data); @@ -140,7 +129,6 @@ private: static void updateColumns(void* user_data); static void onClickProfile(void* user_data); static void onClickAddFriend(void* user_data); - static void onClickRemove(void* user_data); static void onClickExport(void* user_data); static void onClickExport_continued(void* user_data, AIFilePicker* filepicker); static void onClickImport(void* user_data); @@ -148,12 +136,9 @@ private: public: static void FriendImportState(LLUUID id, bool accepted); private: - static void onClickOfferTeleport(void* user_data); static void onClickPay(void* user_data); static void onClickModifyStatus(LLUICtrl* ctrl, void* user_data); - - static bool handleRemove(const LLSD& notification, const LLSD& response); bool modifyRightsConfirmation(const LLSD& notification, const LLSD& response, rights_map_t* rights); private: @@ -168,7 +153,6 @@ private: S32 mNumRightsChanged; S32 mNumOnline; std::string mLastContactSearch; - static bool merging; }; diff --git a/indra/newview/llfloatergroupinfo.cpp b/indra/newview/llfloatergroupinfo.cpp index 1926563f6..cbe1636ea 100644 --- a/indra/newview/llfloatergroupinfo.cpp +++ b/indra/newview/llfloatergroupinfo.cpp @@ -36,101 +36,30 @@ #include "llfloatergroupinfo.h" -#include "llagent.h" -#include "llcommandhandler.h" -#include "llfloaterchatterbox.h" -#include "llpanelgroup.h" -#include "llviewermessage.h" // for inventory_offer_callback -//#include "llviewerwindow.h" -#include "llnotifications.h" - #include "llcachename.h" +#include "llpanelgroup.h" const char FLOATER_TITLE[] = "Group Information"; -const LLRect FGI_RECT(0, 530, 420, 0); - -// -// Globals -// -std::map LLFloaterGroupInfo::sInstances; - -//moved to llgroupactions.cpp in v2 -class LLGroupHandler : public LLCommandHandler -{ -public: - // requires trusted browser to trigger - LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) - { - if (tokens.size() < 1) - { - return false; - } - - if (tokens[0].asString() == "create") - { - LLFloaterGroupInfo::showCreateGroup(NULL); - return true; - } - - if (tokens.size() < 2) - { - return false; - } - - if (tokens[0].asString() == "list") - { - if (tokens[1].asString() == "show") - { - // CP_TODO: get the value we pass in via the XUI name - // of the tab instead of using a literal like this - LLFloaterMyFriends::showInstance( 1 ); - - return true; - } - return false; - } - - LLUUID group_id; - if (!group_id.set(tokens[0], FALSE)) - { - return false; - } - - if ((tokens[1].asString() == "about") || (tokens[1].asString() == "inspect")) - { - LLFloaterGroupInfo::showFromUUID(group_id); - return true; - } - return false; - } -}; -LLGroupHandler gGroupHandler; //----------------------------------------------------------------------------- // Implementation //----------------------------------------------------------------------------- -LLFloaterGroupInfo::LLFloaterGroupInfo(const std::string& name, const LLRect &rect, const std::string& title, const LLUUID& group_id, const std::string& tab_name) -: LLFloater(name, rect, title), - mGroupID( group_id ) +LLFloaterGroupInfo::LLFloaterGroupInfo(const LLUUID& group_id) +: LLFloater(FLOATER_TITLE, LLRect(0, 530, 420, 0), FLOATER_TITLE), LLInstanceTracker(group_id) { - llinfos << name << " : " << title << llendl; - llinfos << rect << llendl; - llinfos << getRect() << llendl; - llinfos << getLocalRect() << llendl; // Construct the filename of the group panel xml definition file. - mPanelGroupp = new LLPanelGroup("panel_group.xml", - "PanelGroup", - group_id, - tab_name); + mPanelGroupp = new LLPanelGroup(group_id); addChild(mPanelGroupp); + if (group_id.notNull()) + { + // Look up the group name. The callback will insert it into the title. + gCacheName->get(group_id, true, boost::bind(&LLFloaterGroupInfo::callbackLoadGroupName, this, _2)); + } } // virtual LLFloaterGroupInfo::~LLFloaterGroupInfo() { - sInstances.erase(mGroupID); } BOOL LLFloaterGroupInfo::canClose() @@ -148,125 +77,11 @@ void LLFloaterGroupInfo::selectTabByName(std::string tab_name) mPanelGroupp->selectTab(tab_name); } -// static -void LLFloaterGroupInfo::showMyGroupInfo(void *) +void LLFloaterGroupInfo::callbackLoadGroupName(const std::string& full_name) { - showFromUUID( gAgent.getGroupID() ); + // Build a new title including the group name. + std::ostringstream title; + title << full_name << " - " << FLOATER_TITLE; + setTitle(title.str()); } -// static -void LLFloaterGroupInfo::showCreateGroup(void *) -{ - showFromUUID(LLUUID::null, "general_tab"); -} - -// static -void LLFloaterGroupInfo::closeGroup(const LLUUID& group_id) -{ - LLFloaterGroupInfo *fgi = get_if_there(sInstances, group_id, (LLFloaterGroupInfo*)NULL); - if (fgi) - { - if (fgi->mPanelGroupp) - { - fgi->mPanelGroupp->close(); - } - } -} - -// static -void LLFloaterGroupInfo::closeCreateGroup() -{ - closeGroup(LLUUID::null); -} - -// static -void LLFloaterGroupInfo::refreshGroup(const LLUUID& group_id) -{ - LLFloaterGroupInfo *fgi = get_if_there(sInstances, group_id, (LLFloaterGroupInfo*)NULL); - if (fgi) - { - if (fgi->mPanelGroupp) - { - fgi->mPanelGroupp->refreshData(); - } - } -} - -// static -void LLFloaterGroupInfo::callbackLoadGroupName(const LLUUID& id, const std::string& full_name, bool is_group) -{ - LLFloaterGroupInfo *fgi = get_if_there(sInstances, id, (LLFloaterGroupInfo*)NULL); - - if (fgi) - { - // Build a new title including the group name. - std::ostringstream title; - title << full_name << " - " << FLOATER_TITLE; - fgi->setTitle(title.str()); - } -} - -// static -void LLFloaterGroupInfo::showFromUUID(const LLUUID& group_id, - const std::string& tab_name) -{ - // If we don't have a floater for this group, create one. - LLFloaterGroupInfo *fgi = get_if_there(sInstances, group_id, (LLFloaterGroupInfo*)NULL); - if (!fgi) - { - fgi = new LLFloaterGroupInfo("groupinfo", - FGI_RECT, - FLOATER_TITLE, - group_id, - tab_name); - - sInstances[group_id] = fgi; - - if (group_id.notNull()) - { - // Look up the group name. - // The callback will insert it into the title. - gCacheName->get(group_id, true, boost::bind(&callbackLoadGroupName, _1, _2, _3)); - } - } - - fgi->selectTabByName(tab_name); - - fgi->center(); - fgi->open(); /*Flawfinder: ignore*/ -} - -// static -void LLFloaterGroupInfo::showNotice(const std::string& subject, - const std::string& message, - const LLUUID& group_id, - const bool& has_inventory, - const std::string& inventory_name, - LLOfferInfo* inventory_offer) -{ - llinfos << "LLFloaterGroupInfo::showNotice : " << subject << llendl; - - if (group_id.isNull()) - { - // We need to clean up that inventory offer. - if (inventory_offer) - { - inventory_offer->forceResponse(IOR_DECLINE); - } - return; - } - - // If we don't have a floater for this group, drop this packet on the floor. - LLFloaterGroupInfo *fgi = get_if_there(sInstances, group_id, (LLFloaterGroupInfo*)NULL); - if (!fgi) - { - // We need to clean up that inventory offer. - if (inventory_offer) - { - inventory_offer->forceResponse(IOR_DECLINE); - } - return; - } - - fgi->mPanelGroupp->showNotice(subject,message,has_inventory,inventory_name,inventory_offer); -} diff --git a/indra/newview/llfloatergroupinfo.h b/indra/newview/llfloatergroupinfo.h index ac1beecd2..fda3c348a 100644 --- a/indra/newview/llfloatergroupinfo.h +++ b/indra/newview/llfloatergroupinfo.h @@ -39,48 +39,29 @@ #define LL_LLFLOATERGROUPINFO_H #include "llfloater.h" -#include "lluuid.h" +#include "llinstancetracker.h" class LLPanelGroup; -struct LLOfferInfo; class LLFloaterGroupInfo -: public LLFloater +: public LLFloater, public LLInstanceTracker { public: + LLFloaterGroupInfo(const LLUUID& group_id); virtual ~LLFloaterGroupInfo(); - static void showCreateGroup(void *); - static void showMyGroupInfo(void *); - static void showFromUUID(const LLUUID &group_id, - const std::string& tab_name = std::string()); - static void closeCreateGroup(); - static void closeGroup(const LLUUID& group_id); - static void refreshGroup(const LLUUID& group_id); - - static void showNotice(const std::string& subject, - const std::string& message, - const LLUUID& group_id, - const bool& has_inventory, - const std::string& inventory_name, - LLOfferInfo* inventory_offer); - - LLUUID getGroupID() { return mGroupID;} - void selectTabByName(std::string tab_name); - // This allow us to block the user from closing the floater - // if there is information that needs to be applied. + // This allows us to block the user from closing the floater if there is information that needs to be applied. virtual BOOL canClose(); + protected: - LLFloaterGroupInfo(const std::string& name, const LLRect &rect, const std::string& title, const LLUUID& group_id = LLUUID::null, const std::string& tab_name = std::string()); + friend class LLGroupActions; + LLPanelGroup* mPanelGroupp; private: - static void callbackLoadGroupName(const LLUUID& id, const std::string& full_name, bool is_group); - static std::map sInstances; - - LLUUID mGroupID; - LLPanelGroup* mPanelGroupp; + void callbackLoadGroupName(const std::string& full_name); }; #endif + diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp index a1b0f4ae9..1f5a9cf24 100644 --- a/indra/newview/llfloatergroups.cpp +++ b/indra/newview/llfloatergroups.cpp @@ -51,6 +51,7 @@ #include "llfloatergroupinfo.h" #include "llfloaterdirectory.h" #include "llfocusmgr.h" +#include "llgroupactions.h" #include "llselectmgr.h" #include "llscrolllistctrl.h" #include "llnotificationsutil.h" @@ -218,23 +219,23 @@ BOOL LLPanelGroups::postBuild() init_group_list(group_list, gAgent.getGroupID(), none_text); group_list->setCommitCallback(boost::bind(&LLPanelGroups::onGroupList,this)); group_list->setSortChangedCallback(boost::bind(&LLPanelGroups::onGroupSortChanged,this)); //Force 'none' to always be first entry. - group_list->setDoubleClickCallback(boost::bind(&LLPanelGroups::onBtnIM,this)); + group_list->setDoubleClickCallback(boost::bind(LLGroupActions::startIM, boost::bind(&LLScrollListCtrl::getCurrentID, group_list))); - childSetAction("Activate", onBtnActivate, this); + getChild("Activate")->setCommitCallback(boost::bind(LLGroupActions::activate, boost::bind(&LLScrollListCtrl::getCurrentID, group_list))); - childSetAction("Info", onBtnInfo, this); + getChild("Info")->setCommitCallback(boost::bind(LLGroupActions::show, boost::bind(&LLScrollListCtrl::getCurrentID, group_list))); - childSetAction("IM", onBtnIM, this); + getChild("IM")->setCommitCallback(boost::bind(LLGroupActions::startIM, boost::bind(&LLScrollListCtrl::getCurrentID, group_list))); - childSetAction("Leave", onBtnLeave, this); + getChild("Leave")->setCommitCallback(boost::bind(LLGroupActions::leave, boost::bind(&LLScrollListCtrl::getCurrentID, group_list))); - childSetAction("Create", onBtnCreate, this); + getChild("Create")->setCommitCallback(boost::bind(LLGroupActions::createGroup)); - childSetAction("Search...", onBtnSearch, this); + getChild("Search...")->setCommitCallback(boost::bind(LLGroupActions::search)); childSetAction("Invite...", onBtnInvite, this); - childSetAction("Titles...", onBtnTitles, this); + getChild("Titles...")->setCommitCallback(boost::bind(HBFloaterGroupTitles::toggle)); setDefaultBtn("IM"); @@ -291,148 +292,12 @@ void LLPanelGroups::enableButtons() } -void LLPanelGroups::onBtnCreate(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->create(); -} - void LLPanelGroups::onBtnInvite(void* userdata) { LLPanelGroups* self = (LLPanelGroups*)userdata; if(self) self->invite(); } -void LLPanelGroups::onBtnActivate(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->activate(); -} - -void LLPanelGroups::onBtnInfo(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->info(); -} - -void LLPanelGroups::onBtnIM(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->startIM(); -} - -void LLPanelGroups::onBtnLeave(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->leave(); -} - -void LLPanelGroups::onBtnSearch(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->search(); -} - -void LLPanelGroups::onBtnTitles(void* userdata) -{ - LLPanelGroups* self = (LLPanelGroups*)userdata; - if(self) self->titles(); -} - -void LLPanelGroups::create() -{ - llinfos << "LLPanelGroups::create" << llendl; - LLFloaterGroupInfo::showCreateGroup(NULL); -} - -void LLPanelGroups::activate() -{ - llinfos << "LLPanelGroups::activate" << llendl; - LLCtrlListInterface *group_list = childGetListInterface("group list"); - LLUUID group_id; - if (group_list) - { - group_id = group_list->getCurrentID(); - } - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ActivateGroup); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_GroupID, group_id); - gAgent.sendReliableMessage(); -} - -void LLPanelGroups::info() -{ - llinfos << "LLPanelGroups::info" << llendl; - LLCtrlListInterface *group_list = childGetListInterface("group list"); - LLUUID group_id; - if (group_list && (group_id = group_list->getCurrentID()).notNull()) - { - LLFloaterGroupInfo::showFromUUID(group_id); - } -} - -void LLPanelGroups::startIM() -{ - //llinfos << "LLPanelFriends::onClickIM()" << llendl; - LLCtrlListInterface *group_list = childGetListInterface("group list"); - LLUUID group_id; - - if (group_list && (group_id = group_list->getCurrentID()).notNull()) - { - LLGroupData group_data; - if (gAgent.getGroupData(group_id, group_data)) - { - static LLCachedControl tear_off("OtherChatsTornOff"); - if (!tear_off) - gIMMgr->setFloaterOpen(TRUE); - gIMMgr->addSession( - group_data.mName, - IM_SESSION_GROUP_START, - group_id); - make_ui_sound("UISndStartIM"); - } - else - { - // this should never happen, as starting a group IM session - // relies on you belonging to the group and hence having the group data - make_ui_sound("UISndInvalidOp"); - } - } -} - -void LLPanelGroups::leave() -{ - llinfos << "LLPanelGroups::leave" << llendl; - LLCtrlListInterface *group_list = childGetListInterface("group list"); - LLUUID group_id; - if (group_list && (group_id = group_list->getCurrentID()).notNull()) - { - S32 count = gAgent.mGroups.count(); - S32 i; - for(i = 0; i < count; ++i) - { - if(gAgent.mGroups.get(i).mID == group_id) - break; - } - if(i < count) - { - LLSD args; - args["GROUP"] = gAgent.mGroups.get(i).mName; - LLSD payload; - payload["group_id"] = group_id; - LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, callbackLeaveGroup); - } - } -} - -void LLPanelGroups::search() -{ - LLFloaterDirectory::showGroups(); -} - void LLPanelGroups::invite() { LLCtrlListInterface *group_list = childGetListInterface("group list"); @@ -448,31 +313,6 @@ void LLPanelGroups::invite() LLFloaterGroupInvite::showForGroup(group_id); } -void LLPanelGroups::titles() -{ - HBFloaterGroupTitles::toggle(); -} - - -// static -bool LLPanelGroups::callbackLeaveGroup(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - LLUUID group_id = notification["payload"]["group_id"].asUUID(); - if(option == 0) - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_LeaveGroupRequest); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_GroupData); - msg->addUUIDFast(_PREHASH_GroupID, group_id); - gAgent.sendReliableMessage(); - } - return false; -} - void LLPanelGroups::onGroupSortChanged() { LLScrollListCtrl *group_list = getChild("group list"); diff --git a/indra/newview/llfloatergroups.h b/indra/newview/llfloatergroups.h index ebf37acd7..b262b3f4a 100644 --- a/indra/newview/llfloatergroups.h +++ b/indra/newview/llfloatergroups.h @@ -52,7 +52,6 @@ class LLUICtrl; class LLTextBox; class LLScrollListCtrl; class LLButton; -class LLFloaterGroupPicker; class LLFloaterGroupPicker : public LLFloater, public LLUIFactory > { @@ -105,29 +104,8 @@ protected: void onGroupSortChanged(); void onGroupList(); - static void onBtnCreate(void* userdata); - static void onBtnActivate(void* userdata); - static void onBtnInfo(void* userdata); - static void onBtnIM(void* userdata); - static void onBtnLeave(void* userdata); - static void onBtnSearch(void* userdata); - static void onBtnVote(void* userdata); static void onBtnInvite(void* userdata); - static void onBtnTitles(void* userdata); - static void onDoubleClickGroup(void* userdata); - - void create(); - void activate(); - void info(); - void startIM(); - void leave(); - void search(); - void callVote(); void invite(); - void titles(); - - static bool callbackLeaveGroup(const LLSD& notification, const LLSD& response); - }; diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp index a46790763..74093f243 100644 --- a/indra/newview/llfloaterimagepreview.cpp +++ b/indra/newview/llfloaterimagepreview.cpp @@ -3,10 +3,9 @@ * @brief LLFloaterImagePreview class implementation * * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2004-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -60,15 +59,9 @@ #include "llviewershadermgr.h" #include "llviewertexturelist.h" #include "llstring.h" -// -#include "llviewercontrol.h" -// #include "hippogridmanager.h" -//static -S32 LLFloaterImagePreview::sUploadAmount = 10; - const S32 PREVIEW_BORDER_WIDTH = 2; const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; @@ -79,20 +72,11 @@ const S32 PREVIEW_TEXTURE_HEIGHT = 300; //----------------------------------------------------------------------------- // LLFloaterImagePreview() //----------------------------------------------------------------------------- -LLFloaterImagePreview::LLFloaterImagePreview(const std::string& filename) : - LLFloaterNameDesc(filename), - mAvatarPreview(NULL), - mSculptedPreview(NULL), - mLastMouseX(0), - mLastMouseY(0), - mImagep(NULL) -{ - loadImage(mFilenameAndPath); -} - // LLFloaterImagePreview::LLFloaterImagePreview(const std::string& filename, void* item) : LLFloaterNameDesc(filename, item), +// + mAvatarPreview(NULL), mSculptedPreview(NULL), mLastMouseX(0), @@ -101,7 +85,6 @@ LLFloaterImagePreview::LLFloaterImagePreview(const std::string& filename, void* { loadImage(mFilenameAndPath); } -// //----------------------------------------------------------------------------- // postBuild() @@ -114,7 +97,6 @@ BOOL LLFloaterImagePreview::postBuild() } childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); - childSetAction("ok_btn", onBtnOK, this); LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo"); if (iface) @@ -129,7 +111,7 @@ BOOL LLFloaterImagePreview::postBuild() PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD); mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f); - childHide("bad_image_text"); + getChildView("bad_image_text")->setVisible(FALSE); if (mRawImagep.notNull() && gAgent.getRegion() != NULL) { @@ -140,7 +122,7 @@ BOOL LLFloaterImagePreview::postBuild() mSculptedPreview->setPreviewTarget(mRawImagep, 2.0f); if (mRawImagep->getWidth() * mRawImagep->getHeight () <= LL_IMAGE_REZ_LOSSLESS_CUTOFF * LL_IMAGE_REZ_LOSSLESS_CUTOFF) - childEnable("lossless_check"); + getChildView("lossless_check")->setEnabled(TRUE); // gSavedSettings.setBOOL("TemporaryUpload",FALSE); @@ -151,11 +133,13 @@ BOOL LLFloaterImagePreview::postBuild() { mAvatarPreview = NULL; mSculptedPreview = NULL; - childShow("bad_image_text"); - childDisable("clothing_type_combo"); - childDisable("ok_btn"); + getChildView("bad_image_text")->setVisible(TRUE); + getChildView("clothing_type_combo")->setEnabled(FALSE); + getChildView("ok_btn")->setEnabled(FALSE); } + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); + return TRUE; } @@ -169,7 +153,6 @@ LLFloaterImagePreview::~LLFloaterImagePreview() mRawImagep = NULL; mAvatarPreview = NULL; mSculptedPreview = NULL; - mImagep = NULL ; } @@ -352,7 +335,6 @@ void LLFloaterImagePreview::draw() bool LLFloaterImagePreview::loadImage(const std::string& src_filename) { std::string exten = gDirUtilp->getExtension(src_filename); - U32 codec = LLImageBase::getCodecFromExtension(exten); LLPointer raw_image = new LLImageRaw; @@ -771,7 +753,6 @@ BOOL LLImagePreviewAvatar::render() LLVertexBuffer::unbind(); avatarp->updateLOD(); - if (avatarp->mDrawable.notNull()) { LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE); @@ -918,7 +899,6 @@ void LLImagePreviewSculpted::setPreviewTarget(LLImageRaw* imagep, F32 distance) BOOL LLImagePreviewSculpted::render() { mNeedsUpdate = FALSE; - LLGLSUIDefault def; LLGLDisable no_blend(GL_BLEND); LLGLEnable cull(GL_CULL_FACE); diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h index e55ea7f45..c2186450a 100644 --- a/indra/newview/llfloaterimagepreview.h +++ b/indra/newview/llfloaterimagepreview.h @@ -3,10 +3,9 @@ * @brief LLFloaterImagePreview class definition * * $LicenseInfo:firstyear=2004&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2004-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -115,9 +114,8 @@ protected: class LLFloaterImagePreview : public LLFloaterNameDesc { public: - LLFloaterImagePreview(const std::string& filename); // - LLFloaterImagePreview(const std::string& filename, void* item); + LLFloaterImagePreview(const std::string& filename, void* item = NULL); // virtual ~LLFloaterImagePreview(); @@ -129,7 +127,6 @@ public: BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); static void onMouseCaptureLostImagePreview(LLMouseHandler*); - static void setUploadAmount(S32 amount) { sUploadAmount = amount; } void clearAllPreviewTextures(); @@ -146,8 +143,6 @@ protected: LLRect mPreviewRect; LLRectf mPreviewImageRect; LLPointer mImagep ; - - static S32 sUploadAmount; }; #endif // LL_LLFLOATERIMAGEPREVIEW_H diff --git a/indra/newview/llfloaterinspect.cpp b/indra/newview/llfloaterinspect.cpp index 007a4670d..33d735f42 100644 --- a/indra/newview/llfloaterinspect.cpp +++ b/indra/newview/llfloaterinspect.cpp @@ -31,7 +31,8 @@ */ #include "llviewerprecompiledheaders.h" -#include "llfloateravatarinfo.h" + +#include "llavataractions.h" #include "llfloaterinspect.h" #include "llfloatertools.h" #include "llcachename.h" @@ -39,7 +40,6 @@ #include "llselectmgr.h" #include "lltoolcomp.h" #include "lltoolmgr.h" -#include "llviewercontrol.h" #include "llviewerobject.h" #include "lluictrlfactory.h" @@ -123,7 +123,7 @@ void LLFloaterInspect::onClickCreatorProfile(void* ctrl) LLSelectNode* node = sInstance->mObjectSelection->getFirstNode(&func); if(node) { - LLFloaterAvatarInfo::showFromDirectory(node->mPermissions->getCreator()); + LLAvatarActions::showProfile(node->mPermissions->getCreator()); } } } @@ -153,10 +153,10 @@ void LLFloaterInspect::onClickOwnerProfile(void* ctrl) // [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) { - LLFloaterAvatarInfo::showFromDirectory(owner_id); + LLAvatarActions::showProfile(owner_id); } // [/RLVa:KB] -// LLFloaterAvatarInfo::showFromDirectory(owner_id); +// LLAvatarActions::showProfile(owner_id); } } } diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index a00a1914d..6aa386ffd 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -47,17 +47,18 @@ #include "llagent.h" #include "llagentaccess.h" +#include "llavataractions.h" #include "llavatarconstants.h" //For new Online check - HgB #include "llbutton.h" #include "llcheckboxctrl.h" #include "llradiogroup.h" #include "llcombobox.h" #include "llfloaterauction.h" -#include "llfloateravatarinfo.h" #include "llfloateravatarpicker.h" #include "llfloatergroups.h" #include "llfloatergroupinfo.h" #include "llfloaterscriptlimits.h" +#include "llgroupactions.h" #include "lllineeditor.h" #include "llnamelistctrl.h" #include "llnotify.h" @@ -848,8 +849,7 @@ void LLPanelLandGeneral::onClickInfoGroup(void* userdata) LLPanelLandGeneral* panelp = (LLPanelLandGeneral*)userdata; LLParcel* parcel = panelp->mParcel->getParcel(); if (!parcel) return; - LLUUID id = parcel->getGroupID(); - if(id.notNull())LLFloaterGroupInfo::showFromUUID(parcel->getGroupID()); + LLGroupActions::show(parcel->getGroupID()); } void LLPanelLandGeneral::onClickProfile() @@ -859,13 +859,11 @@ void LLPanelLandGeneral::onClickProfile() if (parcel->getIsGroupOwned()) { - const LLUUID& group_id = parcel->getGroupID(); - LLFloaterGroupInfo::showFromUUID(group_id); + LLGroupActions::show(parcel->getGroupID()); } else { - const LLUUID& avatar_id = parcel->getOwnerID(); - LLFloaterAvatarInfo::showFromObject(avatar_id); + LLAvatarActions::showProfile(parcel->getOwnerID()); } } @@ -1179,11 +1177,11 @@ void LLPanelLandObjects::onDoubleClickOwner(void *userdata) BOOL is_group = cell->getValue().asString() == OWNER_GROUP; if (is_group) { - LLFloaterGroupInfo::showFromUUID(owner_id); + LLGroupActions::show(owner_id); } else { - LLFloaterAvatarInfo::showFromDirectory(owner_id); + LLAvatarActions::showProfile(owner_id); } } } @@ -2102,7 +2100,6 @@ void LLPanelLandOptions::refresh() } mSeeAvatarsCtrl->set(parcel->getSeeAVs()); - mSeeAvatarsCtrl->setLabel(getString("see_avs_text")); mSeeAvatarsCtrl->setEnabled(can_change_options && parcel->getHaveNewParcelLimitData()); BOOL can_change_landing_point = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp index b60aab459..4949678c1 100644 --- a/indra/newview/llfloaterlandholdings.cpp +++ b/indra/newview/llfloaterlandholdings.cpp @@ -42,8 +42,8 @@ #include "llagent.h" #include "llbutton.h" -#include "llfloatergroupinfo.h" #include "llfloaterworldmap.h" +#include "llgroupactions.h" #include "llproductinforequest.h" #include "llscrolllistctrl.h" #include "llstatusbar.h" @@ -103,7 +103,7 @@ BOOL LLFloaterLandHoldings::postBuild() LLScrollListCtrl *grant_list = getChild("grant list"); // Grant list - grant_list->setDoubleClickCallback(boost::bind(&LLFloaterLandHoldings::onGrantList,this)); + grant_list->setDoubleClickCallback(boost::bind(LLGroupActions::show, boost::bind(&LLScrollListCtrl::getCurrentID, grant_list))); LLCtrlListInterface *list = grant_list->getListInterface(); if (!list) return TRUE; @@ -323,19 +323,6 @@ void LLFloaterLandHoldings::onClickMap(void* data) self->buttonCore(1); } -// static -void LLFloaterLandHoldings::onGrantList(void* data) -{ - LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data; - LLCtrlSelectionInterface *list = self->childGetSelectionInterface("grant list"); - if (!list) return; - LLUUID group_id = list->getCurrentID(); - if (group_id.notNull()) - { - LLFloaterGroupInfo::showFromUUID(group_id); - } -} - void LLFloaterLandHoldings::refreshAggregates() { S32 allowed_area = gStatusBar->getSquareMetersCredit(); diff --git a/indra/newview/llfloaterlandholdings.h b/indra/newview/llfloaterlandholdings.h index def77cf2a..447ebeb82 100644 --- a/indra/newview/llfloaterlandholdings.h +++ b/indra/newview/llfloaterlandholdings.h @@ -60,8 +60,6 @@ public: static void onClickMap(void*); static void onClickLandmark(void*); - static void onGrantList(void* data); - protected: LLFloaterLandHoldings(); virtual ~LLFloaterLandHoldings(); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index 5bba956c3..6c22653a3 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -39,7 +39,6 @@ #pragma warning (disable : 4263) #pragma warning (disable : 4264) #endif -#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverloaded-virtual" #include "dae.h" //#include "dom.h" @@ -62,7 +61,6 @@ #include "dom/domScale.h" #include "dom/domTranslate.h" #include "dom/domVisual_scene.h" -#pragma GCC diagnostic pop #if LL_MSVC #pragma warning (pop) #endif diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp index 9ac324468..3d0645b7c 100644 --- a/indra/newview/llfloaternamedesc.cpp +++ b/indra/newview/llfloaternamedesc.cpp @@ -3,10 +3,9 @@ * @brief LLFloaterNameDesc class implementation * * $LicenseInfo:firstyear=2002&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2002-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -70,31 +69,16 @@ const S32 PREF_BUTTON_HEIGHT = 16; //----------------------------------------------------------------------------- // LLFloaterNameDesc() //----------------------------------------------------------------------------- -LLFloaterNameDesc::LLFloaterNameDesc(const std::string& filename ) - : LLFloater(std::string("Name/Description Floater")) -{ +LLFloaterNameDesc::LLFloaterNameDesc(const LLSD& filename, void* item ) + : LLFloater(std::string("Name/Description Floater")), // - mItem = NULL; + mItem(item), // - mFilenameAndPath = filename; - mFilename = gDirUtilp->getBaseFileName(filename, false); - // SL-5521 Maintain capitalization of filename when making the inventory item. JC - //LLStringUtil::toLower(mFilename); - mIsAudio = FALSE; -} - -// -LLFloaterNameDesc::LLFloaterNameDesc(const std::string& filename, void* item ) - : LLFloater(std::string("Name/Description Floater")) + mIsAudio(FALSE) { - mItem = item; - mFilenameAndPath = filename; - mFilename = gDirUtilp->getBaseFileName(filename, false); - // SL-5521 Maintain capitalization of filename when making the inventory item. JC - //LLStringUtil::toLower(mFilename); - mIsAudio = FALSE; + mFilenameAndPath = filename.asString(); + mFilename = gDirUtilp->getBaseFileName(mFilenameAndPath, false); } -// //----------------------------------------------------------------------------- // postBuild() @@ -109,11 +93,6 @@ BOOL LLFloaterNameDesc::postBuild() LLStringUtil::stripNonprintable(asset_name); LLStringUtil::trim(asset_name); - std::string exten = gDirUtilp->getExtension(asset_name); - if (exten == "wav") - { - mIsAudio = TRUE; - } asset_name = gDirUtilp->getBaseFileName(asset_name, true); // no extsntion setTitle(mFilename); @@ -128,8 +107,8 @@ BOOL LLFloaterNameDesc::postBuild() r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT ); - childSetCommitCallback("name_form", doCommit, this); - childSetValue("name_form", LLSD(asset_name)); + getChild("name_form")->setCommitCallback(boost::bind(&LLFloaterNameDesc::doCommit, this)); + getChild("name_form")->setValue(LLSD(asset_name)); LLLineEditor *NameEditor = getChild("name_form"); if (NameEditor) @@ -142,7 +121,7 @@ BOOL LLFloaterNameDesc::postBuild() y -= PREVIEW_LINE_HEIGHT; r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT ); - childSetCommitCallback("description_form", doCommit, this); + getChild("description_form")->setCommitCallback(boost::bind(&LLFloaterNameDesc::doCommit, this)); LLLineEditor *DescEditor = getChild("description_form"); if (DescEditor) { @@ -153,14 +132,10 @@ BOOL LLFloaterNameDesc::postBuild() y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f); // Cancel button - childSetAction("cancel_btn", onBtnCancel, this); + getChild("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnCancel, this)); + + getChild("ok_btn")->setLabelArg("[UPLOADFEE]", llformat("%s%d", gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str(), LLGlobalEconomy::Singleton::getInstance()->getPriceUpload())); - // OK button - childSetLabelArg("ok_btn", "[UPLOADFEE]", gHippoGridManager->getConnectedGrid()->getUploadFee()); - if (exten == "wav") - { - childSetAction("ok_btn", onBtnOK, this); - } setDefaultBtn("ok_btn"); return TRUE; @@ -182,45 +157,99 @@ void LLFloaterNameDesc::onCommit() { } -// static //----------------------------------------------------------------------------- // onCommit() //----------------------------------------------------------------------------- -void LLFloaterNameDesc::doCommit( class LLUICtrl *, void* userdata ) +void LLFloaterNameDesc::doCommit() { - LLFloaterNameDesc* self = (LLFloaterNameDesc*) userdata; - self->onCommit(); + onCommit(); } -// static //----------------------------------------------------------------------------- // onBtnOK() //----------------------------------------------------------------------------- -void LLFloaterNameDesc::onBtnOK( void* userdata ) +void LLFloaterNameDesc::onBtnOK() { - LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata; - - fp->childDisable("ok_btn"); // don't allow inadvertent extra uploads + getChildView("ok_btn")->setEnabled(FALSE); // don't allow inadvertent extra uploads LLAssetStorage::LLStoreAssetCallback callback = NULL; S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass). void *nruserdata = NULL; std::string display_name = LLStringUtil::null; - upload_new_resource(fp->mFilenameAndPath, // file - fp->childGetValue("name_form").asString(), - fp->childGetValue("description_form").asString(), + upload_new_resource(mFilenameAndPath, // file + getChild("name_form")->getValue().asString(), + getChild("description_form")->getValue().asString(), 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), display_name, callback, expected_upload_cost, nruserdata); - fp->close(false); + close(false); } -// static //----------------------------------------------------------------------------- // onBtnCancel() //----------------------------------------------------------------------------- -void LLFloaterNameDesc::onBtnCancel( void* userdata ) +void LLFloaterNameDesc::onBtnCancel() { - LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata; - fp->close(false); + close(false); +} + + +//----------------------------------------------------------------------------- +// LLFloaterSoundPreview() +//----------------------------------------------------------------------------- + +LLFloaterSoundPreview::LLFloaterSoundPreview(const LLSD& filename, void* item ) + : LLFloaterNameDesc(filename, item) +{ + mIsAudio = TRUE; +} + +BOOL LLFloaterSoundPreview::postBuild() +{ + if (!LLFloaterNameDesc::postBuild()) + { + return FALSE; + } + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); + return TRUE; +} + + +//----------------------------------------------------------------------------- +// LLFloaterAnimPreview() +//----------------------------------------------------------------------------- + +LLFloaterAnimPreview::LLFloaterAnimPreview(const LLSD& filename, void* item ) + : LLFloaterNameDesc(filename, item) +{ +} + +BOOL LLFloaterAnimPreview::postBuild() +{ + if (!LLFloaterNameDesc::postBuild()) + { + return FALSE; + } + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); + return TRUE; +} + +//----------------------------------------------------------------------------- +// LLFloaterScriptPreview() +//----------------------------------------------------------------------------- + +LLFloaterScriptPreview::LLFloaterScriptPreview(const LLSD& filename, void* item ) + : LLFloaterNameDesc(filename, item) +{ + mIsText = TRUE; +} + +BOOL LLFloaterScriptPreview::postBuild() +{ + if (!LLFloaterNameDesc::postBuild()) + { + return FALSE; + } + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this)); + return TRUE; } diff --git a/indra/newview/llfloaternamedesc.h b/indra/newview/llfloaternamedesc.h index 4febf390b..ad7472d4f 100644 --- a/indra/newview/llfloaternamedesc.h +++ b/indra/newview/llfloaternamedesc.h @@ -3,10 +3,9 @@ * @brief LLFloaterNameDesc class definition * * $LicenseInfo:firstyear=2002&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2002-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -44,29 +43,49 @@ class LLRadioGroup; class LLFloaterNameDesc : public LLFloater { public: - LLFloaterNameDesc(const std::string& filename); // - LLFloaterNameDesc(const std::string& filename, void* item); + LLFloaterNameDesc(const LLSD& filename, void* item = NULL); // virtual ~LLFloaterNameDesc(); virtual BOOL postBuild(); - static void doCommit(class LLUICtrl *, void* userdata); + void onBtnOK(); + void onBtnCancel(); + void doCommit(); + protected: virtual void onCommit(); protected: BOOL mIsAudio; + bool mIsText; std::string mFilenameAndPath; std::string mFilename; - // void* mItem; // +}; - static void onBtnOK(void*); - static void onBtnCancel(void*); +class LLFloaterSoundPreview : public LLFloaterNameDesc +{ +public: + LLFloaterSoundPreview(const LLSD& filename, void* item = NULL ); + virtual BOOL postBuild(); +}; + +class LLFloaterAnimPreview : public LLFloaterNameDesc +{ +public: + LLFloaterAnimPreview(const LLSD& filename, void* item = NULL ); + virtual BOOL postBuild(); +}; + +class LLFloaterScriptPreview : public LLFloaterNameDesc +{ +public: + LLFloaterScriptPreview(const LLSD& filename, void* item = NULL ); + virtual BOOL postBuild(); }; #endif // LL_LLFLOATERNAMEDESC_H diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index b8e0a8576..d4133befe 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -264,8 +264,7 @@ BOOL LLFloaterNotification::postBuild() return TRUE; } - responses_combo->setCommitCallback(onCommitResponse); - responses_combo->setCallbackUserData(this); + responses_combo->setCommitCallback(boost::bind(&LLFloaterNotification::respond, this)); LLSD form_sd = form->asLLSD(); diff --git a/indra/newview/llfloaternotificationsconsole.h b/indra/newview/llfloaternotificationsconsole.h index 037255318..c4a361538 100644 --- a/indra/newview/llfloaternotificationsconsole.h +++ b/indra/newview/llfloaternotificationsconsole.h @@ -73,7 +73,6 @@ public: void onClose(bool app_quitting) { setVisible(FALSE); } private: - static void onCommitResponse(LLUICtrl* ctrl, void* data) { ((LLFloaterNotification*)data)->respond(); } LLNotification* mNote; }; #endif diff --git a/indra/newview/llfloaterobjectiminfo.cpp b/indra/newview/llfloaterobjectiminfo.cpp index 101f45bf5..5d433bca6 100644 --- a/indra/newview/llfloaterobjectiminfo.cpp +++ b/indra/newview/llfloaterobjectiminfo.cpp @@ -35,12 +35,12 @@ #include "llfloaterobjectiminfo.h" #include "llagentdata.h" +#include "llavataractions.h" #include "llcachename.h" #include "llcommandhandler.h" -#include "llfloater.h" -#include "llfloateravatarinfo.h" #include "llfloatergroupinfo.h" #include "llfloatermute.h" +#include "llgroupactions.h" #include "llmutelist.h" #include "llslurl.h" #include "lltrans.h" @@ -126,14 +126,14 @@ void LLFloaterObjectIMInfo::onClickOwner(void* data) LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; if (self->mGroupOwned) { - LLFloaterGroupInfo::showFromUUID(self->mOwnerID); + LLGroupActions::show(self->mOwnerID); } // else // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g else if ( (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) || (!RlvUtil::isNearbyAgent(self->mOwnerID)) ) // [/RLVa:KB] { - LLFloaterAvatarInfo::showFromObject(self->mOwnerID); + LLAvatarActions::showProfile(self->mOwnerID); } } diff --git a/indra/newview/llfloaterpostprocess.cpp b/indra/newview/llfloaterpostprocess.cpp index fd42f6d02..22a89e015 100644 --- a/indra/newview/llfloaterpostprocess.cpp +++ b/indra/newview/llfloaterpostprocess.cpp @@ -75,8 +75,8 @@ LLFloaterPostProcess::LLFloaterPostProcess() : LLFloater(std::string("Post-Proce //Hacky, but for now checkboxes and sliders are assumed to link to shader uniforms. if(dynamic_cast(*child_it) || dynamic_cast(*child_it)) { - LLUICtrl *ctrl = dynamic_cast(*child_it); - ctrl->setCommitCallback(boost::bind(&LLFloaterPostProcess::onControlChanged, _1, (void*)ctrl->getName().c_str())); + LLUICtrl* ctrl = static_cast(*child_it); + ctrl->setCommitCallback(boost::bind(&LLFloaterPostProcess::onControlChanged, _1, _2)); } } } @@ -84,14 +84,13 @@ LLFloaterPostProcess::LLFloaterPostProcess() : LLFloater(std::string("Post-Proce // Effect loading and saving. LLComboBox* comboBox = getChild("PPEffectsCombo"); - childSetAction("PPLoadEffect", &LLFloaterPostProcess::onLoadEffect, comboBox); - comboBox->setCommitCallback(onChangeEffectName); + getChild("PPLoadEffect")->setCommitCallback(boost::bind(&LLFloaterPostProcess::onLoadEffect, this, comboBox)); + comboBox->setCommitCallback(boost::bind(&LLFloaterPostProcess::onChangeEffectName, this, _1)); LLLineEditor* editBox = getChild("PPEffectNameEditor"); - childSetAction("PPSaveEffect", &LLFloaterPostProcess::onSaveEffect, editBox); + getChild("PPSaveEffect")->setCommitCallback(boost::bind(&LLFloaterPostProcess::onSaveEffect, this, editBox)); syncMenu(); - } LLFloaterPostProcess::~LLFloaterPostProcess() @@ -113,47 +112,42 @@ LLFloaterPostProcess* LLFloaterPostProcess::instance() } -void LLFloaterPostProcess::onControlChanged(LLUICtrl* ctrl, void* userData) +void LLFloaterPostProcess::onControlChanged(LLUICtrl* ctrl, const LLSD& v) { - LLSD v = ctrl->getValue(); - LLPostProcess::getInstance()->setSelectedEffectValue((char const *)userData, v); + LLPostProcess::getInstance()->setSelectedEffectValue(ctrl->getName(), v); } -void LLFloaterPostProcess::onLoadEffect(void* userData) +void LLFloaterPostProcess::onLoadEffect(LLComboBox* comboBox) { - LLComboBox* comboBox = static_cast(userData); - LLSD::String effectName(comboBox->getSelectedValue().asString()); LLPostProcess::getInstance()->setSelectedEffect(effectName); - sPostProcess->syncMenu(); + syncMenu(); } -void LLFloaterPostProcess::onSaveEffect(void* userData) +void LLFloaterPostProcess::onSaveEffect(LLLineEditor* editBox) { - LLLineEditor* editBox = static_cast(userData); - std::string effectName(editBox->getValue().asString()); if (LLPostProcess::getInstance()->getAllEffectInfo().has(effectName)) { LLSD payload; payload["effect_name"] = effectName; - LLNotificationsUtil::add("PPSaveEffectAlert", LLSD(), payload, &LLFloaterPostProcess::saveAlertCallback); + LLNotificationsUtil::add("PPSaveEffectAlert", LLSD(), payload, boost::bind(&LLFloaterPostProcess::saveAlertCallback, this, _1, _2)); } else { LLPostProcess::getInstance()->saveEffectAs(effectName); - sPostProcess->syncMenu(); + syncMenu(); } } -void LLFloaterPostProcess::onChangeEffectName(LLUICtrl* ctrl, void * userData) +void LLFloaterPostProcess::onChangeEffectName(LLUICtrl* ctrl) { // get the combo box and name LLComboBox * comboBox = static_cast(ctrl); - LLLineEditor* editBox = sPostProcess->getChild("PPEffectNameEditor"); + LLLineEditor* editBox = getChild("PPEffectNameEditor"); // set the parameter's new name editBox->setValue(comboBox->getSelectedValue()); @@ -168,7 +162,7 @@ bool LLFloaterPostProcess::saveAlertCallback(const LLSD& notification, const LLS { LLPostProcess::getInstance()->saveEffectAs(notification["payload"]["effect_name"].asString()); - sPostProcess->syncMenu(); + syncMenu(); } return false; } diff --git a/indra/newview/llfloaterpostprocess.h b/indra/newview/llfloaterpostprocess.h index 4f0ef7a63..f49a33981 100644 --- a/indra/newview/llfloaterpostprocess.h +++ b/indra/newview/llfloaterpostprocess.h @@ -36,6 +36,8 @@ #include "llfloater.h" class LLButton; +class LLComboBox; +class LLLineEditor; class LLSliderCtrl; class LLTabContainer; class LLPanelPermissions; @@ -58,13 +60,13 @@ public: static LLFloaterPostProcess* instance(); /// post process callbacks - static void onControlChanged(LLUICtrl* ctrl, void* userData); - static void onLoadEffect(void* userData); - static void onSaveEffect(void* userData); - static void onChangeEffectName(LLUICtrl* ctrl, void * userData); + static void onControlChanged(LLUICtrl* ctrl, const LLSD& v); + void onLoadEffect(LLComboBox* comboBox); + void onSaveEffect(LLLineEditor* editBox); + void onChangeEffectName(LLUICtrl* ctrl); /// prompts a user when overwriting an effect - static bool saveAlertCallback(const LLSD& notification, const LLSD& response); + bool saveAlertCallback(const LLSD& notification, const LLSD& response); /// show off our menu static void show(); diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index 84d8f0650..5fcdc8bbf 100644 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -41,18 +41,19 @@ #include "llinventorydefines.h" #include "llagent.h" +#include "llavataractions.h" #include "llbutton.h" #include "llcheckboxctrl.h" -#include "llfloateravatarinfo.h" -#include "llfloatergroupinfo.h" +#include "llgroupactions.h" #include "llinventorymodel.h" +#include "llinventoryobserver.h" #include "lllineeditor.h" #include "llradiogroup.h" #include "llresmgr.h" #include "roles_constants.h" #include "llselectmgr.h" #include "lltextbox.h" -#include "lluiconstants.h" +#include "lltrans.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" @@ -593,10 +594,7 @@ void LLFloaterProperties::onClickCreator() { LLInventoryItem* item = findItem(); if(!item) return; - if(!item->getCreatorUUID().isNull()) - { - LLFloaterAvatarInfo::showFromObject(item->getCreatorUUID()); - } + LLAvatarActions::showProfile(item->getCreatorUUID()); } // static @@ -606,16 +604,15 @@ void LLFloaterProperties::onClickOwner() if(!item) return; if(item->getPermissions().isGroupOwned()) { - LLFloaterGroupInfo::showFromUUID(item->getPermissions().getGroup()); + LLGroupActions::show(item->getPermissions().getGroup()); } else { -// if(!item->getPermissions().getOwner().isNull()) // [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) - if ( (!item->getPermissions().getOwner().isNull()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ) + if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) // [/RLVa:KB] { - LLFloaterAvatarInfo::showFromObject(item->getPermissions().getOwner()); + LLAvatarActions::showProfile(item->getPermissions().getOwner()); } } } @@ -970,7 +967,7 @@ void LLFloaterProperties::dirtyAll() /// LLMultiProperties ///---------------------------------------------------------------------------- -LLMultiProperties::LLMultiProperties(const LLRect &rect) : LLMultiFloater(std::string("Properties"), rect) +LLMultiProperties::LLMultiProperties(const LLRect &rect) : LLMultiFloater(LLTrans::getString("MultiPropertiesTitle"), rect) { } diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index e737b3d56..6b181433d 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -38,9 +38,9 @@ #include "llfontgl.h" #include "llagent.h" +#include "llavataractions.h" #include "llbutton.h" #include "llfloatergodtools.h" -#include "llfloateravatarinfo.h" #include "llnotificationsutil.h" #include "llparcel.h" #include "llscrolllistctrl.h" @@ -126,16 +126,14 @@ BOOL LLFloaterTopObjects::postBuild() if (line_editor) { line_editor->setCommitOnFocusLost(FALSE); - line_editor->setCommitCallback(onGetByOwnerName); - line_editor->setCallbackUserData(this); + line_editor->setCommitCallback(onGetByOwnerName, this); } line_editor = getChild("object_name_editor"); if (line_editor) { line_editor->setCommitOnFocusLost(FALSE); - line_editor->setCommitCallback(onGetByObjectName); - line_editor->setCallbackUserData(this); + line_editor->setCommitCallback(onGetByObjectName, this); }*/ mCurrentMode = STAT_REPORT_TOP_SCRIPTS; @@ -481,8 +479,7 @@ void LLFloaterTopObjects::onProfile(void* data) if (!list) return; LLScrollListItem* first_selected = list->getFirstSelected(); if (!first_selected) return; - LLUUID taskid = first_selected->getUUID(); - LLFloaterAvatarInfo::showFromDirectory(taskid); + LLAvatarActions::showProfile(first_selected->getUUID()); } void LLFloaterTopObjects::onKickBtn(void* data) diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp deleted file mode 100644 index 292262878..000000000 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ /dev/null @@ -1,348 +0,0 @@ -/** - * @file llfloatervoicedevicesettings.cpp - * @author Richard Nelson - * @brief Voice communication set-up - * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llfloatervoicedevicesettings.h" - -// Viewer includes -#include "llagent.h" -#include "llbutton.h" -#include "llcombobox.h" -#include "llfocusmgr.h" -#include "lliconctrl.h" -#include "llprefsvoice.h" -#include "llsliderctrl.h" -#include "llviewercontrol.h" -#include "llvoiceclient.h" -#include "llimpanel.h" - -// Library includes (after viewer) -#include "lluictrlfactory.h" - - -LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() -{ - mCtrlInputDevices = NULL; - mCtrlOutputDevices = NULL; - mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); - mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - mDevicesUpdated = FALSE; - - // grab "live" mic volume level - mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - - // ask for new device enumeration - // now do this in onOpen() instead... - //gVoiceClient->refreshDeviceLists(); -} - -LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() -{ -} - -BOOL LLPanelVoiceDeviceSettings::postBuild() -{ - LLSlider* volume_slider = getChild("mic_volume_slider"); - // set mic volume tuning slider based on last mic volume setting - volume_slider->setValue(mMicVolume); - - childSetCommitCallback("voice_input_device", onCommitInputDevice, this); - childSetCommitCallback("voice_output_device", onCommitOutputDevice, this); - - return TRUE; -} - -void LLPanelVoiceDeviceSettings::draw() -{ - // let user know that volume indicator is not yet available - bool is_in_tuning_mode = gVoiceClient->inTuningMode(); - childSetVisible("wait_text", !is_in_tuning_mode); - - LLPanel::draw(); - - F32 voice_power = gVoiceClient->tuningGetEnergy(); - S32 discrete_power = 0; - - if (!is_in_tuning_mode) - { - discrete_power = 0; - } - else - { - discrete_power = llmin(4, llfloor((voice_power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 4.f)); - } - - if (is_in_tuning_mode) - { - for(S32 power_bar_idx = 0; power_bar_idx < 5; power_bar_idx++) - { - std::string view_name = llformat("%s%d", "bar", power_bar_idx); - LLView* bar_view = getChild(view_name); - if (bar_view) - { - if (power_bar_idx < discrete_power) - { - LLColor4 color = (power_bar_idx >= 3) ? gSavedSettings.getColor4("OverdrivenColor") : gSavedSettings.getColor4("SpeakingColor"); - gl_rect_2d(bar_view->getRect(), color, TRUE); - } - gl_rect_2d(bar_view->getRect(), LLColor4::grey, FALSE); - } - } - } -} - -void LLPanelVoiceDeviceSettings::apply() -{ - std::string s; - if(mCtrlInputDevices) - { - s = mCtrlInputDevices->getSimple(); - gSavedSettings.setString("VoiceInputAudioDevice", s); - mInputDevice = s; - } - - if(mCtrlOutputDevices) - { - s = mCtrlOutputDevices->getSimple(); - gSavedSettings.setString("VoiceOutputAudioDevice", s); - mOutputDevice = s; - } - - // assume we are being destroyed by closing our embedding window - LLSlider* volume_slider = getChild("mic_volume_slider"); - if(volume_slider) - { - F32 slider_value = (F32)volume_slider->getValue().asReal(); - gSavedSettings.setF32("AudioLevelMic", slider_value); - mMicVolume = slider_value; - } -} - -void LLPanelVoiceDeviceSettings::cancel() -{ - gSavedSettings.setString("VoiceInputAudioDevice", mInputDevice); - gSavedSettings.setString("VoiceOutputAudioDevice", mOutputDevice); - - if(mCtrlInputDevices) - mCtrlInputDevices->setSimple(mInputDevice); - - if(mCtrlOutputDevices) - mCtrlOutputDevices->setSimple(mOutputDevice); - - gSavedSettings.setF32("AudioLevelMic", mMicVolume); - LLSlider* volume_slider = getChild("mic_volume_slider"); - if(volume_slider) - { - volume_slider->setValue(mMicVolume); - } -} - -void LLPanelVoiceDeviceSettings::refresh() -{ - //grab current volume - LLSlider* volume_slider = getChild("mic_volume_slider"); - // set mic volume tuning slider based on last mic volume setting - F32 current_volume = (F32)volume_slider->getValue().asReal(); - gVoiceClient->tuningSetMicVolume(current_volume); - - // Fill in popup menus - mCtrlInputDevices = getChild("voice_input_device"); - mCtrlOutputDevices = getChild("voice_output_device"); - - if(!gVoiceClient->deviceSettingsAvailable()) - { - // The combo boxes are disabled, since we can't get the device settings from the daemon just now. - // Put the currently set default (ONLY) in the box, and select it. - if(mCtrlInputDevices) - { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add( mInputDevice, ADD_BOTTOM ); - mCtrlInputDevices->setSimple(mInputDevice); - } - if(mCtrlOutputDevices) - { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add( mOutputDevice, ADD_BOTTOM ); - mCtrlOutputDevices->setSimple(mOutputDevice); - } - } - else if (!mDevicesUpdated) - { - LLVoiceClient::deviceList *devices; - - LLVoiceClient::deviceList::iterator iter; - - if(mCtrlInputDevices) - { - mCtrlInputDevices->removeall(); - mCtrlInputDevices->add( getString("default_text"), ADD_BOTTOM ); - - devices = gVoiceClient->getCaptureDevices(); - for(iter=devices->begin(); iter != devices->end(); iter++) - { - mCtrlInputDevices->add( *iter, ADD_BOTTOM ); - } - - if(!mCtrlInputDevices->setSimple(mInputDevice)) - { - mCtrlInputDevices->setSimple(getString("default_text")); - } - } - - if(mCtrlOutputDevices) - { - mCtrlOutputDevices->removeall(); - mCtrlOutputDevices->add( getString("default_text"), ADD_BOTTOM ); - - devices = gVoiceClient->getRenderDevices(); - for(iter=devices->begin(); iter != devices->end(); iter++) - { - mCtrlOutputDevices->add( *iter, ADD_BOTTOM ); - } - - if(!mCtrlOutputDevices->setSimple(mOutputDevice)) - { - mCtrlOutputDevices->setSimple(getString("default_text")); - } - } - mDevicesUpdated = TRUE; - } -} - -void LLPanelVoiceDeviceSettings::onOpen() -{ - mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); - mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - mMicVolume = gSavedSettings.getF32("AudioLevelMic"); - mDevicesUpdated = FALSE; - - // ask for new device enumeration - gVoiceClient->refreshDeviceLists(); - - // put voice client in "tuning" mode - gVoiceClient->tuningStart(); - LLVoiceChannel::suspend(); -} - -void LLPanelVoiceDeviceSettings::onClose(bool app_quitting) -{ - gVoiceClient->tuningStop(); - LLVoiceChannel::resume(); -} - -// static -void LLPanelVoiceDeviceSettings::onCommitInputDevice(LLUICtrl* ctrl, void* user_data) -{ - if(gVoiceClient) - { - gVoiceClient->setCaptureDevice(ctrl->getValue().asString()); - } -} - -// static -void LLPanelVoiceDeviceSettings::onCommitOutputDevice(LLUICtrl* ctrl, void* user_data) -{ - if(gVoiceClient) - { - gVoiceClient->setRenderDevice(ctrl->getValue().asString()); - } -} - -// -// LLFloaterVoiceDeviceSettings -// - -LLFloaterVoiceDeviceSettings::LLFloaterVoiceDeviceSettings(const LLSD& seed) - : LLFloater(std::string("floater_device_settings")), - mDevicePanel(NULL) -{ - mFactoryMap["device_settings"] = LLCallbackMap(createPanelVoiceDeviceSettings, this); - // do not automatically open singleton floaters (as result of getInstance()) - BOOL no_open = FALSE; - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_device_settings.xml", &mFactoryMap, no_open); - center(); -} - -void LLFloaterVoiceDeviceSettings::onOpen() -{ - if(mDevicePanel) - { - mDevicePanel->onOpen(); - } - - LLFloater::onOpen(); -} - -void LLFloaterVoiceDeviceSettings::onClose(bool app_quitting) -{ - if(mDevicePanel) - { - mDevicePanel->onClose(app_quitting); - } - - setVisible(FALSE); -} - -void LLFloaterVoiceDeviceSettings::apply() -{ - if (mDevicePanel) - { - mDevicePanel->apply(); - } -} - -void LLFloaterVoiceDeviceSettings::cancel() -{ - if (mDevicePanel) - { - mDevicePanel->cancel(); - } -} - -void LLFloaterVoiceDeviceSettings::draw() -{ - if (mDevicePanel) - { - mDevicePanel->refresh(); - } - LLFloater::draw(); -} - -// static -void* LLFloaterVoiceDeviceSettings::createPanelVoiceDeviceSettings(void* user_data) -{ - LLFloaterVoiceDeviceSettings* floaterp = (LLFloaterVoiceDeviceSettings*)user_data; - floaterp->mDevicePanel = new LLPanelVoiceDeviceSettings(); - return floaterp->mDevicePanel; -} diff --git a/indra/newview/llfloatervoicedevicesettings.h b/indra/newview/llfloatervoicedevicesettings.h deleted file mode 100644 index d30a57f16..000000000 --- a/indra/newview/llfloatervoicedevicesettings.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * @file llpanelvoicedevicesettings.h - * @author Richard Nelson - * @brief Voice communication set-up wizard - * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -#ifndef LL_LLFLOATERVOICEDEVICESETTINGS_H -#define LL_LLFLOATERVOICEDEVICESETTINGS_H - -#include "llfloater.h" - -class LLPrefsVoiceLogic; - -class LLPanelVoiceDeviceSettings : public LLPanel -{ -public: - LLPanelVoiceDeviceSettings(); - ~LLPanelVoiceDeviceSettings(); - - /*virtual*/ void draw(); - /*virtual*/ BOOL postBuild(); - void apply(); - void cancel(); - void refresh(); - void onOpen(); - void onClose(bool app_quitting); - -protected: - static void onCommitInputDevice(LLUICtrl* ctrl, void* user_data); - static void onCommitOutputDevice(LLUICtrl* ctrl, void* user_data); - - F32 mMicVolume; - std::string mInputDevice; - std::string mOutputDevice; - class LLComboBox *mCtrlInputDevices; - class LLComboBox *mCtrlOutputDevices; - BOOL mDevicesUpdated; -}; - -class LLFloaterVoiceDeviceSettings : public LLFloater, public LLFloaterSingleton -{ -public: - LLFloaterVoiceDeviceSettings(const LLSD& seed); - /*virtual*/ void onOpen(); - /*virtual*/ void onClose(bool app_quitting); - /*virtual*/ void draw(); - void apply(); - void cancel(); - -protected: - static void* createPanelVoiceDeviceSettings(void* user_data); - - LLPanelVoiceDeviceSettings* mDevicePanel; -}; - -#endif // LL_LLFLOATERVOICEDEVICESETTINGS_H diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp new file mode 100644 index 000000000..415798086 --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -0,0 +1,318 @@ +/** + * @file llfloatervoiceeffect.cpp + * @author Aimee + * @brief Selection and preview of voice effect. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloatervoiceeffect.h" + +#include "llscrolllistctrl.h" +#include "lltexteditor.h" // For linked text hack +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llweb.h" + +LLFloaterVoiceEffect::LLFloaterVoiceEffect(const LLSD& key) + : LLFloater(/*key*/) +{ + mCommitCallbackRegistrar.add("VoiceEffect.Record", boost::bind(&LLFloaterVoiceEffect::onClickRecord, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Play", boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Stop", boost::bind(&LLFloaterVoiceEffect::onClickStop, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Activate", boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_voice_effect.xml"); +} + +// virtual +LLFloaterVoiceEffect::~LLFloaterVoiceEffect() +{ + if(LLVoiceClient::instanceExists()) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->removeObserver(this); + } + } +} + +// virtual +BOOL LLFloaterVoiceEffect::postBuild() +{ + setDefaultBtn("record_btn"); + getChild("record_btn")->setFocus(true); + + // Singu Note: Here we must hack together a linked piece of text + if (LLTextEditor* editor = getChild("voice_morphing_link")) + { + editor->setParseHTML(true); // For some reason, adding style doesn't work unless this is true. + const std::string text = editor->getValue(); + editor->clear(); + LLStyleSP link(new LLStyle); + link->setLinkHREF(LLTrans::getString("voice_morphing_url")); + link->setColor(gSavedSettings.getColor4("HTMLLinkColor")); + editor->appendStyledText(text, false, false, link); + } + + mVoiceEffectList = getChild("voice_effect_list"); + if (mVoiceEffectList) + { + mVoiceEffectList->setCommitCallback(boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); +// mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); + } + + return TRUE; +} + +// virtual +void LLFloaterVoiceEffect::onOpen() +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->addObserver(this); + + // Disconnect from the current voice channel ready to record a voice sample for previewing + effect_interface->enablePreviewBuffer(true); + } + + refreshEffectList(); + updateControls(); +} + +// virtual +void LLFloaterVoiceEffect::onClose(bool app_quitting) +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->enablePreviewBuffer(false); + } + setVisible(false); +} + +void LLFloaterVoiceEffect::refreshEffectList() +{ + if (!mVoiceEffectList) + { + return; + } + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) + { + mVoiceEffectList->setEnabled(false); + return; + } + + LL_DEBUGS("Voice")<< "Rebuilding Voice Morph list."<< LL_ENDL; + + // Preserve selected items and scroll position + S32 scroll_pos = mVoiceEffectList->getScrollPos(); + uuid_vec_t selected_items; + std::vector items = mVoiceEffectList->getAllSelected(); + for(std::vector::const_iterator it = items.begin(); it != items.end(); it++) + { + selected_items.push_back((*it)->getUUID()); + } + + mVoiceEffectList->deleteAllItems(); + + { + // Add the "No Voice Morph" entry + LLSD element; + + element["id"] = LLUUID::null; + element["columns"][NAME_COLUMN]["column"] = "name"; + element["columns"][NAME_COLUMN]["value"] = getString("no_voice_effect"); + element["columns"][NAME_COLUMN]["font"] = "SANSSERIF"; + element["columns"][NAME_COLUMN]["font-style"] = "BOLD"; + + /*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM); + /* Singu Note: Ours works + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + ((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(LLFontGL::BOLD); + } + */ + } + + // Add each Voice Morph template, if there are any (template list includes all usable effects) + const voice_effect_list_t& template_list = effect_interface->getVoiceEffectTemplateList(); + if (!template_list.empty()) + { + for (voice_effect_list_t::const_iterator it = template_list.begin(); it != template_list.end(); ++it) + { + const LLUUID& effect_id = it->second; + + std::string localized_effect = "effect_" + it->first; + std::string effect_name = hasString(localized_effect) ? getString(localized_effect) : it->first; // XML contains localized effects names + + LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id); + + // Tag the active effect. + if (effect_id == LLVoiceClient::instance().getVoiceEffectDefault()) + { + effect_name += " " + getString("active_voice_effect"); + } + + // Tag available effects that are new this session + if (effect_properties["is_new"].asBoolean()) + { + effect_name += " " + getString("new_voice_effect"); + } + + LLDate expiry_date = effect_properties["expiry_date"].asDate(); + bool is_template_only = effect_properties["template_only"].asBoolean(); + + std::string font_style = "NORMAL"; + if (!is_template_only) + { + font_style = "BOLD"; + } + + LLSD element; + element["id"] = effect_id; + + element["columns"][NAME_COLUMN]["column"] = "name"; + element["columns"][NAME_COLUMN]["value"] = effect_name; + element["columns"][NAME_COLUMN]["font"] = "SANSSERIF"; + element["columns"][NAME_COLUMN]["font-style"] = font_style; + + element["columns"][1]["column"] = "expires"; + if (!is_template_only) + { + element["columns"][DATE_COLUMN]["value"] = expiry_date; + element["columns"][DATE_COLUMN]["type"] = "date"; + } + else { + element["columns"][DATE_COLUMN]["value"] = getString("unsubscribed_voice_effect"); + } + element["columns"][DATE_COLUMN]["font"] = "SANSSERIF"; + element["columns"][DATE_COLUMN]["font-style"] = "NORMAL"; + + /*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM); + /* Singu Note: Ours works + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + LLFontGL::StyleFlags style = is_template_only ? LLFontGL::NORMAL : LLFontGL::BOLD; + LLScrollListText* slt = dynamic_cast(sl_item->getColumn(0)); + llassert(slt); + if (slt) + { + slt->setFontStyle(style); + } + } + */ + } + } + + // Re-select items that were selected before, and restore the scroll position + for(uuid_vec_t::iterator it = selected_items.begin(); it != selected_items.end(); it++) + { + mVoiceEffectList->selectByID(*it); + } + mVoiceEffectList->setScrollPos(scroll_pos); + mVoiceEffectList->setEnabled(true); +} + +void LLFloaterVoiceEffect::updateControls() +{ + bool recording = false; + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + recording = effect_interface->isPreviewRecording(); + } + + getChild("record_btn")->setVisible(!recording); + getChild("record_stop_btn")->setVisible(recording); +} + +// virtual +void LLFloaterVoiceEffect::onVoiceEffectChanged(bool effect_list_updated) +{ + if (effect_list_updated) + { + refreshEffectList(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickRecord() +{ + LL_DEBUGS("Voice") << "Record clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->recordPreviewBuffer(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickPlay() +{ + LL_DEBUGS("Voice") << "Play clicked" << LL_ENDL; + if (!mVoiceEffectList) + { + return; + } + + const LLUUID& effect_id = mVoiceEffectList->getCurrentID(); + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->playPreviewBuffer(effect_id); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickStop() +{ + LL_DEBUGS("Voice") << "Stop clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->stopPreviewBuffer(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickActivate() +{ + LL_DEBUGS("Voice") << "Activate clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface && mVoiceEffectList) + { + //effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); // Singu Note: This would return early, since we're disconnected from voice, just set the string and refresh list + gSavedPerAccountSettings.setString("VoiceEffectDefault", mVoiceEffectList->getCurrentID().asString()); + refreshEffectList(); + } +} + diff --git a/indra/newview/llfloatervoiceeffect.h b/indra/newview/llfloatervoiceeffect.h new file mode 100644 index 000000000..9bee8937c --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.h @@ -0,0 +1,74 @@ +/** + * @file llfloatervoiceeffect.h + * @author Aimee + * @brief Selection and preview of voice effects. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERVOICEEFFECT_H +#define LL_LLFLOATERVOICEEFFECT_H + +#include "llfloater.h" +#include "llvoiceclient.h" + +class LLButton; +class LLScrollListCtrl; + +class LLFloaterVoiceEffect + : public LLFloater + , public LLVoiceEffectObserver + , public LLFloaterSingleton +{ +public: + LOG_CLASS(LLFloaterVoiceEffect); + + LLFloaterVoiceEffect(const LLSD& key); + virtual ~LLFloaterVoiceEffect(); + + virtual BOOL postBuild(); + virtual void onOpen(); + virtual void onClose(bool app_quitting); + +private: + enum ColumnIndex + { + NAME_COLUMN = 0, + DATE_COLUMN = 1, + }; + + void refreshEffectList(); + void updateControls(); + + /// Called by voice effect provider when voice effect list is changed. + virtual void onVoiceEffectChanged(bool effect_list_updated); + + void onClickRecord(); + void onClickPlay(); + void onClickStop(); + void onClickActivate(); + + LLUUID mSelectedID; + LLScrollListCtrl* mVoiceEffectList; +}; + +#endif diff --git a/indra/newview/llfloaterwater.cpp b/indra/newview/llfloaterwater.cpp index bb938e94a..f444a7cbc 100644 --- a/indra/newview/llfloaterwater.cpp +++ b/indra/newview/llfloaterwater.cpp @@ -34,36 +34,22 @@ #include "llfloaterwater.h" -#include "pipeline.h" -#include "llsky.h" - -#include "llsliderctrl.h" -#include "llspinctrl.h" -#include "llcolorswatch.h" +// libs #include "llcheckboxctrl.h" +#include "llcolorswatch.h" +#include "llcombobox.h" +#include "llnotificationsutil.h" +#include "llsliderctrl.h" #include "lltexturectrl.h" #include "lluictrlfactory.h" -#include "llviewercamera.h" -#include "llcombobox.h" -#include "lllineeditor.h" #include "llfloaterdaycycle.h" -#include "llboost.h" -#include "llmultisliderctrl.h" +// newview #include "llagent.h" -#include "llinventorymodel.h" -#include "llviewerinventory.h" - -#include "v4math.h" -#include "llviewerdisplay.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llsavedsettingsglue.h" - -#include "llwaterparamset.h" #include "llwaterparammanager.h" +#include "llwaterparamset.h" -#undef max +#undef max // Fixes a Windows compiler error LLFloaterWater* LLFloaterWater::sWaterMenu = NULL; @@ -79,15 +65,13 @@ LLFloaterWater::LLFloaterWater() : LLFloater(std::string("water floater")) if (mWaterPresetCombo != NULL) { populateWaterPresetsList(); - mWaterPresetCombo->setCommitCallback(onChangePresetName); + mWaterPresetCombo->setCommitCallback(boost::bind(&LLFloaterWater::onChangePresetName, this, _1)); } - std::string def_water = getString("WLDefaultWaterNames"); - // no editing or deleting of the blank string sDefaultPresets.insert(""); - boost_tokenizer tokens(def_water, boost::char_separator(":")); + boost_tokenizer tokens(getString("WLDefaultWaterNames"), boost::char_separator(":")); for (boost_tokenizer::iterator token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) { std::string tok(*token_iter); @@ -102,7 +86,8 @@ LLFloaterWater::~LLFloaterWater() { } -void LLFloaterWater::initCallbacks(void) { +void LLFloaterWater::initCallbacks(void) +{ // help buttons initHelpBtn("WaterFogColorHelp", "HelpWaterFogColor"); @@ -121,52 +106,52 @@ void LLFloaterWater::initCallbacks(void) { initHelpBtn("WaterWave1Help", "HelpWaterWave1"); initHelpBtn("WaterWave2Help", "HelpWaterWave2"); - LLWaterParamManager * param_mgr = LLWaterParamManager::getInstance(); + //------------------------------------------------------------------------- - childSetCommitCallback("WaterFogColor", onWaterFogColorMoved, ¶m_mgr->mFogColor); + LLWaterParamManager& water_mgr = LLWaterParamManager::instance(); - // - //childSetCommitCallback("WaterGlow", onColorControlAMoved, ¶m_mgr->mFogColor); + getChild("WaterFogColor")->setCommitCallback(boost::bind(&LLFloaterWater::onWaterFogColorMoved, this, _1, &water_mgr.mFogColor)); + //getChild("WaterGlow")->setCommitCallback(boost::bind(&LLFloaterWater::onColorControlAMoved, this, _1, &water_mgr.mFogColor)); // fog density - childSetCommitCallback("WaterFogDensity", onExpFloatControlMoved, ¶m_mgr->mFogDensity); - childSetCommitCallback("WaterUnderWaterFogMod", onFloatControlMoved, ¶m_mgr->mUnderWaterFogMod); + getChild("WaterFogDensity")->setCommitCallback(boost::bind(&LLFloaterWater::onExpFloatControlMoved, this, _1, &water_mgr.mFogDensity)); + getChild("WaterUnderWaterFogMod")->setCommitCallback(boost::bind(&LLFloaterWater::onFloatControlMoved, this, _1, &water_mgr.mUnderWaterFogMod)); // blue density - childSetCommitCallback("WaterNormalScaleX", onVector3ControlXMoved, ¶m_mgr->mNormalScale); - childSetCommitCallback("WaterNormalScaleY", onVector3ControlYMoved, ¶m_mgr->mNormalScale); - childSetCommitCallback("WaterNormalScaleZ", onVector3ControlZMoved, ¶m_mgr->mNormalScale); + getChild("WaterNormalScaleX")->setCommitCallback(boost::bind(&LLFloaterWater::onVector3ControlXMoved, this, _1, &water_mgr.mNormalScale)); + getChild("WaterNormalScaleY")->setCommitCallback(boost::bind(&LLFloaterWater::onVector3ControlYMoved, this, _1, &water_mgr.mNormalScale)); + getChild("WaterNormalScaleZ")->setCommitCallback(boost::bind(&LLFloaterWater::onVector3ControlZMoved, this, _1, &water_mgr.mNormalScale)); // fresnel - childSetCommitCallback("WaterFresnelScale", onFloatControlMoved, ¶m_mgr->mFresnelScale); - childSetCommitCallback("WaterFresnelOffset", onFloatControlMoved, ¶m_mgr->mFresnelOffset); + getChild("WaterFresnelScale")->setCommitCallback(boost::bind(&LLFloaterWater::onFloatControlMoved, this, _1, &water_mgr.mFresnelScale)); + getChild("WaterFresnelOffset")->setCommitCallback(boost::bind(&LLFloaterWater::onFloatControlMoved, this, _1, &water_mgr.mFresnelOffset)); // scale above/below - childSetCommitCallback("WaterScaleAbove", onFloatControlMoved, ¶m_mgr->mScaleAbove); - childSetCommitCallback("WaterScaleBelow", onFloatControlMoved, ¶m_mgr->mScaleBelow); + getChild("WaterScaleAbove")->setCommitCallback(boost::bind(&LLFloaterWater::onFloatControlMoved, this, _1, &water_mgr.mScaleAbove)); + getChild("WaterScaleBelow")->setCommitCallback(boost::bind(&LLFloaterWater::onFloatControlMoved, this, _1, &water_mgr.mScaleBelow)); // blur mult - childSetCommitCallback("WaterBlurMult", onFloatControlMoved, ¶m_mgr->mBlurMultiplier); + getChild("WaterBlurMult")->setCommitCallback(boost::bind(&LLFloaterWater::onFloatControlMoved, this, _1, &water_mgr.mBlurMultiplier)); // Load/save - //childSetAction("WaterLoadPreset", onLoadPreset, mWaterPresetCombo); - childSetAction("WaterNewPreset", onNewPreset, mWaterPresetCombo); - childSetAction("WaterDeletePreset", onDeletePreset, mWaterPresetCombo); - childSetCommitCallback("WaterSavePreset", onSavePreset, this); + //getChild("WaterLoadPreset")->setCommitCallback(boost::bind(&LLFloaterWater::onLoadPreset, this, mWaterPresetCombo)); + getChild("WaterNewPreset")->setCommitCallback(boost::bind(&LLFloaterWater::onNewPreset, this)); + getChild("WaterDeletePreset")->setCommitCallback(boost::bind(&LLFloaterWater::onDeletePreset, this)); + getChild("WaterSavePreset")->setCommitCallback(boost::bind(&LLFloaterWater::onSavePreset, this, _1)); // wave direction - childSetCommitCallback("WaterWave1DirX", onVector2ControlXMoved, ¶m_mgr->mWave1Dir); - childSetCommitCallback("WaterWave1DirY", onVector2ControlYMoved, ¶m_mgr->mWave1Dir); - childSetCommitCallback("WaterWave2DirX", onVector2ControlXMoved, ¶m_mgr->mWave2Dir); - childSetCommitCallback("WaterWave2DirY", onVector2ControlYMoved, ¶m_mgr->mWave2Dir); + getChild("WaterWave1DirX")->setCommitCallback(boost::bind(&LLFloaterWater::onVector2ControlXMoved, this, _1, &water_mgr.mWave1Dir)); + getChild("WaterWave1DirY")->setCommitCallback(boost::bind(&LLFloaterWater::onVector2ControlYMoved, this, _1, &water_mgr.mWave1Dir)); + getChild("WaterWave2DirX")->setCommitCallback(boost::bind(&LLFloaterWater::onVector2ControlXMoved, this, _1, &water_mgr.mWave2Dir)); + getChild("WaterWave2DirY")->setCommitCallback(boost::bind(&LLFloaterWater::onVector2ControlYMoved, this, _1, &water_mgr.mWave2Dir)); - LLTextureCtrl* textCtrl = getChild("WaterNormalMap"); - textCtrl->setDefaultImageAssetID(DEFAULT_WATER_NORMAL); - childSetCommitCallback("WaterNormalMap", onNormalMapPicked, NULL); + LLTextureCtrl* texture_ctrl = getChild("WaterNormalMap"); + texture_ctrl->setDefaultImageAssetID(DEFAULT_WATER_NORMAL); + texture_ctrl->setCommitCallback(boost::bind(&LLFloaterWater::onNormalMapPicked, this, _1)); // next/prev buttons - //childSetAction("next", onClickNext, this); - //childSetAction("prev", onClickPrev, this); + //getChild("next")->setCommitCallback(boost::bind(&LLFloaterWater::onClickNext, this)); + //getChild("prev")->setCommitCallback(boost::bind(&LLFloaterWater::onClickPrev, this)); } void LLFloaterWater::onClickHelp(void* data) @@ -185,14 +170,14 @@ void LLFloaterWater::initHelpBtn(const std::string& name, const std::string& xml bool LLFloaterWater::newPromptCallback(const LLSD& notification, const LLSD& response) { std::string text = response["message"].asString(); - S32 option = LLNotification::getSelectedOption(notification, response); - - if(text == "") + if(text.empty()) { return false; } - if(option == 0) { + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if(option == 0) + { LLWaterParamManager * param_mgr = LLWaterParamManager::getInstance(); // add the current parameters to the list @@ -201,10 +186,9 @@ bool LLFloaterWater::newPromptCallback(const LLSD& notification, const LLSD& res if(!LLWaterParamManager::getInstance()->hasParamSet(text)) { param_mgr->addParamSet(text, param_mgr->mCurParams); - sWaterMenu->mWaterPresetCombo->add(text); - sWaterMenu->mWaterPresetCombo->sortByName(); - - sWaterMenu->mWaterPresetCombo->selectByValue(text); + mWaterPresetCombo->add(text); + mWaterPresetCombo->sortByName(); + mWaterPresetCombo->selectByValue(text); param_mgr->savePreset(text); @@ -214,7 +198,7 @@ bool LLFloaterWater::newPromptCallback(const LLSD& notification, const LLSD& res } else { - LLNotifications::instance().add("ExistsWaterPresetAlert"); + LLNotificationsUtil::add("ExistsWaterPresetAlert"); } } return false; @@ -224,9 +208,9 @@ void LLFloaterWater::syncMenu() { bool err; - LLWaterParamManager * param_mgr = LLWaterParamManager::getInstance(); + LLWaterParamManager& water_mgr = LLWaterParamManager::instance(); - LLWaterParamSet & current_params = param_mgr->mCurParams; + LLWaterParamSet& current_params = water_mgr.mCurParams; if (mWaterPresetCombo->getSelectedItemLabel() != LLEnvManagerNew::instance().getWaterPresetName()) { @@ -234,58 +218,56 @@ void LLFloaterWater::syncMenu() } // blue horizon - param_mgr->mFogColor = current_params.getVector4(param_mgr->mFogColor.mName, err); + water_mgr.mFogColor = current_params.getVector4(water_mgr.mFogColor.mName, err); - LLColor4 col = param_mgr->getFogColor(); - //childSetValue("WaterGlow", col.mV[3]); + LLColor4 col = water_mgr.getFogColor(); + //getChild("WaterGlow")->setValue(col.mV[3]); col.mV[3] = 1.0f; - LLColorSwatchCtrl* colCtrl = sWaterMenu->getChild("WaterFogColor"); - - colCtrl->set(col); + getChild("WaterFogColor")->set(col); // fog and wavelets - param_mgr->mFogDensity.mExp = - log(current_params.getFloat(param_mgr->mFogDensity.mName, err)) / - log(param_mgr->mFogDensity.mBase); - param_mgr->setDensitySliderValue(param_mgr->mFogDensity.mExp); - childSetValue("WaterFogDensity", param_mgr->mFogDensity.mExp); + water_mgr.mFogDensity.mExp = + log(current_params.getFloat(water_mgr.mFogDensity.mName, err)) / + log(water_mgr.mFogDensity.mBase); + water_mgr.setDensitySliderValue(water_mgr.mFogDensity.mExp); + getChild("WaterFogDensity")->setValue(water_mgr.mFogDensity.mExp); - param_mgr->mUnderWaterFogMod.mX = - current_params.getFloat(param_mgr->mUnderWaterFogMod.mName, err); - childSetValue("WaterUnderWaterFogMod", param_mgr->mUnderWaterFogMod.mX); + water_mgr.mUnderWaterFogMod.mX = + current_params.getFloat(water_mgr.mUnderWaterFogMod.mName, err); + getChild("WaterUnderWaterFogMod")->setValue(water_mgr.mUnderWaterFogMod.mX); - param_mgr->mNormalScale = current_params.getVector3(param_mgr->mNormalScale.mName, err); - childSetValue("WaterNormalScaleX", param_mgr->mNormalScale.mX); - childSetValue("WaterNormalScaleY", param_mgr->mNormalScale.mY); - childSetValue("WaterNormalScaleZ", param_mgr->mNormalScale.mZ); + water_mgr.mNormalScale = current_params.getVector3(water_mgr.mNormalScale.mName, err); + getChild("WaterNormalScaleX")->setValue(water_mgr.mNormalScale.mX); + getChild("WaterNormalScaleY")->setValue(water_mgr.mNormalScale.mY); + getChild("WaterNormalScaleZ")->setValue(water_mgr.mNormalScale.mZ); // Fresnel - param_mgr->mFresnelScale.mX = current_params.getFloat(param_mgr->mFresnelScale.mName, err); - childSetValue("WaterFresnelScale", param_mgr->mFresnelScale.mX); - param_mgr->mFresnelOffset.mX = current_params.getFloat(param_mgr->mFresnelOffset.mName, err); - childSetValue("WaterFresnelOffset", param_mgr->mFresnelOffset.mX); + water_mgr.mFresnelScale.mX = current_params.getFloat(water_mgr.mFresnelScale.mName, err); + getChild("WaterFresnelScale")->setValue(water_mgr.mFresnelScale.mX); + water_mgr.mFresnelOffset.mX = current_params.getFloat(water_mgr.mFresnelOffset.mName, err); + getChild("WaterFresnelOffset")->setValue(water_mgr.mFresnelOffset.mX); // Scale Above/Below - param_mgr->mScaleAbove.mX = current_params.getFloat(param_mgr->mScaleAbove.mName, err); - childSetValue("WaterScaleAbove", param_mgr->mScaleAbove.mX); - param_mgr->mScaleBelow.mX = current_params.getFloat(param_mgr->mScaleBelow.mName, err); - childSetValue("WaterScaleBelow", param_mgr->mScaleBelow.mX); + water_mgr.mScaleAbove.mX = current_params.getFloat(water_mgr.mScaleAbove.mName, err); + getChild("WaterScaleAbove")->setValue(water_mgr.mScaleAbove.mX); + water_mgr.mScaleBelow.mX = current_params.getFloat(water_mgr.mScaleBelow.mName, err); + getChild("WaterScaleBelow")->setValue(water_mgr.mScaleBelow.mX); // blur mult - param_mgr->mBlurMultiplier.mX = current_params.getFloat(param_mgr->mBlurMultiplier.mName, err); - childSetValue("WaterBlurMult", param_mgr->mBlurMultiplier.mX); + water_mgr.mBlurMultiplier.mX = current_params.getFloat(water_mgr.mBlurMultiplier.mName, err); + getChild("WaterBlurMult")->setValue(water_mgr.mBlurMultiplier.mX); // wave directions - param_mgr->mWave1Dir = current_params.getVector2(param_mgr->mWave1Dir.mName, err); - childSetValue("WaterWave1DirX", param_mgr->mWave1Dir.mX); - childSetValue("WaterWave1DirY", param_mgr->mWave1Dir.mY); + water_mgr.mWave1Dir = current_params.getVector2(water_mgr.mWave1Dir.mName, err); + getChild("WaterWave1DirX")->setValue(water_mgr.mWave1Dir.mX); + getChild("WaterWave1DirY")->setValue(water_mgr.mWave1Dir.mY); - param_mgr->mWave2Dir = current_params.getVector2(param_mgr->mWave2Dir.mName, err); - childSetValue("WaterWave2DirX", param_mgr->mWave2Dir.mX); - childSetValue("WaterWave2DirY", param_mgr->mWave2Dir.mY); + water_mgr.mWave2Dir = current_params.getVector2(water_mgr.mWave2Dir.mName, err); + getChild("WaterWave2DirX")->setValue(water_mgr.mWave2Dir.mX); + getChild("WaterWave2DirY")->setValue(water_mgr.mWave2Dir.mY); - LLTextureCtrl* textCtrl = sWaterMenu->getChild("WaterNormalMap"); - textCtrl->setImageAssetID(param_mgr->getNormalMapID()); + LLTextureCtrl* textCtrl = getChild("WaterNormalMap"); + textCtrl->setImageAssetID(water_mgr.getNormalMapID()); } @@ -342,276 +324,257 @@ void LLFloaterWater::onClose(bool app_quitting) } } -// vector control callbacks -void LLFloaterWater::onVector3ControlXMoved(LLUICtrl* ctrl, void* userData) -{ - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterVector3Control * vectorControl = static_cast(userData); - - vectorControl->mX = sldrCtrl->getValueF32(); - - vectorControl->update(LLWaterParamManager::getInstance()->mCurParams); - - LLWaterParamManager::getInstance()->propagateParameters(); -} - -// vector control callbacks -void LLFloaterWater::onVector3ControlYMoved(LLUICtrl* ctrl, void* userData) -{ - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterVector3Control * vectorControl = static_cast(userData); - - vectorControl->mY = sldrCtrl->getValueF32(); - - vectorControl->update(LLWaterParamManager::getInstance()->mCurParams); - - LLWaterParamManager::getInstance()->propagateParameters(); -} - -// vector control callbacks -void LLFloaterWater::onVector3ControlZMoved(LLUICtrl* ctrl, void* userData) -{ - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterVector3Control * vectorControl = static_cast(userData); - - vectorControl->mZ = sldrCtrl->getValueF32(); - - vectorControl->update(LLWaterParamManager::getInstance()->mCurParams); - - LLWaterParamManager::getInstance()->propagateParameters(); -} - - -// vector control callbacks -void LLFloaterWater::onVector2ControlXMoved(LLUICtrl* ctrl, void* userData) -{ - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterVector2Control * vectorControl = static_cast(userData); - - vectorControl->mX = sldrCtrl->getValueF32(); - - vectorControl->update(LLWaterParamManager::getInstance()->mCurParams); - - LLWaterParamManager::getInstance()->propagateParameters(); -} - -// vector control callbacks -void LLFloaterWater::onVector2ControlYMoved(LLUICtrl* ctrl, void* userData) -{ - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterVector2Control * vectorControl = static_cast(userData); - - vectorControl->mY = sldrCtrl->getValueF32(); - - vectorControl->update(LLWaterParamManager::getInstance()->mCurParams); - - LLWaterParamManager::getInstance()->propagateParameters(); -} - // color control callbacks -void LLFloaterWater::onColorControlRMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onColorControlRMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterColorControl * colorControl = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - colorControl->mR = sldrCtrl->getValueF32(); + color_ctrl->mR = sldr_ctrl->getValueF32(); // move i if it's the max - if(colorControl->mR >= colorControl->mG - && colorControl->mR >= colorControl->mB - && colorControl->mHasSliderName) + if (color_ctrl->mR >= color_ctrl->mG + && color_ctrl->mR >= color_ctrl->mB + && color_ctrl->mHasSliderName) { - colorControl->mI = colorControl->mR; - std::string name = colorControl->mSliderName; + color_ctrl->mI = color_ctrl->mR; + std::string name = color_ctrl->mSliderName; name.append("I"); - sWaterMenu->childSetValue(name, colorControl->mR); + getChild(name)->setValue(color_ctrl->mR); } - colorControl->update(LLWaterParamManager::getInstance()->mCurParams); + color_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } -void LLFloaterWater::onColorControlGMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onColorControlGMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterColorControl * colorControl = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - colorControl->mG = sldrCtrl->getValueF32(); + color_ctrl->mG = sldr_ctrl->getValueF32(); // move i if it's the max - if(colorControl->mG >= colorControl->mR - && colorControl->mG >= colorControl->mB - && colorControl->mHasSliderName) + if (color_ctrl->mG >= color_ctrl->mR + && color_ctrl->mG >= color_ctrl->mB + && color_ctrl->mHasSliderName) { - colorControl->mI = colorControl->mG; - std::string name = colorControl->mSliderName; + color_ctrl->mI = color_ctrl->mG; + std::string name = color_ctrl->mSliderName; name.append("I"); - sWaterMenu->childSetValue(name, colorControl->mG); + getChild(name)->setValue(color_ctrl->mG); } - colorControl->update(LLWaterParamManager::getInstance()->mCurParams); + color_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } -void LLFloaterWater::onColorControlBMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onColorControlBMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterColorControl * colorControl = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - colorControl->mB = sldrCtrl->getValueF32(); + color_ctrl->mB = sldr_ctrl->getValueF32(); // move i if it's the max - if(colorControl->mB >= colorControl->mR - && colorControl->mB >= colorControl->mG - && colorControl->mHasSliderName) + if (color_ctrl->mB >= color_ctrl->mR + && color_ctrl->mB >= color_ctrl->mG + && color_ctrl->mHasSliderName) { - colorControl->mI = colorControl->mB; - std::string name = colorControl->mSliderName; + color_ctrl->mI = color_ctrl->mB; + std::string name = color_ctrl->mSliderName; name.append("I"); - sWaterMenu->childSetValue(name, colorControl->mB); + getChild(name)->setValue(color_ctrl->mB); } - colorControl->update(LLWaterParamManager::getInstance()->mCurParams); + color_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } -void LLFloaterWater::onColorControlAMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onColorControlAMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterColorControl * colorControl = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - colorControl->mA = sldrCtrl->getValueF32(); + color_ctrl->mA = sldr_ctrl->getValueF32(); - colorControl->update(LLWaterParamManager::getInstance()->mCurParams); + color_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } -void LLFloaterWater::onColorControlIMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onColorControlIMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterColorControl * colorControl = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - colorControl->mI = sldrCtrl->getValueF32(); + color_ctrl->mI = sldr_ctrl->getValueF32(); // only for sliders where we pass a name - if(colorControl->mHasSliderName) + if (color_ctrl->mHasSliderName) { // set it to the top - F32 maxVal = std::max(std::max(colorControl->mR, colorControl->mG), colorControl->mB); + F32 maxVal = std::max(std::max(color_ctrl->mR, color_ctrl->mG), color_ctrl->mB); F32 iVal; - iVal = colorControl->mI; + iVal = color_ctrl->mI; // get the names of the other sliders - std::string rName = colorControl->mSliderName; + std::string rName = color_ctrl->mSliderName; rName.append("R"); - std::string gName = colorControl->mSliderName; + std::string gName = color_ctrl->mSliderName; gName.append("G"); - std::string bName = colorControl->mSliderName; + std::string bName = color_ctrl->mSliderName; bName.append("B"); // handle if at 0 if(iVal == 0) { - colorControl->mR = 0; - colorControl->mG = 0; - colorControl->mB = 0; + color_ctrl->mR = 0; + color_ctrl->mG = 0; + color_ctrl->mB = 0; // if all at the start // set them all to the intensity } else if (maxVal == 0) { - colorControl->mR = iVal; - colorControl->mG = iVal; - colorControl->mB = iVal; + color_ctrl->mR = iVal; + color_ctrl->mG = iVal; + color_ctrl->mB = iVal; } else { // add delta amounts to each F32 delta = (iVal - maxVal) / maxVal; - colorControl->mR *= (1.0f + delta); - colorControl->mG *= (1.0f + delta); - colorControl->mB *= (1.0f + delta); + color_ctrl->mR *= (1.0f + delta); + color_ctrl->mG *= (1.0f + delta); + color_ctrl->mB *= (1.0f + delta); } // set the sliders to the new vals - sWaterMenu->childSetValue(rName, colorControl->mR); - sWaterMenu->childSetValue(gName, colorControl->mG); - sWaterMenu->childSetValue(bName, colorControl->mB); + getChild(rName)->setValue(color_ctrl->mR); + getChild(gName)->setValue(color_ctrl->mG); + getChild(bName)->setValue(color_ctrl->mB); } // now update the current parameters and send them to shaders - colorControl->update(LLWaterParamManager::getInstance()->mCurParams); + color_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } -void LLFloaterWater::onExpFloatControlMoved(LLUICtrl* ctrl, void* userData) +// vector control callbacks +void LLFloaterWater::onVector3ControlXMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterExpFloatControl * expFloatControl = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - F32 val = sldrCtrl->getValueF32(); + vector_ctrl->mX = sldr_ctrl->getValueF32(); + + vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); + + LLWaterParamManager::getInstance()->propagateParameters(); +} + +// vector control callbacks +void LLFloaterWater::onVector3ControlYMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl) +{ + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); + + vector_ctrl->mY = sldr_ctrl->getValueF32(); + + vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); + + LLWaterParamManager::getInstance()->propagateParameters(); +} + +// vector control callbacks +void LLFloaterWater::onVector3ControlZMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl) +{ + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); + + vector_ctrl->mZ = sldr_ctrl->getValueF32(); + + vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); + + LLWaterParamManager::getInstance()->propagateParameters(); +} + + +// vector control callbacks +void LLFloaterWater::onVector2ControlXMoved(LLUICtrl* ctrl, WaterVector2Control* vector_ctrl) +{ + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); + + vector_ctrl->mX = sldr_ctrl->getValueF32(); + + vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); + + LLWaterParamManager::getInstance()->propagateParameters(); +} + +// vector control callbacks +void LLFloaterWater::onVector2ControlYMoved(LLUICtrl* ctrl, WaterVector2Control* vector_ctrl) +{ + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); + + vector_ctrl->mY = sldr_ctrl->getValueF32(); + + vector_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); + + LLWaterParamManager::getInstance()->propagateParameters(); +} + +void LLFloaterWater::onFloatControlMoved(LLUICtrl* ctrl, WaterFloatControl* floatControl) +{ + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); + + floatControl->mX = sldr_ctrl->getValueF32() / floatControl->mMult; + + floatControl->update(LLWaterParamManager::getInstance()->mCurParams); + LLWaterParamManager::getInstance()->propagateParameters(); +} + +void LLFloaterWater::onExpFloatControlMoved(LLUICtrl* ctrl, WaterExpFloatControl* expFloatControl) +{ + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); + + F32 val = sldr_ctrl->getValueF32(); expFloatControl->mExp = val; LLWaterParamManager::getInstance()->setDensitySliderValue(val); expFloatControl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } - -void LLFloaterWater::onFloatControlMoved(LLUICtrl* ctrl, void* userData) -{ - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - WaterFloatControl * floatControl = static_cast(userData); - - floatControl->mX = sldrCtrl->getValueF32() / floatControl->mMult; - - floatControl->update(LLWaterParamManager::getInstance()->mCurParams); - LLWaterParamManager::getInstance()->propagateParameters(); -} -void LLFloaterWater::onWaterFogColorMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onWaterFogColorMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl) { LLColorSwatchCtrl* swatch = static_cast(ctrl); - WaterColorControl * colorControl = static_cast(userData); - *colorControl = swatch->get(); + *color_ctrl = swatch->get(); - colorControl->update(LLWaterParamManager::getInstance()->mCurParams); + color_ctrl->update(LLWaterParamManager::getInstance()->mCurParams); LLWaterParamManager::getInstance()->propagateParameters(); } -void LLFloaterWater::onBoolToggle(LLUICtrl* ctrl, void* userData) -{ - LLCheckBoxCtrl* cbCtrl = static_cast(ctrl); - - bool value = cbCtrl->get(); - (*(static_cast(userData))) = value; -} - -void LLFloaterWater::onNormalMapPicked(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onNormalMapPicked(LLUICtrl* ctrl) { LLTextureCtrl* textCtrl = static_cast(ctrl); LLUUID textID = textCtrl->getImageAssetID(); LLWaterParamManager::getInstance()->setNormalMapID(textID); } -void LLFloaterWater::onNewPreset(void* userData) +//============================================================================= + +void LLFloaterWater::onNewPreset() { - LLNotifications::instance().add("NewWaterPreset", LLSD(), LLSD(), newPromptCallback); + LLNotificationsUtil::add("NewWaterPreset", LLSD(), LLSD(), boost::bind(&LLFloaterWater::newPromptCallback, this, _1, _2)); } -void LLFloaterWater::onSavePreset(LLUICtrl* ctrl, void* userData) +void LLFloaterWater::onSavePreset(LLUICtrl* ctrl) { // don't save the empty name - if(sWaterMenu->mWaterPresetCombo->getSelectedItemLabel() == "") + if(mWaterPresetCombo->getSelectedItemLabel().empty()) { return; } @@ -622,25 +585,23 @@ void LLFloaterWater::onSavePreset(LLUICtrl* ctrl, void* userData) } else { - LLWaterParamManager::getInstance()->mCurParams.mName = - sWaterMenu->mWaterPresetCombo->getSelectedItemLabel(); + LLWaterParamManager::getInstance()->mCurParams.mName = mWaterPresetCombo->getSelectedItemLabel(); // check to see if it's a default and shouldn't be overwritten - std::set::iterator sIt = sDefaultPresets.find( - sWaterMenu->mWaterPresetCombo->getSelectedItemLabel()); + std::set::iterator sIt = sDefaultPresets.find(mWaterPresetCombo->getSelectedItemLabel()); if(sIt != sDefaultPresets.end() && !gSavedSettings.getBOOL("WaterEditPresets")) { - LLNotifications::instance().add("WLNoEditDefault"); + LLNotificationsUtil::add("WLNoEditDefault"); return; } - LLNotifications::instance().add("WLSavePresetAlert", LLSD(), LLSD(), saveAlertCallback); + LLNotificationsUtil::add("WLSavePresetAlert", LLSD(), LLSD(), boost::bind(&LLFloaterWater::saveAlertCallback, this, _1, _2)); } } bool LLFloaterWater::saveNotecardCallback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); // if they choose save, do it. Otherwise, don't do anything if(option == 0) { @@ -653,15 +614,13 @@ bool LLFloaterWater::saveNotecardCallback(const LLSD& notification, const LLSD& bool LLFloaterWater::saveAlertCallback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); // if they choose save, do it. Otherwise, don't do anything if(option == 0) { - LLWaterParamManager * param_mgr = LLWaterParamManager::getInstance(); + LLWaterParamManager* param_mgr = LLWaterParamManager::getInstance(); - param_mgr->setParamSet( - param_mgr->mCurParams.mName, - param_mgr->mCurParams); + param_mgr->setParamSet(param_mgr->mCurParams.mName, param_mgr->mCurParams); // comment this back in to save to file param_mgr->savePreset(param_mgr->mCurParams.mName); @@ -669,22 +628,23 @@ bool LLFloaterWater::saveAlertCallback(const LLSD& notification, const LLSD& res return false; } -void LLFloaterWater::onDeletePreset(void* userData) +void LLFloaterWater::onDeletePreset() { - if(sWaterMenu->mWaterPresetCombo->getSelectedValue().asString() == "") + if(mWaterPresetCombo->getSelectedValue().asString().empty()) { return; } LLSD args; - args["SKY"] = sWaterMenu->mWaterPresetCombo->getSelectedValue().asString(); - LLNotifications::instance().add("WLDeletePresetAlert", args, LLSD(), deleteAlertCallback); + args["SKY"] = mWaterPresetCombo->getSelectedValue().asString(); + LLNotificationsUtil::add("WLDeletePresetAlert", args, LLSD(), boost::bind(&LLFloaterWater::deleteAlertCallback, this, _1, _2)); } bool LLFloaterWater::deleteAlertCallback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); - // if they choose delete, do it. Otherwise, don't do anything + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + // If they choose delete, do it. Otherwise, don't do anything if(option == 0) { LLFloaterDayCycle* day_cycle = NULL; @@ -696,22 +656,22 @@ bool LLFloaterWater::deleteAlertCallback(const LLSD& notification, const LLSD& r key_combo = day_cycle->getChild("WaterKeyPresets"); } - std::string name = sWaterMenu->mWaterPresetCombo->getSelectedValue().asString(); + std::string name = mWaterPresetCombo->getSelectedValue().asString(); // check to see if it's a default and shouldn't be deleted std::set::iterator sIt = sDefaultPresets.find(name); if(sIt != sDefaultPresets.end()) { - LLNotifications::instance().add("WaterNoEditDefault"); + LLNotificationsUtil::add("WaterNoEditDefault"); return false; } LLWaterParamManager::getInstance()->removeParamSet(name, true); // remove and choose another - S32 new_index = sWaterMenu->mWaterPresetCombo->getCurrentIndex(); + S32 new_index = mWaterPresetCombo->getCurrentIndex(); - sWaterMenu->mWaterPresetCombo->remove(name); + mWaterPresetCombo->remove(name); if(key_combo != NULL) { @@ -724,23 +684,22 @@ bool LLFloaterWater::deleteAlertCallback(const LLSD& notification, const LLSD& r // pick the previously selected index after delete if(new_index > 0) { - new_index--; + --new_index; } - if(sWaterMenu->mWaterPresetCombo->getItemCount() > 0) + if(mWaterPresetCombo->getItemCount() > 0) { - sWaterMenu->mWaterPresetCombo->setCurrentByIndex(new_index); + mWaterPresetCombo->setCurrentByIndex(new_index); } } return false; } - -void LLFloaterWater::onChangePresetName(LLUICtrl* ctrl, void * userData) +void LLFloaterWater::onChangePresetName(LLUICtrl* ctrl) { - LLComboBox * combo_box = static_cast(ctrl); + LLComboBox* combo_box = static_cast(ctrl); - if(combo_box->getSimple() == "") + if(combo_box->getSimple().empty()) { return; } @@ -756,29 +715,29 @@ void LLFloaterWater::onChangePresetName(LLUICtrl* ctrl, void * userData) // LLEnvManagerNew::instance().useRegionWater(); LLEnvManagerNew::instance().setUseWaterPreset("Default"); } - sWaterMenu->syncMenu(); + syncMenu(); } -void LLFloaterWater::onClickNext(void* user_data) +void LLFloaterWater::onClickNext() { - S32 index = sWaterMenu->mWaterPresetCombo->getCurrentIndex(); - index++; - if (index == sWaterMenu->mWaterPresetCombo->getItemCount()) + S32 index = mWaterPresetCombo->getCurrentIndex(); + ++index; + if (index == mWaterPresetCombo->getItemCount()) index = 0; - sWaterMenu->mWaterPresetCombo->setCurrentByIndex(index); + mWaterPresetCombo->setCurrentByIndex(index); - LLFloaterWater::onChangePresetName(sWaterMenu->mWaterPresetCombo, sWaterMenu); + LLFloaterWater::onChangePresetName(mWaterPresetCombo); } -void LLFloaterWater::onClickPrev(void* user_data) +void LLFloaterWater::onClickPrev() { - S32 index = sWaterMenu->mWaterPresetCombo->getCurrentIndex(); + S32 index = mWaterPresetCombo->getCurrentIndex(); if (index == 0) - index = sWaterMenu->mWaterPresetCombo->getItemCount(); - index--; - sWaterMenu->mWaterPresetCombo->setCurrentByIndex(index); + index = mWaterPresetCombo->getItemCount(); + --index; + mWaterPresetCombo->setCurrentByIndex(index); - LLFloaterWater::onChangePresetName(sWaterMenu->mWaterPresetCombo, sWaterMenu); + LLFloaterWater::onChangePresetName(mWaterPresetCombo); } void LLFloaterWater::populateWaterPresetsList() diff --git a/indra/newview/llfloaterwater.h b/indra/newview/llfloaterwater.h index fbd7e26a9..fd6da0f30 100644 --- a/indra/newview/llfloaterwater.h +++ b/indra/newview/llfloaterwater.h @@ -39,15 +39,16 @@ #include "llfloater.h" -#include #include "llwlparamset.h" #include "llwlparammanager.h" // for LLWLParamKey -struct WaterColorControl; -struct WaterloatControl; - class LLComboBox; +struct WaterVector2Control; +struct WaterVector3Control; +struct WaterColorControl; +struct WaterFloatControl; +struct WaterExpFloatControl; /// Menuing system for all of windlight's functionality class LLFloaterWater : public LLFloater @@ -67,53 +68,54 @@ public: static void onClickHelp(void* data); void initHelpBtn(const std::string& name, const std::string& xml_alert); - static bool newPromptCallback(const LLSD& notification, const LLSD& response); + //-- WL stuff begins ------------------------------------------------------ - /// general purpose callbacks for dealing with color controllers - static void onColorControlRMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlGMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlBMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlAMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlIMoved(LLUICtrl* ctrl, void* userData); + bool newPromptCallback(const LLSD& notification, const LLSD& response); - static void onVector3ControlXMoved(LLUICtrl* ctrl, void* userData); - static void onVector3ControlYMoved(LLUICtrl* ctrl, void* userData); - static void onVector3ControlZMoved(LLUICtrl* ctrl, void* userData); + // general purpose callbacks for dealing with color controllers + void onColorControlRMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl); + void onColorControlGMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl); + void onColorControlBMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl); + void onColorControlAMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl); + void onColorControlIMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl); - static void onVector2ControlXMoved(LLUICtrl* ctrl, void* userData); - static void onVector2ControlYMoved(LLUICtrl* ctrl, void* userData); + void onVector3ControlXMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl); + void onVector3ControlYMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl); + void onVector3ControlZMoved(LLUICtrl* ctrl, WaterVector3Control* vector_ctrl); - static void onFloatControlMoved(LLUICtrl* ctrl, void* userData); + void onVector2ControlXMoved(LLUICtrl* ctrl, WaterVector2Control* vector_ctrl); + void onVector2ControlYMoved(LLUICtrl* ctrl, WaterVector2Control* vector_ctrl); - static void onExpFloatControlMoved(LLUICtrl* ctrl, void* userData); + void onFloatControlMoved(LLUICtrl* ctrl, WaterFloatControl* floatControl); - static void onWaterFogColorMoved(LLUICtrl* ctrl, void* userData); + void onExpFloatControlMoved(LLUICtrl* ctrl, WaterExpFloatControl* expFloatControl); - static void onBoolToggle(LLUICtrl* ctrl, void* userData); + void onWaterFogColorMoved(LLUICtrl* ctrl, WaterColorControl* color_ctrl); - /// handle if they choose a new normal map - static void onNormalMapPicked(LLUICtrl* ctrl, void* userData); + void onNormalMapPicked(LLUICtrl* ctrl); /// handle if they choose a new normal map + + //-- WL stuff ends -------------------------------------------------------- /// when user hits the load preset button - static void onNewPreset(void* userData); + void onNewPreset(); /// when user hits the save preset button - static void onSavePreset(LLUICtrl* ctrl, void* userData); + void onSavePreset(LLUICtrl* ctrl); /// prompts a user when overwriting a preset notecard - static bool saveNotecardCallback(const LLSD& notification, const LLSD& response); + bool saveNotecardCallback(const LLSD& notification, const LLSD& response); /// prompts a user when overwriting a preset - static bool saveAlertCallback(const LLSD& notification, const LLSD& response); + bool saveAlertCallback(const LLSD& notification, const LLSD& response); /// when user hits the save preset button - static void onDeletePreset(void* userData); + void onDeletePreset(); /// prompts a user when overwriting a preset - static bool deleteAlertCallback(const LLSD& notification, const LLSD& response); + bool deleteAlertCallback(const LLSD& notification, const LLSD& response); /// what to do when you change the preset name - static void onChangePresetName(LLUICtrl* ctrl, void* userData); + void onChangePresetName(LLUICtrl* ctrl); //// menu management @@ -135,8 +137,8 @@ private: static std::set sDefaultPresets; - static void onClickNext(void* user_data); - static void onClickPrev(void* user_data); + void onClickNext(); + void onClickPrev(); void populateWaterPresetsList(); diff --git a/indra/newview/llfloaterwindlight.cpp b/indra/newview/llfloaterwindlight.cpp index 278f9c084..7e9eb6a1f 100644 --- a/indra/newview/llfloaterwindlight.cpp +++ b/indra/newview/llfloaterwindlight.cpp @@ -34,37 +34,21 @@ #include "llfloaterwindlight.h" -#include "pipeline.h" -#include "llsky.h" - -#include "llsliderctrl.h" -#include "llmultislider.h" -#include "llmultisliderctrl.h" -#include "llspinctrl.h" +// libs #include "llcheckboxctrl.h" -#include "lluictrlfactory.h" -#include "llviewercamera.h" #include "llcombobox.h" -#include "lllineeditor.h" +#include "llmultisliderctrl.h" +#include "llnotificationsutil.h" +#include "llsliderctrl.h" #include "llfloaterdaycycle.h" #include "lltabcontainer.h" -#include "llboost.h" +#include "lluictrlfactory.h" +// newview #include "llagent.h" -#include "llinventorymodel.h" -#include "llviewerinventory.h" - -#include "v4math.h" -#include "llviewerdisplay.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llsavedsettingsglue.h" - #include "llwlparamset.h" #include "llwlparammanager.h" -#undef max - LLFloaterWindLight* LLFloaterWindLight::sWindLight = NULL; std::set LLFloaterWindLight::sDefaultPresets; @@ -79,18 +63,16 @@ LLFloaterWindLight::LLFloaterWindLight() : LLFloater(std::string("windlight floa // add the combo boxes mSkyPresetCombo = getChild("WLPresetsCombo"); - - if(mSkyPresetCombo != NULL) { + if (mSkyPresetCombo) + { populateSkyPresetsList(); - mSkyPresetCombo->setCommitCallback(onChangePresetName); + mSkyPresetCombo->setCommitCallback(boost::bind(&LLFloaterWindLight::onChangePresetName, this, _1)); } // add the list of presets - std::string def_days = getString("WLDefaultSkyNames"); - // no editing or deleting of the blank string sDefaultPresets.insert(""); - boost_tokenizer tokens(def_days, boost::char_separator(":")); + boost_tokenizer tokens(getString("WLDefaultSkyNames"), boost::char_separator(":")); for (boost_tokenizer::iterator token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) { std::string tok(*token_iter); @@ -105,7 +87,8 @@ LLFloaterWindLight::~LLFloaterWindLight() { } -void LLFloaterWindLight::initCallbacks(void) { +void LLFloaterWindLight::initCallbacks(void) +{ // help buttons initHelpBtn("WLBlueHorizonHelp", "HelpBlueHorizon"); @@ -137,93 +120,92 @@ void LLFloaterWindLight::initCallbacks(void) { initHelpBtn("WLClassicCloudsHelp", "HelpClassicClouds"); - LLWLParamManager * param_mgr = LLWLParamManager::getInstance(); + LLWLParamManager& param_mgr = LLWLParamManager::instance(); // blue horizon - childSetCommitCallback("WLBlueHorizonR", onColorControlRMoved, ¶m_mgr->mBlueHorizon); - childSetCommitCallback("WLBlueHorizonG", onColorControlGMoved, ¶m_mgr->mBlueHorizon); - childSetCommitCallback("WLBlueHorizonB", onColorControlBMoved, ¶m_mgr->mBlueHorizon); - childSetCommitCallback("WLBlueHorizonI", onColorControlIMoved, ¶m_mgr->mBlueHorizon); + getChild("WLBlueHorizonR")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mBlueHorizon)); + getChild("WLBlueHorizonG")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mBlueHorizon)); + getChild("WLBlueHorizonB")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mBlueHorizon)); + getChild("WLBlueHorizonI")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlIMoved, this, _1, ¶m_mgr.mBlueHorizon)); // haze density, horizon, mult, and altitude - childSetCommitCallback("WLHazeDensity", onFloatControlMoved, ¶m_mgr->mHazeDensity); - childSetCommitCallback("WLHazeHorizon", onFloatControlMoved, ¶m_mgr->mHazeHorizon); - childSetCommitCallback("WLDensityMult", onFloatControlMoved, ¶m_mgr->mDensityMult); - childSetCommitCallback("WLMaxAltitude", onFloatControlMoved, ¶m_mgr->mMaxAlt); + getChild("WLHazeDensity")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mHazeDensity)); + getChild("WLHazeHorizon")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mHazeHorizon)); + getChild("WLDensityMult")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mDensityMult)); + getChild("WLMaxAltitude")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mMaxAlt)); // blue density - childSetCommitCallback("WLBlueDensityR", onColorControlRMoved, ¶m_mgr->mBlueDensity); - childSetCommitCallback("WLBlueDensityG", onColorControlGMoved, ¶m_mgr->mBlueDensity); - childSetCommitCallback("WLBlueDensityB", onColorControlBMoved, ¶m_mgr->mBlueDensity); - childSetCommitCallback("WLBlueDensityI", onColorControlIMoved, ¶m_mgr->mBlueDensity); + getChild("WLBlueDensityR")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mBlueDensity)); + getChild("WLBlueDensityG")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mBlueDensity)); + getChild("WLBlueDensityB")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mBlueDensity)); + getChild("WLBlueDensityI")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlIMoved, this, _1, ¶m_mgr.mBlueDensity)); // Lighting // sunlight - childSetCommitCallback("WLSunlightR", onColorControlRMoved, ¶m_mgr->mSunlight); - childSetCommitCallback("WLSunlightG", onColorControlGMoved, ¶m_mgr->mSunlight); - childSetCommitCallback("WLSunlightB", onColorControlBMoved, ¶m_mgr->mSunlight); - childSetCommitCallback("WLSunlightI", onColorControlIMoved, ¶m_mgr->mSunlight); + getChild("WLSunlightR")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mSunlight)); + getChild("WLSunlightG")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mSunlight)); + getChild("WLSunlightB")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mSunlight)); + getChild("WLSunlightI")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlIMoved, this, _1, ¶m_mgr.mSunlight)); // glow - childSetCommitCallback("WLGlowR", onGlowRMoved, ¶m_mgr->mGlow); - childSetCommitCallback("WLGlowB", onGlowBMoved, ¶m_mgr->mGlow); + getChild("WLGlowR")->setCommitCallback(boost::bind(&LLFloaterWindLight::onGlowRMoved, this, _1, ¶m_mgr.mGlow)); + getChild("WLGlowB")->setCommitCallback(boost::bind(&LLFloaterWindLight::onGlowBMoved, this, _1, ¶m_mgr.mGlow)); // ambient - childSetCommitCallback("WLAmbientR", onColorControlRMoved, ¶m_mgr->mAmbient); - childSetCommitCallback("WLAmbientG", onColorControlGMoved, ¶m_mgr->mAmbient); - childSetCommitCallback("WLAmbientB", onColorControlBMoved, ¶m_mgr->mAmbient); - childSetCommitCallback("WLAmbientI", onColorControlIMoved, ¶m_mgr->mAmbient); + getChild("WLAmbientR")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mAmbient)); + getChild("WLAmbientG")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mAmbient)); + getChild("WLAmbientB")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mAmbient)); + getChild("WLAmbientI")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlIMoved, this, _1, ¶m_mgr.mAmbient)); // time of day - childSetCommitCallback("WLSunAngle", onSunMoved, ¶m_mgr->mLightnorm); - childSetCommitCallback("WLEastAngle", onSunMoved, ¶m_mgr->mLightnorm); + getChild("WLSunAngle")->setCommitCallback(boost::bind(&LLFloaterWindLight::onSunMoved, this, _1, ¶m_mgr.mLightnorm)); + getChild("WLEastAngle")->setCommitCallback(boost::bind(&LLFloaterWindLight::onSunMoved, this, _1, ¶m_mgr.mLightnorm)); // Clouds // Cloud Color - childSetCommitCallback("WLCloudColorR", onColorControlRMoved, ¶m_mgr->mCloudColor); - childSetCommitCallback("WLCloudColorG", onColorControlGMoved, ¶m_mgr->mCloudColor); - childSetCommitCallback("WLCloudColorB", onColorControlBMoved, ¶m_mgr->mCloudColor); - childSetCommitCallback("WLCloudColorI", onColorControlIMoved, ¶m_mgr->mCloudColor); + getChild("WLCloudColorR")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mCloudColor)); + getChild("WLCloudColorG")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mCloudColor)); + getChild("WLCloudColorB")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mCloudColor)); + getChild("WLCloudColorI")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlIMoved, this, _1, ¶m_mgr.mCloudColor)); // Cloud - childSetCommitCallback("WLCloudX", onColorControlRMoved, ¶m_mgr->mCloudMain); - childSetCommitCallback("WLCloudY", onColorControlGMoved, ¶m_mgr->mCloudMain); - childSetCommitCallback("WLCloudDensity", onColorControlBMoved, ¶m_mgr->mCloudMain); + getChild("WLCloudX")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mCloudMain)); + getChild("WLCloudY")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mCloudMain)); + getChild("WLCloudDensity")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mCloudMain)); // Cloud Detail - childSetCommitCallback("WLCloudDetailX", onColorControlRMoved, ¶m_mgr->mCloudDetail); - childSetCommitCallback("WLCloudDetailY", onColorControlGMoved, ¶m_mgr->mCloudDetail); - childSetCommitCallback("WLCloudDetailDensity", onColorControlBMoved, ¶m_mgr->mCloudDetail); + getChild("WLCloudDetailX")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlRMoved, this, _1, ¶m_mgr.mCloudDetail)); + getChild("WLCloudDetailY")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlGMoved, this, _1, ¶m_mgr.mCloudDetail)); + getChild("WLCloudDetailDensity")->setCommitCallback(boost::bind(&LLFloaterWindLight::onColorControlBMoved, this, _1, ¶m_mgr.mCloudDetail)); // Cloud extras - childSetCommitCallback("WLCloudCoverage", onFloatControlMoved, ¶m_mgr->mCloudCoverage); - childSetCommitCallback("WLCloudScale", onFloatControlMoved, ¶m_mgr->mCloudScale); - childSetCommitCallback("WLCloudLockX", onCloudScrollXToggled, NULL); - childSetCommitCallback("WLCloudLockY", onCloudScrollYToggled, NULL); - childSetCommitCallback("WLCloudScrollX", onCloudScrollXMoved, NULL); - childSetCommitCallback("WLCloudScrollY", onCloudScrollYMoved, NULL); - childSetCommitCallback("WLDistanceMult", onFloatControlMoved, ¶m_mgr->mDistanceMult); - childSetCommitCallback("DrawClassicClouds", onCommitControlSetting(gSavedSettings), (void*)"SkyUseClassicClouds"); + getChild("WLCloudCoverage")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mCloudCoverage)); + getChild("WLCloudScale")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mCloudScale)); + getChild("WLCloudLockX")->setCommitCallback(boost::bind(&LLFloaterWindLight::onCloudScrollXToggled, this, _2)); + getChild("WLCloudLockY")->setCommitCallback(boost::bind(&LLFloaterWindLight::onCloudScrollYToggled, this, _2)); + getChild("WLCloudScrollX")->setCommitCallback(boost::bind(&LLFloaterWindLight::onCloudScrollXMoved, this, _2)); + getChild("WLCloudScrollY")->setCommitCallback(boost::bind(&LLFloaterWindLight::onCloudScrollYMoved, this, _2)); + getChild("WLDistanceMult")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mDistanceMult)); // WL Top - childSetAction("WLDayCycleMenuButton", onOpenDayCycle, NULL); - + getChild("WLDayCycleMenuButton")->setCommitCallback(boost::bind(LLFloaterDayCycle::show)); + // Load/save - //childSetAction("WLLoadPreset", onLoadPreset, mSkyPresetCombo); - childSetAction("WLNewPreset", onNewPreset, mSkyPresetCombo); - childSetAction("WLDeletePreset", onDeletePreset, mSkyPresetCombo); - childSetCommitCallback("WLSavePreset", onSavePreset, this); + //getChild("WLLoadPreset")->setCommitCallback(boost::bind(&LLFloaterWindLight::onLoadPreset, this, mSkyPresetCombo)); + getChild("WLNewPreset")->setCommitCallback(boost::bind(&LLFloaterWindLight::onNewPreset, this)); + getChild("WLDeletePreset")->setCommitCallback(boost::bind(&LLFloaterWindLight::onDeletePreset, this)); + getChild("WLSavePreset")->setCommitCallback(boost::bind(&LLFloaterWindLight::onSavePreset, this, _1)); // Dome - childSetCommitCallback("WLGamma", onFloatControlMoved, ¶m_mgr->mWLGamma); - childSetCommitCallback("WLStarAlpha", onStarAlphaMoved, NULL); + getChild("WLGamma")->setCommitCallback(boost::bind(&LLFloaterWindLight::onFloatControlMoved, this, _1, ¶m_mgr.mWLGamma)); + getChild("WLStarAlpha")->setCommitCallback(boost::bind(&LLFloaterWindLight::onStarAlphaMoved, this, _1)); // next/prev buttons - //childSetAction("next", onClickNext, this); - //childSetAction("prev", onClickPrev, this); + //getChild("next")->setCommitCallback(boost::bind(&LLFloaterWindLight::onClickNext, this)); + //getChild("prev")->setCommitCallback(boost::bind(&LLFloaterWindLight::onClickPrev, this)); } void LLFloaterWindLight::onClickHelp(void* data) @@ -242,22 +224,20 @@ void LLFloaterWindLight::initHelpBtn(const std::string& name, const std::string& bool LLFloaterWindLight::newPromptCallback(const LLSD& notification, const LLSD& response) { std::string text = response["message"].asString(); - S32 option = LLNotification::getSelectedOption(notification, response); - - if(text == "") + if(text.empty()) { return false; } + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(option == 0) { LLFloaterDayCycle* sDayCycle = NULL; LLComboBox* keyCombo = NULL; - if(LLFloaterDayCycle::isOpen()) + if(LLFloaterDayCycle::isOpen()) { sDayCycle = LLFloaterDayCycle::instance(); - keyCombo = sDayCycle->getChild( - "WLKeyPresets"); + keyCombo = sDayCycle->getChild("WLKeyPresets"); } // add the current parameters to the list @@ -268,20 +248,19 @@ bool LLFloaterWindLight::newPromptCallback(const LLSD& notification, const LLSD& // if not there, add a new one if(!LLWLParamManager::getInstance()->hasParamSet(key)) { - LLWLParamManager::getInstance()->addParamSet(key, - LLWLParamManager::getInstance()->mCurParams); - sWindLight->mSkyPresetCombo->add(text); - sWindLight->mSkyPresetCombo->sortByName(); + LLWLParamManager::getInstance()->addParamSet(key, LLWLParamManager::getInstance()->mCurParams); + mSkyPresetCombo->add(text); + mSkyPresetCombo->sortByName(); // add a blank to the bottom - sWindLight->mSkyPresetCombo->selectFirstItem(); - if(sWindLight->mSkyPresetCombo->getSimple() == "") + mSkyPresetCombo->selectFirstItem(); + if(mSkyPresetCombo->getSimple().empty()) { - sWindLight->mSkyPresetCombo->remove(0); + mSkyPresetCombo->remove(0); } - sWindLight->mSkyPresetCombo->add(LLStringUtil::null); + mSkyPresetCombo->add(LLStringUtil::null); - sWindLight->mSkyPresetCombo->selectByValue(text); + mSkyPresetCombo->selectByValue(text); if(LLFloaterDayCycle::isOpen()) { @@ -294,10 +273,10 @@ bool LLFloaterWindLight::newPromptCallback(const LLSD& notification, const LLSD& LLEnvManagerNew::instance().setUseSkyPreset(text); // otherwise, send a message to the user - } - else + } + else { - LLNotifications::instance().add("ExistsSkyPresetAlert"); + LLNotificationsUtil::add("ExistsSkyPresetAlert"); } } return false; @@ -385,7 +364,6 @@ void LLFloaterWindLight::syncMenu() bool lockY = !param_mgr->mCurParams.getEnableCloudScrollY(); childSetValue("WLCloudLockX", lockX); childSetValue("WLCloudLockY", lockY); - childSetValue("DrawClassicClouds", gSavedSettings.getBOOL("SkyUseClassicClouds")); // disable if locked, enable if not if (lockX) @@ -423,16 +401,17 @@ void LLFloaterWindLight::syncMenu() void LLFloaterWindLight::setColorSwatch(const std::string& name, const WLColorControl& from_ctrl, F32 k) { std::string child_name(name); + const size_t end = child_name.length(); LLVector4 color_vec = from_ctrl; color_vec/=k; - + child_name.push_back('R'); childSetValue(name.data(), color_vec[0]); - child_name.replace(child_name.length()-1,1,1,'G'); + child_name.replace(end,1,1,'G'); childSetValue(child_name, color_vec[1]); - child_name.replace(child_name.length()-1,1,1,'B'); + child_name.replace(end,1,1,'B'); childSetValue(child_name, color_vec[2]); - child_name.replace(child_name.length()-1,1,1,'I'); + child_name.replace(end,1,1,'I'); childSetValue(child_name, llmax(color_vec.mV[0], color_vec.mV[1], color_vec.mV[2])); } @@ -516,15 +495,15 @@ void LLFloaterWindLight::onColorControlRMoved(LLUICtrl* ctrl, void* userdata) if (color_ctrl->isSunOrAmbientColor) { - sWindLight->childSetValue(name, color_ctrl->r / WL_SUN_AMBIENT_SLIDER_SCALE); + childSetValue(name, color_ctrl->r / WL_SUN_AMBIENT_SLIDER_SCALE); } else if (color_ctrl->isBlueHorizonOrDensity) { - sWindLight->childSetValue(name, color_ctrl->r / WL_BLUE_HORIZON_DENSITY_SCALE); + childSetValue(name, color_ctrl->r / WL_BLUE_HORIZON_DENSITY_SCALE); } else { - sWindLight->childSetValue(name, color_ctrl->r); + childSetValue(name, color_ctrl->r); } } @@ -559,15 +538,15 @@ void LLFloaterWindLight::onColorControlGMoved(LLUICtrl* ctrl, void* userdata) if (color_ctrl->isSunOrAmbientColor) { - sWindLight->childSetValue(name, color_ctrl->g / WL_SUN_AMBIENT_SLIDER_SCALE); + childSetValue(name, color_ctrl->g / WL_SUN_AMBIENT_SLIDER_SCALE); } else if (color_ctrl->isBlueHorizonOrDensity) { - sWindLight->childSetValue(name, color_ctrl->g / WL_BLUE_HORIZON_DENSITY_SCALE); + childSetValue(name, color_ctrl->g / WL_BLUE_HORIZON_DENSITY_SCALE); } else { - sWindLight->childSetValue(name, color_ctrl->g); + childSetValue(name, color_ctrl->g); } } @@ -602,15 +581,15 @@ void LLFloaterWindLight::onColorControlBMoved(LLUICtrl* ctrl, void* userdata) if (color_ctrl->isSunOrAmbientColor) { - sWindLight->childSetValue(name, color_ctrl->b / WL_SUN_AMBIENT_SLIDER_SCALE); + childSetValue(name, color_ctrl->b / WL_SUN_AMBIENT_SLIDER_SCALE); } else if (color_ctrl->isBlueHorizonOrDensity) { - sWindLight->childSetValue(name, color_ctrl->b / WL_BLUE_HORIZON_DENSITY_SCALE); + childSetValue(name, color_ctrl->b / WL_BLUE_HORIZON_DENSITY_SCALE); } else { - sWindLight->childSetValue(name, color_ctrl->b); + childSetValue(name, color_ctrl->b); } } @@ -619,28 +598,27 @@ void LLFloaterWindLight::onColorControlBMoved(LLUICtrl* ctrl, void* userdata) LLWLParamManager::getInstance()->propagateParameters(); } -void LLFloaterWindLight::onColorControlIMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWindLight::onColorControlIMoved(LLUICtrl* ctrl, void* userdata) { LLWLParamManager::getInstance()->mAnimator.deactivate(); LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - WLColorControl * color_ctrl = static_cast(userData); + WLColorControl* color_ctrl = static_cast(userdata); color_ctrl->i = sldr_ctrl->getValueF32(); // only for sliders where we pass a name if(color_ctrl->hasSliderName) { - // set it to the top - F32 maxVal = std::max(std::max(color_ctrl->r, color_ctrl->g), color_ctrl->b); - + F32 maxVal = llmax(llmax(color_ctrl->r, color_ctrl->g), color_ctrl->b); + F32 scale = 1.f; if(color_ctrl->isSunOrAmbientColor) scale = WL_SUN_AMBIENT_SLIDER_SCALE; else if(color_ctrl->isBlueHorizonOrDensity) scale = WL_BLUE_HORIZON_DENSITY_SCALE; - + F32 iVal = color_ctrl->i * scale; // handle if at 0 @@ -658,7 +636,6 @@ void LLFloaterWindLight::onColorControlIMoved(LLUICtrl* ctrl, void* userData) color_ctrl->r = iVal; color_ctrl->g = iVal; color_ctrl->b = iVal; - } else { @@ -671,12 +648,13 @@ void LLFloaterWindLight::onColorControlIMoved(LLUICtrl* ctrl, void* userData) // set the sliders to the new vals std::string child_name(color_ctrl->mSliderName); + const size_t end = child_name.length(); child_name.push_back('R'); - sWindLight->childSetValue(child_name, color_ctrl->r/scale); - child_name.replace(child_name.length()-1,1,1,'G'); - sWindLight->childSetValue(child_name, color_ctrl->g/scale); - child_name.replace(child_name.length()-1,1,1,'B'); - sWindLight->childSetValue(child_name, color_ctrl->b/scale); + childSetValue(child_name, color_ctrl->r/scale); + child_name.replace(end,1,1,'G'); + childSetValue(child_name, color_ctrl->g/scale); + child_name.replace(end,1,1,'B'); + childSetValue(child_name, color_ctrl->b/scale); } // now update the current parameters and send them to shaders @@ -727,34 +705,23 @@ void LLFloaterWindLight::onFloatControlMoved(LLUICtrl* ctrl, void* userdata) LLWLParamManager::getInstance()->propagateParameters(); } -void LLFloaterWindLight::onBoolToggle(LLUICtrl* ctrl, void* userData) -{ - LLWLParamManager::getInstance()->mAnimator.deactivate(); - - LLCheckBoxCtrl* cbCtrl = static_cast(ctrl); - - bool value = cbCtrl->get(); - (*(static_cast(userData))) = value; -} - // Lighting callbacks // time of day -void LLFloaterWindLight::onSunMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWindLight::onSunMoved(LLUICtrl* ctrl, void* userdata) { LLWLParamManager::getInstance()->mAnimator.deactivate(); - LLSliderCtrl* sunSldr = sWindLight->getChild("WLSunAngle"); - LLSliderCtrl* eastSldr = sWindLight->getChild("WLEastAngle"); - - WLColorControl * color_ctrl = static_cast(userData); + LLSliderCtrl* sun_sldr = getChild("WLSunAngle"); + LLSliderCtrl* east_sldr = getChild("WLEastAngle"); + WLColorControl* color_ctrl = static_cast(userdata); // get the two angles LLWLParamManager * param_mgr = LLWLParamManager::getInstance(); - param_mgr->mCurParams.setSunAngle(F_TWO_PI * sunSldr->getValueF32()); - param_mgr->mCurParams.setEastAngle(F_TWO_PI * eastSldr->getValueF32()); + param_mgr->mCurParams.setSunAngle(F_TWO_PI * sun_sldr->getValueF32()); + param_mgr->mCurParams.setEastAngle(F_TWO_PI * east_sldr->getValueF32()); // set the sun vector color_ctrl->r = -sin(param_mgr->mCurParams.getEastAngle()) * @@ -768,35 +735,59 @@ void LLFloaterWindLight::onSunMoved(LLUICtrl* ctrl, void* userData) param_mgr->propagateParameters(); } -void LLFloaterWindLight::onFloatTweakMoved(LLUICtrl* ctrl, void* userData) +void LLFloaterWindLight::onStarAlphaMoved(LLUICtrl* ctrl) { LLWLParamManager::getInstance()->mAnimator.deactivate(); - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - F32 * tweak = static_cast(userData); + LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - (*tweak) = sldrCtrl->getValueF32(); - LLWLParamManager::getInstance()->propagateParameters(); + LLWLParamManager::getInstance()->mCurParams.setStarBrightness(sldr_ctrl->getValueF32()); } -void LLFloaterWindLight::onStarAlphaMoved(LLUICtrl* ctrl, void* userData) +// Clouds +void LLFloaterWindLight::onCloudScrollXMoved(const LLSD& value) { LLWLParamManager::getInstance()->mAnimator.deactivate(); - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - - LLWLParamManager::getInstance()->mCurParams.setStarBrightness(sldrCtrl->getValueF32()); + // *HACK all cloud scrolling is off by an additive of 10. + LLWLParamManager::getInstance()->mCurParams.setCloudScrollX(value.asFloat() + 10.0f); } -void LLFloaterWindLight::onNewPreset(void* userData) +void LLFloaterWindLight::onCloudScrollYMoved(const LLSD& value) { - LLNotifications::instance().add("NewSkyPreset", LLSD(), LLSD(), newPromptCallback); + LLWLParamManager::getInstance()->mAnimator.deactivate(); + + // *HACK all cloud scrolling is off by an additive of 10. + LLWLParamManager::getInstance()->mCurParams.setCloudScrollY(value.asFloat() + 10.0f); } -void LLFloaterWindLight::onSavePreset(LLUICtrl* ctrl, void* userData) +void LLFloaterWindLight::onCloudScrollXToggled(const LLSD& value) +{ + LLWLParamManager::getInstance()->mAnimator.deactivate(); + + bool lock = value.asBoolean(); + LLWLParamManager::getInstance()->mCurParams.setEnableCloudScrollX(!lock); + getChild("WLCloudScrollX")->setEnabled(!lock); +} + +void LLFloaterWindLight::onCloudScrollYToggled(const LLSD& value) +{ + LLWLParamManager::getInstance()->mAnimator.deactivate(); + + bool lock = value.asBoolean(); + LLWLParamManager::getInstance()->mCurParams.setEnableCloudScrollY(!lock); + getChild("WLCloudScrollY")->setEnabled(!lock); +} + +void LLFloaterWindLight::onNewPreset() +{ + LLNotificationsUtil::add("NewSkyPreset", LLSD(), LLSD(), boost::bind(&LLFloaterWindLight::newPromptCallback, this, _1, _2)); +} + +void LLFloaterWindLight::onSavePreset(LLUICtrl* ctrl) { // don't save the empty name - if(sWindLight->mSkyPresetCombo->getSelectedItemLabel() == "") + if(mSkyPresetCombo->getSelectedItemLabel().empty()) { return; } @@ -808,24 +799,22 @@ void LLFloaterWindLight::onSavePreset(LLUICtrl* ctrl, void* userData) else { // check to see if it's a default and shouldn't be overwritten - std::set::iterator sIt = sDefaultPresets.find( - sWindLight->mSkyPresetCombo->getSelectedItemLabel()); + std::set::iterator sIt = sDefaultPresets.find(mSkyPresetCombo->getSelectedItemLabel()); if(sIt != sDefaultPresets.end() && !gSavedSettings.getBOOL("SkyEditPresets")) { - LLNotifications::instance().add("WLNoEditDefault"); + LLNotificationsUtil::add("WLNoEditDefault"); return; } - LLWLParamManager::getInstance()->mCurParams.mName = - sWindLight->mSkyPresetCombo->getSelectedItemLabel(); + LLWLParamManager::getInstance()->mCurParams.mName = mSkyPresetCombo->getSelectedItemLabel(); - LLNotifications::instance().add("WLSavePresetAlert", LLSD(), LLSD(), saveAlertCallback); + LLNotificationsUtil::add("WLSavePresetAlert", LLSD(), LLSD(), boost::bind(&LLFloaterWindLight::saveAlertCallback, this, _1, _2)); } } bool LLFloaterWindLight::saveNotecardCallback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); // if they choose save, do it. Otherwise, don't do anything if(option == 0) { @@ -838,9 +827,9 @@ bool LLFloaterWindLight::saveNotecardCallback(const LLSD& notification, const LL bool LLFloaterWindLight::saveAlertCallback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); // if they choose save, do it. Otherwise, don't do anything - if(option == 0) + if(option == 0) { LLWLParamManager * param_mgr = LLWLParamManager::getInstance(); @@ -853,30 +842,29 @@ bool LLFloaterWindLight::saveAlertCallback(const LLSD& notification, const LLSD& return false; } -void LLFloaterWindLight::onDeletePreset(void* userData) +void LLFloaterWindLight::onDeletePreset() { - if(sWindLight->mSkyPresetCombo->getSelectedValue().asString() == "") + if(mSkyPresetCombo->getSelectedValue().asString().empty()) { return; } LLSD args; - args["SKY"] = sWindLight->mSkyPresetCombo->getSelectedValue().asString(); - LLNotifications::instance().add("WLDeletePresetAlert", args, LLSD(), - boost::bind(&LLFloaterWindLight::deleteAlertCallback, sWindLight, _1, _2)); + args["SKY"] = mSkyPresetCombo->getSelectedValue().asString(); + LLNotificationsUtil::add("WLDeletePresetAlert", args, LLSD(), boost::bind(&LLFloaterWindLight::deleteAlertCallback, this, _1, _2)); } bool LLFloaterWindLight::deleteAlertCallback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); // if they choose delete, do it. Otherwise, don't do anything - if(option == 0) + if(option == 0) { LLFloaterDayCycle* day_cycle = NULL; LLComboBox* key_combo = NULL; - if(LLFloaterDayCycle::isOpen()) + if(LLFloaterDayCycle::isOpen()) { day_cycle = LLFloaterDayCycle::instance(); key_combo = day_cycle->getChild("WLKeyPresets"); @@ -888,7 +876,7 @@ bool LLFloaterWindLight::deleteAlertCallback(const LLSD& notification, const LLS std::set::iterator sIt = sDefaultPresets.find(name); if(sIt != sDefaultPresets.end()) { - LLNotifications::instance().add("WLNoEditDefault"); + LLNotificationsUtil::add("WLNoEditDefault"); return false; } @@ -907,12 +895,12 @@ bool LLFloaterWindLight::deleteAlertCallback(const LLSD& notification, const LLS } // pick the previously selected index after delete - if(new_index > 0) + if(new_index > 0) { - new_index--; + --new_index; } - if(mSkyPresetCombo->getItemCount() > 0) + if(mSkyPresetCombo->getItemCount() > 0) { mSkyPresetCombo->setCurrentByIndex(new_index); @@ -929,11 +917,11 @@ bool LLFloaterWindLight::deleteAlertCallback(const LLSD& notification, const LLS } -void LLFloaterWindLight::onChangePresetName(LLUICtrl* ctrl, void * userData) +void LLFloaterWindLight::onChangePresetName(LLUICtrl* ctrl) { - LLComboBox * combo_box = static_cast(ctrl); + LLComboBox* combo_box = static_cast(ctrl); - if(combo_box->getSimple() == "") + if(combo_box->getSimple().empty()) { return; } @@ -951,97 +939,30 @@ void LLFloaterWindLight::onChangePresetName(LLUICtrl* ctrl, void * userData) } //LL_INFOS("WindLight") << "Current inventory ID: " << LLWLParamManager::getInstance()->mCurParams.mInventoryID << LL_ENDL; - sWindLight->syncMenu(); -} - -void LLFloaterWindLight::onOpenDayCycle(void* userData) -{ - LLFloaterDayCycle::show(); -} - -// Clouds -void LLFloaterWindLight::onCloudScrollXMoved(LLUICtrl* ctrl, void* userData) -{ - LLWLParamManager::getInstance()->mAnimator.deactivate(); - - LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - // *HACK all cloud scrolling is off by an additive of 10. - LLWLParamManager::getInstance()->mCurParams.setCloudScrollX(sldr_ctrl->getValueF32() + 10.0f); -} - -void LLFloaterWindLight::onCloudScrollYMoved(LLUICtrl* ctrl, void* userData) -{ - LLWLParamManager::getInstance()->mAnimator.deactivate(); - - LLSliderCtrl* sldr_ctrl = static_cast(ctrl); - - // *HACK all cloud scrolling is off by an additive of 10. - LLWLParamManager::getInstance()->mCurParams.setCloudScrollY(sldr_ctrl->getValueF32() + 10.0f); -} - -void LLFloaterWindLight::onCloudScrollXToggled(LLUICtrl* ctrl, void* userData) -{ - LLWLParamManager::getInstance()->mAnimator.deactivate(); - - LLCheckBoxCtrl* cb_ctrl = static_cast(ctrl); - - bool lock = cb_ctrl->get(); - LLWLParamManager::getInstance()->mCurParams.setEnableCloudScrollX(!lock); - - LLSliderCtrl* sldr = sWindLight->getChild("WLCloudScrollX"); - - if (cb_ctrl->get()) - { - sldr->setEnabled(false); - } - else - { - sldr->setEnabled(true); - } - -} - -void LLFloaterWindLight::onCloudScrollYToggled(LLUICtrl* ctrl, void* userData) -{ - LLWLParamManager::getInstance()->mAnimator.deactivate(); - - LLCheckBoxCtrl* cb_ctrl = static_cast(ctrl); - bool lock = cb_ctrl->get(); - LLWLParamManager::getInstance()->mCurParams.setEnableCloudScrollY(!lock); - - LLSliderCtrl* sldr = sWindLight->getChild("WLCloudScrollY"); - - if (cb_ctrl->get()) - { - sldr->setEnabled(false); - } - else - { - sldr->setEnabled(true); - } + syncMenu(); } -void LLFloaterWindLight::onClickNext(void* user_data) +void LLFloaterWindLight::onClickNext() { - S32 index = sWindLight->mSkyPresetCombo->getCurrentIndex(); - index++; - if (index == sWindLight->mSkyPresetCombo->getItemCount()) + S32 index = mSkyPresetCombo->getCurrentIndex(); + ++index; + if (index == mSkyPresetCombo->getItemCount()) index = 0; - sWindLight->mSkyPresetCombo->setCurrentByIndex(index); + mSkyPresetCombo->setCurrentByIndex(index); - LLFloaterWindLight::onChangePresetName(sWindLight->mSkyPresetCombo, sWindLight); + onChangePresetName(mSkyPresetCombo); } -void LLFloaterWindLight::onClickPrev(void* user_data) +void LLFloaterWindLight::onClickPrev() { - S32 index = sWindLight->mSkyPresetCombo->getCurrentIndex(); + S32 index = mSkyPresetCombo->getCurrentIndex(); if (index == 0) - index = sWindLight->mSkyPresetCombo->getItemCount(); - index--; - sWindLight->mSkyPresetCombo->setCurrentByIndex(index); + index = mSkyPresetCombo->getItemCount(); + --index; + mSkyPresetCombo->setCurrentByIndex(index); - LLFloaterWindLight::onChangePresetName(sWindLight->mSkyPresetCombo, sWindLight); + onChangePresetName(mSkyPresetCombo); } //static diff --git a/indra/newview/llfloaterwindlight.h b/indra/newview/llfloaterwindlight.h index c4828bf7f..2d3578702 100644 --- a/indra/newview/llfloaterwindlight.h +++ b/indra/newview/llfloaterwindlight.h @@ -39,9 +39,6 @@ #include "llfloater.h" -#include -#include "llwlparamset.h" - struct WLColorControl; struct WLFloatControl; @@ -55,84 +52,76 @@ public: LLFloaterWindLight(); virtual ~LLFloaterWindLight(); - /// initialize all + // initialize all void initCallbacks(void); - /// one and one instance only + // one and one instance only static LLFloaterWindLight* instance(); // help button stuff static void onClickHelp(void* data); void initHelpBtn(const std::string& name, const std::string& xml_alert); - static bool newPromptCallback(const LLSD& notification, const LLSD& response); + bool newPromptCallback(const LLSD& notification, const LLSD& response); void setColorSwatch(const std::string& name, const WLColorControl& from_ctrl, F32 k); - /// general purpose callbacks for dealing with color controllers - static void onColorControlRMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlGMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlBMoved(LLUICtrl* ctrl, void* userData); - static void onColorControlIMoved(LLUICtrl* ctrl, void* userData); - static void onFloatControlMoved(LLUICtrl* ctrl, void* userData); - static void onBoolToggle(LLUICtrl* ctrl, void* userData); + // general purpose callbacks for dealing with color controllers + void onColorControlRMoved(LLUICtrl* ctrl, void* userdata); + void onColorControlGMoved(LLUICtrl* ctrl, void* userdata); + void onColorControlBMoved(LLUICtrl* ctrl, void* userdata); + void onColorControlIMoved(LLUICtrl* ctrl, void* userdata); + void onFloatControlMoved(LLUICtrl* ctrl, void* userdata); - /// lighting callbacks for glow - static void onGlowRMoved(LLUICtrl* ctrl, void* userData); - //static void onGlowGMoved(LLUICtrl* ctrl, void* userData); - static void onGlowBMoved(LLUICtrl* ctrl, void* userData); + // lighting callbacks for glow + void onGlowRMoved(LLUICtrl* ctrl, void* userdata); + void onGlowBMoved(LLUICtrl* ctrl, void* userdata); - /// lighting callbacks for sun - static void onSunMoved(LLUICtrl* ctrl, void* userData); + // lighting callbacks for sun + void onSunMoved(LLUICtrl* ctrl, void* userdata); - /// handle if float is changed - static void onFloatTweakMoved(LLUICtrl* ctrl, void* userData); + // for handling when the star slider is moved to adjust the alpha + void onStarAlphaMoved(LLUICtrl* ctrl); - /// for handling when the star slider is moved to adjust the alpha - static void onStarAlphaMoved(LLUICtrl* ctrl, void* userData); + // when user hits the load preset button + void onNewPreset(); - /// when user hits the load preset button - static void onNewPreset(void* userData); - - /// when user hits the save to file button - static void onSavePreset(LLUICtrl* ctrl, void* userData); + // when user hits the save to file button + void onSavePreset(LLUICtrl* ctrl); - /// prompts a user when overwriting a preset notecard - static bool saveNotecardCallback(const LLSD& notification, const LLSD& response); + // prompts a user when overwriting a preset notecard + bool saveNotecardCallback(const LLSD& notification, const LLSD& response); - /// prompts a user when overwriting a preset - static bool saveAlertCallback(const LLSD& notification, const LLSD& response); + // prompts a user when overwriting a preset + bool saveAlertCallback(const LLSD& notification, const LLSD& response); - /// when user hits the save preset button - static void onDeletePreset(void* userData); + // when user hits the save preset button + void onDeletePreset(); - /// prompts a user when overwriting a preset + // prompts a user when overwriting a preset bool deleteAlertCallback(const LLSD& notification, const LLSD& response); - /// what to do when you change the preset name - static void onChangePresetName(LLUICtrl* ctrl, void* userData); + // what to do when you change the preset name + void onChangePresetName(LLUICtrl* ctrl); - /// when user hits the save preset button - static void onOpenDayCycle(void* userData); - - /// handle cloud scrolling - static void onCloudScrollXMoved(LLUICtrl* ctrl, void* userData); - static void onCloudScrollYMoved(LLUICtrl* ctrl, void* userData); - static void onCloudScrollXToggled(LLUICtrl* ctrl, void* userData); - static void onCloudScrollYToggled(LLUICtrl* ctrl, void* userData); + // handle cloud scrolling + void onCloudScrollXMoved(const LLSD& value); + void onCloudScrollYMoved(const LLSD& value); + void onCloudScrollXToggled(const LLSD& value); + void onCloudScrollYToggled(const LLSD& value); //// menu management - /// show off our menu + // show off our menu static void show(); - /// return if the menu exists or not + // return if the menu exists or not static bool isOpen(); - /// stuff to do on exit + // stuff to do on exit virtual void onClose(bool app_quitting); - /// sync up sliders with parameters + // sync up sliders with parameters void syncMenu(); @@ -144,11 +133,11 @@ private: static std::set sDefaultPresets; - static void onClickNext(void* user_data); - static void onClickPrev(void* user_data); - + void onClickNext(); + void onClickPrev(); + void populateSkyPresetsList(); - + LLComboBox* mSkyPresetCombo; }; diff --git a/indra/newview/llgiveinventory.cpp b/indra/newview/llgiveinventory.cpp index 09f690cf0..25885e68f 100644 --- a/indra/newview/llgiveinventory.cpp +++ b/indra/newview/llgiveinventory.cpp @@ -320,6 +320,20 @@ void LLGiveInventory::logInventoryOffer(const LLUUID& to_agent, const LLUUID &im { gIMMgr->addSystemMessage(im_session_id, "inventory_item_offered", args); } +// [RLVa:KB] - Checked: 2010-05-26 (RLVa-1.2.2a) | Modified: RLVa-1.2.0h + else if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(to_agent)) && + (!RlvUIEnabler::hasOpenProfile(to_agent)) ) + { + // Log to chat history if the user didn't drop on an IM session or a profile to avoid revealing the name of the recipient + std::string strMsgName = "inventory_item_offered"; LLSD args; LLAvatarName avName; + if (LLAvatarNameCache::get(to_agent, &avName)) + { + args["NAME"] = RlvStrings::getAnonym(avName); + strMsgName = "inventory_item_offered_rlv"; + } + gIMMgr->addSystemMessage(LLUUID::null, strMsgName, args); + } +// [/RLVa:KB] // If this item was given by drag-and-drop on avatar while IM panel was open, log this action in the IM panel chat. else if (gIMMgr->isIMSessionOpen(session_id)) { diff --git a/indra/newview/llgivemoney.cpp b/indra/newview/llgivemoney.cpp index b153216ac..6816c378a 100644 --- a/indra/newview/llgivemoney.cpp +++ b/indra/newview/llgivemoney.cpp @@ -347,17 +347,15 @@ void LLFloaterPay::onCacheOwnerName(const LLUUID& owner_id, const std::string& full_name, bool is_group) { - if (is_group) + if (LLView* view = findChild("payee_group")) { - childSetVisible("payee_group",true); - childSetVisible("payee_resident",false); + view->setVisible(is_group); } - else + if (LLView* view = findChild("payee_resident")) { - childSetVisible("payee_group",false); - childSetVisible("payee_resident",true); + view->setVisible(!is_group); } - + childSetTextArg("payee_name", "[NAME]", full_name); } diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp new file mode 100644 index 000000000..ade7e1d1f --- /dev/null +++ b/indra/newview/llgroupactions.cpp @@ -0,0 +1,503 @@ +/** + * @file llgroupactions.cpp + * @brief Group-related actions (join, leave, new, delete, etc) + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" + +#include "llgroupactions.h" + +#include "llagent.h" +#include "llcommandhandler.h" +#include "llfloaterchatterbox.h" // for LLFloaterMyFriends +#include "llfloaterdirectory.h" +#include "llfloatergroupinfo.h" +#include "llgroupmgr.h" +#include "llimview.h" // for gIMMgr +#include "llnotificationsutil.h" +#include "llpanelgroup.h" +#include "llviewermessage.h" +#include "groupchatlistener.h" +#include "hippolimits.h" // for getMaxAgentGroups +// [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.3.0f) +#include "llslurl.h" +#include "rlvhandler.h" +// [/RLVa:KB] + +// +// Globals +// +static GroupChatListener sGroupChatListener; + +class LLGroupHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { } + bool handle(const LLSD& tokens, const LLSD& query_map, + LLMediaCtrl* web) + { + /*if (!LLUI::sSettingGroups["config"]->getBOOL("EnableGroupInfo")) + { + LLNotificationsUtil::add("NoGroupInfo", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + }*/ + + if (tokens.size() < 1) + { + return false; + } + + if (tokens[0].asString() == "create") + { + LLGroupActions::createGroup(); + return true; + } + + if (tokens.size() < 2) + { + return false; + } + + if (tokens[0].asString() == "list") + { + if (tokens[1].asString() == "show") + { + // CP_TODO: get the value we pass in via the XUI name + // of the tab instead of using a literal like this + LLFloaterMyFriends::showInstance( 1 ); + + return true; + } + return false; + } + + LLUUID group_id; + if (!group_id.set(tokens[0], FALSE)) + { + return false; + } + + if (tokens[1].asString() == "about") + { + if (group_id.isNull()) + return true; + + LLGroupActions::show(group_id); + + return true; + } + if (tokens[1].asString() == "inspect") + { + if (group_id.isNull()) + return true; + LLGroupActions::inspect(group_id); + return true; + } + return false; + } +}; +LLGroupHandler gGroupHandler; + +// static +void LLGroupActions::search() +{ + //LLFloaterReg::showInstance("search", LLSD().with("category", "groups")); + LLFloaterDirectory::showGroups(); +} + +// static +void LLGroupActions::startCall(const LLUUID& group_id) +{ + // create a new group voice session + LLGroupData gdata; + + if (!gAgent.getGroupData(group_id, gdata)) + { + llwarns << "Error getting group data" << llendl; + return; + } + +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(group_id)) && (!gIMMgr->hasSession(group_id)) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("group", group_id, "about").getSLURLString())); + return; + } +// [/RLVa:KB] + + LLUUID session_id = gIMMgr->addSession(gdata.mName, IM_SESSION_GROUP_START, group_id, true); + if (session_id.isNull()) + { + llwarns << "Error adding session" << llendl; + return; + } + + // start the call + gIMMgr->autoStartCallOnStartup(session_id); + + make_ui_sound("UISndStartIM"); +} + +// static +void LLGroupActions::join(const LLUUID& group_id) +{ + if (gAgent.mGroups.count() >= gHippoLimits->getMaxAgentGroups()) //!gAgent.canJoinGroups() + { + LLNotificationsUtil::add("JoinedTooManyGroups"); + return; + } + + LLGroupMgrGroupData* gdatap = + LLGroupMgr::getInstance()->getGroupData(group_id); + + if (gdatap) + { + S32 cost = gdatap->mMembershipFee; + LLSD args; + args["COST"] = llformat("%d", cost); + args["NAME"] = gdatap->mName; + LLSD payload; + payload["group_id"] = group_id; + + if (can_afford_transaction(cost)) + { + if(cost > 0) + LLNotificationsUtil::add("JoinGroupCanAfford", args, payload, onJoinGroup); + else + LLNotificationsUtil::add("JoinGroupNoCost", args, payload, onJoinGroup); + } + else + { + LLNotificationsUtil::add("JoinGroupCannotAfford", args, payload); + } + } + else + { + llwarns << "LLGroupMgr::getInstance()->getGroupData(" << group_id + << ") was NULL" << llendl; + } +} + +// static +bool LLGroupActions::onJoinGroup(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (option == 1) + { + // user clicked cancel + return false; + } + + LLGroupMgr::getInstance()-> + sendGroupMemberJoin(notification["payload"]["group_id"].asUUID()); + return false; +} + +// static +void LLGroupActions::leave(const LLUUID& group_id) +{ +// if (group_id.isNull()) +// return; +// [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.4.1a) | Added: RLVa-1.3.0f + if ( (group_id.isNull()) || ((gAgent.getGroupID() == group_id) && (gRlvHandler.hasBehaviour(RLV_BHVR_SETGROUP))) ) + return; +// [/RLVa:KB] + + S32 count = gAgent.mGroups.count(); + S32 i; + for (i = 0; i < count; ++i) + { + if(gAgent.mGroups.get(i).mID == group_id) + break; + } + if (i < count) + { + LLSD args; + args["GROUP"] = gAgent.mGroups.get(i).mName; + LLSD payload; + payload["group_id"] = group_id; + LLNotificationsUtil::add("GroupLeaveConfirmMember", args, payload, onLeaveGroup); + } +} + +// static +void LLGroupActions::activate(const LLUUID& group_id) +{ +// [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.4.1a) | Added: RLVa-1.3.0f + if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SETGROUP)) && (gRlvHandler.getAgentGroup() != group_id) ) + { + return; + } +// [/RLVa:KB] + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_ActivateGroup); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_GroupID, group_id); + gAgent.sendReliableMessage(); +} + +/* Singu Note: this function serves no purpose to us, it is here as an honorable mention +static bool isGroupUIVisible() +{ + static LLPanel* panel = 0; + if(!panel) + panel = LLFloaterSidePanelContainer::getPanel("people", "panel_group_info_sidetray"); + if(!panel) + return false; + return panel->isInVisibleChain(); +} +*/ + +// static +void LLGroupActions::inspect(const LLUUID& group_id) +{ + //LLFloaterReg::showInstance("inspect_group", LLSD().with("group_id", group_id)); + openGroupProfile(group_id); +} + + +// static +void LLGroupActions::show(const LLUUID& group_id) +{ + if (group_id.isNull()) + return; + + /* + LLSD params; + params["group_id"] = group_id; + params["open_tab_name"] = "panel_group_info_sidetray"; + + LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params); + */ + openGroupProfile(group_id); +} + +// static +void LLGroupActions::showTab(const LLUUID& group_id, const std::string& tab_name) +{ + if (group_id.isNull()) return; + + openGroupProfile(group_id)->selectTabByName(tab_name); +} + +// static +void LLGroupActions::showNotice(const std::string& subj, const std::string& mes, const LLUUID& group_id, const bool& has_inventory, const std::string& item_name, LLOfferInfo* info) +{ + if (LLFloaterGroupInfo* fgi = LLFloaterGroupInfo::getInstance(group_id)) + { + fgi->mPanelGroupp->showNotice(subj, mes, has_inventory, item_name, info); + } + else + { + // We need to clean up that inventory offer. + if (info) + { + info->forceResponse(IOR_DECLINE); + } + } +} + +/* Singu Note: How could this ever work with a null id, it's only used by llnotificationgrouphandler.cpp which we don't have +void LLGroupActions::refresh_notices() +{ + if(!isGroupUIVisible()) + return; + + LLSD params; + params["group_id"] = LLUUID::null; + params["open_tab_name"] = "panel_group_info_sidetray"; + params["action"] = "refresh_notices"; + + LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params); +} +*/ + +//static +void LLGroupActions::refresh(const LLUUID& group_id) +{ + /* + if(!isGroupUIVisible()) + return; + + LLSD params; + params["group_id"] = group_id; + params["open_tab_name"] = "panel_group_info_sidetray"; + params["action"] = "refresh"; + + LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params); + */ + if (LLFloaterGroupInfo* fgi = LLFloaterGroupInfo::getInstance(group_id)) + if (LLPanelGroup* pg = fgi->mPanelGroupp) + pg->refreshData(); +} + +//static +void LLGroupActions::createGroup() +{ + /* + LLSD params; + params["group_id"] = LLUUID::null; + params["open_tab_name"] = "panel_group_info_sidetray"; + params["action"] = "create"; + + LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params); + */ + openGroupProfile(LLUUID::null); +} +//static +void LLGroupActions::closeGroup(const LLUUID& group_id) +{ + /* + if(!isGroupUIVisible()) + return; + + LLSD params; + params["group_id"] = group_id; + params["open_tab_name"] = "panel_group_info_sidetray"; + params["action"] = "close"; + + LLFloaterSidePanelContainer::showPanel("people", "panel_group_info_sidetray", params); + */ + if (LLFloaterGroupInfo* fgi = LLFloaterGroupInfo::getInstance(group_id)) + fgi->close(); +} + + +// static +LLUUID LLGroupActions::startIM(const LLUUID& group_id) +{ + if (group_id.isNull()) return LLUUID::null; + +// [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h + if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(group_id)) && (!gIMMgr->hasSession(group_id)) ) + { + make_ui_sound("UISndInvalidOp"); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("group", group_id, "about").getSLURLString())); + return LLUUID::null; + } +// [/RLVa:KB] + + LLGroupData group_data; + if (gAgent.getGroupData(group_id, group_data)) + { + static LLCachedControl tear_off("OtherChatsTornOff"); + if (!tear_off) gIMMgr->setFloaterOpen(TRUE); + LLUUID session_id = gIMMgr->addSession( + group_data.mName, + IM_SESSION_GROUP_START, + group_id); + make_ui_sound("UISndStartIM"); + return session_id; + } + else + { + // this should never happen, as starting a group IM session + // relies on you belonging to the group and hence having the group data + make_ui_sound("UISndInvalidOp"); + return LLUUID::null; + } +} + +// static +void LLGroupActions::endIM(const LLUUID& group_id) +{ + if (group_id.isNull()) + return; + + LLUUID session_id = gIMMgr->computeSessionID(IM_SESSION_GROUP_START, group_id); + if (session_id.notNull()) + { + gIMMgr->removeSession(session_id); + } +} + +// static +bool LLGroupActions::isInGroup(const LLUUID& group_id) +{ + // *TODO: Move all the LLAgent group stuff into another class, such as + // this one. + return gAgent.isInGroup(group_id); +} + +// static +bool LLGroupActions::isAvatarMemberOfGroup(const LLUUID& group_id, const LLUUID& avatar_id) +{ + if(group_id.isNull() || avatar_id.isNull()) + { + return false; + } + + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(group_id); + if(!group_data) + { + return false; + } + + if(group_data->mMembers.end() == group_data->mMembers.find(avatar_id)) + { + return false; + } + + return true; +} + +//-- Private methods ---------------------------------------------------------- + +// static +bool LLGroupActions::onLeaveGroup(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + LLUUID group_id = notification["payload"]["group_id"].asUUID(); + if(option == 0) + { + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_LeaveGroupRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_GroupData); + msg->addUUIDFast(_PREHASH_GroupID, group_id); + gAgent.sendReliableMessage(); + } + return false; +} + +// Singu helper function, open a profile and center it +// static +LLFloaterGroupInfo* LLGroupActions::openGroupProfile(const LLUUID& group_id) +{ + LLFloaterGroupInfo* fgi = LLFloaterGroupInfo::getInstance(group_id); + if (!fgi) fgi = new LLFloaterGroupInfo(group_id); + fgi->center(); + fgi->open(); + return fgi; +} + diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h new file mode 100644 index 000000000..b59fbbe40 --- /dev/null +++ b/indra/newview/llgroupactions.h @@ -0,0 +1,131 @@ +/** + * @file llgroupactions.h + * @brief Group-related actions (join, leave, new, delete, etc) + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLGROUPACTIONS_H +#define LL_LLGROUPACTIONS_H + +class LLFloaterGroupInfo; +class LLOfferInfo; + +/** + * Group-related actions (join, leave, new, delete, etc) + */ +class LLGroupActions +{ +public: + /** + * Invokes group search floater. + */ + static void search(); + + /// Join a group. Assumes LLGroupMgr has data for that group already. + static void join(const LLUUID& group_id); + + /** + * Invokes "Leave Group" floater. + */ + static void leave(const LLUUID& group_id); + + /** + * Activate group. + */ + static void activate(const LLUUID& group_id); + + /** + * Show group information panel. + */ + static void show(const LLUUID& group_id); + + /** + * Show group information panel, with specific tab open. + */ + static void showTab(const LLUUID& group_id, const std::string& tab_name); + + /** + * Show group notice that has come in. + */ + static void showNotice(const std::string& subj, const std::string& mes, const LLUUID& group_id, const bool& has_inventory, const std::string& item_name, LLOfferInfo* info); + + /** + * Show group inspector floater. + */ + static void inspect(const LLUUID& group_id); + + /** + * Refresh group information panel. + */ + static void refresh(const LLUUID& group_id); + + /** + * Refresh group notices panel. + */ + static void refresh_notices(); + + /** + * Refresh group information panel. + */ + static void createGroup(); + + /** + * Close group information panel. + */ + static void closeGroup(const LLUUID& group_id); + + /** + * Start group instant messaging session. + */ + static LLUUID startIM(const LLUUID& group_id); + + /** + * End group instant messaging session. + */ + static void endIM(const LLUUID& group_id); + + /// Returns if the current user is a member of the group + static bool isInGroup(const LLUUID& group_id); + + /** + * Start a group voice call. + */ + static void startCall(const LLUUID& group_id); + + /** + * Returns true if avatar is in group. + * + * Note that data about group members is loaded from server. + * If data has not been loaded yet, function will return inaccurate result. + * See LLGroupMgr::sendGroupMembersRequest + */ + static bool isAvatarMemberOfGroup(const LLUUID& group_id, const LLUUID& avatar_id); + +private: + static bool onJoinGroup(const LLSD& notification, const LLSD& response); + static bool onLeaveGroup(const LLSD& notification, const LLSD& response); + static LLFloaterGroupInfo* openGroupProfile(const LLUUID& group_id); +}; + +#endif // LL_LLGROUPACTIONS_H + diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 8b7ef5c6f..70eddbcdc 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -44,11 +44,10 @@ #include "lltransactiontypes.h" #include "llstatusbar.h" #include "lleconomy.h" -#include "llviewercontrol.h" #include "llviewerregion.h" #include "llviewerwindow.h" #include "llfloaterdirectory.h" -#include "llfloatergroupinfo.h" +#include "llgroupactions.h" #include "llnotificationsutil.h" #include "lluictrlfactory.h" #include "lltrans.h" @@ -1361,7 +1360,7 @@ void LLGroupMgr::processEjectGroupMemberReply(LLMessageSystem* msg, void ** data // If we had a failure, the group panel needs to be updated. if (!success) { - LLFloaterGroupInfo::refreshGroup(group_id); + LLGroupActions::refresh(group_id); } } @@ -1381,7 +1380,7 @@ void LLGroupMgr::processJoinGroupReply(LLMessageSystem* msg, void ** data) LLGroupMgr::getInstance()->clearGroupData(group_id); // refresh the floater for this group, if any. - LLFloaterGroupInfo::refreshGroup(group_id); + LLGroupActions::refresh(group_id); // refresh the group panel of the search window, if necessary. LLFloaterDirectory::refreshGroup(group_id); } @@ -1403,7 +1402,7 @@ void LLGroupMgr::processLeaveGroupReply(LLMessageSystem* msg, void ** data) LLGroupMgr::getInstance()->clearGroupData(group_id); // close the floater for this group, if any. - LLFloaterGroupInfo::closeGroup(group_id); + LLGroupActions::closeGroup(group_id); // refresh the group panel of the search window, if necessary. LLFloaterDirectory::refreshGroup(group_id); } @@ -1439,8 +1438,8 @@ void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data) gAgent.mGroups.push_back(gd); - LLFloaterGroupInfo::closeCreateGroup(); - LLFloaterGroupInfo::showFromUUID(group_id,"roles_tab"); + LLGroupActions::closeGroup(LLUUID::null); + LLGroupActions::showTab(group_id, "roles_tab"); } else { diff --git a/indra/newview/llgroupnotify.cpp b/indra/newview/llgroupnotify.cpp index dd0667288..ca9c834f8 100644 --- a/indra/newview/llgroupnotify.cpp +++ b/indra/newview/llgroupnotify.cpp @@ -39,13 +39,13 @@ #include "llbutton.h" #include "lliconctrl.h" #include "llfloaterchat.h" // for add_chat_history() +#include "llgroupactions.h" #include "llnotify.h" #include "lltextbox.h" #include "llviewertexteditor.h" #include "lluiconstants.h" #include "llui.h" #include "llviewercontrol.h" -#include "llfloatergroupinfo.h" #include "llinventoryicon.h" #include "llinventory.h" #include "lltrans.h" @@ -279,7 +279,7 @@ LLGroupNotifyBox::LLGroupNotifyBox(const std::string& subject, wide_btn_width, BTN_HEIGHT); - btn = new LLButton(LLTrans::getString("GroupNotifyGroupNotices"), btn_rect, LLStringUtil::null, boost::bind(&LLGroupNotifyBox::onClickGroupInfo,this)); + btn = new LLButton(LLTrans::getString("GroupNotifyGroupNotices"), btn_rect, LLStringUtil::null, boost::bind(LLGroupActions::showTab, mGroupID, "notices_tab")); btn->setToolTip(LLTrans::getString("GroupNotifyViewPastNotices")); addChild(btn, -1); @@ -457,12 +457,6 @@ void LLGroupNotifyBox::onClickOk() close(); } -void LLGroupNotifyBox::onClickGroupInfo() -{ - LLFloaterGroupInfo::showFromUUID(mGroupID, "notices_tab"); - //Leave notice open until explicitly closed -} - void LLGroupNotifyBox::onClickSaveInventory() { mInventoryOffer->forceResponse(IOR_ACCEPT); diff --git a/indra/newview/llgroupnotify.h b/indra/newview/llgroupnotify.h index 1a0819577..bd19ccd89 100644 --- a/indra/newview/llgroupnotify.h +++ b/indra/newview/llgroupnotify.h @@ -90,7 +90,6 @@ protected: // internal handler for button being clicked void onClickOk(); - void onClickGroupInfo(); void onClickSaveInventory(); // for "next" button diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 9e9cd87c8..88d8467df 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -3,10 +3,9 @@ * @brief LLIMPanel class definition * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -45,15 +44,13 @@ #include "llnotificationsutil.h" #include "llagent.h" +#include "llavataractions.h" #include "llbutton.h" #include "llcallingcard.h" #include "llchat.h" #include "llconsole.h" -#include "llfloater.h" -#include "llfloateractivespeakers.h" -#include "llfloateravatarinfo.h" +#include "llgroupactions.h" #include "llfloaterchat.h" -#include "llfloatergroupinfo.h" #include "llimview.h" #include "llinventory.h" #include "llinventoryfunctions.h" @@ -62,7 +59,9 @@ #include "llkeyboard.h" #include "lllineeditor.h" #include "llnotify.h" +#include "llparticipantlist.h" #include "llresmgr.h" +#include "llspeakers.h" #include "lltrans.h" #include "lltabcontainer.h" #include "llviewertexteditor.h" @@ -71,6 +70,7 @@ #include "llviewercontrol.h" #include "lluictrlfactory.h" #include "llviewerwindow.h" +#include "llvoicechannel.h" #include "lllogchat.h" #include "llweb.h" #include "llhttpclient.h" @@ -86,7 +86,6 @@ class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy startConferenceChatResponder_timeout; -extern AIHTTPTimeoutPolicy voiceCallCapResponder_timeout; extern AIHTTPTimeoutPolicy sessionInviteResponder_timeout; // @@ -95,7 +94,6 @@ extern AIHTTPTimeoutPolicy sessionInviteResponder_timeout; const S32 LINE_HEIGHT = 16; const S32 MIN_WIDTH = 200; const S32 MIN_HEIGHT = 130; -const U32 DEFAULT_RETRIES_COUNT = 3; // // Statics @@ -105,13 +103,6 @@ static std::string sTitleString = "Instant Message with [NAME]"; static std::string sTypingStartString = "[NAME]: ..."; static std::string sSessionStartString = "Starting session with [NAME] please wait."; -LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; -LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; -LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; -LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; - -BOOL LLVoiceChannel::sSuspended = FALSE; - void session_starter_helper( const LLUUID& temp_session_id, const LLUUID& other_participant_id, @@ -300,792 +291,6 @@ bool send_start_session_messages( return false; } -class LLVoiceCallCapResponder : public LLHTTPClient::ResponderWithResult -{ -public: - LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; - - /*virtual*/ void error(U32 status, const std::string& reason); // called with bad status codes - /*virtual*/ void result(const LLSD& content); - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceCallCapResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "LLVoiceCallCapResponder"; } - -private: - LLUUID mSessionID; -}; - - -void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) -{ - llwarns << "LLVoiceCallCapResponder::error(" - << status << ": " << reason << ")" - << llendl; - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if ( channelp ) - { - if ( 403 == status ) - { - //403 == no ability - LLNotifications::instance().add( - "VoiceNotAllowed", - channelp->getNotifyArgs()); - } - else - { - LLNotifications::instance().add( - "VoiceCallGenericError", - channelp->getNotifyArgs()); - } - channelp->deactivate(); - } -} - -void LLVoiceCallCapResponder::result(const LLSD& content) -{ - LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); - if (channelp) - { - // *TODO: DEBUG SPAM - LLSD::map_const_iterator iter; - for(iter = content.beginMap(); iter != content.endMap(); ++iter) - { - llinfos << "LLVoiceCallCapResponder::result got " - << iter->first << llendl; - } - - channelp->setChannelInfo( - content["voice_credentials"]["channel_uri"].asString(), - content["voice_credentials"]["channel_credentials"].asString()); - } -} - -// -// LLVoiceChannel -// -LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) : - mSessionID(session_id), - mState(STATE_NO_CHANNEL_INFO), - mSessionName(session_name), - mIgnoreNextSessionLeave(FALSE) -{ - mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName; - - if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) - { - // a voice channel already exists for this session id, so this instance will be orphaned - // the end result should simply be the failure to make voice calls - llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; - } - - LLVoiceClient::getInstance()->addObserver(this); -} - -LLVoiceChannel::~LLVoiceChannel() -{ - // Don't use LLVoiceClient::getInstance() here -- this can get called during atexit() time and that singleton MAY have already been destroyed. - if(gVoiceClient) - { - gVoiceClient->removeObserver(this); - } - - sVoiceChannelMap.erase(mSessionID); - sVoiceChannelURIMap.erase(mURI); -} - -void LLVoiceChannel::setChannelInfo( - const std::string& uri, - const std::string& credentials) -{ - setURI(uri); - - mCredentials = credentials; - - if (mState == STATE_NO_CHANNEL_INFO) - { - if (mURI.empty()) - { - LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty URI for channel " << mSessionName << llendl; - deactivate(); - } - else if (mCredentials.empty()) - { - LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); - llwarns << "Received empty credentials for channel " << mSessionName << llendl; - deactivate(); - } - else - { - setState(STATE_READY); - - // if we are supposed to be active, reconnect - // this will happen on initial connect, as we request credentials on first use - if (sCurrentVoiceChannel == this) - { - // just in case we got new channel info while active - // should move over to new channel - activate(); - } - } - } -} - -void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) -{ - if (channelURI != mURI) - { - return; - } - - if (type < BEGIN_ERROR_STATUS) - { - handleStatusChange(type); - } - else - { - handleError(type); - } -} - -void LLVoiceChannel::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_LOGIN_RETRY: - //mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); - LLNotificationsUtil::add("VoiceLoginRetry"); - break; - case STATUS_LOGGED_IN: - //if (!mLoginNotificationHandle.isDead()) - //{ - // LLNotifyBox* notifyp = (LLNotifyBox*)mLoginNotificationHandle.get(); - // if (notifyp) - // { - // notifyp->close(); - // } - // mLoginNotificationHandle.markDead(); - //} - break; - case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - // if forceably removed from channel - // update the UI and revert to default channel - LLNotificationsUtil::add("VoiceChannelDisconnected", mNotifyArgs); - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - break; - case STATUS_JOINING: - if (callStarted()) - { - setState(STATE_RINGING); - } - break; - case STATUS_JOINED: - if (callStarted()) - { - setState(STATE_CONNECTED); - } - default: - break; - } -} - -// default behavior is to just deactivate channel -// derived classes provide specific error messages -void LLVoiceChannel::handleError(EStatusType type) -{ - deactivate(); - setState(STATE_ERROR); -} - -BOOL LLVoiceChannel::isActive() -{ - // only considered active when currently bound channel matches what our channel - return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; -} - -BOOL LLVoiceChannel::callStarted() -{ - return mState >= STATE_CALL_STARTED; -} - -void LLVoiceChannel::deactivate() -{ - if (mState >= STATE_RINGING) - { - // ignore session leave event - mIgnoreNextSessionLeave = TRUE; - } - - if (callStarted()) - { - setState(STATE_HUNG_UP); - // mute the microphone if required when returning to the proximal channel - if (gSavedSettings.getBOOL("AutoDisengageMic") && sCurrentVoiceChannel == this) - { - gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); - } - } - - if (sCurrentVoiceChannel == this) - { - // default channel is proximal channel - sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); - sCurrentVoiceChannel->activate(); - } -} - -void LLVoiceChannel::activate() -{ - if (callStarted()) - { - return; - } - - // deactivate old channel and mark ourselves as the active one - if (sCurrentVoiceChannel != this) - { - // mark as current before deactivating the old channel to prevent - // activating the proximal channel between IM calls - LLVoiceChannel* old_channel = sCurrentVoiceChannel; - sCurrentVoiceChannel = this; - if (old_channel) - { - old_channel->deactivate(); - } - } - - if (mState == STATE_NO_CHANNEL_INFO) - { - // responsible for setting status to active - getChannelInfo(); - } - else - { - setState(STATE_CALL_STARTED); - } -} - -void LLVoiceChannel::getChannelInfo() -{ - // pretend we have everything we need - if (sCurrentVoiceChannel == this) - { - setState(STATE_CALL_STARTED); - } -} - -//static -LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) -{ - voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); - if (found_it == sVoiceChannelMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - -//static -LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) -{ - voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); - if (found_it == sVoiceChannelURIMap.end()) - { - return NULL; - } - else - { - return found_it->second; - } -} - - -void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) -{ - sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); - mSessionID = new_session_id; - sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); -} - -void LLVoiceChannel::setURI(std::string uri) -{ - sVoiceChannelURIMap.erase(mURI); - mURI = uri; - sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); -} - -void LLVoiceChannel::setState(EState state) -{ - switch(state) - { - case STATE_RINGING: - gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); - break; - case STATE_CONNECTED: - gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); - break; - case STATE_HUNG_UP: - gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); - break; - default: - break; - } - - mState = state; -} - - -//static -void LLVoiceChannel::initClass() -{ - sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); -} - - -//static -void LLVoiceChannel::suspend() -{ - if (!sSuspended) - { - sSuspendedVoiceChannel = sCurrentVoiceChannel; - sSuspended = TRUE; - } -} - -//static -void LLVoiceChannel::resume() -{ - if (sSuspended) - { - if (gVoiceClient->voiceEnabled()) - { - if (sSuspendedVoiceChannel) - { - sSuspendedVoiceChannel->activate(); - } - else - { - LLVoiceChannelProximal::getInstance()->activate(); - } - } - sSuspended = FALSE; - } -} - - -// -// LLVoiceChannelGroup -// - -LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : - LLVoiceChannel(session_id, session_name) -{ - mRetries = DEFAULT_RETRIES_COUNT; - mIsRetrying = FALSE; -} - -void LLVoiceChannelGroup::deactivate() -{ - if (callStarted()) - { - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); - } - LLVoiceChannel::deactivate(); -} - -void LLVoiceChannelGroup::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials); - } -} - -void LLVoiceChannelGroup::getChannelInfo() -{ - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - std::string url = region->getCapability("ChatSessionRequest"); - LLSD data; - data["method"] = "call"; - data["session-id"] = mSessionID; - LLHTTPClient::post(url, - data, - new LLVoiceCallCapResponder(mSessionID)); - } -} - -void LLVoiceChannelGroup::setChannelInfo( - const std::string& uri, - const std::string& credentials) -{ - setURI(uri); - - mCredentials = credentials; - - if (mState == STATE_NO_CHANNEL_INFO) - { - if(!mURI.empty() && !mCredentials.empty()) - { - setState(STATE_READY); - - // if we are supposed to be active, reconnect - // this will happen on initial connect, as we request credentials on first use - if (sCurrentVoiceChannel == this) - { - // just in case we got new channel info while active - // should move over to new channel - activate(); - } - } - else - { - // *TODO: notify user - llwarns << "Received invalid credentials for channel " << mSessionName << llendl; - deactivate(); - } - } - else if ( mIsRetrying ) - { - // we have the channel info, just need to use it now - LLVoiceClient::getInstance()->setNonSpatialChannel( - mURI, - mCredentials); - } -} - -void LLVoiceChannelGroup::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_JOINED: - mRetries = 3; - mIsRetrying = FALSE; - default: - break; - } - - LLVoiceChannel::handleStatusChange(type); -} - -void LLVoiceChannelGroup::handleError(EStatusType status) -{ - std::string notify; - switch(status) - { - case ERROR_CHANNEL_LOCKED: - case ERROR_CHANNEL_FULL: - notify = "VoiceChannelFull"; - break; - case ERROR_NOT_AVAILABLE: - //clear URI and credentials - //set the state to be no info - //and activate - if ( mRetries > 0 ) - { - mRetries--; - mIsRetrying = TRUE; - mIgnoreNextSessionLeave = TRUE; - - getChannelInfo(); - return; - } - else - { - notify = "VoiceChannelJoinFailed"; - mRetries = DEFAULT_RETRIES_COUNT; - mIsRetrying = FALSE; - } - - break; - - case ERROR_UNKNOWN: - default: - break; - } - - // notification - if (!notify.empty()) - { - LLNotificationPtr notification = LLNotifications::instance().add(notify, mNotifyArgs); - // echo to im window - gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage()); - } - - LLVoiceChannel::handleError(status); -} - -void LLVoiceChannelGroup::setState(EState state) -{ - switch(state) - { - case STATE_RINGING: - if ( !mIsRetrying ) - { - gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); - } - - mState = state; - break; - default: - LLVoiceChannel::setState(state); - } -} - -// -// LLVoiceChannelProximal -// -LLVoiceChannelProximal::LLVoiceChannelProximal() : - LLVoiceChannel(LLUUID::null, LLStringUtil::null) -{ - activate(); -} - -BOOL LLVoiceChannelProximal::isActive() -{ - return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); -} - -void LLVoiceChannelProximal::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // this implicitly puts you back in the spatial channel - LLVoiceClient::getInstance()->leaveNonSpatialChannel(); - } -} - -void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) -{ - if (!proximal) - { - return; - } - - if (type < BEGIN_ERROR_STATUS) - { - handleStatusChange(type); - } - else - { - handleError(type); - } -} - -void LLVoiceChannelProximal::handleStatusChange(EStatusType status) -{ - // status updates - switch(status) - { - case STATUS_LEFT_CHANNEL: - // do not notify user when leaving proximal channel - return; - case STATUS_VOICE_DISABLED: - gIMMgr->addSystemMessage(LLUUID::null, "unavailable", mNotifyArgs); - return; - default: - break; - } - LLVoiceChannel::handleStatusChange(status); -} - - -void LLVoiceChannelProximal::handleError(EStatusType status) -{ - std::string notify; - switch(status) - { - case ERROR_CHANNEL_LOCKED: - case ERROR_CHANNEL_FULL: - notify = "ProximalVoiceChannelFull"; - break; - default: - break; - } - - // notification - if (!notify.empty()) - { - LLNotifications::instance().add(notify, mNotifyArgs); - } - - LLVoiceChannel::handleError(status); -} - -void LLVoiceChannelProximal::deactivate() -{ - if (callStarted()) - { - setState(STATE_HUNG_UP); - } -} - - -// -// LLVoiceChannelP2P -// -LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : - LLVoiceChannelGroup(session_id, session_name), - mOtherUserID(other_user_id), - mReceivedCall(FALSE) -{ - // make sure URI reflects encoded version of other user's agent id - setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); -} - -void LLVoiceChannelP2P::handleStatusChange(EStatusType type) -{ - // status updates - switch(type) - { - case STATUS_LEFT_CHANNEL: - if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) - { - if (mState == STATE_RINGING) - { - // other user declined call - LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs); - } - else - { - // other user hung up - LLNotificationsUtil::add("VoiceChannelDisconnectedP2P", mNotifyArgs); - } - deactivate(); - } - mIgnoreNextSessionLeave = FALSE; - return; - default: - break; - } - - LLVoiceChannel::handleStatusChange(type); -} - -void LLVoiceChannelP2P::handleError(EStatusType type) -{ - switch(type) - { - case ERROR_NOT_AVAILABLE: - LLNotificationsUtil::add("P2PCallNoAnswer", mNotifyArgs); - break; - default: - break; - } - - LLVoiceChannel::handleError(type); -} - -void LLVoiceChannelP2P::activate() -{ - if (callStarted()) return; - - LLVoiceChannel::activate(); - - if (callStarted()) - { - // no session handle yet, we're starting the call - if (mSessionHandle.empty()) - { - mReceivedCall = FALSE; - LLVoiceClient::getInstance()->callUser(mOtherUserID); - } - // otherwise answering the call - else - { - LLVoiceClient::getInstance()->answerInvite(mSessionHandle); - - // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. - mSessionHandle.clear(); - } - } -} - -void LLVoiceChannelP2P::getChannelInfo() -{ - // pretend we have everything we need, since P2P doesn't use channel info - if (sCurrentVoiceChannel == this) - { - setState(STATE_CALL_STARTED); - } -} - -// receiving session from other user who initiated call -void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI) -{ - BOOL needs_activate = FALSE; - if (callStarted()) - { - // defer to lower agent id when already active - if (mOtherUserID < gAgent.getID()) - { - // pretend we haven't started the call yet, so we can connect to this session instead - deactivate(); - needs_activate = TRUE; - } - else - { - // we are active and have priority, invite the other user again - // under the assumption they will join this new session - mSessionHandle.clear(); - LLVoiceClient::getInstance()->callUser(mOtherUserID); - return; - } - } - - mSessionHandle = handle; - - // The URI of a p2p session should always be the other end's SIP URI. - if(!inURI.empty()) - { - setURI(inURI); - } - else - { - setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); - } - - mReceivedCall = TRUE; - - if (needs_activate) - { - activate(); - } -} - -void LLVoiceChannelP2P::setState(EState state) -{ - // you only "answer" voice invites in p2p mode - // so provide a special purpose message here - if (mReceivedCall && state == STATE_RINGING) - { - gIMMgr->addSystemMessage(mSessionID, "answering", mNotifyArgs); - mState = state; - return; - } - LLVoiceChannel::setState(state); -} - // // LLFloaterIMPanel @@ -1110,7 +315,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mSentTypingState(TRUE), mNumUnreadMessages(0), mShowSpeakersOnConnect(TRUE), - mAutoConnect(FALSE), + mStartCallOnInitialize(false), mTextIMPossible(TRUE), mProfileButtonEnabled(TRUE), mCallBackEnabled(TRUE), @@ -1147,7 +352,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mTypingLineStartIndex(0), mSentTypingState(TRUE), mShowSpeakersOnConnect(TRUE), - mAutoConnect(FALSE), + mStartCallOnInitialize(false), mTextIMPossible(TRUE), mProfileButtonEnabled(TRUE), mCallBackEnabled(TRUE), @@ -1310,13 +515,13 @@ LLFloaterIMPanel::~LLFloaterIMPanel() mSpeakers = NULL; // End the text IM session if necessary - if(gVoiceClient && mOtherParticipantUUID.notNull()) + if(LLVoiceClient::instanceExists() && mOtherParticipantUUID.notNull()) { switch(mDialog) { case IM_NOTHING_SPECIAL: case IM_SESSION_P2P_INVITE: - gVoiceClient->endUserIMSession(mOtherParticipantUUID); + LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantUUID); break; default: @@ -1358,26 +563,23 @@ BOOL LLFloaterIMPanel::postBuild() if (LLButton* btn = findChild("profile_callee_btn")) { - btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickProfile, this)); + btn->setCommitCallback(boost::bind(LLAvatarActions::showProfile, mOtherParticipantUUID)); if (!mProfileButtonEnabled) btn->setEnabled(false); } if (LLButton* btn = findChild("profile_tele_btn")) - btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickTeleport, this)); + btn->setCommitCallback(boost::bind(static_cast(LLAvatarActions::offerTeleport), mOtherParticipantUUID)); if (LLButton* btn = findChild("group_info_btn")) - btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickGroupInfo, this)); + btn->setCommitCallback(boost::bind(LLGroupActions::show, mSessionUUID)); childSetAction("history_btn", onClickHistory, this); if (LLUICtrl* ctrl = findChild("rp_mode")) ctrl->setCommitCallback(boost::bind(&LLFloaterIMPanel::onRPMode, this, _2)); - childSetAction("start_call_btn", onClickStartCall, this); - childSetAction("end_call_btn", onClickEndCall, this); + getChild("start_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::startCall, gIMMgr, mSessionUUID, LLVoiceChannel::OUTGOING_CALL)); + getChild("end_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::endCall, gIMMgr, mSessionUUID)); getChild("send_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onSendMsg,this)); if (LLButton* btn = findChild("toggle_active_speakers_btn")) btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickToggleActiveSpeakers, this, _2)); - //LLButton* close_btn = getChild("close_btn"); - //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); - mHistoryEditor = getChild("im_history"); mHistoryEditor->setParseHTML(TRUE); mHistoryEditor->setParseHighlights(TRUE); @@ -1398,8 +600,8 @@ BOOL LLFloaterIMPanel::postBuild() if (mDialog == IM_NOTHING_SPECIAL) { - childSetAction("mute_btn", onClickMuteVoice, this); - childSetCommitCallback("speaker_volume", onVolumeChange, this); + getChild("mute_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickMuteVoice, this)); + getChild("speaker_volume")->setCommitCallback(boost::bind(&LLVoiceClient::setUserVolume, LLVoiceClient::getInstance(), mOtherParticipantUUID, _2)); } setDefaultBtn("send_btn"); @@ -1419,37 +621,20 @@ BOOL LLFloaterIMPanel::postBuild() void* LLFloaterIMPanel::createSpeakersPanel(void* data) { LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)data; - floaterp->mSpeakerPanel = new LLPanelActiveSpeakers(floaterp->mSpeakers, TRUE); + floaterp->mSpeakerPanel = new LLParticipantList(floaterp->mSpeakers, true); return floaterp->mSpeakerPanel; } -//static -void LLFloaterIMPanel::onClickMuteVoice(void* user_data) +void LLFloaterIMPanel::onClickMuteVoice() { - LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; - if (floaterp) + LLMute mute(mOtherParticipantUUID, getTitle(), LLMute::AGENT); + if (!LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)) { - BOOL is_muted = LLMuteList::getInstance()->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat); - - LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT); - if (!is_muted) - { - LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); - } - else - { - LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); - } + LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); } -} - -//static -void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data) -{ - LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; - if (floaterp) + else { - gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); + LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); } } @@ -1461,14 +646,14 @@ void LLFloaterIMPanel::draw() BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "") && mSessionInitialized - && LLVoiceClient::voiceEnabled() + && LLVoiceClient::getInstance()->voiceEnabled() && mCallBackEnabled; // hide/show start call and end call buttons - mEndCallBtn->setVisible(LLVoiceClient::voiceEnabled() && mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); - mStartCallBtn->setVisible(LLVoiceClient::voiceEnabled() && mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); + mEndCallBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); + mStartCallBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); mStartCallBtn->setEnabled(enable_connect); - mSendBtn->setEnabled(!childGetValue("chat_editor").asString().empty()); + mSendBtn->setEnabled(!mInputEditor->getValue().asString().empty()); LLPointer self_speaker = mSpeakers->findSpeaker(gAgent.getID()); if(!mTextIMPossible) @@ -1487,12 +672,6 @@ void LLFloaterIMPanel::draw() mInputEditor->setLabel(getString("default_text_label")); } - if (mAutoConnect && enable_connect) - { - onClickStartCall(this); - mAutoConnect = FALSE; - } - // show speakers window when voice first connects if (mShowSpeakersOnConnect && mVoiceChannel->isActive()) { @@ -1531,11 +710,11 @@ void LLFloaterIMPanel::draw() else { // refresh volume and mute checkbox - mVolumeSlider->setVisible(LLVoiceClient::voiceEnabled() && mVoiceChannel->isActive()); - mVolumeSlider->setValue(gVoiceClient->getUserVolume(mOtherParticipantUUID)); + mVolumeSlider->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->isActive()); + mVolumeSlider->setValue(LLVoiceClient::getInstance()->getUserVolume(mOtherParticipantUUID)); mMuteBtn->setValue(LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)); - mMuteBtn->setVisible(LLVoiceClient::voiceEnabled() && mVoiceChannel->isActive()); + mMuteBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->isActive()); } LLFloater::draw(); } @@ -1550,7 +729,8 @@ public: /*virtual*/ void error(U32 statusNum, const std::string& reason) { - llinfos << "Error inviting all agents to session" << llendl; + llwarns << "Error inviting all agents to session [status:" + << statusNum << "]: " << reason << llendl; //throw something back to the viewer here? } @@ -1876,25 +1056,6 @@ void LLFloaterIMPanel::onTabClick(void* userdata) } -void LLFloaterIMPanel::onClickProfile() -{ - // Bring up the Profile window - if (mOtherParticipantUUID.notNull()) - { - LLFloaterAvatarInfo::showFromDirectory(mOtherParticipantUUID); - } -} - -void LLFloaterIMPanel::onClickTeleport() -{ - if (mOtherParticipantUUID.notNull()) - { - handle_lure(mOtherParticipantUUID); - //do a teleport to other part id - //LLFloaterAvatarInfo::showFromDirectory(mOtherParticipantID); - } -} - void LLFloaterIMPanel::onRPMode(const LLSD& value) { mRPMode = value.asBoolean(); @@ -1919,22 +1080,6 @@ void LLFloaterIMPanel::onClickHistory( void* userdata ) } } -void LLFloaterIMPanel::onClickGroupInfo() -{ - // Bring up the Profile window - LLFloaterGroupInfo::showFromUUID(mSessionUUID); -} - -// static -void LLFloaterIMPanel::onClickClose( void* userdata ) -{ - LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; - if(self) - { - self->close(); - } -} - // static void LLFloaterIMPanel::onClickStartCall(void* userdata) { @@ -1943,14 +1088,6 @@ void LLFloaterIMPanel::onClickStartCall(void* userdata) self->mVoiceChannel->activate(); } -// static -void LLFloaterIMPanel::onClickEndCall(void* userdata) -{ - LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; - - self->getVoiceChannel()->deactivate(); -} - void LLFloaterIMPanel::onClickToggleActiveSpeakers(const LLSD& value) { childSetVisible("active_speakers_panel", !value); @@ -2030,7 +1167,7 @@ void deliver_message(const std::string& utf8_text, if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id))) { // User is online through the OOW connector, but not with a regular viewer. Try to send the message via SLVoice. - sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text); + sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text); } if(!sent) @@ -2286,11 +1423,6 @@ LL_WARNS("Splitting") << "Pos: " << pos << " next_split: " << next_split << LL_E mSentTypingState = TRUE; } -void LLFloaterIMPanel::updateSpeakersList(const LLSD& speaker_updates) -{ - mSpeakers->updateSpeakers(speaker_updates); -} - void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) { if ( @@ -2310,15 +1442,10 @@ void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) //update the speakers dropdown too - mSpeakerPanel->setVoiceModerationCtrlMode(voice_moderated); + mSpeakerPanel->setVoiceModerationCtrlMode(session_update); } } -void LLFloaterIMPanel::setSpeakers(const LLSD& speaker_list) -{ - mSpeakers->setSpeakers(speaker_list); -} - void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) { mSessionUUID = session_id; @@ -2344,11 +1471,12 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) mOtherParticipantUUID, mDialog); } -} -void LLFloaterIMPanel::requestAutoConnect() -{ - mAutoConnect = TRUE; + // auto-start the call on session initialization? + if (mStartCallOnInitialize) + { + gIMMgr->startCall(mSessionUUID); + } } void LLFloaterIMPanel::setTyping(BOOL typing) diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 95f5eb41e..0f659a820 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -3,10 +3,9 @@ * @brief LLIMPanel class definition * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -47,134 +46,9 @@ class LLViewerTextEditor; class LLInventoryItem; class LLInventoryCategory; class LLIMSpeakerMgr; -class LLPanelActiveSpeakers; -class LLPanel; +class LLParticipantList; class LLButton; - -class LLVoiceChannel : public LLVoiceClientStatusObserver -{ -public: - typedef enum e_voice_channel_state - { - STATE_NO_CHANNEL_INFO, - STATE_ERROR, - STATE_HUNG_UP, - STATE_READY, - STATE_CALL_STARTED, - STATE_RINGING, - STATE_CONNECTED - } EState; - - LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); - virtual ~LLVoiceChannel(); - - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - - virtual void handleStatusChange(EStatusType status); - virtual void handleError(EStatusType status); - virtual void deactivate(); - virtual void activate(); - virtual void setChannelInfo( - const std::string& uri, - const std::string& credentials); - virtual void getChannelInfo(); - virtual BOOL isActive(); - virtual BOOL callStarted(); - - const LLUUID getSessionID() { return mSessionID; } - EState getState() { return mState; } - - void updateSessionID(const LLUUID& new_session_id); - const LLSD& getNotifyArgs() { return mNotifyArgs; } - - static LLVoiceChannel* getChannelByID(const LLUUID& session_id); - static LLVoiceChannel* getChannelByURI(std::string uri); - static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } - static void initClass(); - - static void suspend(); - static void resume(); - -protected: - virtual void setState(EState state); - void setURI(std::string uri); - - std::string mURI; - std::string mCredentials; - LLUUID mSessionID; - EState mState; - std::string mSessionName; - LLSD mNotifyArgs; - BOOL mIgnoreNextSessionLeave; - LLHandle mLoginNotificationHandle; - - typedef std::map voice_channel_map_t; - static voice_channel_map_t sVoiceChannelMap; - - typedef std::map voice_channel_map_uri_t; - static voice_channel_map_uri_t sVoiceChannelURIMap; - - static LLVoiceChannel* sCurrentVoiceChannel; - static LLVoiceChannel* sSuspendedVoiceChannel; - static BOOL sSuspended; -}; - -class LLVoiceChannelGroup : public LLVoiceChannel -{ -public: - LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); - - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ void activate(); - /*virtual*/ void deactivate(); - /*vritual*/ void setChannelInfo( - const std::string& uri, - const std::string& credentials); - /*virtual*/ void getChannelInfo(); - -protected: - virtual void setState(EState state); - -private: - U32 mRetries; - BOOL mIsRetrying; -}; - -class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton -{ -public: - LLVoiceChannelProximal(); - - /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ BOOL isActive(); - /*virtual*/ void activate(); - /*virtual*/ void deactivate(); - -}; - -class LLVoiceChannelP2P : public LLVoiceChannelGroup -{ -public: - LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); - - /*virtual*/ void handleStatusChange(EStatusType status); - /*virtual*/ void handleError(EStatusType status); - /*virtual*/ void activate(); - /*virtual*/ void getChannelInfo(); - - void setSessionHandle(const std::string& handle, const std::string &inURI); - -protected: - virtual void setState(EState state); - -private: - std::string mSessionHandle; - LLUUID mOtherUserID; - BOOL mReceivedCall; -}; +class LLVoiceChannel; class LLFloaterIMPanel : public LLFloater { @@ -239,31 +113,23 @@ public: void onInputEditorKeystroke(LLLineEditor* caller); static void onTabClick( void* userdata ); - void onClickProfile(); static void onClickHistory( void* userdata ); void onRPMode(const LLSD& value); - void onClickTeleport(); - void onClickGroupInfo(); - static void onClickClose( void* userdata ); static void onClickStartCall( void* userdata ); static void onClickEndCall( void* userdata ); void onClickToggleActiveSpeakers(const LLSD& value); static void* createSpeakersPanel(void* data); //callbacks for P2P muting and volume control - static void onClickMuteVoice(void* user_data); - static void onVolumeChange(LLUICtrl* source, void* user_data); + void onClickMuteVoice(); const LLUUID& getSessionID() const { return mSessionUUID; } const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; } - void updateSpeakersList(const LLSD& speaker_updates); void processSessionUpdate(const LLSD& update); - void setSpeakers(const LLSD& speaker_list); LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } + LLIMSpeakerMgr* getSpeakerManager() const { return mSpeakers; } // Singu TODO: LLIMModel::getSpeakerManager EInstantMessage getDialogType() const { return mDialog; } - void requestAutoConnect(); - void sessionInitReplyReceived(const LLUUID& im_session_id); // Handle other participant in the session typing. @@ -290,6 +156,10 @@ public: bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} SType mSessionType; + // LLIMModel Functionality + bool getSessionInitialized() const { return mSessionInitialized; } + bool mStartCallOnInitialize; + private: // called by constructors void init(const std::string& session_label); @@ -365,14 +235,12 @@ private: BOOL mShowSpeakersOnConnect; - BOOL mAutoConnect; - BOOL mTextIMPossible; BOOL mProfileButtonEnabled; BOOL mCallBackEnabled; LLIMSpeakerMgr* mSpeakers; - LLPanelActiveSpeakers* mSpeakerPanel; + LLParticipantList* mSpeakerPanel; // Optimization: Don't send "User is typing..." until the // user has actually been typing for a little while. Prevents diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index b04a2cd18..837b76ff8 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -36,7 +36,6 @@ #include "llfontgl.h" #include "llrect.h" -#include "llerror.h" #include "llbutton.h" #include "llhttpclient.h" #include "llsdutil_math.h" @@ -46,6 +45,7 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavataractions.h" #include "llcallingcard.h" #include "llchat.h" #include "llresmgr.h" @@ -53,25 +53,17 @@ #include "llfloaterchatterbox.h" #include "llhttpnode.h" #include "llimpanel.h" -#include "llresizebar.h" +#include "llnotificationsutil.h" #include "llsdserialize.h" +#include "llspeakers.h" #include "lltabcontainer.h" -#include "llviewercontrol.h" -#include "llfloater.h" #include "llmutelist.h" -#include "llresizehandle.h" -#include "llkeyboard.h" -#include "llui.h" #include "llviewermenu.h" -#include "llcallingcard.h" -#include "lltoolbar.h" #include "llviewermessage.h" #include "llviewerwindow.h" #include "llnotify.h" #include "llviewerregion.h" -#include "llfirstuse.h" - // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -123,6 +115,7 @@ LLColor4 agent_chat_color(const LLUUID& id, const std::string& name, bool local_ //{ // return (LLStringUtil::compareDict( a->mName, b->mName ) < 0); //} + class LLViewerChatterBoxInvitationAcceptResponder : public LLHTTPClient::ResponderWithResult { public: @@ -138,10 +131,9 @@ public: { if ( gIMMgr) { - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); - - if (floaterp) + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(mSessionID); + LLIMSpeakerMgr* speaker_mgr = floater ? floater->getSpeakerManager() : NULL; + if (speaker_mgr) { //we've accepted our invitation //and received a list of agents that were @@ -155,26 +147,26 @@ public: //but unfortunately, our base that we are receiving here //may not be the most up to date. It was accurate at //some point in time though. - floaterp->setSpeakers(content); + speaker_mgr->setSpeakers(content); //we now have our base of users in the session //that was accurate at some point, but maybe not now //so now we apply all of the udpates we've received //in case of race conditions - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(mSessionID)); + speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID)); + } - if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE ) - { - floaterp->requestAutoConnect(); - LLFloaterIMPanel::onClickStartCall(floaterp); - // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(TRUE); - } - else if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE ) - { - LLFloaterChatterBox::showInstance(TRUE); - } + if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType) + { + gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL); + } + + if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE + || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) + && gIMMgr->hasSession(mSessionID)) + { + // always open IM window when connecting to voice + LLFloaterChatterBox::showInstance(TRUE); } gIMMgr->clearPendingAgentListUpdates(mSessionID); @@ -184,6 +176,8 @@ public: /*virtual*/ void error(U32 statusNum, const std::string& reason) { + llwarns << "LLViewerChatterBoxInvitationAcceptResponder error [status:" + << statusNum << "]: " << reason << llendl; //throw something back to the viewer here? if ( gIMMgr ) { @@ -294,11 +288,14 @@ protected: bool inviteUserResponse(const LLSD& notification, const LLSD& response) { + if (!gIMMgr) + return false; + const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"].asUUID(); EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch(option) { case 0: // accept @@ -312,16 +309,10 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) payload["session_handle"].asString(), payload["session_uri"].asString()); - LLFloaterIMPanel* im_floater = - gIMMgr->findFloaterBySession( - session_id); - if (im_floater) - { - im_floater->requestAutoConnect(); - LLFloaterIMPanel::onClickStartCall(im_floater); - // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(session_id); - } + gIMMgr->startCall(session_id); + + // always open IM window when connecting to voice + LLFloaterChatterBox::showInstance(session_id); gIMMgr->clearPendingAgentListUpdates(session_id); gIMMgr->clearPendingInvitation(session_id); @@ -363,11 +354,8 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) { if (type == IM_SESSION_P2P_INVITE) { - if(gVoiceClient) - { - std::string s = payload["session_handle"].asString(); - gVoiceClient->declineInvite(s); - } + std::string s = payload["session_handle"].asString(); + LLVoiceClient::getInstance()->declineInvite(s); } else { @@ -402,7 +390,7 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) EInstantMessage LLIMMgr::defaultIMTypeForAgent(const LLUUID& agent_id) { EInstantMessage type = IM_NOTHING_SPECIAL; - if(is_agent_friend(agent_id)) + if (LLAvatarActions::isFriend(agent_id)) { if(LLAvatarTracker::instance().isBuddyOnline(agent_id)) { @@ -696,6 +684,22 @@ BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid) return FALSE; } +void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = findFloaterBySession(session_id); + if (!floater) return; + + if (floater->getSessionInitialized()) + { + startCall(session_id); + } + else + { + floater->mStartCallOnInitialize = true; + } +} + LLUUID LLIMMgr::addP2PSession(const std::string& name, const LLUUID& other_participant_id, const std::string& voice_session_handle, @@ -1033,6 +1037,31 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) } } +void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) +{ + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + if (!im_floater) return; + LLIMSpeakerMgr* speaker_mgr = im_floater->getSpeakerManager(); + if (speaker_mgr) + { + speaker_mgr->updateSpeakers(body); + + // also the same call is added into LLVoiceClient::participantUpdatedEvent because + // sometimes it is called AFTER LLViewerChatterBoxSessionAgentListUpdates::post() + // when moderation state changed too late. See EXT-3544. + speaker_mgr->update(true); + } + else + { + //we don't have a speaker manager yet..something went wrong + //we are probably receiving an update here before + //a start or an acceptance of an invitation. Race condition. + gIMMgr->addPendingAgentListUpdates( + session_id, + body); + } +} + LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id) { if ( mPendingAgentListUpdates.has(session_id.asString()) ) @@ -1113,6 +1142,39 @@ void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id) } } +bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (!floater) return false; + + LLVoiceChannel* voice_channel = floater->getVoiceChannel(); + if (!voice_channel) return false; + + voice_channel->setCallDirection(direction); + voice_channel->activate(); + return true; +} + +bool LLIMMgr::endCall(const LLUUID& session_id) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (!floater) return false; + + LLVoiceChannel* voice_channel = floater->getVoiceChannel(); + if (!voice_channel) return false; + + voice_channel->deactivate(); + /*LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id); + if (im_session)*/ + { + // need to update speakers' state + floater->getSpeakerManager()->update(FALSE); + } + return true; +} + // create a floater and update internal representation for // consistency. Returns the pointer, caller (the class instance since // it is a private method) is not responsible for deleting the @@ -1426,35 +1488,30 @@ public: if ( success ) { session_id = body["session_id"].asUUID(); - gIMMgr->updateFloaterSessionID( - temp_session_id, - session_id); - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) + // Singu TODO: LLIMModel + gIMMgr->updateFloaterSessionID(temp_session_id, session_id); + + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + LLIMSpeakerMgr* speaker_mgr = im_floater ? im_floater->getSpeakerManager() : NULL; + if (speaker_mgr) { - floaterp->setSpeakers(body); - - //apply updates we've possibly received previously - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(session_id)); + speaker_mgr->setSpeakers(body); + speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id)); + } + if (im_floater) + { if ( body.has("session_info") ) { - floaterp->processSessionUpdate(body["session_info"]); + im_floater->processSessionUpdate(body["session_info"]); } - - //apply updates we've possibly received previously - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(session_id)); } + gIMMgr->clearPendingAgentListUpdates(session_id); } else { - //throw an error dialog and close the temp session's floater - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id); - - if ( floater ) + if (LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id)) { floater->showSessionStartError(body["error"].asString()); } @@ -1534,21 +1591,8 @@ public: const LLSD& context, const LLSD& input) const { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); - if (floaterp) - { - floaterp->updateSpeakersList( - input["body"]); - } - else - { - //we don't have a floater yet..something went wrong - //we are probably receiving an update here before - //a start or an acceptance of an invitation. Race condition. - gIMMgr->addPendingAgentListUpdates( - input["body"]["session_id"].asUUID(), - input["body"]); - } + const LLUUID& session_id = input["body"]["session_id"].asUUID(); + gIMMgr->processAgentListUpdates(session_id, input["body"]); } }; @@ -1560,10 +1604,16 @@ public: const LLSD& context, const LLSD& input) const { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); - if (floaterp) + LLUUID session_id = input["body"]["session_id"].asUUID(); + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + if ( im_floater ) { - floaterp->processSessionUpdate(input["body"]["info"]); + im_floater->processSessionUpdate(input["body"]["info"]); + } + LLIMSpeakerMgr* im_mgr = im_floater ? im_floater->getSpeakerManager() : NULL; //LLIMModel::getInstance()->getSpeakerManager(session_id); + if (im_mgr) + { + im_mgr->processSessionUpdate(input["body"]["info"]); } } }; @@ -1653,7 +1703,9 @@ public: std::string saved; if(offline == IM_OFFLINE) { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } std::string buffer = separator_string + saved + message.substr(message_offset); @@ -1728,7 +1780,7 @@ public: return; } - if(!LLVoiceClient::voiceEnabled()) + if(!LLVoiceClient::getInstance()->voiceEnabled()) { // Don't display voice invites unless the user has voice enabled. return; diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 4db4b75f9..3e7bc6fff 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -36,12 +36,12 @@ #include "llmultifloater.h" #include "llinstantmessage.h" #include "lluuid.h" +#include "llvoicechannel.h" + class LLFloaterChatterBox; -class LLUUID; class LLFloaterIMPanel; class LLFriendObserver; -class LLFloaterIM; class LLIMMgr : public LLSingleton { @@ -131,6 +131,9 @@ public: void notifyNewIM(); void clearNewIMNotification(); + // automatically start a call once the session has initialized + void autoStartCallOnStartup(const LLUUID& session_id); + // IM received that you haven't seen yet BOOL getIMReceived() const; int getIMUnreadCount(); @@ -163,6 +166,7 @@ public: void clearPendingInvitation(const LLUUID& session_id); + void processAgentListUpdates(const LLUUID& session_id, const LLSD& body); LLSD getPendingAgentListUpdates(const LLUUID& session_id); void addPendingAgentListUpdates( const LLUUID& sessioN_id, @@ -178,6 +182,18 @@ public: // Returns true if group chat is ignored for the UUID, false if not bool getIgnoreGroup(const LLUUID& group_id) const; + /** + * Start call in a session + * @return false if voice channel doesn't exist + **/ + bool startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction = LLVoiceChannel::OUTGOING_CALL); + + /** + * End call in a session + * @return false if voice channel doesn't exist + **/ + bool endCall(const LLUUID& session_id); + private: // create a panel and update internal representation for // consistency. Returns the pointer, caller (the class instance diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 983311680..a2658eb35 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -39,9 +39,9 @@ #include "llagentwearables.h" #include "llappearancemgr.h" #include "llattachmentsmgr.h" +#include "llavataractions.h" #include "llcallingcard.h" #include "llfirstuse.h" -#include "llfloateravatarinfo.h" #include "llfloaterchat.h" #include "llfloatercustomize.h" #include "llfloateropenobject.h" @@ -88,8 +88,6 @@ #include "rlvlocks.h" // [/RLVa:KB] -extern LLUUID gSystemFolderRoot; - // Marketplace outbox current disabled #define ENABLE_MERCHANT_OUTBOX_CONTEXT_MENU 1 #define ENABLE_MERCHANT_SEND_TO_MARKETPLACE_CONTEXT_MENU 1 @@ -385,33 +383,26 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArraygetUUID()); --update[item->getParentUUID()]; ++update[trash_id]; - // - if(!gInventory.isObjectDescendentOf(item->getUUID(), gSystemFolderRoot)) + if(start_new_message) { - // - if(start_new_message) - { - start_new_message = false; - msg->newMessageFast(_PREHASH_MoveInventoryItem); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addBOOLFast(_PREHASH_Stamp, TRUE); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_ItemID, item->getUUID()); - msg->addUUIDFast(_PREHASH_FolderID, trash_id); - msg->addString("NewName", NULL); - if(msg->isSendFullFast(_PREHASH_InventoryData)) - { - start_new_message = true; - gAgent.sendReliableMessage(); - gInventory.accountForUpdate(update); - update.clear(); - } - // + start_new_message = false; + msg->newMessageFast(_PREHASH_MoveInventoryItem); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addBOOLFast(_PREHASH_Stamp, TRUE); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_ItemID, item->getUUID()); + msg->addUUIDFast(_PREHASH_FolderID, trash_id); + msg->addString("NewName", NULL); + if(msg->isSendFullFast(_PREHASH_InventoryData)) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); } - // } } if(!start_new_message) @@ -433,32 +424,25 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArraygetUUID()); --update[cat->getParentUUID()]; ++update[trash_id]; - // - if(!gInventory.isObjectDescendentOf(cat->getUUID(), gSystemFolderRoot)) //Avoid fake items. + if(start_new_message) { - // - if(start_new_message) - { - start_new_message = false; - msg->newMessageFast(_PREHASH_MoveInventoryFolder); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addBOOL("Stamp", TRUE); - } - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID()); - msg->addUUIDFast(_PREHASH_ParentID, trash_id); - if(msg->isSendFullFast(_PREHASH_InventoryData)) - { - start_new_message = true; - gAgent.sendReliableMessage(); - gInventory.accountForUpdate(update); - update.clear(); - } - // + start_new_message = false; + msg->newMessageFast(_PREHASH_MoveInventoryFolder); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addBOOL("Stamp", TRUE); + } + msg->nextBlockFast(_PREHASH_InventoryData); + msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID()); + msg->addUUIDFast(_PREHASH_ParentID, trash_id); + if(msg->isSendFullFast(_PREHASH_InventoryData)) + { + start_new_message = true; + gAgent.sendReliableMessage(); + gInventory.accountForUpdate(update); + update.clear(); } - // } } if(!start_new_message) @@ -472,37 +456,6 @@ void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray trash problem - if(gInventory.isObjectDescendentOf(*it, gSystemFolderRoot)) - { - // if it's a category, delete descendents - if(gInventory.getCategory(*it)) - { - LLViewerInventoryCategory* cat = gInventory.getCategory(*it); - cat->setDescendentCount(0); - LLInventoryModel::cat_array_t categories; - LLInventoryModel::item_array_t items; - gInventory.collectDescendents(cat->getUUID(), - categories, - items, - false); // include trash? - S32 count = items.count(); - S32 i; - for(i = 0; i < count; ++i) - { - gInventory.deleteObject(items.get(i)->getUUID()); - } - count = categories.count(); - for(i = 0; i < count; ++i) - { - gInventory.deleteObject(categories.get(i)->getUUID()); - } - } - // delete it - gInventory.deleteObject(*it); - } - else - // gInventory.moveObject((*it), trash_id); } @@ -1520,9 +1473,6 @@ void LLItemBridge::selectItem() LLViewerInventoryItem* item = static_cast(getItem()); if(item && !item->isFinished()) { - // - if(!(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot))) - // //item->fetchFromServer(); LLInventoryModelBackgroundFetch::instance().start(item->getUUID(), false); } @@ -1794,16 +1744,6 @@ BOOL LLItemBridge::removeItem() // Already in trash if (model->isObjectDescendentOf(mUUID, trash_id)) return FALSE; - // trash problem - if(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)) - { - LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), -1); - gInventory.deleteObject(mUUID); - gInventory.accountForUpdate(up); - gInventory.notifyObservers(); - } - // - LLNotification::Params params("ConfirmItemDeleteHasLinks"); params.functor(boost::bind(&LLItemBridge::confirmRemoveItem, this, _1, _2)); @@ -2896,7 +2836,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) restoreItem(); return; } -#ifndef LL_RELEASE_FOR_DOWNLOAD +#ifdef DELETE_SYSTEM_FOLDERS else if ("delete_system_folder" == action) { removeSystemFolder(); @@ -3455,7 +3395,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags) mDisabledItems.push_back(std::string("Delete")); } -#ifndef LL_RELEASE_FOR_DOWNLOAD +#ifdef DELETE_SYSTEM_FOLDERS if (LLFolderType::lookupIsProtectedType(type)) { mItems.push_back(std::string("Delete System Folder")); @@ -4730,7 +4670,7 @@ void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags) LLInventoryItem* item = getItem(); BOOL good_card = (item - && (LLUUID::null != item->getCreatorUUID()) + && (item->getCreatorUUID().notNull()) && (item->getCreatorUUID() != gAgent.getID())); BOOL user_online = FALSE; if (item) @@ -6441,10 +6381,9 @@ public: virtual void doIt() { LLViewerInventoryItem* item = getItem(); - if (item && item->getCreatorUUID().notNull()) + if (item) { - bool online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()); - LLFloaterAvatarInfo::showFromFriend(item->getCreatorUUID(), online); + LLAvatarActions::showProfile(item->getCreatorUUID()); } LLInvFVBridgeAction::doIt(); } diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index b5a572c0c..1099c165d 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -409,10 +409,12 @@ BOOL get_is_category_removable(const LLInventoryModel* model, const LLUUID& id) const LLFolderType::EType folder_type = category->getPreferredType(); +#ifndef DELETE_SYSTEM_FOLDERS if (LLFolderType::lookupIsProtectedType(folder_type)) { return FALSE; } +#endif // Can't delete the outfit that is currently being worn. if (folder_type == LLFolderType::FT_OUTFIT) diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index f854e8607..069508896 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1062,14 +1062,13 @@ void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id) return; } - cat_map_t::iterator it; - if((object_id == cat_id) || (it = mCategoryMap.find(cat_id))==mCategoryMap.end()) + if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id)) { llwarns << "Could not move inventory object " << object_id << " to " << cat_id << llendl; return; } - LLViewerInventoryCategory* cat = it->second; + LLViewerInventoryCategory* cat = getCategory(object_id); if(cat && (cat->getParentUUID() != cat_id)) { cat_array_t* cat_array; @@ -1778,10 +1777,6 @@ bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const } } - // - if((cat_id == gSystemFolderRoot) || gInventory.isObjectDescendentOf(cat_id, gSystemFolderRoot)) return true; - // - return false; } diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 1a15523cc..21c51c25c 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -871,8 +871,7 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask) BOOL handled = LLView::handleHover(x, y, mask); if(handled) { - ECursorType cursor = getWindow()->getCursor(); - if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() && cursor == UI_CURSOR_ARROW) + if (LLInventoryModelBackgroundFetch::instance().folderFetchActive()) { // replace arrow cursor with arrow and hourglass cursor getWindow()->setCursor(UI_CURSOR_WORKING); diff --git a/indra/newview/lllocalinventory.cpp b/indra/newview/lllocalinventory.cpp deleted file mode 100644 index edab999ff..000000000 --- a/indra/newview/lllocalinventory.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// -#include "llviewerprecompiledheaders.h" - -#include "lllocalinventory.h" - -#include "llviewerinventory.h" -#include "llviewercontrol.h" - -#include "llpreviewsound.h" -#include "llpreviewanim.h" -#include "llpreviewtexture.h" -#include "llpreviewgesture.h" -#include "llpreviewlandmark.h" - -#include "llappviewer.h" - -#include "lluictrlfactory.h" -#include "llcombobox.h" -#include "llnotificationsutil.h" - -#include "llagent.h" // gAgent -#include "llviewerwindow.h" // alertXml - - -LLUUID LLLocalInventory::addItem(std::string name, int type, LLUUID asset_id, bool open_automatically) -{ - LLUUID item_id = addItem(name, type, asset_id); - if(open_automatically) open(item_id); - return item_id; -} - -LLUUID LLLocalInventory::addItem(std::string name, int type, LLUUID asset_id) -{ - LLUUID item_id; - item_id.generate(); - LLPermissions* perms = new LLPermissions(); - perms->set(LLPermissions::DEFAULT); - perms->setOwnerAndGroup(LLUUID::null, LLUUID::null, LLUUID::null, false); - perms->setMaskBase(0); - perms->setMaskEveryone(0); - perms->setMaskGroup(0); - perms->setMaskNext(0); - perms->setMaskOwner(0); - LLViewerInventoryItem* item = new LLViewerInventoryItem( - item_id, - gSystemFolderAssets, - *perms, - asset_id, - (LLAssetType::EType)type, - (LLInventoryType::EType)type, - name, - "", - LLSaleInfo::DEFAULT, - 0, - time_corrected()); - addItem(item); - return item_id; -} - -void LLLocalInventory::addItem(LLViewerInventoryItem* item) -{ - //gInventory.addPretendItem(item); - LLInventoryModel::update_map_t update; - ++update[item->getParentUUID()]; - gInventory.accountForUpdate(update); - gInventory.updateItem(item); - gInventory.notifyObservers(); -} - -void LLLocalInventory::open(LLUUID item_id) -{ - LLViewerInventoryItem* item = gInventory.getItem(item_id); - if(!item) - { - llwarns << "Trying to open non-existent item" << llendl; - return; - } - - LLAssetType::EType type = item->getType(); - - if(type == LLAssetType::AT_SOUND) - { - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PreviewSoundRect"); - rect.translate(left - rect.mLeft, top - rect.mTop); - LLPreviewSound* floaterp; - floaterp = new LLPreviewSound("Preview sound", - rect, - "", - item_id); - floaterp->setFocus(TRUE); - gFloaterView->adjustToFitScreen(floaterp, FALSE); - } - else if(type == LLAssetType::AT_ANIMATION) - { - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PreviewAnimRect"); - rect.translate(left - rect.mLeft, top - rect.mTop); - LLPreviewAnim* floaterp; - floaterp = new LLPreviewAnim("Preview anim", - rect, - "", - item_id, - LLPreviewAnim::NONE); - floaterp->setFocus(TRUE); - gFloaterView->adjustToFitScreen(floaterp, FALSE); - } - else if(type == LLAssetType::AT_TEXTURE) - { - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PreviewTextureRect"); - rect.translate( left - rect.mLeft, top - rect.mTop ); - - LLPreviewTexture* preview; - preview = new LLPreviewTexture("preview texture", - rect, - "Preview texture", - item_id, - LLUUID::null, - FALSE); - //preview->setSourceID(source_id); - preview->setFocus(TRUE); - - gFloaterView->adjustToFitScreen(preview, FALSE); - } - else if(type == LLAssetType::AT_GESTURE) - { - // If only the others were like this - LLPreviewGesture::show("preview gesture", item_id, LLUUID::null, TRUE); - } - else if(type == LLAssetType::AT_LANDMARK) - { - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect"); - rect.translate( left - rect.mLeft, top - rect.mTop ); - - LLPreviewLandmark* preview; - preview = new LLPreviewLandmark("preview landmark", - rect, - "Preview landmark", - item_id); - preview->setFocus(TRUE); - - gFloaterView->adjustToFitScreen(preview, FALSE); - } - else - { - llwarns << "Dunno how to open type " << type << llendl; - } -} - -//static -void LLLocalInventory::loadInvCache(std::string filename) -{ - std::string extension = gDirUtilp->getExtension(filename); - std::string inv_filename = filename; - if(extension == "gz") - { - LLUUID random; - random.generate(); - inv_filename = filename.substr(0, filename.length() - 3) + "." + random.asString(); - - if(!gunzip_file(filename, inv_filename)) - { - // failure... message? - return; - } - } - - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - bool is_cache_obsolete = false; - if(LLInventoryModel::loadFromFile(inv_filename, cats, items, is_cache_obsolete)) - { - // create a container category for everything - LLViewerInventoryCategory* container = new LLViewerInventoryCategory(gAgent.getID()); - container->rename(gDirUtilp->getBaseFileName(filename, false)); - LLUUID container_id; - container_id.generate(); - container->setUUID(container_id); - container->setParent(gSystemFolderRoot); - container->setPreferredType(LLFolderType::FT_NONE); - LLInventoryModel::update_map_t container_update; - ++container_update[container->getParentUUID()]; - gInventory.accountForUpdate(container_update); - gInventory.updateCategory(container); - gInventory.notifyObservers(); - - LLViewerInventoryCategory* orphaned_items = new LLViewerInventoryCategory(gAgent.getID()); - orphaned_items->rename("Orphaned Items"); - LLUUID orphaned_items_id; - - orphaned_items_id.generate(); - orphaned_items->setUUID(orphaned_items_id); - orphaned_items->setParent(container_id); - orphaned_items->setPreferredType(LLFolderType::FT_NONE); - - LLInventoryModel::update_map_t orphaned_items_update; - ++orphaned_items_update[orphaned_items->getParentUUID()]; - gInventory.accountForUpdate(orphaned_items_update); - gInventory.updateCategory(orphaned_items); - gInventory.notifyObservers(); - - //conflict handling - std::map conflicting_cats; - int dropped_cats = 0; - int dropped_items = 0; - - // Add all categories - LLInventoryModel::cat_array_t::iterator cat_iter = cats.begin(); - LLInventoryModel::cat_array_t::iterator cat_end = cats.end(); - for(; cat_iter != cat_end; ++cat_iter) - { - // Conditionally change its parent - // Note: Should I search for missing parent id's? - - //if the parent is null, it goes in the very root of the tree! - if((*cat_iter)->getParentUUID().isNull()) - { - (*cat_iter)->setParent(container_id); - } - // If the parent exists and outside of pretend inventory, generate a new uuid - else if(gInventory.getCategory((*cat_iter)->getParentUUID())) - { - if(!gInventory.isObjectDescendentOf((*cat_iter)->getParentUUID(), gSystemFolderRoot, TRUE)) - { - std::map::iterator itr = conflicting_cats.find((*cat_iter)->getParentUUID()); - if(itr == conflicting_cats.end()) - { - dropped_cats++; - continue; - } - (*cat_iter)->setParent(itr->second); - } - } else { - //well balls, this is orphaned. - (*cat_iter)->setParent(orphaned_items_id); - } - // If this category already exists, generate a new uuid - if(gInventory.getCategory((*cat_iter)->getUUID())) - { - LLUUID cat_random; - cat_random.generate(); - conflicting_cats[(*cat_iter)->getUUID()] = cat_random; - (*cat_iter)->setUUID(cat_random); - } - - LLInventoryModel::update_map_t update; - ++update[(*cat_iter)->getParentUUID()]; - gInventory.accountForUpdate(update); - gInventory.updateCategory(*cat_iter); - gInventory.notifyObservers(); - } - - // Add all items - LLInventoryModel::item_array_t::iterator item_iter = items.begin(); - LLInventoryModel::item_array_t::iterator item_end = items.end(); - for(; item_iter != item_end; ++item_iter) - { - // Conditionally change its parent - // Note: Should I search for missing parent id's? - - //if the parent is null, it goes in the very root of the tree! - if((*item_iter)->getParentUUID().isNull()) - { - (*item_iter)->setParent(container_id); - } - - // If the parent exists and outside of pretend inventory, generate a new uuid - if(gInventory.getCategory((*item_iter)->getParentUUID())) - { - if(!gInventory.isObjectDescendentOf((*item_iter)->getParentUUID(), gSystemFolderRoot, TRUE)) - { - std::map::iterator itr = conflicting_cats.find((*item_iter)->getParentUUID()); - if(itr == conflicting_cats.end()) - { - dropped_items++; - continue; - } - (*item_iter)->setParent(itr->second); - } - } else { - //well balls, this is orphaned. - (*item_iter)->setParent(orphaned_items_id); - } - // Avoid conflicts with real inventory... - // If this item id already exists, generate a new uuid - if(gInventory.getItem((*item_iter)->getUUID())) - { - LLUUID item_random; - item_random.generate(); - (*item_iter)->setUUID(item_random); - } - - LLInventoryModel::update_map_t update; - ++update[(*item_iter)->getParentUUID()]; - gInventory.accountForUpdate(update); - gInventory.updateItem(*item_iter); - gInventory.notifyObservers(); - } - - // Quality time - if(dropped_items || dropped_cats) - { - std::ostringstream message; - message << "Some items were ignored due to conflicts:\n\n"; - if(dropped_cats) message << dropped_cats << " folders\n"; - if(dropped_items) message << dropped_items << " items\n"; - - LLSD args; - args["ERROR_MESSAGE"] = message.str(); - LLNotificationsUtil::add("ErrorMessage", args); - } - conflicting_cats.clear();// srsly dont think this is need but w/e :D - } - - // remove temporary unzipped file - if(extension == "gz") - { - LLFile::remove(inv_filename); - } - -} - -//static -void LLLocalInventory::saveInvCache(std::string filename, LLFolderView* folder) -{ - LLInventoryModel* model = &gInventory; - std::set selected_items = folder->getSelectionList(); - if(selected_items.size() < 1) - { - // No items selected? Wtfboom - return; - } - LLInventoryModel::cat_array_t cats; - LLInventoryModel::item_array_t items; - // Make complete lists of child categories and items - std::set::iterator sel_iter = selected_items.begin(); - std::set::iterator sel_end = selected_items.end(); - for( ; sel_iter != sel_end; ++sel_iter) - { - LLInventoryCategory* cat = model->getCategory(*sel_iter); - if(cat) - { - climb(cat, cats, items); - } - } - // And what about items inside a folder that wasn't selected? - // I guess I will just add selected items, so long as they aren't already added - for(sel_iter = selected_items.begin(); sel_iter != sel_end; ++sel_iter) - { - LLInventoryItem* item = model->getItem(*sel_iter); - if(item) - { - if(std::find(items.begin(), items.end(), item) == items.end()) - { - items.push_back(LLPointer((LLViewerInventoryItem*)item)); - LLInventoryCategory* parent = model->getCategory(item->getParentUUID()); - if(std::find(cats.begin(), cats.end(), parent) == cats.end()) - { - cats.push_back(LLPointer((LLViewerInventoryCategory*)parent)); - } - } - } - } - LLInventoryModel::saveToFile(filename, cats, items); -} - -// static -void LLLocalInventory::climb(LLInventoryCategory* cat, - LLInventoryModel::cat_array_t& cats, - LLInventoryModel::item_array_t& items) -{ - LLInventoryModel* model = &gInventory; - - // Add this category - cats.push_back(LLPointer((LLViewerInventoryCategory*)cat)); - - LLInventoryModel::cat_array_t *direct_cats; - LLInventoryModel::item_array_t *direct_items; - model->getDirectDescendentsOf(cat->getUUID(), direct_cats, direct_items); - - // Add items - LLInventoryModel::item_array_t::iterator item_iter = direct_items->begin(); - LLInventoryModel::item_array_t::iterator item_end = direct_items->end(); - for( ; item_iter != item_end; ++item_iter) - { - items.push_back(*item_iter); - } - - // Do subcategories - LLInventoryModel::cat_array_t::iterator cat_iter = direct_cats->begin(); - LLInventoryModel::cat_array_t::iterator cat_end = direct_cats->end(); - for( ; cat_iter != cat_end; ++cat_iter) - { - climb(*cat_iter, cats, items); - } -} - - - - - - - - - - - - - - - - - - - - - - - -LLUUID LLFloaterNewLocalInventory::sLastCreatorId = LLUUID::null; - -LLFloaterNewLocalInventory::LLFloaterNewLocalInventory() -: LLFloater() -{ - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_new_local_inventory.xml"); -} - -LLFloaterNewLocalInventory::~LLFloaterNewLocalInventory() -{ -} - -BOOL LLFloaterNewLocalInventory::postBuild(void) -{ - // Fill in default values - - getChild("creator_id_line")->setText(std::string("00000000-0000-0000-0000-000000000000")); - getChild("owner_id_line")->setText(gAgent.getID().asString()); - getChild("asset_id_line")->setText(std::string("00000000-0000-0000-0000-000000000000")); - getChild("name_line")->setText(std::string("")); - getChild("desc_line")->setText(std::string("")); - - // Set up callbacks - - childSetAction("ok_btn", onClickOK, this); - - return TRUE; -} - -// static -void LLFloaterNewLocalInventory::onClickOK(void* user_data) -{ - LLFloaterNewLocalInventory* floater = (LLFloaterNewLocalInventory*)user_data; - - LLUUID item_id; - item_id.generate(); - - std::string name = floater->getChild("name_line")->getText(); - std::string desc = floater->getChild("desc_line")->getText(); - LLUUID asset_id = LLUUID(floater->getChild("asset_id_line")->getText()); - LLUUID creator_id = LLUUID(floater->getChild("creator_id_line")->getText()); - LLUUID owner_id = LLUUID(floater->getChild("owner_id_line")->getText()); - - LLAssetType::EType type = LLAssetType::lookup(floater->getChild("type_combo")->getValue().asString()); - LLInventoryType::EType inv_type = LLInventoryType::IT_NONE; - switch(type) - { - case LLAssetType::AT_TEXTURE: - case LLAssetType::AT_TEXTURE_TGA: - case LLAssetType::AT_IMAGE_TGA: - case LLAssetType::AT_IMAGE_JPEG: - inv_type = LLInventoryType::IT_TEXTURE; - break; - case LLAssetType::AT_SOUND: - case LLAssetType::AT_SOUND_WAV: - inv_type = LLInventoryType::IT_SOUND; - break; - case LLAssetType::AT_CALLINGCARD: - inv_type = LLInventoryType::IT_CALLINGCARD; - break; - case LLAssetType::AT_LANDMARK: - inv_type = LLInventoryType::IT_LANDMARK; - break; - case LLAssetType::AT_SCRIPT: - inv_type = LLInventoryType::IT_LSL; - break; - case LLAssetType::AT_CLOTHING: - inv_type = LLInventoryType::IT_WEARABLE; - break; - case LLAssetType::AT_OBJECT: - inv_type = LLInventoryType::IT_OBJECT; - break; - case LLAssetType::AT_NOTECARD: - inv_type = LLInventoryType::IT_NOTECARD; - break; - case LLAssetType::AT_CATEGORY: - inv_type = LLInventoryType::IT_CATEGORY; - break; - //No longer asset types. - /*case LLFolderType::FT_ROOT_CATEGORY: - case LLFolderType::FT_TRASH: - case LLFolderType::FT_SNAPSHOT_CATEGORY: - case LLFolderType::FT_LOST_AND_FOUND: - inv_type = LLInventoryType::IT_ROOT_CATEGORY; - break;*/ - case LLAssetType::AT_LSL_TEXT: - case LLAssetType::AT_LSL_BYTECODE: - inv_type = LLInventoryType::IT_LSL; - break; - case LLAssetType::AT_BODYPART: - inv_type = LLInventoryType::IT_WEARABLE; - break; - case LLAssetType::AT_ANIMATION: - inv_type = LLInventoryType::IT_ANIMATION; - break; - case LLAssetType::AT_GESTURE: - inv_type = LLInventoryType::IT_GESTURE; - break; - //case LLAssetType::AT_SIMSTATE: - default: - //inv_type = LLInventoryType::IT_CALLINGCARD; - break; - } - - if(inv_type == LLInventoryType::IT_NONE) - return; - - LLPermissions* perms = new LLPermissions(); - perms->init(creator_id, owner_id, LLUUID::null, LLUUID::null); - - LLViewerInventoryItem* item = new LLViewerInventoryItem( - item_id, - gSystemFolderRoot, - *perms, - asset_id, - type, - inv_type, - name, - desc, - LLSaleInfo::DEFAULT, - 0, - 0); - - LLLocalInventory::addItem(item); - if(floater->childGetValue("chk_open")) - { - LLLocalInventory::open(item_id); - } - - LLFloaterNewLocalInventory::sLastCreatorId = creator_id; - floater->close(); -} - - - -// diff --git a/indra/newview/lllocalinventory.h b/indra/newview/lllocalinventory.h deleted file mode 100644 index 4159918f8..000000000 --- a/indra/newview/lllocalinventory.h +++ /dev/null @@ -1,45 +0,0 @@ -// -#ifndef LL_LLLOCALINVENTORY_H -#define LL_LLLOCALINVENTORY_H - -#include "llviewerinventory.h" - -#include "llfloater.h" - -#include "llfolderview.h" -#include "llinventorymodel.h" // cat_array_t, item_array_t - -class LLLocalInventory -{ -public: - static LLUUID addItem(std::string name, int type, LLUUID asset_id, bool open); - static LLUUID addItem(std::string name, int type, LLUUID asset_id); - static void addItem(LLViewerInventoryItem* item); - static void open(LLUUID item_id); - static void loadInvCache(std::string filename); - static void saveInvCache(std::string filename, LLFolderView* folder); - static void climb(LLInventoryCategory* cat, - LLInventoryModel::cat_array_t& cats, - LLInventoryModel::item_array_t& items); -}; - - - - -class LLFloaterNewLocalInventory -: public LLFloater -{ -public: - LLFloaterNewLocalInventory(); - BOOL postBuild(void); - - static void onClickOK(void* user_data); - static LLUUID sLastCreatorId; - -private: - virtual ~LLFloaterNewLocalInventory(); - -}; - -#endif -// diff --git a/indra/newview/llmakeoutfitdialog.cpp b/indra/newview/llmakeoutfitdialog.cpp index be70012d1..2c607e6c7 100644 --- a/indra/newview/llmakeoutfitdialog.cpp +++ b/indra/newview/llmakeoutfitdialog.cpp @@ -86,7 +86,6 @@ LLMakeOutfitDialog::LLMakeOutfitDialog(bool modal) : LLModalDialog(LLStringUtil: if (!gHippoGridManager->getConnectedGrid()->supportsInvLinks()) { - childSetEnabled("checkbox_use_links", false); childSetValue("checkbox_use_links", false); childSetEnabled("checkbox_use_outfits", false); childSetValue("checkbox_use_outfits", false); @@ -140,7 +139,8 @@ void LLMakeOutfitDialog::refresh() if (fUseOutfits) pCheckCtrl->setValue(true); } - childSetEnabled("checkbox_use_links", !fUseOutfits); + getChild("checkbox_use_links")->setEnabled(!fUseOutfits && gHippoGridManager->getConnectedGrid()->supportsInvLinks()); + getChild("checkbox_legacy_copy_changes")->setEnabled(!fUseOutfits); } diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp index 988496eeb..94f70d379 100644 --- a/indra/newview/llmenucommands.cpp +++ b/indra/newview/llmenucommands.cpp @@ -78,12 +78,6 @@ void handle_track_avatar(const LLUUID& agent_id, const std::string& name) LLFloaterWorldMap::show(true); } -void handle_pay_by_id(const LLUUID& agent_id) -{ - const BOOL is_group = FALSE; - LLFloaterPay::payDirectly(&give_money, agent_id, is_group); -} - void handle_mouselook(void*) { gAgentCamera.changeCameraToMouselook(); diff --git a/indra/newview/llmenucommands.h b/indra/newview/llmenucommands.h index 03f7a2571..1c4550b07 100644 --- a/indra/newview/llmenucommands.h +++ b/indra/newview/llmenucommands.h @@ -36,7 +36,6 @@ class LLUUID; void handle_track_avatar(const LLUUID& agent_id, const std::string& name); -void handle_pay_by_id(const LLUUID& agent_id); void handle_mouselook(void*); void handle_map(void*); void handle_mini_map(void*); diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 051e5f7da..99b541b23 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -52,22 +52,16 @@ #include -#include "llcrc.h" -#include "lldir.h" #include "lldispatcher.h" -#include "llsdserialize.h" #include "llxfermanager.h" -#include "message.h" #include "llagent.h" #include "llviewergenericmessage.h" // for gGenericDispatcher -#include "llviewerwindow.h" #include "llworld.h" //for particle system banning -#include "llchat.h" #include "llfloaterchat.h" #include "llimpanel.h" #include "llimview.h" -#include "llnotificationsutil.h" +#include "llnotifications.h" #include "lluistring.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -116,6 +110,7 @@ const char BY_NAME_SUFFIX[] = " (by name)"; const char AGENT_SUFFIX[] = " (resident)"; const char OBJECT_SUFFIX[] = " (object)"; const char GROUP_SUFFIX[] = " (group)"; +const char EXTERNAL_SUFFIX[] = " (avaline)"; LLMute::LLMute(const LLUUID& id, const std::string& name, EType type, U32 flags) @@ -160,6 +155,9 @@ std::string LLMute::getDisplayName() const case GROUP: name_with_suffix += GROUP_SUFFIX; break; + case EXTERNAL: + name_with_suffix += EXTERNAL_SUFFIX; + break; } return name_with_suffix; } @@ -201,6 +199,14 @@ void LLMute::setFromDisplayName(const std::string& display_name) return; } + pos = mName.rfind(EXTERNAL_SUFFIX); + if (pos != std::string::npos) + { + mName.erase(pos); + mType = EXTERNAL; + return; + } + llwarns << "Unable to set mute from display name " << display_name << llendl; return; } @@ -224,39 +230,12 @@ LLMuteList* LLMuteList::getInstance() // LLMuteList() //----------------------------------------------------------------------------- LLMuteList::LLMuteList() : - mIsLoaded(FALSE), - mUserVolumesLoaded(FALSE) + mIsLoaded(FALSE) { gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); -} -void LLMuteList::loadUserVolumes() -{ - // call once, after LLDir::setLindenUserDir() has been called - if (mUserVolumesLoaded) - return; - mUserVolumesLoaded = TRUE; - - // load per-resident voice volume information - // conceptually, this is part of the mute list information, although it is only stored locally - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "volume_settings.xml"); - - LLSD settings_llsd; - llifstream file; - file.open(filename); - if (file.is_open()) - { - LLSDSerialize::fromXML(settings_llsd, file); - } - - for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); - iter != settings_llsd.endMap(); ++iter) - { - mUserVolumeSettings.insert(std::make_pair(LLUUID(iter->first), (F32)iter->second.asReal())); - } - - LLEnvManagerNew::instance().setRegionChangeCallback(boost::bind(&LLMuteList::checkNewRegion, this)); checkNewRegion(); + LLEnvManagerNew::instance().setRegionChangeCallback(boost::bind(&LLMuteList::checkNewRegion, this)); } //----------------------------------------------------------------------------- @@ -264,24 +243,7 @@ void LLMuteList::loadUserVolumes() //----------------------------------------------------------------------------- LLMuteList::~LLMuteList() { - // If we quit from the login screen we will not have an SL account - // name. Don't try to save, otherwise we'll dump a file in - // C:\Program Files\SecondLife\ JC - std::string user_dir = gDirUtilp->getLindenUserDir(true); - if (!user_dir.empty()) - { - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "volume_settings.xml"); - LLSD settings_llsd; - for(user_volume_map_t::iterator iter = mUserVolumeSettings.begin(); iter != mUserVolumeSettings.end(); ++iter) - { - settings_llsd[iter->first.asString()] = iter->second; - } - - llofstream file; - file.open(filename); - LLSDSerialize::toPrettyXML(settings_llsd, file); - } } bool LLMuteList::isLinden(const LLUUID& id) const @@ -333,7 +295,7 @@ BOOL LLMuteList::add(const LLMute& mute, U32 flags) if ((mute.mType == LLMute::AGENT) && isLinden(mute.mName) && (flags & LLMute::flagTextChat || flags == 0)) { - LLNotificationsUtil::add("MuteLinden"); + LLNotifications::instance().add("MuteLinden", LLSD(), LLSD()); return FALSE; } @@ -439,6 +401,12 @@ BOOL LLMuteList::add(const LLMute& mute, U32 flags) void LLMuteList::updateAdd(const LLMute& mute) { + // External mutes (e.g. Avaline callers) are local only, don't send them to the server. + if (mute.mType == LLMute::EXTERNAL) + { + return; + } + // Update the database LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_UpdateMuteListEntry); @@ -526,6 +494,12 @@ BOOL LLMuteList::remove(const LLMute& mute, U32 flags) void LLMuteList::updateRemove(const LLMute& mute) { + // External mutes are not sent to the server anyway, no need to remove them. + if (mute.mType == LLMute::EXTERNAL) + { + return; + } + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_RemoveMuteListEntry); msg->nextBlockFast(_PREHASH_AgentData); @@ -557,7 +531,7 @@ void notify_automute_callback(const LLUUID& agent_id, const std::string& full_na LLSD args; args["NAME"] = full_name; - LLNotificationPtr notif_ptr = LLNotifications::instance().add(notif_name, args); + LLNotificationPtr notif_ptr = LLNotifications::instance().add(notif_name, args, LLSD()); if (notif_ptr) { std::string message = notif_ptr->getMessage(); @@ -708,9 +682,14 @@ BOOL LLMuteList::saveToFile(const std::string& filename) it != mMutes.end(); ++it) { - it->mID.toString(id_string); - const std::string& name = it->mName; - fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string.c_str(), name.c_str(), it->mFlags); + // Don't save external mutes as they are not sent to the server and probably won't + //be valid next time anyway. + if (it->mType != LLMute::EXTERNAL) + { + it->mID.toString(id_string); + const std::string& name = it->mName; + fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string.c_str(), name.c_str(), it->mFlags); + } } fclose(fp); return TRUE; @@ -755,8 +734,6 @@ BOOL LLMuteList::isMuted(const LLUUID& id, const std::string& name, U32 flags) c //----------------------------------------------------------------------------- void LLMuteList::requestFromServer(const LLUUID& agent_id) { - loadUserVolumes(); - std::string agent_id_string; std::string filename; agent_id.toString(agent_id_string); @@ -791,26 +768,6 @@ void LLMuteList::cache(const LLUUID& agent_id) } } -void LLMuteList::setSavedResidentVolume(const LLUUID& id, F32 volume) -{ - // store new value in volume settings file - mUserVolumeSettings[id] = volume; -} - -F32 LLMuteList::getSavedResidentVolume(const LLUUID& id) -{ - const F32 DEFAULT_VOLUME = 0.5f; - - user_volume_map_t::iterator found_it = mUserVolumeSettings.find(id); - if (found_it != mUserVolumeSettings.end()) - { - return found_it->second; - } - //FIXME: assumes default, should get this from somewhere - return DEFAULT_VOLUME; -} - - //----------------------------------------------------------------------------- // Static message handlers //----------------------------------------------------------------------------- @@ -925,7 +882,7 @@ void LLMuteList::parseSimulatorFeatures() { LLSD godNames = info["god_names"]["last_names"]; - for (LLSD::array_iterator godNames_it = godNames.beginArray(); godNames_it != godNames.endArray(); godNames_it++) + for (LLSD::array_iterator godNames_it = godNames.beginArray(); godNames_it != godNames.endArray(); ++godNames_it) mGodLastNames.insert((*godNames_it).asString()); } @@ -933,7 +890,7 @@ void LLMuteList::parseSimulatorFeatures() { LLSD godNames = info["god_names"]["full_names"]; - for (LLSD::array_iterator godNames_it = godNames.beginArray(); godNames_it != godNames.endArray(); godNames_it++) + for (LLSD::array_iterator godNames_it = godNames.beginArray(); godNames_it != godNames.endArray(); ++godNames_it) mGodFullNames.insert((*godNames_it).asString()); } } diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 10cb4842e..cbf78e25a 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -45,7 +45,8 @@ class LLMute { public: // Legacy mutes are BY_NAME and have null UUID. - enum EType { BY_NAME = 0, AGENT = 1, OBJECT = 2, GROUP = 3, COUNT = 4 }; + // EXTERNAL mutes are only processed through an external system (e.g. Voice) and not stored. + enum EType { BY_NAME = 0, AGENT = 1, OBJECT = 2, GROUP = 3, EXTERNAL = 4, COUNT = 5 }; // Bits in the mute flags. For backwards compatibility (since any mute list entries that were created before the flags existed // will have a flags field of 0), some of the flags are "inverted". @@ -128,12 +129,7 @@ public: // call this method on logout to save everything. void cache(const LLUUID& agent_id); - void setSavedResidentVolume(const LLUUID& id, F32 volume); - F32 getSavedResidentVolume(const LLUUID& id); - private: - void loadUserVolumes(); - BOOL loadFromFile(const std::string& filename); BOOL saveToFile(const std::string& filename); @@ -183,13 +179,9 @@ private: observer_set_t mObservers; BOOL mIsLoaded; - BOOL mUserVolumesLoaded; friend class LLDispatchEmptyMuteList; - typedef std::map user_volume_map_t; - user_volume_map_t mUserVolumeSettings; - std::set mGodLastNames; std::set mGodFullNames; }; diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp index 43fa927b4..7094c67b1 100644 --- a/indra/newview/llnetmap.cpp +++ b/indra/newview/llnetmap.cpp @@ -35,8 +35,6 @@ #include "llnetmap.h" -#include "indra_constants.h" -#include "llui.h" #include "llmath.h" // clampf() #include "llfocusmgr.h" #include "lllocalcliprect.h" @@ -46,11 +44,10 @@ #include "llagent.h" #include "llagentcamera.h" +#include "llavataractions.h" #include "llavatarnamecache.h" #include "llcallingcard.h" #include "llcolorscheme.h" -#include "llviewercontrol.h" -#include "llfloateravatarinfo.h" #include "llfloaterworldmap.h" #include "llframetimer.h" #include "lltracker.h" @@ -58,7 +55,6 @@ #include "llsurface.h" #include "lltextbox.h" #include "lluictrlfactory.h" -#include "lluuid.h" #include "llviewercamera.h" #include "llviewertexturelist.h" #include "llviewermenu.h" @@ -432,7 +428,7 @@ void LLNetMap::draw() avColor = em_color; } //without these dots, SL would suck. - else if(is_agent_friend(id)) + else if(LLAvatarActions::isFriend(id)) { avColor = friend_color; } @@ -1150,10 +1146,10 @@ bool LLNetMap::LLShowAgentProfile::handleEvent(LLPointer event, const L // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Modified: RLVa-0.2.0b if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) { - LLFloaterAvatarInfo::show(self->mClosestAgentAtLastRightClick); + LLAvatarActions::showProfile(self->mClosestAgentAtLastRightClick); } // [/RLVa:KB] - //LLFloaterAvatarInfo::show(self->mClosestAgentAtLastRightClick); + //LLAvatarActions::showProfile(self->mClosestAgentAtLastRightClick); return true; } diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 7fbf65fb7..114bc56f1 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -350,13 +350,13 @@ void LLOverlayBar::refresh() static const LLCachedControl enable_ao_remote("EnableAORemote", true); mMediaRemoteContainer->setVisible(!in_mouselook); - mVoiceRemoteContainer->setVisible(!in_mouselook && LLVoiceClient::voiceEnabled()); + mVoiceRemoteContainer->setVisible(!in_mouselook && LLVoiceClient::getInstance()->voiceEnabled()); mAdvSettingsContainer->setVisible(!in_mouselook); mAORemoteContainer->setVisible(!in_mouselook && enable_ao_remote); mStateManagementContainer->setVisible(!in_mouselook); } if(!in_mouselook) - mVoiceRemoteContainer->setVisible(LLVoiceClient::voiceEnabled()); + mVoiceRemoteContainer->setVisible(LLVoiceClient::getInstance()->voiceEnabled()); if (buttons_changed) { diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 831b4afa0..bbf57a1b5 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -34,74 +34,48 @@ #include "llpanelavatar.h" -#include "llclassifiedflags.h" -#include "llfontgl.h" -#include "llcachename.h" - #include "llavatarconstants.h" -#include "lluiconstants.h" -#include "lltextbox.h" -#include "llviewertexteditor.h" -#include "lltexturectrl.h" -#include "llagent.h" -#include "llviewerwindow.h" +#include "llavatarnamecache.h" #include "llbutton.h" -#include "llcallingcard.h" #include "llcheckboxctrl.h" -#include "llfloater.h" +#include "llclassifiedflags.h" +#include "lltextbox.h" +#include "lltexteditor.h" +#include "lltexturectrl.h" +#include "llwindow.h" -#include "llfloaterfriends.h" +#include "llagent.h" +#include "llavataractions.h" +#include "llcallingcard.h" +#include "lldroptarget.h" #include "llfloatergroupinfo.h" -#include "llfloatergroups.h" -#include "llfloaterinventory.h" -#include "llfloaterworldmap.h" #include "llfloatermute.h" #include "llfloateravatarinfo.h" -#include "lliconctrl.h" +#include "llgroupactions.h" #include "lllineeditor.h" #include "llnameeditor.h" -#include "llmutelist.h" #include "llnotificationsutil.h" #include "llpanelclassified.h" #include "llpanelpick.h" #include "llpreviewtexture.h" #include "llpluginclassmedia.h" #include "llscrolllistctrl.h" -#include "llstatusbar.h" #include "lltabcontainer.h" -#include "llimview.h" -#include "llvoavatar.h" -#include "llviewercontrol.h" -#include "llviewergenericmessage.h" // send_generic_message -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llweb.h" -#include "llinventorymodel.h" -#include "roles_constants.h" #include "lluictrlfactory.h" -#include "llavatarnamecache.h" -#include "lldroptarget.h" - +#include "llviewerwindow.h" +#include "llweb.h" #include #include - - // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] -#include "llavatarname.h" - // Statics std::list LLPanelAvatar::sAllPanels; BOOL LLPanelAvatar::sAllowFirstLife = FALSE; -extern void callback_invite_to_group(LLUUID group_id, void *user_data); -extern void handle_lure(const LLUUID& invitee); -extern void handle_pay_by_id(const LLUUID& payee); -BOOL is_agent_friend(const LLUUID& agent_id); BOOL is_agent_mappable(const LLUUID& agent_id); @@ -197,12 +171,6 @@ void LLPanelAvatarSecondLife::clearControls() { group_list->deleteAllItems(); } - /*LLScrollListCtrl* ratings_list = getChild("ratings"); createDummyWidget Making Dummy -HgB - if(ratings_list) - { - ratings_list->deleteAllItems(); - }*/ - } // virtual @@ -421,7 +389,7 @@ void LLPanelAvatarFirstLife::processProperties(void* data, EAvatarProcessorType void LLPanelAvatarSecondLife::onClickImage(void* data) { LLPanelAvatarSecondLife* self = (LLPanelAvatarSecondLife*)data; - LLNameEditor* name_ctrl = self->getChild("name"); + LLNameEditor* name_ctrl = self->getChild("dnname"); if(name_ctrl) { std::string name_text = name_ctrl->getText(); @@ -463,18 +431,14 @@ void LLPanelAvatarSecondLife::onClickImage(void* data) void LLPanelAvatarSecondLife::onDoubleClickGroup(void* data) { LLPanelAvatarSecondLife* self = (LLPanelAvatarSecondLife*)data; - LLScrollListCtrl* group_list = self->getChild("groups"); if(group_list) { LLScrollListItem* item = group_list->getFirstSelected(); - - if(item && item->getUUID().notNull()) + if (item) { - llinfos << "Show group info " << item->getUUID() << llendl; - - LLFloaterGroupInfo::showFromUUID(item->getUUID()); + LLGroupActions::show(item->getUUID()); } } } @@ -506,11 +470,7 @@ bool LLPanelAvatarSecondLife::onClickPartnerHelpLoadURL(const LLSD& notification void LLPanelAvatarSecondLife::onClickPartnerInfo(void *data) { LLPanelAvatarSecondLife* self = (LLPanelAvatarSecondLife*) data; - if (self->mPartnerID.notNull()) - { - LLFloaterAvatarInfo::showFromProfile(self->mPartnerID, - self->calcScreenRect()); - } + LLAvatarActions::showProfile(self->mPartnerID); } //----------------------------------------------------------------------------- @@ -542,8 +502,8 @@ BOOL LLPanelAvatarSecondLife::postBuild(void) childSetEnabled("partner_info", mPartnerID.notNull()); childSetAction("?",onClickPublishHelp,this); - BOOL own_avatar = (getPanelAvatar()->getAvatarID() == gAgent.getID() ); - enableControls(own_avatar); + LLPanelAvatar* pa = getPanelAvatar(); + enableControls(pa->getAvatarID() == gAgentID); childSetVisible("About:",LLPanelAvatar::sAllowFirstLife); childSetVisible("(500 chars)",LLPanelAvatar::sAllowFirstLife); @@ -554,16 +514,15 @@ BOOL LLPanelAvatarSecondLife::postBuild(void) childSetVisible("online_yes",FALSE); - childSetAction("Find on Map", LLPanelAvatar::onClickTrack, getPanelAvatar()); - childSetAction("Instant Message...", LLPanelAvatar::onClickIM, getPanelAvatar()); - childSetAction("GroupInvite_Button", LLPanelAvatar::onClickGroupInvite, getPanelAvatar()); + getChild("Find on Map")->setCommitCallback(boost::bind(LLAvatarActions::showOnMap, boost::bind(&LLPanelAvatar::getAvatarID, pa))); + getChild("Instant Message...")->setCommitCallback(boost::bind(LLAvatarActions::startIM, boost::bind(&LLPanelAvatar::getAvatarID, pa))); + getChild("GroupInvite_Button")->setCommitCallback(boost::bind(LLAvatarActions::inviteToGroup, boost::bind(&LLPanelAvatar::getAvatarID, pa))); - childSetAction("Add Friend...", LLPanelAvatar::onClickAddFriend, getPanelAvatar()); - childSetAction("Pay...", LLPanelAvatar::onClickPay, getPanelAvatar()); - childSetAction("Mute", LLPanelAvatar::onClickMute, getPanelAvatar() ); + getChild("Add Friend...")->setCommitCallback(boost::bind(LLAvatarActions::requestFriendshipDialog, boost::bind(&LLPanelAvatar::getAvatarID, pa))); + getChild("Pay...")->setCommitCallback(boost::bind(LLAvatarActions::pay, boost::bind(&LLPanelAvatar::getAvatarID, pa))); + childSetAction("Mute", LLPanelAvatar::onClickMute, pa); - childSetAction("Offer Teleport...", LLPanelAvatar::onClickOfferTeleport, - getPanelAvatar() ); + getChild("Offer Teleport...")->setCommitCallback(boost::bind(static_cast(LLAvatarActions::offerTeleport), boost::bind(&LLPanelAvatar::getAvatarID, pa))); getChild("groups")->setDoubleClickCallback(boost::bind(&LLPanelAvatarSecondLife::onDoubleClickGroup,this)); @@ -587,7 +546,7 @@ BOOL LLPanelAvatarFirstLife::postBuild(void) BOOL LLPanelAvatarNotes::postBuild(void) { - childSetCommitCallback("notes edit",onCommitNotes,this); + getChild("notes edit")->setCommitCallback(boost::bind(&LLPanelAvatar::sendAvatarNotesUpdate, getPanelAvatar())); LLTextEditor* te = getChild("notes edit"); if(te) te->setCommitOnFocusLost(TRUE); @@ -600,8 +559,6 @@ BOOL LLPanelAvatarWeb::postBuild(void) url_edit->setKeystrokeCallback(boost::bind(&LLPanelAvatarWeb::onURLKeystroke,this,_1)); url_edit->setCommitCallback(boost::bind(&LLPanelAvatarWeb::onCommitURL,this,_2)); - getChild("load")->setCommitCallback(boost::bind(&LLPanelAvatarWeb::onCommitLoad,this,_2)); - childSetAction("web_profile_help",onClickWebProfileHelp,this); childSetControlName("auto_load","AutoLoadWebProfiles"); @@ -621,7 +578,7 @@ void LLPanelAvatarWeb::processProperties(void* data, EAvatarProcessorType type) if(type == APT_PROPERTIES) { const LLAvatarData* pAvatarData = static_cast( data ); - if (pAvatarData && (mAvatarID == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null)) + if (pAvatarData && (mAvatarID == pAvatarData->avatar_id) && (pAvatarData->avatar_id.notNull())) { setWebURL(pAvatarData->profile_url); } @@ -940,14 +897,6 @@ void LLPanelAvatarNotes::clearControls() childSetEnabled("notes edit", false); } -// static -void LLPanelAvatarNotes::onCommitNotes(LLUICtrl*, void* userdata) -{ - LLPanelAvatarNotes* self = (LLPanelAvatarNotes*)userdata; - - self->getPanelAvatar()->sendAvatarNotesUpdate(); -} - //----------------------------------------------------------------------------- // LLPanelAvatarClassified() @@ -1257,7 +1206,7 @@ void LLPanelAvatarPicks::processProperties(void* data, EAvatarProcessorType type for(LLAvatarPicks::picks_list_t::iterator it = picks->picks_list.begin(); it != picks->picks_list.end(); ++it) { - LLPanelPick* panel_pick = new LLPanelPick(FALSE); + LLPanelPick* panel_pick = new LLPanelPick(); panel_pick->setPickID(it->first, mAvatarID); // This will request data from the server when the pick is first @@ -1302,7 +1251,7 @@ void LLPanelAvatarPicks::onClickNew(void* data) } // [/RLVa:KB] LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data; - LLPanelPick* panel_pick = new LLPanelPick(FALSE); + LLPanelPick* panel_pick = new LLPanelPick(); LLTabContainer* tabs = self->getChild("picks tab"); panel_pick->initNewPick(); @@ -1318,7 +1267,7 @@ void LLPanelAvatarPicks::onClickNew(void* data) void LLPanelAvatarPicks::onClickImport(void* data) { LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data; - self->mPanelPick = new LLPanelPick(FALSE); + self->mPanelPick = new LLPanelPick(); self->mPanelPick->importNewPick(&LLPanelAvatarPicks::onClickImport_continued, data); } @@ -1424,14 +1373,13 @@ LLPanelAvatar::LLPanelAvatar( mPanelNotes(NULL), mPanelFirstLife(NULL), mPanelWeb(NULL), - mAvatarID( LLUUID::null ), // mAvatarID is set with 'setAvatar' or 'setAvatarID' + mAvatarID(LLUUID::null), // mAvatarID is set with setAvatarID() mHaveProperties(FALSE), mHaveStatistics(FALSE), mHaveNotes(false), mLastNotes(), mAllowEdit(allow_edit) { - sAllPanels.push_back(this); LLCallbackMap::map_t factory_map; @@ -1447,22 +1395,19 @@ LLPanelAvatar::LLPanelAvatar( LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar.xml", &factory_map); selectTab(0); - - } BOOL LLPanelAvatar::postBuild(void) { mTab = getChild("tab"); - childSetAction("Kick",onClickKick,this); - childSetAction("Freeze",onClickFreeze, this); - childSetAction("Unfreeze", onClickUnfreeze, this); - childSetAction("csr_btn", onClickCSR, this); + getChild("Kick")->setCommitCallback(boost::bind(LLAvatarActions::kick, boost::bind(&LLPanelAvatar::getAvatarID, this))); + getChild("Freeze")->setCommitCallback(boost::bind(LLAvatarActions::freeze, boost::bind(&LLPanelAvatar::getAvatarID, this))); + getChild("Unfreeze")->setCommitCallback(boost::bind(LLAvatarActions::unfreeze, boost::bind(&LLPanelAvatar::getAvatarID, this))); + getChild("csr_btn")->setCommitCallback(boost::bind(LLAvatarActions::csr, boost::bind(&LLPanelAvatar::getAvatarID, this))); childSetAction("OK", onClickOK, this); childSetAction("Cancel", onClickCancel, this); childSetAction("copy_key",onClickGetKey,this); - childSetCommitCallback("avatar_key",onCommitKey,this); if(mTab && !sAllowFirstLife) { @@ -1502,38 +1447,6 @@ BOOL LLPanelAvatar::canClose() return !mPanelClassified || mPanelClassified->canClose(); } -void LLPanelAvatar::setAvatar(LLViewerObject *avatarp) -{ - // find the avatar and grab the name - LLNameValue *firstname = avatarp->getNVPair("FirstName"); - LLNameValue *lastname = avatarp->getNVPair("LastName"); - - std::string name; - if (firstname && lastname) - { - name.assign( firstname->getString() ); - name.append(" "); - name.append( lastname->getString() ); - } - else - { - name.assign(""); - } - - // If we have an avatar pointer, they must be online. - setAvatarID(avatarp->getID(), name, ONLINE_STATUS_YES); -} - -void LLPanelAvatar::onCommitKey(LLUICtrl* ctrl, void* data) -{ - LLPanelAvatar* self = (LLPanelAvatar*) data; - std::string keystring = self->getChild("avater_key")->getText(); - LLUUID av_key = LLUUID::null; - if(LLUUID::validate(keystring)) av_key = (LLUUID)keystring; - - self->setAvatarID(av_key, LLStringUtil::null, ONLINE_STATUS_NO); -} - void LLPanelAvatar::setOnlineStatus(EOnlineStatus online_status) { // Online status NO could be because they are hidden @@ -1554,6 +1467,7 @@ void LLPanelAvatar::setOnlineStatus(EOnlineStatus online_status) if (mAvatarID != gAgent.getID()) { childSetVisible("Offer Teleport...",TRUE); + childSetVisible("Find on Map", true); } BOOL in_prelude = gAgent.inPrelude(); @@ -1572,15 +1486,34 @@ void LLPanelAvatar::setOnlineStatus(EOnlineStatus online_status) childSetEnabled("Offer Teleport...", TRUE /*(online_status == ONLINE_STATUS_YES)*/); childSetToolTip("Offer Teleport...", getString("TeleportNormal")); } + + // Note: we don't always know online status, so always allow gods to try to track + childSetEnabled("Find on Map", gAgent.isGodlike() || is_agent_mappable(mAvatarID)); + if (!mIsFriend) + { + childSetToolTip("Find on Map", getString("ShowOnMapNonFriend")); + } + else if (ONLINE_STATUS_YES != online_status) + { + childSetToolTip("Find on Map", getString("ShowOnMapFriendOffline")); + } + else + { + childSetToolTip("Find on Map", getString("ShowOnMapFriendOnline")); + } } -void LLPanelAvatar::onAvatarNameResponse(const LLUUID& agent_id, const LLAvatarName& av_name){ - LLLineEditor* dnname_edit = getChild("dnname"); - if(LLAvatarNameCache::useDisplayNames() && agent_id==mAvatarID) dnname_edit->setText(av_name.getCompleteName()); +void LLPanelAvatar::onAvatarNameResponse(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + std::string name; + if (gSavedSettings.getBOOL("SinguCompleteNameProfiles")) + name = av_name.getCompleteName(); + else + LLAvatarNameCache::getPNSName(av_name, name); + getChild("dnname")->setText(name); } -void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const std::string &name, - EOnlineStatus online_status) +void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id) { if (avatar_id.isNull()) return; @@ -1598,13 +1531,12 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const std::string &name LLAvatarPropertiesProcessor::getInstance()->addObserver(mAvatarID, this); // Determine if we have their calling card. - mIsFriend = is_agent_friend(mAvatarID); + mIsFriend = LLAvatarActions::isFriend(mAvatarID); // setOnlineStatus uses mIsFriend - setOnlineStatus(online_status); - + setOnlineStatus(ONLINE_STATUS_NO); + BOOL own_avatar = (mAvatarID == gAgent.getID() ); - BOOL avatar_is_friend = LLAvatarTracker::instance().getBuddyInfo(mAvatarID) != NULL; for(std::list::iterator it=mAvatarPanelList.begin();it!=mAvatarPanelList.end();++it) { @@ -1617,41 +1549,10 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const std::string &name // Teens don't have this. if (mPanelFirstLife) mPanelFirstLife->enableControls(own_avatar && mAllowEdit); - getChild("drop_target_rect")->setEntityID(mAvatarID); + if (LLDropTarget* drop_target = findChild("drop_target_rect")) + drop_target->setEntityID(mAvatarID); - LLNameEditor* name_edit = getChild("name"); - if(name_edit) - { - if (name.empty()) - { - name_edit->setNameID(avatar_id, FALSE); - } - else - { - name_edit->setText(name); - } - } - - LLLineEditor* dnname_edit = getChild("dnname"); - LLAvatarName av_name; - if(dnname_edit){ - if(LLAvatarNameCache::useDisplayNames()){ - if(LLAvatarNameCache::get(avatar_id, &av_name)){ - dnname_edit->setText(av_name.getCompleteName()); - } - else{ - dnname_edit->setText(name_edit->getText()); - LLAvatarNameCache::get(avatar_id, boost::bind(&LLPanelAvatar::onAvatarNameResponse, this, _1, _2)); - } - childSetVisible("dnname",TRUE); - childSetVisible("name",FALSE); - } - else - { - childSetVisible("dnname",FALSE); - childSetVisible("name",TRUE); - } - } + LLAvatarNameCache::get(avatar_id, boost::bind(&LLPanelAvatar::onAvatarNameResponse, this, _1, _2)); LLNameEditor* key_edit = getChild("avatar_key"); if(key_edit) @@ -1727,25 +1628,8 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const std::string &name childSetVisible("Mute",TRUE); childSetEnabled("Mute",FALSE); - - childSetVisible("Find on Map",TRUE); - // Note: we don't always know online status, so always allow gods to try to track - BOOL enable_track = gAgent.isGodlike() || is_agent_mappable(mAvatarID); - childSetEnabled("Find on Map",enable_track); - if (!mIsFriend) - { - childSetToolTip("Find on Map", getString("ShowOnMapNonFriend")); - } - else if (ONLINE_STATUS_YES != online_status) - { - childSetToolTip("Find on Map", getString("ShowOnMapFriendOffline")); - } - else - { - childSetToolTip("Find on Map", getString("ShowOnMapFriendOnline")); - } childSetVisible("Add Friend...", true); - childSetEnabled("Add Friend...", !avatar_is_friend); + childSetEnabled("Add Friend...", !mIsFriend); childSetVisible("Pay...",TRUE); childSetEnabled("Pay...",FALSE); } @@ -1756,9 +1640,7 @@ void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const std::string &name } } - BOOL is_god = FALSE; - if (gAgent.isGodlike()) is_god = TRUE; - + bool is_god = gAgent.isGodlike(); childSetVisible("Kick", is_god); childSetEnabled("Kick", is_god); childSetVisible("Freeze", is_god); @@ -1832,36 +1714,6 @@ void LLPanelAvatar::resetGroupList() } } -// static -//----------------------------------------------------------------------------- -// onClickIM() -//----------------------------------------------------------------------------- -void LLPanelAvatar::onClickIM(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - gIMMgr->setFloaterOpen(TRUE); - - std::string name; - LLNameEditor* nameedit = self->mPanelSecondLife->getChild("name"); - if (nameedit) name = nameedit->getText(); - gIMMgr->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID); -} - -void LLPanelAvatar::onClickGroupInvite(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - if (self->getAvatarID().notNull()) - { - LLFloaterGroupPicker* widget; - widget = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID())); - if (widget) - { - widget->center(); - widget->setPowersMask(GP_MEMBER_INVITE); - widget->setSelectCallback(callback_invite_to_group, (void *)&(self->getAvatarID())); - } - } -} //static void LLPanelAvatar::onClickGetKey(void *userdata) { @@ -1873,37 +1725,6 @@ void LLPanelAvatar::onClickGetKey(void *userdata) gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(agent_id.asString())); } -// static -//----------------------------------------------------------------------------- -// onClickTrack() -//----------------------------------------------------------------------------- -void LLPanelAvatar::onClickTrack(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - - if( gFloaterWorldMap ) - { - std::string name; - LLNameEditor* nameedit = self->mPanelSecondLife->getChild("name"); - if (nameedit) name = nameedit->getText(); - gFloaterWorldMap->trackAvatar(self->mAvatarID, name); - LLFloaterWorldMap::show(true); - } -} - - -// static -void LLPanelAvatar::onClickAddFriend(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - LLNameEditor* name_edit = self->mPanelSecondLife->getChild("name"); - if (name_edit) - { - LLPanelFriends::requestFriendshipDialog(self->getAvatarID(), - name_edit->getText()); - } -} - //----------------------------------------------------------------------------- // onClickMute() //----------------------------------------------------------------------------- @@ -1912,43 +1733,18 @@ void LLPanelAvatar::onClickMute(void *userdata) LLPanelAvatar* self = (LLPanelAvatar*) userdata; LLUUID agent_id = self->getAvatarID(); - LLNameEditor* name_edit = self->mPanelSecondLife->getChild("name"); - - if (name_edit) + + LLFloaterMute::showInstance(); + if (LLAvatarActions::isBlocked(agent_id)) { - std::string agent_name = name_edit->getText(); - LLFloaterMute::showInstance(); - - if (LLMuteList::getInstance()->isMuted(agent_id)) - { - LLFloaterMute::getInstance()->selectMute(agent_id); - } - else - { - LLMute mute(agent_id, agent_name, LLMute::AGENT); - LLMuteList::getInstance()->add(mute); - } + LLFloaterMute::getInstance()->selectMute(agent_id); + } + else + { + LLAvatarActions::toggleBlock(agent_id); } } - -// static -void LLPanelAvatar::onClickOfferTeleport(void *userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - - handle_lure(self->mAvatarID); -} - - -// static -void LLPanelAvatar::onClickPay(void *userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - handle_pay_by_id(self->mAvatarID); -} - - // static void LLPanelAvatar::onClickOK(void *userdata) { @@ -2043,7 +1839,7 @@ void LLPanelAvatar::processProperties(void* data, EAvatarProcessorType type) if(type == APT_PROPERTIES) { const LLAvatarData* pAvatarData = static_cast( data ); - if (pAvatarData && (mAvatarID == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null)) + if (pAvatarData && (mAvatarID == pAvatarData->avatar_id) && (pAvatarData->avatar_id.notNull())) { childSetEnabled("Instant Message...",TRUE); childSetEnabled("GroupInvite_Button",TRUE); @@ -2166,136 +1962,6 @@ void LLPanelAvatar::selectTabByName(std::string tab_name) } } -// static -void LLPanelAvatar::onClickKick(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - - S32 left, top; - gFloaterView->getNewFloaterPosition(&left, &top); - LLRect rect(left, top, left+400, top-300); - - LLSD payload; - payload["avatar_id"] = self->mAvatarID; - LLNotificationsUtil::add("KickUser", LLSD(), payload, finishKick); -} - -//static -bool LLPanelAvatar::finishKick(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - - if (option == 0) - { - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - LLMessageSystem* msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_GodKickUser); - msg->nextBlockFast(_PREHASH_UserInfo); - msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_AgentID, avatar_id ); - msg->addU32("KickFlags", KICK_FLAGS_DEFAULT ); - msg->addStringFast(_PREHASH_Reason, response["message"].asString() ); - gAgent.sendReliableMessage(); - } - return false; -} - -// static -void LLPanelAvatar::onClickFreeze(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - LLSD payload; - payload["avatar_id"] = self->mAvatarID; - LLNotificationsUtil::add("FreezeUser", LLSD(), payload, LLPanelAvatar::finishFreeze); -} - -// static -bool LLPanelAvatar::finishFreeze(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - - if (option == 0) - { - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - LLMessageSystem* msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_GodKickUser); - msg->nextBlockFast(_PREHASH_UserInfo); - msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_AgentID, avatar_id ); - msg->addU32("KickFlags", KICK_FLAGS_FREEZE ); - msg->addStringFast(_PREHASH_Reason, response["message"].asString() ); - gAgent.sendReliableMessage(); - } - return false; -} - -// static -void LLPanelAvatar::onClickUnfreeze(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*) userdata; - LLSD payload; - payload["avatar_id"] = self->mAvatarID; - LLNotificationsUtil::add("UnFreezeUser", LLSD(), payload, LLPanelAvatar::finishUnfreeze); -} - -// static -bool LLPanelAvatar::finishUnfreeze(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - std::string text = response["message"].asString(); - if (option == 0) - { - LLUUID avatar_id = notification["payload"]["avatar_id"].asUUID(); - LLMessageSystem* msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_GodKickUser); - msg->nextBlockFast(_PREHASH_UserInfo); - msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_AgentID, avatar_id ); - msg->addU32("KickFlags", KICK_FLAGS_UNFREEZE ); - msg->addStringFast(_PREHASH_Reason, text ); - gAgent.sendReliableMessage(); - } - return false; -} - -// static -void LLPanelAvatar::onClickCSR(void* userdata) -{ - LLPanelAvatar* self = (LLPanelAvatar*)userdata; - if (!self) return; - - LLNameEditor* name_edit = self->getChild("name"); - if (!name_edit) return; - - std::string name = name_edit->getText(); - if (name.empty()) return; - - std::string url = "http://csr.lindenlab.com/agent/"; - - // slow and stupid, but it's late - S32 len = name.length(); - for (S32 i = 0; i < len; i++) - { - if (name[i] == ' ') - { - url += "%20"; - } - else - { - url += name[i]; - } - } - - LLWeb::loadURL(url); -} - - void* LLPanelAvatar::createPanelAvatarSecondLife(void* data) { LLPanelAvatar* self = (LLPanelAvatar*)data; diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index a524cd915..ef9841992 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -216,8 +216,6 @@ public: /*virtual*/ void processProperties(void* data, EAvatarProcessorType type){} void clearControls(); - - static void onCommitNotes(LLUICtrl* field, void* userdata); }; @@ -303,8 +301,7 @@ public: // Fill in the avatar ID and handle some field fill-in, as well as // button enablement. - // Pass one of the ONLINE_STATUS_foo constants above. - void setAvatarID(const LLUUID &avatar_id, const std::string &name, EOnlineStatus online_status); + void setAvatarID(const LLUUID &avatar_id); void setOnlineStatus(EOnlineStatus online_status); @@ -328,30 +325,14 @@ public: BOOL haveData() { return mHaveProperties && mHaveStatistics; } BOOL isEditable() const { return mAllowEdit; } - static void onClickTrack( void *userdata); - static void onClickIM( void *userdata); - static void onClickGroupInvite( void *userdata); - static void onClickOfferTeleport( void *userdata); - static void onClickPay( void *userdata); static void onClickGetKey(void *userdata); - static void onClickAddFriend(void* userdata); static void onClickOK( void *userdata); static void onClickCancel( void *userdata); - static void onClickKick( void *userdata); - static void onClickFreeze( void *userdata); - static void onClickUnfreeze(void *userdata); - static void onClickCSR( void *userdata); static void onClickMute( void *userdata); - static void onCommitKey(LLUICtrl* ctrl, void* data); private: void enableOKIfReady(); - static bool finishKick(const LLSD& notification, const LLSD& response); - static bool finishFreeze(const LLSD& notification, const LLSD& response); - static bool finishUnfreeze(const LLSD& notification, const LLSD& response); - - static void showProfileCallback(S32 option, void *userdata); static void* createPanelAvatar(void* data); static void* createFloaterAvatarInfo(void* data); static void* createPanelAvatarSecondLife(void* data); @@ -393,8 +374,4 @@ private: static panel_list_t sAllPanels; }; -// helper funcs -void add_left_label(LLPanel *panel, const std::string& name, S32 y); - - #endif // LL_LLPANELAVATAR_H diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index d0d850db3..de5f765cc 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -46,20 +46,18 @@ #include "message.h" #include "llagent.h" +#include "llavataractions.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llclassifiedflags.h" #include "llclassifiedstatsresponder.h" #include "llcommandhandler.h" // for classified HTML detail page click tracking -#include "llviewercontrol.h" #include "lllineeditor.h" -#include "llfloateravatarinfo.h" #include "llfloaterclassified.h" #include "lltextbox.h" #include "llcombobox.h" #include "llviewertexteditor.h" #include "lltexturectrl.h" -#include "lluiconstants.h" #include "llurldispatcher.h" // for classified HTML detail click teleports #include "lluictrlfactory.h" #include "llviewerparcelmgr.h" @@ -246,40 +244,37 @@ void LLPanelClassified::reset() BOOL LLPanelClassified::postBuild() { mSnapshotCtrl = getChild("snapshot_ctrl"); - mSnapshotCtrl->setCommitCallback(onCommitAny); - mSnapshotCtrl->setCallbackUserData(this); + mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this)); mSnapshotSize = mSnapshotCtrl->getRect(); mNameEditor = getChild("given_name_editor"); mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN); mNameEditor->setCommitOnFocusLost(TRUE); - mNameEditor->setFocusReceivedCallback(boost::bind(focusReceived, _1, this)); - mNameEditor->setCommitCallback(onCommitAny); - mNameEditor->setCallbackUserData(this); + mNameEditor->setFocusReceivedCallback(boost::bind(&LLPanelClassified::checkDirty, this)); + mNameEditor->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this)); mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII ); mDescEditor = getChild("desc_editor"); mDescEditor->setCommitOnFocusLost(TRUE); - mDescEditor->setFocusReceivedCallback(boost::bind(focusReceived, _1, this)); - mDescEditor->setCommitCallback(onCommitAny); - mDescEditor->setCallbackUserData(this); + mDescEditor->setFocusReceivedCallback(boost::bind(&LLPanelClassified::checkDirty, this)); + mDescEditor->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this)); mDescEditor->setTabsToNextField(TRUE); mLocationEditor = getChild("location_editor"); mSetBtn = getChild( "set_location_btn"); - mSetBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickSet, this)); + mSetBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickSet, this)); mTeleportBtn = getChild( "classified_teleport_btn"); - mTeleportBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickTeleport, this)); + mTeleportBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickTeleport, this)); mMapBtn = getChild( "classified_map_btn"); - mMapBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickMap, this)); + mMapBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickMap, this)); if(mInFinder) { mProfileBtn = getChild( "classified_profile_btn"); - mProfileBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickProfile, this)); + mProfileBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickProfile, this)); } mCategoryCombo = getChild( "classified_category_combo"); @@ -291,13 +286,11 @@ BOOL LLPanelClassified::postBuild() mCategoryCombo->add(iter->second, (void *)((intptr_t)iter->first), ADD_BOTTOM); } mCategoryCombo->setCurrentByIndex(0); - mCategoryCombo->setCommitCallback(onCommitAny); - mCategoryCombo->setCallbackUserData(this); + mCategoryCombo->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this)); mMatureCombo = getChild( "classified_mature_check"); mMatureCombo->setCurrentByIndex(0); - mMatureCombo->setCommitCallback(onCommitAny); - mMatureCombo->setCallbackUserData(this); + mMatureCombo->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this)); if (gAgent.wantsPGOnly()) { // Teens don't get to set mature flag. JC @@ -308,13 +301,11 @@ BOOL LLPanelClassified::postBuild() if (!mInFinder) { mAutoRenewCheck = getChild( "auto_renew_check"); - mAutoRenewCheck->setCommitCallback(onCommitAny); - mAutoRenewCheck->setCallbackUserData(this); + mAutoRenewCheck->setCommitCallback(boost::bind(&LLPanelClassified::checkDirty, this)); } mUpdateBtn = getChild("classified_update_btn"); - mUpdateBtn->setClickedCallback(boost::bind(&LLPanelClassified::onClickUpdate, this)); - mUpdateBtn->setCallbackUserData(this); + mUpdateBtn->setCommitCallback(boost::bind(&LLPanelClassified::onClickUpdate, this)); if (!mInFinder) { @@ -394,9 +385,6 @@ void LLPanelClassified::processProperties(void* data, EAvatarProcessorType type) mUpdateBtn->setLabel(getString("update_txt")); resetDirty(); - - // I don't know if a second call is deliberate or a bad merge, so I'm leaving it here. - resetDirty(); } } } @@ -498,7 +486,7 @@ void LLPanelClassified::initNewClassified() mUpdateBtn->setLabel(getString("publish_txt")); // simulate clicking the "location" button - LLPanelClassified::onClickSet(this); + onClickSet(); } @@ -715,33 +703,28 @@ void LLPanelClassified::refresh() } } -// static -void LLPanelClassified::onClickUpdate(void* data) +void LLPanelClassified::onClickUpdate() { - LLPanelClassified* self = (LLPanelClassified*)data; - - if(self == NULL) return; - // Disallow leading spaces, punctuation, etc. that screw up // sort order. - if ( ! self->titleIsValid() ) + if (!titleIsValid()) { return; - }; + } // If user has not set mature, do not allow publish - if(self->mMatureCombo->getCurrentIndex() == DECLINE_TO_STATE) + if (mMatureCombo->getCurrentIndex() == DECLINE_TO_STATE) { // Tell user about it LLNotificationsUtil::add("SetClassifiedMature", LLSD(), LLSD(), - boost::bind(&LLPanelClassified::confirmMature, self, _1, _2)); + boost::bind(&LLPanelClassified::confirmMature, this, _1, _2)); return; } // Mature content flag is set, proceed - self->gotMature(); + gotMature(); } // Callback from a dialog indicating response to mature notification @@ -783,38 +766,30 @@ void LLPanelClassified::gotMature() else { // Ask the user how much they want to pay - LLFloaterPriceForListing::show( callbackGotPriceForListing, this ); + new LLFloaterPriceForListing(boost::bind(&LLPanelClassified::callbackGotPriceForListing, this, _1)); } } -// static -void LLPanelClassified::callbackGotPriceForListing(S32 option, std::string text, void* data) +void LLPanelClassified::callbackGotPriceForListing(const std::string& text) { - LLPanelClassified* self = (LLPanelClassified*)data; - - // Only do something if user hits publish - if (option != 0) return; - S32 price_for_listing = strtol(text.c_str(), NULL, 10); if (price_for_listing < MINIMUM_PRICE_FOR_LISTING) { LLSD args; - std::string price_text = llformat("%d", MINIMUM_PRICE_FOR_LISTING); - args["MIN_PRICE"] = price_text; - + args["MIN_PRICE"] = llformat("%d", MINIMUM_PRICE_FOR_LISTING); LLNotificationsUtil::add("MinClassifiedPrice", args); return; } // price is acceptable, put it in the dialog for later read by // update send - self->mPriceForListing = price_for_listing; + mPriceForListing = price_for_listing; LLSD args; args["AMOUNT"] = llformat("%d", price_for_listing); args["CURRENCY"] = gHippoGridManager->getConnectedGrid()->getCurrencySymbol(); LLNotificationsUtil::add("PublishClassified", args, LLSD(), - boost::bind(&LLPanelClassified::confirmPublish, self, _1, _2)); + boost::bind(&LLPanelClassified::confirmPublish, this, _1, _2)); } void LLPanelClassified::resetDirty() @@ -864,51 +839,39 @@ bool LLPanelClassified::confirmPublish(const LLSD& notification, const LLSD& res return false; } - -// static -void LLPanelClassified::onClickTeleport(void* data) +void LLPanelClassified::onClickTeleport() { - LLPanelClassified* self = (LLPanelClassified*)data; - - if (!self->mPosGlobal.isExactlyZero()) + if (!mPosGlobal.isExactlyZero()) { - gAgent.teleportViaLocation(self->mPosGlobal); - gFloaterWorldMap->trackLocation(self->mPosGlobal); + gAgent.teleportViaLocation(mPosGlobal); + gFloaterWorldMap->trackLocation(mPosGlobal); - self->sendClassifiedClickMessage("teleport"); + sendClassifiedClickMessage("teleport"); } } - -// static -void LLPanelClassified::onClickMap(void* data) +void LLPanelClassified::onClickMap() { - LLPanelClassified* self = (LLPanelClassified*)data; - gFloaterWorldMap->trackLocation(self->mPosGlobal); + gFloaterWorldMap->trackLocation(mPosGlobal); LLFloaterWorldMap::show(true); - self->sendClassifiedClickMessage("map"); + sendClassifiedClickMessage("map"); } -// static -void LLPanelClassified::onClickProfile(void* data) +void LLPanelClassified::onClickProfile() { - LLPanelClassified* self = (LLPanelClassified*)data; - LLFloaterAvatarInfo::showFromDirectory(self->mCreatorID); - self->sendClassifiedClickMessage("profile"); + LLAvatarActions::showProfile(mCreatorID); + sendClassifiedClickMessage("profile"); } -// static /* -void LLPanelClassified::onClickLandmark(void* data) +void LLPanelClassified::onClickLandmark() { - LLPanelClassified* self = (LLPanelClassified*)data; - create_landmark(self->mNameEditor->getText(), "", self->mPosGlobal); + create_landmark(mNameEditor->getText(), "", mPosGlobal); } */ -// static -void LLPanelClassified::onClickSet(void* data) +void LLPanelClassified::onClickSet() { // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) @@ -916,10 +879,9 @@ void LLPanelClassified::onClickSet(void* data) return; } // [/RLVa:KB] - LLPanelClassified* self = (LLPanelClassified*)data; // Save location for later. - self->mPosGlobal = gAgent.getPositionGlobal(); + mPosGlobal = gAgent.getPositionGlobal(); std::string location_text; std::string regionName = "(will update after publish)"; @@ -931,22 +893,22 @@ void LLPanelClassified::onClickSet(void* data) location_text.assign(regionName); location_text.append(", "); - S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]); + S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llround((F32)mPosGlobal.mdV[VZ]); - location_text.append(self->mSimName); + location_text.append(mSimName); location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); - self->mLocationEditor->setText(location_text); - self->mLocationChanged = true; + mLocationEditor->setText(location_text); + mLocationChanged = true; - self->setDefaultAccessCombo(); + setDefaultAccessCombo(); // Set this to null so it updates on the next save. - self->mParcelID.setNull(); + mParcelID.setNull(); - onCommitAny(NULL, data); + checkDirty(); } @@ -965,23 +927,6 @@ BOOL LLPanelClassified::checkDirty() return mDirty; } -// static -void LLPanelClassified::onCommitAny(LLUICtrl* ctrl, void* data) -{ - LLPanelClassified* self = (LLPanelClassified*)data; - if (self) - { - self->checkDirty(); - } -} - -// static -void LLPanelClassified::focusReceived(LLFocusableElement* ctrl, void* data) -{ - // allow the data to be saved - onCommitAny((LLUICtrl*)ctrl, data); -} - void LLPanelClassified::sendClassifiedClickMessage(const std::string& type) { @@ -1002,15 +947,22 @@ void LLPanelClassified::sendClassifiedClickMessage(const std::string& type) //////////////////////////////////////////////////////////////////////////////////////////// -LLFloaterPriceForListing::LLFloaterPriceForListing() +LLFloaterPriceForListing::LLFloaterPriceForListing(const signal_t::slot_type& cb) : LLFloater(std::string("PriceForListing")), - mCallback(NULL), - mUserData(NULL) -{ } + mSignal(new signal_t) +{ + // Builds and adds to gFloaterView + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_price_for_listing.xml"); + center(); + + mSignal->connect(cb); +} //virtual LLFloaterPriceForListing::~LLFloaterPriceForListing() -{ } +{ + delete mSignal; +} //virtual BOOL LLFloaterPriceForListing::postBuild() @@ -1025,50 +977,18 @@ BOOL LLFloaterPriceForListing::postBuild() edit->setFocus(TRUE); } - childSetAction("set_price_btn", onClickSetPrice, this); + getChild("set_price_btn")->setCommitCallback(boost::bind(&LLFloaterPriceForListing::buttonCore, this)); - childSetAction("cancel_btn", onClickCancel, this); + getChild("cancel_btn")->setCommitCallback(boost::bind(&LLFloater::close, this, false)); setDefaultBtn("set_price_btn"); return TRUE; } -//static -void LLFloaterPriceForListing::show( void (*callback)(S32, std::string, void*), void* userdata) +void LLFloaterPriceForListing::buttonCore() { - LLFloaterPriceForListing *self = new LLFloaterPriceForListing(); - - // Builds and adds to gFloaterView - LLUICtrlFactory::getInstance()->buildFloater(self, "floater_price_for_listing.xml"); - self->center(); - - self->mCallback = callback; - self->mUserData = userdata; -} - -//static -void LLFloaterPriceForListing::onClickSetPrice(void* data) -{ - buttonCore(0, data); -} - -//static -void LLFloaterPriceForListing::onClickCancel(void* data) -{ - buttonCore(1, data); -} - -//static -void LLFloaterPriceForListing::buttonCore(S32 button, void* data) -{ - LLFloaterPriceForListing* self = (LLFloaterPriceForListing*)data; - - if (self->mCallback) - { - std::string text = self->childGetText("price_edit"); - self->mCallback(button, text, self->mUserData); - self->close(); - } + (*mSignal)(childGetText("price_edit")); + close(); } void LLPanelClassified::setDefaultAccessCombo() diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index a3bb42554..04432edfd 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -38,23 +38,19 @@ #define LL_LLPANELCLASSIFIED_H #include "llavatarpropertiesprocessor.h" -#include "llpanel.h" #include "llclassifiedinfo.h" #include "v3dmath.h" #include "lluuid.h" #include "llfloater.h" -//#include "llrect.h" class LLButton; class LLCheckBoxCtrl; class LLComboBox; -class LLIconCtrl; class LLLineEditor; class LLTextBox; class LLTextEditor; class LLTextureCtrl; class LLUICtrl; -class LLMessageSystem; class LLPanelClassified : public LLPanel, public LLAvatarPropertiesObserver { @@ -104,7 +100,7 @@ public: // Confirmation dialogs flow in this order bool confirmMature(const LLSD& notification, const LLSD& response); void gotMature(); - static void callbackGotPriceForListing(S32 option, std::string text, void* data); + void callbackGotPriceForListing(const std::string& text); bool confirmPublish(const LLSD& notification, const LLSD& response); void sendClassifiedClickMessage(const std::string& type); @@ -112,14 +108,11 @@ public: protected: bool saveCallback(const LLSD& notification, const LLSD& response); - static void onClickUpdate(void* data); - static void onClickTeleport(void* data); - static void onClickMap(void* data); - static void onClickProfile(void* data); - static void onClickSet(void* data); - - static void focusReceived(LLFocusableElement* ctrl, void* data); - static void onCommitAny(LLUICtrl* ctrl, void* data); + void onClickUpdate(); + void onClickTeleport(); + void onClickMap(); + void onClickProfile(); + void onClickSet(); void setDefaultAccessCombo(); // Default AO and PG regions to proper classified access @@ -183,20 +176,15 @@ class LLFloaterPriceForListing : public LLFloater { public: - LLFloaterPriceForListing(); + typedef boost::signals2::signal signal_t; + LLFloaterPriceForListing(const signal_t::slot_type& cb); virtual ~LLFloaterPriceForListing(); virtual BOOL postBuild(); - static void show( void (*callback)(S32 option, std::string value, void* userdata), void* userdata ); - private: - static void onClickSetPrice(void*); - static void onClickCancel(void*); - static void buttonCore(S32 button, void* data); + void buttonCore(); -private: - void (*mCallback)(S32 option, std::string, void*); - void* mUserData; + signal_t* mSignal; }; diff --git a/indra/newview/llpaneldirbrowser.cpp b/indra/newview/llpaneldirbrowser.cpp index 7480fdffc..929b5c6b8 100644 --- a/indra/newview/llpaneldirbrowser.cpp +++ b/indra/newview/llpaneldirbrowser.cpp @@ -422,7 +422,7 @@ void LLPanelDirBrowser::showDetailPanel(S32 type, LLSD id) if (mFloaterDirectory && mFloaterDirectory->mPanelAvatarp) { mFloaterDirectory->mPanelAvatarp->setVisible(TRUE); - mFloaterDirectory->mPanelAvatarp->setAvatarID(id.asUUID(), LLStringUtil::null, ONLINE_STATUS_NO); + mFloaterDirectory->mPanelAvatarp->setAvatarID(id.asUUID()); } break; case EVENT_CODE: diff --git a/indra/newview/llpaneldirclassified.cpp b/indra/newview/llpaneldirclassified.cpp index e41fbf622..2bc854864 100644 --- a/indra/newview/llpaneldirclassified.cpp +++ b/indra/newview/llpaneldirclassified.cpp @@ -112,8 +112,6 @@ BOOL LLPanelDirClassified::postBuild() getChild("Browse")->setClickedCallback(boost::bind(&LLPanelDirBrowser::onClickSearchCore,this)); setDefaultBtn( "Browse" ); - getChild("Place an Ad...")->setClickedCallback(boost::bind(&LLPanelDirClassified::onClickCreateNewClassified,this)); - getChild("Delete")->setClickedCallback(boost::bind(&LLPanelDirClassified::onClickDelete,this)); childDisable("Delete"); childHide("Delete"); @@ -149,11 +147,6 @@ void LLPanelDirClassified::refresh() updateMaturityCheckbox(); } -void LLPanelDirClassified::onClickCreateNewClassified() -{ - LLFloaterAvatarInfo::showFromObject(gAgent.getID(), "Classified"); -} - void LLPanelDirClassified::onClickDelete() { LLUUID classified_id; diff --git a/indra/newview/llpaneldirclassified.h b/indra/newview/llpaneldirclassified.h index 92b05183f..6e0f73c8b 100644 --- a/indra/newview/llpaneldirclassified.h +++ b/indra/newview/llpaneldirclassified.h @@ -65,9 +65,6 @@ protected: void onClickSearch(); void onKeystrokeNameClassified(LLLineEditor* line); - - // - void onClickCreateNewClassified(); protected: }; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 1e6269fa0..c6ed7163f 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -113,10 +113,9 @@ BOOL LLPanelFace::postBuild() { mTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectTexture" ))); mTextureCtrl->setCommitCallback( boost::bind(&LLPanelFace::onCommitTexture, this, _2) ); - mTextureCtrl->setOnCancelCallback( LLPanelFace::onCancelTexture ); - mTextureCtrl->setOnSelectCallback( LLPanelFace::onSelectTexture ); - mTextureCtrl->setDragCallback(LLPanelFace::onDragTexture); - mTextureCtrl->setCallbackUserData( this ); + mTextureCtrl->setOnCancelCallback( boost::bind(&LLPanelFace::onCancelTexture, this, _2) ); + mTextureCtrl->setOnSelectCallback( boost::bind(&LLPanelFace::onSelectTexture, this, _2) ); + mTextureCtrl->setDragCallback(boost::bind(&LLPanelFace::onDragTexture, this, _2)); mTextureCtrl->setFollowsTop(); mTextureCtrl->setFollowsLeft(); // Don't allow (no copy) or (no transfer) textures to be selected during immediate mode @@ -144,9 +143,8 @@ BOOL LLPanelFace::postBuild() if(mColorSwatch) { mColorSwatch->setCommitCallback(boost::bind(&LLPanelFace::onCommitColor, this, _2)); - mColorSwatch->setOnCancelCallback(LLPanelFace::onCancelColor); - mColorSwatch->setOnSelectCallback(LLPanelFace::onSelectColor); - mColorSwatch->setCallbackUserData( this ); + mColorSwatch->setOnCancelCallback(boost::bind(&LLPanelFace::onCancelColor, this, _2)); + mColorSwatch->setOnSelectCallback(boost::bind(&LLPanelFace::onSelectColor, this, _2)); mColorSwatch->setFollowsTop(); mColorSwatch->setFollowsLeft(); mColorSwatch->setCanApplyImmediately(TRUE); @@ -1046,18 +1044,15 @@ void LLPanelFace::onCommitAlpha(const LLSD& data) sendAlpha(); } -// static -void LLPanelFace::onCancelColor(LLUICtrl* ctrl, void* userdata) +void LLPanelFace::onCancelColor(const LLSD& data) { LLSelectMgr::getInstance()->selectionRevertColors(); } -// static -void LLPanelFace::onSelectColor(LLUICtrl* ctrl, void* userdata) +void LLPanelFace::onSelectColor(const LLSD& data) { - LLPanelFace* self = (LLPanelFace*) userdata; LLSelectMgr::getInstance()->saveSelectedObjectColors(); - self->sendColor(); + sendColor(); } // static @@ -1096,7 +1091,7 @@ void LLPanelFace::onCommitGlow(LLUICtrl* ctrl, void* userdata) } // static -BOOL LLPanelFace::onDragTexture(LLUICtrl*, LLInventoryItem* item, void*) +BOOL LLPanelFace::onDragTexture(LLUICtrl*, LLInventoryItem* item) { BOOL accept = TRUE; for (LLObjectSelection::root_iterator iter = LLSelectMgr::getInstance()->getSelection()->root_begin(); @@ -1119,18 +1114,15 @@ void LLPanelFace::onCommitTexture( const LLSD& data ) sendTexture(); } -// static -void LLPanelFace::onCancelTexture(LLUICtrl* ctrl, void* userdata) +void LLPanelFace::onCancelTexture(const LLSD& data) { LLSelectMgr::getInstance()->selectionRevertTextures(); } -// static -void LLPanelFace::onSelectTexture(LLUICtrl* ctrl, void* userdata) +void LLPanelFace::onSelectTexture(const LLSD& data) { - LLPanelFace* self = (LLPanelFace*) userdata; - LLSelectMgr::getInstance()->saveSelectedObjectTextures(); - self->sendTexture(); + LLSelectMgr::getInstance()->saveSelectedObjectTextures(); + sendTexture(); } diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index e9700a21e..52ccbaf10 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -74,28 +74,27 @@ protected: void sendMedia(); // this function is to return TRUE if the drag should succeed. - static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item, void* ud); + static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item); void onCommitTexture(const LLSD& data); - static void onCancelTexture( LLUICtrl* ctrl, void* userdata); - static void onSelectTexture( LLUICtrl* ctrl, void* userdata); + void onCancelTexture(const LLSD& data); + void onSelectTexture(const LLSD& data); void onCommitColor(const LLSD& data); void onCommitAlpha(const LLSD& data); - static void onCancelColor( LLUICtrl* ctrl, void* userdata); - static void onSelectColor( LLUICtrl* ctrl, void* userdata); - - static void onCommitTextureInfo( LLUICtrl* ctrl, void* userdata); + void onCancelColor(const LLSD& data); + void onSelectColor(const LLSD& data); + static void onCommitTextureInfo( LLUICtrl* ctrl, void* userdata); static void onCommitBump( LLUICtrl* ctrl, void* userdata); static void onCommitTexGen( LLUICtrl* ctrl, void* userdata); static void onCommitShiny( LLUICtrl* ctrl, void* userdata); static void onCommitFullbright( LLUICtrl* ctrl, void* userdata); static void onCommitGlow( LLUICtrl* ctrl, void *userdata); static void onCommitPlanarAlign( LLUICtrl* ctrl, void* userdata); - static void onClickApply(void*); static void onClickAutoFix(void*); static void onClickCopy(void*); static void onClickPaste(void*); + static F32 valueGlow(LLViewerObject* object, S32 face); }; diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index 4f8f4e969..816ba6fba 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -123,18 +123,14 @@ void LLPanelGroupTab::handleClickHelp() } } -LLPanelGroup::LLPanelGroup(const std::string& filename, - const std::string& name, - const LLUUID& group_id, - const std::string& initial_tab_selected) -: LLPanel(name, LLRect(), FALSE), +LLPanelGroup::LLPanelGroup(const LLUUID& group_id) +: LLPanel("PanelGroup", LLRect(), FALSE), LLGroupMgrObserver( group_id ), mCurrentTab( NULL ), mRequestedTab( NULL ), mTabContainer( NULL ), mIgnoreTransition( FALSE ), mForceClose( FALSE ), - mInitialTab(initial_tab_selected), mAllowEdit( TRUE ), mShowingNotifyDialog( FALSE ) { @@ -157,18 +153,14 @@ LLPanelGroup::LLPanelGroup(const std::string& filename, LLGroupMgr::getInstance()->addObserver(this); // Pass on construction of this panel to the control factory. - LLUICtrlFactory::getInstance()->buildPanel(this, filename, &getFactoryMap()); - mFilename = filename; + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_group.xml", &getFactoryMap()); } LLPanelGroup::~LLPanelGroup() { LLGroupMgr::getInstance()->removeObserver(this); - int i; - int tab_count = mTabContainer->getTabCount(); - - for (i = tab_count - 1; i >=0; --i) + for (int i = mTabContainer->getTabCount() - 1; i >=0; --i) { LLPanelGroupTab* panelp = (LLPanelGroupTab*) mTabContainer->getPanelByIndex(i); @@ -179,10 +171,7 @@ LLPanelGroup::~LLPanelGroup() void LLPanelGroup::updateTabVisibility() { - S32 i; - S32 tab_count = mTabContainer->getTabCount(); - - for (i = tab_count - 1; i >=0; --i) + for (int i = mTabContainer->getTabCount() - 1; i >=0; --i) { LLPanelGroupTab* panelp = (LLPanelGroupTab*) mTabContainer->getPanelByIndex(i); @@ -209,38 +198,23 @@ BOOL LLPanelGroup::postBuild() if (mTabContainer) { - // Select the initial tab specified via constructor - const BOOL recurse = TRUE; - LLPanelGroupTab* tabp = - getChild(mInitialTab, recurse); + //our initial tab selection was invalid, just select the + //first tab then or default to selecting the initial + //selected tab specified in the layout file + LLPanelGroupTab* tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel(); + //no tab was initially selected through constructor + //or the XML, select the first tab if (!tabp) { - //our initial tab selection was invalid, just select the - //first tab then or default to selecting the initial - //selected tab specified in the layout file + mTabContainer->selectFirstTab(); tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel(); - - //no tab was initially selected through constructor - //or the XML, select the first tab - if (!tabp) - { - mTabContainer->selectFirstTab(); - tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel(); - } - } - else - { - mTabContainer->selectTabPanel(tabp); } mCurrentTab = tabp; // Add click callbacks. - S32 i; - S32 tab_count = mTabContainer->getTabCount(); - - for (i = tab_count - 1; i >=0; --i) + for (int i = mTabContainer->getTabCount() - 1; i >=0; --i) { LLPanel* tab_panel = mTabContainer->getPanelByIndex(i); LLPanelGroupTab* panelp =(LLPanelGroupTab*)tab_panel; // bit of a hack @@ -270,14 +244,13 @@ BOOL LLPanelGroup::postBuild() if (button) { button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnCancel,this)); - button->setVisible(mAllowEdit); + button->setEnabled(mAllowEdit); // Cancel should always be enabled for standalone group floater, this is expected behavior and may be used for simply closing } button = getChild("btn_apply"); if (button) { button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnApply,this)); - button->setVisible(mAllowEdit); button->setEnabled(FALSE); mApplyBtn = button; @@ -287,7 +260,6 @@ BOOL LLPanelGroup::postBuild() if (button) { button->setClickedCallback(boost::bind(&LLPanelGroup::onBtnRefresh,this)); - button->setVisible(mAllowEdit); } return TRUE; @@ -310,11 +282,15 @@ void LLPanelGroup::tabChanged() { //some tab information has changed,....enable/disable the apply button //based on if they need an apply + std::string str; + const bool need = mCurrentTab->needsApply(str); if ( mApplyBtn ) { - std::string mesg; - mApplyBtn->setEnabled(mCurrentTab->needsApply(mesg)); + mApplyBtn->setEnabled(need); } + if (mAllowEdit) return; // Cancel should always be enabled for standalone group floater, this is expected behavior and may be used for simply closing + if (LLUICtrl* ctrl = getChild("btn_cancel")) + ctrl->setEnabled(need); } void LLPanelGroup::handleClickTab() @@ -340,8 +316,6 @@ void LLPanelGroup::handleClickTab() void LLPanelGroup::setGroupID(const LLUUID& group_id) { - LLRect rect(getRect()); - LLGroupMgr::getInstance()->removeObserver(this); mID = group_id; LLGroupMgr::getInstance()->addObserver(this); @@ -353,7 +327,7 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) // For now, rebuild panel //delete children and rebuild panel deleteAllChildren(); - LLUICtrlFactory::getInstance()->buildPanel(this, mFilename, &getFactoryMap()); + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_group.xml", &getFactoryMap()); } void LLPanelGroup::selectTab(std::string tab_name) @@ -503,7 +477,10 @@ void LLPanelGroup::onBtnOK(void* user_data) void LLPanelGroup::onBtnCancel(void* user_data) { LLPanelGroup* self = static_cast(user_data); - self->close(); + if (self->mAllowEdit) // We're in a standalone floater + self->close(); + else // We're in search, we can't close out, just refreshData to kill changes + self->refreshData(); } // static diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index 6de4efe27..f9ea38136 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -36,7 +36,7 @@ #include "llpanel.h" #include "lltimer.h" -struct LLOfferInfo; +class LLOfferInfo; const F32 UPDATE_MEMBERS_SECONDS_PER_FRAME = 0.005f; // 5ms @@ -58,10 +58,7 @@ class LLPanelGroup : public LLPanel, public LLPanelGroupTabObserver { public: - LLPanelGroup(const std::string& filename, - const std::string& name, - const LLUUID& group_id, - const std::string& initial_tab_selected = std::string()); + LLPanelGroup(const LLUUID& group_id); virtual ~LLPanelGroup(); virtual BOOL postBuild(); @@ -119,9 +116,6 @@ protected: BOOL mForceClose; - std::string mInitialTab; - std::string mFilename; - std::string mDefaultNeedsApplyMesg; std::string mWantApplyMesg; diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index 6b7a445c9..31482d15e 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -36,9 +36,10 @@ #include "lluictrlfactory.h" #include "llagent.h" +#include "llavataractions.h" +#include "llfloatergroups.h" +#include "llgroupactions.h" #include "roles_constants.h" -#include "llfloateravatarinfo.h" -#include "llfloatergroupinfo.h" // UI elements #include "llbutton.h" @@ -133,13 +134,13 @@ BOOL LLPanelGroupGeneral::postBuild() mBtnJoinGroup = getChild("join_button", recurse); if ( mBtnJoinGroup ) { - mBtnJoinGroup->setClickedCallback(boost::bind(&LLPanelGroupGeneral::onClickJoin, this)); + mBtnJoinGroup->setClickedCallback(boost::bind(LLGroupActions::join, mGroupID)); } mBtnInfo = getChild("info_button", recurse); if ( mBtnInfo ) { - mBtnInfo->setClickedCallback(boost::bind(&LLPanelGroupGeneral::onClickInfo, this)); + mBtnInfo->setClickedCallback(boost::bind(LLGroupActions::show, mGroupID)); } mFounderName = getChild("founder_name"); @@ -147,7 +148,7 @@ BOOL LLPanelGroupGeneral::postBuild() mListVisibleMembers = getChild("visible_members", recurse); if (mListVisibleMembers) { - mListVisibleMembers->setDoubleClickCallback(boost::bind(&LLPanelGroupGeneral::openProfile,this)); + mListVisibleMembers->setDoubleClickCallback(boost::bind(LLAvatarActions::showProfile, boost::bind(&LLScrollListCtrl::getCurrentID, mListVisibleMembers))); } // Options @@ -290,8 +291,7 @@ void LLPanelGroupGeneral::onCommitEnrollment() } // Make sure the agent can change enrollment info. - if (!gAgent.hasPowerInGroup(mGroupID,GP_MEMBER_OPTIONS) - || !mAllowEdit) + if (!gAgent.hasPowerInGroup(mGroupID,GP_MEMBER_OPTIONS)) { return; } @@ -309,89 +309,12 @@ void LLPanelGroupGeneral::onCommitEnrollment() void LLPanelGroupGeneral::onCommitTitle() { - if (mGroupID.isNull() || !mAllowEdit) return; + if (mGroupID.isNull()) return; LLGroupMgr::getInstance()->sendGroupTitleUpdate(mGroupID,mComboActiveTitle->getCurrentID()); update(GC_TITLES); mComboActiveTitle->resetDirty(); } -// static -void LLPanelGroupGeneral::onClickInfo(void *userdata) -{ - LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata; - - if ( !self ) return; - - lldebugs << "open group info: " << self->mGroupID << llendl; - - LLFloaterGroupInfo::showFromUUID(self->mGroupID); -} - -// static -void LLPanelGroupGeneral::onClickJoin(void *userdata) -{ - LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata; - - if ( !self ) return; - - lldebugs << "joining group: " << self->mGroupID << llendl; - - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(self->mGroupID); - - if (gdatap) - { - S32 cost = gdatap->mMembershipFee; - LLSD args; - args["COST"] = llformat("%d", cost); - LLSD payload; - payload["group_id"] = self->mGroupID; - - if (can_afford_transaction(cost)) - { - LLNotificationsUtil::add("JoinGroupCanAfford", args, payload, LLPanelGroupGeneral::joinDlgCB); - } - else - { - LLNotificationsUtil::add("JoinGroupCannotAfford", args, payload); - } - } - else - { - llwarns << "LLGroupMgr::getInstance()->getGroupData(" << self->mGroupID - << ") was NULL" << llendl; - } -} - -// static -bool LLPanelGroupGeneral::joinDlgCB(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - - if (option == 1) - { - // user clicked cancel - return false; - } - - LLGroupMgr::getInstance()->sendGroupMemberJoin(notification["payload"]["group_id"].asUUID()); - return false; -} - -// static -void LLPanelGroupGeneral::openProfile(void* data) -{ - LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data; - - if (self && self->mListVisibleMembers) - { - LLScrollListItem* selected = self->mListVisibleMembers->getFirstSelected(); - if (selected) - { - LLFloaterAvatarInfo::showFromDirectory( selected->getUUID() ); - } - } -} - bool LLPanelGroupGeneral::needsApply(std::string& mesg) { updateChanged(); @@ -633,7 +556,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) if (mComboActiveTitle) { mComboActiveTitle->setVisible(is_member); - mComboActiveTitle->setEnabled(mAllowEdit); if ( mActiveTitleLabel) mActiveTitleLabel->setVisible(is_member); @@ -693,7 +615,7 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) if (mCtrlShowInGroupList) { mCtrlShowInGroupList->set(gdatap->mShowInList); - mCtrlShowInGroupList->setEnabled(mAllowEdit && can_change_ident); + mCtrlShowInGroupList->setEnabled(can_change_ident); mCtrlShowInGroupList->resetDirty(); } @@ -707,20 +629,20 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) { mComboMature->setCurrentByIndex(NON_MATURE_CONTENT); } - mComboMature->setEnabled(mAllowEdit && can_change_ident); + mComboMature->setEnabled(can_change_ident); mComboMature->setVisible( !gAgent.isTeen() ); mComboMature->resetDirty(); } if (mCtrlOpenEnrollment) { mCtrlOpenEnrollment->set(gdatap->mOpenEnrollment); - mCtrlOpenEnrollment->setEnabled(mAllowEdit && can_change_member_opts); + mCtrlOpenEnrollment->setEnabled(can_change_member_opts); mCtrlOpenEnrollment->resetDirty(); } if (mCtrlEnrollmentFee) { mCtrlEnrollmentFee->set(gdatap->mMembershipFee > 0); - mCtrlEnrollmentFee->setEnabled(mAllowEdit && can_change_member_opts); + mCtrlEnrollmentFee->setEnabled(can_change_member_opts); mCtrlEnrollmentFee->resetDirty(); } @@ -728,9 +650,7 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) { S32 fee = gdatap->mMembershipFee; mSpinEnrollmentFee->set((F32)fee); - mSpinEnrollmentFee->setEnabled( mAllowEdit && - (fee > 0) && - can_change_member_opts); + mSpinEnrollmentFee->setEnabled(fee && can_change_member_opts); mSpinEnrollmentFee->resetDirty(); } if ( mBtnJoinGroup ) @@ -762,7 +682,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) mCtrlReceiveNotices->setVisible(is_member); if (is_member) { - mCtrlReceiveNotices->setEnabled(mAllowEdit); if(!mCtrlReceiveNotices->isDirty()) //If the user hasn't edited this then refresh it. Value may have changed in groups panel, etc. { mCtrlReceiveNotices->set(agent_gdatap.mAcceptNotices); @@ -776,7 +695,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) mCtrlListGroup->setVisible(is_member); if (is_member) { - mCtrlListGroup->setEnabled(mAllowEdit); if(!mCtrlListGroup->isDirty()) //If the user hasn't edited this then refresh it. Value may have changed in groups panel, etc. { mCtrlListGroup->set(agent_gdatap.mListInProfile); @@ -790,7 +708,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) mCtrlReceiveChat->setVisible(is_member); if (is_member) { - mCtrlReceiveChat->setEnabled(mAllowEdit); if(!mCtrlReceiveChat->isDirty()) //If the user hasn't edited this then refresh it. Value may have changed in groups panel, etc. { mCtrlReceiveChat->set(!gIMMgr->getIgnoreGroup(mGroupID)); @@ -799,8 +716,8 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) } } - if (mInsignia) mInsignia->setEnabled(mAllowEdit && can_change_ident); - if (mEditCharter) mEditCharter->setEnabled(mAllowEdit && can_change_ident); + if (mInsignia) mInsignia->setEnabled(can_change_ident); + if (mEditCharter) mEditCharter->setEnabled(can_change_ident); if (mGroupName) mGroupName->setText(gdatap->mName); if (mGroupNameEditor) mGroupNameEditor->setVisible(FALSE); diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index db400ad7c..84058ac84 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -75,14 +75,9 @@ private: void onCommitUserOnly(); void onCommitTitle(); void onCommitEnrollment(); - static void onClickJoin(void* userdata); - static void onClickInfo(void* userdata); static void onReceiveNotices(LLUICtrl* ctrl, void* data); - static void openProfile(void* data); void addMember(LLGroupMemberData* member); - static bool joinDlgCB(const LLSD& notification, const LLSD& response); - void updateMembers(); void updateChanged(); bool confirmMatureApply(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index c1c29be8d..6b4d0e680 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -39,6 +39,7 @@ #include "llbutton.h" #include "llcallingcard.h" #include "llcombobox.h" +#include "llgroupactions.h" #include "llgroupmgr.h" #include "llnamelistctrl.h" #include "llnotificationsutil.h" @@ -49,8 +50,6 @@ #include "lluictrlfactory.h" #include "llviewerwindow.h" -#include - class LLPanelGroupInvite::impl : public boost::signals2::trackable { public: @@ -183,7 +182,7 @@ void LLPanelGroupInvite::impl::submitInvitations() iter != items.end(); ++iter) { LLScrollListItem* item = *iter; - if(gdatap->mMembers.find(item->getUUID()) != gdatap->mMembers.end()) + if(LLGroupActions::isAvatarMemberOfGroup(mGroupID, item->getUUID())) { already_in_group = true; continue; diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index a69b4c76a..3deb47999 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -222,8 +222,7 @@ BOOL LLPanelGroupNotices::postBuild() mNoticesList = getChild("notice_list",recurse); mNoticesList->setCommitOnSelectionChange(TRUE); - mNoticesList->setCommitCallback(onSelectNotice); - mNoticesList->setCallbackUserData(this); + mNoticesList->setCommitCallback(boost::bind(&LLPanelGroupNotices::onSelectNotice, this)); mBtnNewMessage = getChild("create_new_notice",recurse); mBtnNewMessage->setClickedCallback(boost::bind(&LLPanelGroupNotices::onClickNewMessage,this)); @@ -494,14 +493,11 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) mNoticesList->updateSort(); } -void LLPanelGroupNotices::onSelectNotice(LLUICtrl* ctrl, void* data) +void LLPanelGroupNotices::onSelectNotice() { - LLPanelGroupNotices* self = (LLPanelGroupNotices*)data; - - if(!self) return; - LLScrollListItem* item = self->mNoticesList->getFirstSelected(); + LLScrollListItem* item = mNoticesList->getFirstSelected(); if (!item) return; - + LLMessageSystem* msg = gMessageSystem; msg->newMessage("GroupNoticeRequest"); msg->nextBlock("AgentData"); diff --git a/indra/newview/llpanelgroupnotices.h b/indra/newview/llpanelgroupnotices.h index 916032c1b..fdd878394 100644 --- a/indra/newview/llpanelgroupnotices.h +++ b/indra/newview/llpanelgroupnotices.h @@ -78,7 +78,7 @@ private: static void onClickRefreshNotices(void* data); void processNotices(LLMessageSystem* msg); - static void onSelectNotice(LLUICtrl* ctrl, void* data); + void onSelectNotice(); enum ENoticeView { diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index ab9ff5664..0b5acb543 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -34,10 +34,10 @@ #include "llcheckboxctrl.h" #include "llagent.h" +#include "llavataractions.h" #include "llavatarnamecache.h" #include "llbutton.h" #include "llfiltereditor.h" -#include "llfloateravatarinfo.h" #include "llfloatergroupinvite.h" #include "lliconctrl.h" #include "lllineeditor.h" @@ -198,7 +198,7 @@ BOOL LLPanelGroupRoles::isVisibleByAgent(LLAgent* agentp) GP_MEMBER_EJECT | GP_MEMBER_OPTIONS ); */ - return mAllowEdit && agentp->isInGroup(mGroupID); + return agentp->isInGroup(mGroupID); } @@ -1202,8 +1202,7 @@ void LLPanelGroupMembersSubTab::handleMemberDoubleClick() LLScrollListItem* selected = mMembersList->getFirstSelected(); if (selected) { - LLUUID member_id = selected->getUUID(); - LLFloaterAvatarInfo::showFromDirectory( member_id ); + LLAvatarActions::showProfile(selected->getUUID()); } } diff --git a/indra/newview/llpanelgroupvoting.cpp b/indra/newview/llpanelgroupvoting.cpp index 0eac7e5ec..2b03523b8 100644 --- a/indra/newview/llpanelgroupvoting.cpp +++ b/indra/newview/llpanelgroupvoting.cpp @@ -1491,7 +1491,7 @@ LLPanelGroupVoting::~LLPanelGroupVoting() BOOL LLPanelGroupVoting::isVisibleByAgent(LLAgent* agentp) { //if they are in the group, the panel is viewable - return mAllowEdit && agentp->isInGroup(mGroupID); + return agentp->isInGroup(mGroupID); } BOOL LLPanelGroupVoting::postBuild() diff --git a/indra/newview/llpanelinput.cpp b/indra/newview/llpanelinput.cpp index 685f9a7fb..41287c4d7 100644 --- a/indra/newview/llpanelinput.cpp +++ b/indra/newview/llpanelinput.cpp @@ -45,10 +45,9 @@ LLPanelInput::LLPanelInput() LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_input.xml"); } -static void onFOVAdjust(LLUICtrl* source, void* data) +static void onFOVAdjust(const LLSD& value) { - LLSliderCtrl* slider = dynamic_cast(source); - LLViewerCamera::getInstance()->setDefaultFOV(slider->getValueF32()); + LLViewerCamera::getInstance()->setDefaultFOV(value.asFloat()); } BOOL LLPanelInput::postBuild() @@ -68,7 +67,7 @@ BOOL LLPanelInput::postBuild() childSetValue("first_person_avatar_visible", gSavedSettings.getBOOL("FirstPersonAvatarVisible")); LLSliderCtrl* fov_slider = getChild("camera_fov"); - fov_slider->setCommitCallback(&onFOVAdjust); + fov_slider->setCommitCallback(boost::bind(onFOVAdjust, _2)); fov_slider->setMinValue(LLViewerCamera::getInstance()->getMinView()); fov_slider->setMaxValue(LLViewerCamera::getInstance()->getMaxView()); fov_slider->setValue(LLViewerCamera::getInstance()->getView()); diff --git a/indra/newview/llpanellandmedia.cpp b/indra/newview/llpanellandmedia.cpp index 07fc30553..a515eadc7 100644 --- a/indra/newview/llpanellandmedia.cpp +++ b/indra/newview/llpanellandmedia.cpp @@ -88,8 +88,7 @@ BOOL LLPanelLandMedia::postBuild() { mMediaTextureCtrl = getChild("media texture"); - mMediaTextureCtrl->setCommitCallback( onCommitAny ); - mMediaTextureCtrl->setCallbackUserData( this ); + mMediaTextureCtrl->setCommitCallback( onCommitAny, this ); mMediaTextureCtrl->setAllowNoTexture ( TRUE ); mMediaTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); mMediaTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index e6fed8486..0986ac024 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -239,7 +239,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, reshape(rect.getWidth(), rect.getHeight()); LLComboBox* name_combo = sInstance->getChild("name_combo"); - name_combo->setCommitCallback(onSelectLoginEntry); + name_combo->setCommitCallback(boost::bind(LLPanelLogin::onSelectLoginEntry, _1, this)); name_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLoginComboLostFocus, this, name_combo)); name_combo->setPrevalidate(LLLineEditor::prevalidatePrintableNotPipe); name_combo->setSuppressTentative(true); diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index eb5e430b8..7983b445a 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -209,7 +209,7 @@ BOOL LLInventoryView::postBuild() if (mQuickFilterCombo) { - mQuickFilterCombo->setCommitCallback(onQuickFilterCommit); + mQuickFilterCombo->setCommitCallback(boost::bind(LLInventoryView::onQuickFilterCommit, _1, this)); } diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp index 90714d99f..7a05212a3 100644 --- a/indra/newview/llpanelobject.cpp +++ b/indra/newview/llpanelobject.cpp @@ -339,11 +339,10 @@ BOOL LLPanelObject::postBuild() if (mCtrlSculptTexture) { mCtrlSculptTexture->setDefaultImageAssetID(LLUUID(SCULPT_DEFAULT_TEXTURE)); - mCtrlSculptTexture->setCommitCallback( LLPanelObject::onCommitSculpt ); - mCtrlSculptTexture->setOnCancelCallback( LLPanelObject::onCancelSculpt ); - mCtrlSculptTexture->setOnSelectCallback( LLPanelObject::onSelectSculpt ); - mCtrlSculptTexture->setDropCallback(LLPanelObject::onDropSculpt); - mCtrlSculptTexture->setCallbackUserData( this ); + mCtrlSculptTexture->setCommitCallback( boost::bind(&LLPanelObject::onCommitSculpt, this, _2 )); + mCtrlSculptTexture->setOnCancelCallback( boost::bind(&LLPanelObject::onCancelSculpt, this, _2 )); + mCtrlSculptTexture->setOnSelectCallback( boost::bind(&LLPanelObject::onSelectSculpt, this, _2 )); + mCtrlSculptTexture->setDropCallback( boost::bind(&LLPanelObject::onDropSculpt, this, _2 )); // Don't allow (no copy) or (no transfer) textures to be selected during immediate mode mCtrlSculptTexture->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); // Allow any texture to be used during non-immediate mode. @@ -2361,60 +2360,49 @@ void LLPanelObject::onCommitPhantom( LLUICtrl* ctrl, void* userdata ) self->sendIsPhantom(); } -// static -void LLPanelObject::onSelectSculpt(LLUICtrl* ctrl, void* userdata) +void LLPanelObject::onSelectSculpt(const LLSD& data) { - LLPanelObject* self = (LLPanelObject*) userdata; - - LLTextureCtrl* mTextureCtrl = self->getChild("sculpt texture control"); + LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if (mTextureCtrl) { - self->mSculptTextureRevert = mTextureCtrl->getImageAssetID(); + mSculptTextureRevert = mTextureCtrl->getImageAssetID(); } - self->sendSculpt(); + sendSculpt(); } -void LLPanelObject::onCommitSculpt( LLUICtrl* ctrl, void* userdata ) +void LLPanelObject::onCommitSculpt( const LLSD& data ) { - LLPanelObject* self = (LLPanelObject*) userdata; - - self->sendSculpt(); + sendSculpt(); } -// static -BOOL LLPanelObject::onDropSculpt(LLUICtrl*, LLInventoryItem* item, void* userdata) +BOOL LLPanelObject::onDropSculpt(LLInventoryItem* item) { - LLPanelObject* self = (LLPanelObject*) userdata; - - LLTextureCtrl* mTextureCtrl = self->getChild("sculpt texture control"); + LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if (mTextureCtrl) { LLUUID asset = item->getAssetUUID(); mTextureCtrl->setImageAssetID(asset); - self->mSculptTextureRevert = asset; + mSculptTextureRevert = asset; } return TRUE; } -// static -void LLPanelObject::onCancelSculpt(LLUICtrl* ctrl, void* userdata) +void LLPanelObject::onCancelSculpt(const LLSD& data) { - LLPanelObject* self = (LLPanelObject*) userdata; - - LLTextureCtrl* mTextureCtrl = self->getChild("sculpt texture control"); + LLTextureCtrl* mTextureCtrl = getChild("sculpt texture control"); if(!mTextureCtrl) return; - mTextureCtrl->setImageAssetID(self->mSculptTextureRevert); + mTextureCtrl->setImageAssetID(mSculptTextureRevert); - self->sendSculpt(); + sendSculpt(); } // static diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h index e34d318c6..675a93053 100644 --- a/indra/newview/llpanelobject.h +++ b/indra/newview/llpanelobject.h @@ -95,10 +95,10 @@ public: static void onCommitMaterial( LLUICtrl* ctrl, void* userdata); - static void onCommitSculpt( LLUICtrl* ctrl, void* userdata); - static void onCancelSculpt( LLUICtrl* ctrl, void* userdata); - static void onSelectSculpt( LLUICtrl* ctrl, void* userdata); - static BOOL onDropSculpt( LLUICtrl* ctrl, LLInventoryItem* item, void* ud); + void onCommitSculpt(const LLSD& data); + void onCancelSculpt(const LLSD& data); + void onSelectSculpt(const LLSD& data); + BOOL onDropSculpt(LLInventoryItem* item); static void onCommitSculptType( LLUICtrl *ctrl, void* userdata); static void onClickBuildConstants(void *); diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index c81ae35e9..326fb952d 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -36,16 +36,14 @@ #include "llpanelpermissions.h" -#include "lluuid.h" #include "llpermissions.h" -#include "llcategory.h" #include "llclickaction.h" #include "llfocusmgr.h" #include "llnotificationsutil.h" -#include "llstring.h" +#include "lltrans.h" +#include "llwindow.h" #include "llviewerwindow.h" -#include "llwindow.h" #include "llresmgr.h" #include "lltextbox.h" #include "llbutton.h" @@ -53,20 +51,16 @@ #include "llviewerobject.h" #include "llselectmgr.h" #include "llagent.h" -#include "llstatusbar.h" // for getBalance() +#include "llavataractions.h" +#include "llfloatergroups.h" +#include "llgroupactions.h" #include "lllineeditor.h" #include "llradiogroup.h" #include "llcombobox.h" -#include "llfloateravatarinfo.h" -#include "lluiconstants.h" #include "lldbstrings.h" -#include "llfloatergroupinfo.h" -#include "llfloatergroups.h" #include "llnamebox.h" -#include "llviewercontrol.h" #include "lluictrlfactory.h" #include "roles_constants.h" -#include "lltrans.h" #include "llinventoryfunctions.h" #include "lfsimfeaturehandler.h" @@ -267,7 +261,7 @@ void LLPanelPermissions::disableAll() combo_click_action->clear(); } getChildView("B:")->setVisible( FALSE); - getChildView("O:")->setVisible( FALSE); + //getChildView("O:")->setVisible( FALSE); getChildView("G:")->setVisible( FALSE); getChildView("E:")->setVisible( FALSE); getChildView("N:")->setVisible( FALSE); @@ -672,8 +666,8 @@ void LLPanelPermissions::refresh() perm_string = mask_to_string(owner_mask_on); if (!supports_export && owner_mask_on & PERM_EXPORT) // Hide Export when not available perm_string.erase(perm_string.find_last_of("E")); - getChild("O:")->setValue("O: " + perm_string); - getChildView("O:")->setVisible( TRUE); + //getChild("O:")->setValue("O: " + perm_string); + //getChildView("O:")->setVisible( TRUE); getChild("G:")->setValue("G: " + mask_to_string(group_mask_on)); getChildView("G:")->setVisible( TRUE); @@ -701,7 +695,7 @@ void LLPanelPermissions::refresh() { childSetVisible("perm_modify", true); getChildView("B:")->setVisible( FALSE); - getChildView("O:")->setVisible( FALSE); + //getChildView("O:")->setVisible( FALSE); getChildView("G:")->setVisible( FALSE); getChildView("E:")->setVisible( FALSE); getChildView("N:")->setVisible( FALSE); @@ -1007,7 +1001,7 @@ void LLPanelPermissions::onClickCreator(void *data) { LLPanelPermissions *self = (LLPanelPermissions *)data; - LLFloaterAvatarInfo::showFromObject(self->mCreatorID); + LLAvatarActions::showProfile(self->mCreatorID); } // static @@ -1019,25 +1013,24 @@ void LLPanelPermissions::onClickOwner(void *data) { LLUUID group_id; LLSelectMgr::getInstance()->selectGetGroup(group_id); - LLFloaterGroupInfo::showFromUUID(group_id); + LLGroupActions::show(group_id); } else { // [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) { - LLFloaterAvatarInfo::showFromObject(self->mOwnerID); + LLAvatarActions::showProfile(self->mOwnerID); } // [/RLVa:KB] -// LLFloaterAvatarInfo::showFromObject(self->mOwnerID); +// LLAvatarActions::showProfile(self->mOwnerID); } } void LLPanelPermissions::onClickLastOwner(void *data) { LLPanelPermissions *self = (LLPanelPermissions *)data; - if(self->mLastOwnerID.notNull()) - LLFloaterAvatarInfo::showFromObject(self->mLastOwnerID); + LLAvatarActions::showProfile(self->mLastOwnerID); } void LLPanelPermissions::onClickGroup(void* data) @@ -1071,7 +1064,7 @@ void LLPanelPermissions::onClickOpenGroup(void* data) LLUUID group_id; LLSelectMgr::getInstance()->selectGetGroup(group_id); - LLFloaterGroupInfo::showFromUUID(group_id); + LLGroupActions::show(group_id); } // static diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index f599fc915..674d9b65d 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -30,82 +30,65 @@ * $/LicenseInfo$ */ -// Display of a "Top Pick" used both for the global top picks in the -// Find directory, and also for each individual user's picks in their -// profile. +// Display of each individual user's picks in their profile. #include "llviewerprecompiledheaders.h" #include "llpanelpick.h" -#include "lldir.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" #include "llparcel.h" -#include "message.h" +#include "lltexteditor.h" +#include "lltexturectrl.h" +#include "lluictrlfactory.h" #include "llagent.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llviewercontrol.h" -#include "lllineeditor.h" -#include "lltextbox.h" -#include "llviewertexteditor.h" -#include "lltexturectrl.h" -#include "lluiconstants.h" -#include "llviewergenericmessage.h" -#include "lluictrlfactory.h" -#include "llviewerparcelmgr.h" -#include "llworldmap.h" #include "llfloaterworldmap.h" +#include "llpreviewtexture.h" +#include "llviewerparcelmgr.h" #include "llviewerregion.h" -#include "llviewerwindow.h" -#include "llnotificationsutil.h" // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] - //For pick import and export - RK #include "statemachine/aifilepicker.h" -#include "llviewernetwork.h" -#include "llsdserialize.h" #include "hippogridmanager.h" +#include "llsdserialize.h" + +void show_picture(const LLUUID& id, const std::string& name) +{ + // Try to show and focus existing preview + if (LLPreview::show(id)) return; + // If there isn't one, make a new preview + LLPreview* preview = new LLPreviewTexture("preview texture", gSavedSettings.getRect("PreviewTextureRect"), name, id); + preview->setFocus(true); +} + //static std::list LLPanelPick::sAllPanels; -LLPanelPick::LLPanelPick(BOOL top_pick) +LLPanelPick::LLPanelPick() : LLPanel(std::string("Top Picks Panel")), - mTopPick(top_pick), mPickID(), mCreatorID(), mParcelID(), - mDataRequested(FALSE), - mDataReceived(FALSE), + mDataRequested(false), + mDataReceived(false), mPosGlobal(), mSnapshotCtrl(NULL), mNameEditor(NULL), mDescEditor(NULL), mLocationEditor(NULL), - mTeleportBtn(NULL), - mMapBtn(NULL), - //mLandmarkBtn(NULL), - mSortOrderText(NULL), - mSortOrderEditor(NULL), - mEnabledCheck(NULL), mSetBtn(NULL), + mOpenBtn(NULL), mImporting(0) { sAllPanels.push_back(this); - std::string pick_def_file; - if (top_pick) - { - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_top_pick.xml"); - } - else - { - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_pick.xml"); - } + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_avatar_pick.xml"); } @@ -130,8 +113,8 @@ void LLPanelPick::reset() mParcelID.setNull(); // Don't request data, this isn't valid - mDataRequested = TRUE; - mDataReceived = FALSE; + mDataRequested = true; + mDataReceived = false; mPosGlobal.clearVec(); @@ -142,83 +125,55 @@ void LLPanelPick::reset() BOOL LLPanelPick::postBuild() { mSnapshotCtrl = getChild("snapshot_ctrl"); - mSnapshotCtrl->setCommitCallback(onCommitAny); - mSnapshotCtrl->setCallbackUserData(this); + mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelPick::onCommitAny, this)); mNameEditor = getChild("given_name_editor"); - mNameEditor->setCommitOnFocusLost(TRUE); - mNameEditor->setCommitCallback(onCommitAny); - mNameEditor->setCallbackUserData(this); + mNameEditor->setCommitOnFocusLost(true); + mNameEditor->setCommitCallback(boost::bind(&LLPanelPick::onCommitAny, this)); mDescEditor = getChild("desc_editor"); - mDescEditor->setCommitOnFocusLost(TRUE); - mDescEditor->setCommitCallback(onCommitAny); - mDescEditor->setCallbackUserData(this); - mDescEditor->setTabsToNextField(TRUE); + mDescEditor->setCommitOnFocusLost(true); + mDescEditor->setCommitCallback(boost::bind(&LLPanelPick::onCommitAny, this)); + mDescEditor->setTabsToNextField(true); mLocationEditor = getChild("location_editor"); - mSetBtn = getChild( "set_location_btn"); - mSetBtn->setClickedCallback(boost::bind(&LLPanelPick::onClickSet,this)); + mSetBtn = getChild( "set_location_btn"); + mSetBtn->setCommitCallback(boost::bind(&LLPanelPick::onClickSet,this)); - mTeleportBtn = getChild( "pick_teleport_btn"); - mTeleportBtn->setClickedCallback(boost::bind(&LLPanelPick::onClickTeleport,this)); + mOpenBtn = getChild("open_picture_btn"); + mOpenBtn->setCommitCallback(boost::bind(show_picture, boost::bind(&LLTextureCtrl::getImageAssetID, mSnapshotCtrl), boost::bind(&LLLineEditor::getText, mNameEditor))); - mMapBtn = getChild( "pick_map_btn"); - mMapBtn->setClickedCallback(boost::bind(&LLPanelPick::onClickMap,this)); - - mSortOrderText = getChild("sort_order_text"); - - mSortOrderEditor = getChild("sort_order_editor"); - mSortOrderEditor->setPrevalidate(LLLineEditor::prevalidateInt); - mSortOrderEditor->setCommitOnFocusLost(TRUE); - mSortOrderEditor->setCommitCallback(onCommitAny); - mSortOrderEditor->setCallbackUserData(this); - - mEnabledCheck = getChild( "enabled_check"); - mEnabledCheck->setCommitCallback(onCommitAny); - mEnabledCheck->setCallbackUserData(this); + getChild("pick_teleport_btn")->setCommitCallback(boost::bind(&LLPanelPick::onClickTeleport,this)); + getChild("pick_map_btn")->setCommitCallback(boost::bind(&LLPanelPick::onClickMap,this)); return TRUE; } void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) { - if(APT_PICK_INFO != type) - { - return; - } + if (!data || APT_PICK_INFO != type) return; LLPickData* pick_info = static_cast(data); - //llassert_always(pick_info->creator_id != gAgent.getID()); - //llassert_always(mCreatorID != gAgent.getID()); - if(!pick_info - || pick_info->creator_id != mCreatorID - || pick_info->pick_id != mPickID) - { + if (pick_info->creator_id != mCreatorID || pick_info->pick_id != mPickID) return; - } LLAvatarPropertiesProcessor::getInstance()->removeObserver(mCreatorID, this); // "Location text" is actually the owner name, the original // name that owner gave the parcel, and the location. std::string location_text = pick_info->user_name + ", "; - if (!pick_info->original_name.empty()) { - location_text.append(pick_info->original_name); - location_text.append(", "); + location_text += pick_info->original_name + ", "; } - - location_text.append(pick_info->sim_name); - location_text.append(" "); + location_text += pick_info->sim_name + " "; //Fix for location text importing - RK for (panel_list_t::iterator iter = sAllPanels.begin(); iter != sAllPanels.end(); ++iter) { LLPanelPick* self = *iter; - if(!self->mImporting) self->mLocationText = location_text; + if (!self->mImporting) self->mLocationText = location_text; else location_text = self->mLocationText; self->mImporting = false; } @@ -226,10 +181,9 @@ void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) S32 region_x = llround((F32)pick_info->pos_global.mdV[VX]) % REGION_WIDTH_UNITS; S32 region_y = llround((F32)pick_info->pos_global.mdV[VY]) % REGION_WIDTH_UNITS; S32 region_z = llround((F32)pick_info->pos_global.mdV[VZ]); - location_text.append(llformat("(%d, %d, %d)", region_x, region_y, region_z)); - mDataReceived = TRUE; + mDataReceived = true; // Found the panel, now fill in the information mPickID = pick_info->pick_id; @@ -243,24 +197,17 @@ void LLPanelPick::processProperties(void* data, EAvatarProcessorType type) mDescEditor->setText(pick_info->desc); mSnapshotCtrl->setImageAssetID(pick_info->snapshot_id); mLocationEditor->setText(location_text); - mEnabledCheck->set(pick_info->enabled); - - mSortOrderEditor->setText(llformat("%d", pick_info->sort_order)); - } // Fill in some reasonable defaults for a new pick. void LLPanelPick::initNewPick() { mPickID.generate(); - - mCreatorID = gAgent.getID(); - + mCreatorID = gAgentID; mPosGlobal = gAgent.getPositionGlobal(); // Try to fill in the current parcel - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (parcel) + if (LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel()) { mNameEditor->setText(parcel->getName()); mDescEditor->setText(parcel->getDesc()); @@ -284,21 +231,14 @@ void LLPanelPick::importNewPick_continued(void (*callback)(void*, bool), void* d bool result = false; if (filepicker->hasFilename()) { - std::string file = filepicker->getFilename(); - - llifstream importer(file); + llifstream importer(filepicker->getFilename()); LLSD data; LLSDSerialize::fromXMLDocument(data, importer); - LLSD file_data = data["Data"]; - data = LLSD(); mPickID.generate(); - - mCreatorID = gAgent.getID(); - + mCreatorID = gAgentID; mPosGlobal = LLVector3d(file_data["globalPos"]); - mNameEditor->setText(file_data["name"].asString()); mDescEditor->setText(file_data["desc"].asString()); mSnapshotCtrl->setImageAssetID(file_data["snapshotID"].asUUID()); @@ -325,10 +265,10 @@ void LLPanelPick::exportPick_continued(AIFilePicker* filepicker) if (!filepicker->hasFilename()) return; - std::string destination = filepicker->getFilename(); + LLSD header; + header["Version"] = 2; LLSD datas; - datas["name"] = mNameEditor->getText(); datas["desc"] = mDescEditor->getText(); datas["parcelID"] = mParcelID; @@ -337,16 +277,12 @@ void LLPanelPick::exportPick_continued(AIFilePicker* filepicker) datas["locationText"] = mLocationText; LLSD file; - LLSD header; - header["Version"] = 2; file["Header"] = header; - std::string grid_uri = gHippoGridManager->getConnectedGrid()->getLoginUri(); - //LLStringUtil::toLower(uris[0]); - file["Grid"] = grid_uri; + file["Grid"] = gHippoGridManager->getConnectedGrid()->getLoginUri(); file["Data"] = datas; // Create a file stream and write to it - llofstream export_file(destination); + llofstream export_file(filepicker->getFilename()); LLSDSerialize::toPrettyXML(file, export_file); // Open the file save dialog export_file.close(); @@ -364,24 +300,17 @@ void LLPanelPick::setPickID(const LLUUID& pick_id, const LLUUID& creator_id) // from the server next time it is drawn. void LLPanelPick::markForServerRequest() { - mDataRequested = FALSE; - mDataReceived = FALSE; -} - - -std::string LLPanelPick::getPickName() -{ - return mNameEditor->getText(); + mDataRequested = false; + mDataReceived = false; } void LLPanelPick::sendPickInfoRequest() { - //llassert_always(mCreatorID != gAgent.getID()); LLAvatarPropertiesProcessor::getInstance()->addObserver(mCreatorID, this); LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest(mCreatorID, mPickID); - mDataRequested = TRUE; + mDataRequested = true; } @@ -396,24 +325,18 @@ void LLPanelPick::sendPickInfoUpdate() mPickID.generate(); } - pick_data.agent_id = gAgent.getID(); - pick_data.session_id = gAgent.getSessionID(); + pick_data.agent_id = gAgentID; + pick_data.session_id = gAgentSessionID; pick_data.pick_id = mPickID; - pick_data.creator_id = gAgent.getID(); - - //legacy var need to be deleted - pick_data.top_pick = mTopPick; + pick_data.creator_id = gAgentID; + pick_data.top_pick = false; //legacy var need to be deleted pick_data.parcel_id = mParcelID; pick_data.name = mNameEditor->getText(); pick_data.desc = mDescEditor->getText(); pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); pick_data.pos_global = mPosGlobal; - if(mTopPick) - pick_data.sort_order = atoi(mSortOrderEditor->getText().c_str()); - else - pick_data.sort_order = 0; - - pick_data.enabled = mEnabledCheck->get(); + pick_data.sort_order = 0; + pick_data.enabled = true; LLAvatarPropertiesProcessor::getInstance()->sendPickInfoUpdate(&pick_data); } @@ -421,106 +344,50 @@ void LLPanelPick::sendPickInfoUpdate() void LLPanelPick::draw() -{ - refresh(); - - LLPanel::draw(); -} - - -void LLPanelPick::refresh() { if (!mDataRequested) { sendPickInfoRequest(); } - // Check for god mode - BOOL godlike = gAgent.isGodlike(); - BOOL is_self = (gAgent.getID() == mCreatorID); - // Set button visibility/enablement appropriately - if (mTopPick) - { - mSnapshotCtrl->setEnabled(godlike); - mNameEditor->setEnabled(godlike); - mDescEditor->setEnabled(godlike); - - mSortOrderText->setVisible(godlike); - - mSortOrderEditor->setVisible(godlike); - mSortOrderEditor->setEnabled(godlike); - - mEnabledCheck->setVisible(godlike); - mEnabledCheck->setEnabled(godlike); - - mSetBtn->setVisible(godlike); - //mSetBtn->setEnabled(godlike); + bool is_self = gAgentID == mCreatorID; + mSnapshotCtrl->setEnabled(is_self); + mNameEditor->setEnabled(is_self); + mDescEditor->setEnabled(is_self); + mSetBtn->setVisible(is_self); + //mSetBtn->setEnabled(is_self); // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) - mSetBtn->setEnabled(godlike && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ); -// [/RLVa:KB] - } - - - - else - { - mSnapshotCtrl->setEnabled(is_self); - mNameEditor->setEnabled(is_self); - mDescEditor->setEnabled(is_self); - - mSortOrderText->setVisible(FALSE); - - mSortOrderEditor->setVisible(FALSE); - mSortOrderEditor->setEnabled(FALSE); - - mEnabledCheck->setVisible(FALSE); - mEnabledCheck->setEnabled(FALSE); - - mSetBtn->setVisible(is_self); - //mSetBtn->setEnabled(is_self); -// [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) - mSetBtn->setEnabled(is_self && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) ); + mSetBtn->setEnabled(is_self && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); // [/RLVa] - } + mOpenBtn->setVisible(!is_self); + + LLPanel::draw(); } - - - - -// static -void LLPanelPick::onClickTeleport(void* data) +void LLPanelPick::onClickTeleport() { - LLPanelPick* self = (LLPanelPick*)data; - - if (!self->mPosGlobal.isExactlyZero()) + if (!mPosGlobal.isExactlyZero()) { - gAgent.teleportViaLocation(self->mPosGlobal); - gFloaterWorldMap->trackLocation(self->mPosGlobal); + gAgent.teleportViaLocation(mPosGlobal); + gFloaterWorldMap->trackLocation(mPosGlobal); } } - -// static -void LLPanelPick::onClickMap(void* data) +void LLPanelPick::onClickMap() { - LLPanelPick* self = (LLPanelPick*)data; - gFloaterWorldMap->trackLocation(self->mPosGlobal); + gFloaterWorldMap->trackLocation(mPosGlobal); LLFloaterWorldMap::show(true); } -// static /* -void LLPanelPick::onClickLandmark(void* data) +void LLPanelPick::onClickLandmark() { - LLPanelPick* self = (LLPanelPick*)data; - create_landmark(self->mNameEditor->getText(), "", self->mPosGlobal); + create_landmark(mNameEditor->getText(), "", mPosGlobal); } */ -// static -void LLPanelPick::onClickSet(void* data) +void LLPanelPick::onClickSet() { // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) @@ -528,63 +395,41 @@ void LLPanelPick::onClickSet(void* data) return; } // [/RLVa:KB] - LLPanelPick* self = (LLPanelPick*)data; // Save location for later. - self->mPosGlobal = gAgent.getPositionGlobal(); + mPosGlobal = gAgent.getPositionGlobal(); - std::string location_text; - location_text.assign("(will update after save)"); - location_text.append(", "); + std::string location_text("(will update after save), " + mSimName); - S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]); - - location_text.append(self->mSimName); + S32 region_x = llround((F32)mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = llround((F32)mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = llround((F32)mPosGlobal.mdV[VZ]); location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); // if sim name in pick is different from current sim name // make sure it's clear that all that's being changed // is the location and nothing else - if ( gAgent.getRegion ()->getName () != self->mSimName ) + if (gAgent.getRegion()->getName() != mSimName) { LLNotificationsUtil::add("SetPickLocation"); - }; + } - self->mLocationEditor->setText(location_text); + mLocationEditor->setText(location_text); - onCommitAny(NULL, data); + onCommitAny(); } - -// static -void LLPanelPick::onCommitAny(LLUICtrl* ctrl, void* data) +void LLPanelPick::onCommitAny() { - LLPanelPick* self = (LLPanelPick*)data; - - if(self->mCreatorID != gAgent.getID()) - return; + if (mCreatorID != gAgentID) return; // have we received up to date data for this pick? - if (self->mDataReceived) + if (mDataReceived) { - self->sendPickInfoUpdate(); - - // Big hack - assume that top picks are always in a browser, - // and non-top-picks are always in a tab container. - /*if (self->mTopPick) + sendPickInfoUpdate(); + if (LLTabContainer* tab = dynamic_cast(getParent())) { - LLPanelDirPicks* panel = (LLPanelDirPicks*)self->getParent(); - panel->renamePick(self->mPickID, self->mNameEditor->getText()); + tab->setCurrentTabName(mNameEditor->getText()); } - else - {*/ - LLTabContainer* tab = (LLTabContainer*)self->getParent(); - if (tab) - { - if(tab) tab->setCurrentTabName(self->mNameEditor->getText()); - } - //} } } diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 84f41b8da..eb2d30a89 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -30,43 +30,30 @@ * $/LicenseInfo$ */ -// Display of a "Top Pick" used both for the global top picks in the -// Find directory, and also for each individual user's picks in their -// profile. +// Display of each individual user's picks in their profile. #ifndef LL_LLPANELPICK_H #define LL_LLPANELPICK_H #include "llpanel.h" -#include "v3dmath.h" -#include "lluuid.h" #include "llavatarpropertiesprocessor.h" -class LLButton; -class LLCheckBoxCtrl; -class LLIconCtrl; class LLLineEditor; -class LLTextBox; class LLTextEditor; class LLTextureCtrl; -class LLUICtrl; -class LLMessageSystem; class AIFilePicker; class LLPanelPick : public LLPanel, public LLAvatarPropertiesObserver { public: - LLPanelPick(BOOL top_pick); + LLPanelPick(); /*virtual*/ ~LLPanelPick(); void reset(); /*virtual*/ BOOL postBuild(); - /*virtual*/ void draw(); - /*virtual*/ void refresh(); - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); // Setup a new pick, including creating an id, giving a sane @@ -87,7 +74,7 @@ public: // from the server next time it is drawn. void markForServerRequest(); - std::string getPickName(); + const std::string& getPickName() const { return mNameEditor->getText(); } const LLUUID& getPickID() const { return mPickID; } const LLUUID& getPickCreatorID() const { return mCreatorID; } @@ -95,26 +82,24 @@ public: void sendPickInfoUpdate(); protected: - static void onClickTeleport(void* data); - static void onClickMap(void* data); - //static void onClickLandmark(void* data); - static void onClickSet(void* data); + void onClickTeleport(); + void onClickMap(); + //void onClickLandmark(); + void onClickSet(); - static void onCommitAny(LLUICtrl* ctrl, void* data); + void onCommitAny(); -protected: //Pick import and export - RK - BOOL mImporting; + bool mImporting; std::string mLocationText; - BOOL mTopPick; LLUUID mPickID; LLUUID mCreatorID; LLUUID mParcelID; // Data will be requested on first draw - BOOL mDataRequested; - BOOL mDataReceived; + bool mDataRequested; + bool mDataReceived; std::string mSimName; LLVector3d mPosGlobal; @@ -124,13 +109,9 @@ protected: LLTextEditor* mDescEditor; LLLineEditor* mLocationEditor; - LLButton* mTeleportBtn; - LLButton* mMapBtn; - - LLTextBox* mSortOrderText; - LLLineEditor* mSortOrderEditor; - LLCheckBoxCtrl* mEnabledCheck; - LLButton* mSetBtn; + LLUICtrl* mTeleportBtn; + LLUICtrl* mSetBtn; + LLUICtrl* mOpenBtn; typedef std::list panel_list_t; static panel_list_t sAllPanels; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 2bf44165e..aa2c5dbd2 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -29,9 +29,13 @@ #ifdef AI_UNUSED #include "llagent.h" +#endif // AI_UNUSED #include "llavataractions.h" +#ifdef AI_UNUSED #include "llfloaterreg.h" +#endif // AI_UNUSED #include "llcommandhandler.h" +#ifdef AI_UNUSED #include "llnotificationsutil.h" #include "llpanelpicks.h" #include "lltabcontainer.h" @@ -55,7 +59,6 @@ std::string getProfileURL(const std::string& agent_name) return url; } -#ifdef AI_UNUSED class LLProfileHandler : public LLCommandHandler { public: @@ -101,7 +104,9 @@ public: if (verb == "inspect") { - LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); + LLAvatarActions::showProfile(avatar_id); + //Singu TODO: inspect? + //LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); return true; } @@ -113,11 +118,13 @@ public: if (verb == "pay") { + /* if (!LLUI::sSettingGroups["config"]->getBOOL("EnableAvatarPay")) { LLNotificationsUtil::add("NoAvatarPay", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); return true; } + */ LLAvatarActions::pay(avatar_id); return true; @@ -159,6 +166,7 @@ public: LLAgentHandler gAgentHandler; +#ifdef AI_UNUSED //-- LLPanelProfile::ChildStack begins ---------------------------------------- LLPanelProfile::ChildStack::ChildStack() : mParent(NULL) diff --git a/indra/newview/llpanelskins.cpp b/indra/newview/llpanelskins.cpp index 3d5958a5f..610c8249d 100644 --- a/indra/newview/llpanelskins.cpp +++ b/indra/newview/llpanelskins.cpp @@ -48,24 +48,20 @@ #include "llsdserialize.h" -LLPanelSkins* LLPanelSkins::sInstance; LLPanelSkins::LLPanelSkins() { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_skins.xml"); - if(sInstance)delete sInstance; - sInstance = this; } LLPanelSkins::~LLPanelSkins() { - sInstance = NULL; } BOOL LLPanelSkins::postBuild() { mSkin = gSavedSettings.getString("SkinCurrent"); oldSkin=mSkin; - getChild("custom_skin_combo")->setCommitCallback(onComboBoxCommit); + getChild("custom_skin_combo")->setCommitCallback(boost::bind(&LLPanelSkins::onComboBoxCommit, this, _2)); refresh(); return TRUE; } @@ -159,27 +155,21 @@ void LLPanelSkins::cancel() gSavedSettings.setString("SkinCurrent", oldSkin); } -//static -void LLPanelSkins::onComboBoxCommit(LLUICtrl* ctrl, void* userdata) +void LLPanelSkins::onComboBoxCommit(const LLSD& value) { - LLComboBox* box = (LLComboBox*)ctrl; - if(box) + const std::string skinName = value.asString(); + for(int i = 0; i < (int)datas.size(); ++i) { - std::string skinName = box->getValue().asString(); - for(int i =0;i<(int)sInstance->datas.size();i++) + const LLSD tdata = datas[i]; + if (tdata["skin_name"].asString() == skinName) { - LLSD tdata=sInstance->datas[i]; - std::string tempName = tdata["skin_name"].asString(); - if(tempName==skinName) - { - std::string newFolder(tdata["folder_name"].asString()); - gSavedSettings.setString("SkinCurrent",newFolder); - sInstance->mSkin=newFolder; + std::string newFolder(tdata["folder_name"].asString()); + gSavedSettings.setString("SkinCurrent", newFolder); + mSkin = newFolder; - if(sInstance)sInstance->refresh(); - return; - } + refresh(); + return; } - } + } } diff --git a/indra/newview/llpanelskins.h b/indra/newview/llpanelskins.h index 0208309e1..030668834 100644 --- a/indra/newview/llpanelskins.h +++ b/indra/newview/llpanelskins.h @@ -48,11 +48,10 @@ public: void refresh(); void apply(); void cancel(); - static void onComboBoxCommit(LLUICtrl* ctrl, void* userdata); + void onComboBoxCommit(const LLSD& value); std::string mSkin; private: - static LLPanelSkins* sInstance; std::string oldSkin; }; diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp new file mode 100644 index 000000000..f890e55f8 --- /dev/null +++ b/indra/newview/llpanelvoicedevicesettings.cpp @@ -0,0 +1,329 @@ +/** + * @file llpanelvoicedevicesettings.cpp + * @author Richard Nelson + * @brief Voice communication set-up + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelvoicedevicesettings.h" + +// Viewer includes +#include "llcombobox.h" +#include "llsliderctrl.h" +#include "llviewercontrol.h" +#include "llvoiceclient.h" +#include "llvoicechannel.h" + +// Library includes (after viewer) +#include "lluictrlfactory.h" + + +static const std::string DEFAULT_DEVICE("Default"); + + +LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings() + : LLPanel() +{ + mCtrlInputDevices = NULL; + mCtrlOutputDevices = NULL; + mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + mDevicesUpdated = FALSE; + mUseTuningMode = true; + + // grab "live" mic volume level + mMicVolume = gSavedSettings.getF32("AudioLevelMic"); + +} + +LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() +{ + if (getVisible()) cleanup(); // Singu Note: If we're still visible, we'll need to cleanup +} + +BOOL LLPanelVoiceDeviceSettings::postBuild() +{ + LLSlider* volume_slider = getChild("mic_volume_slider"); + // set mic volume tuning slider based on last mic volume setting + volume_slider->setValue(mMicVolume); + + mCtrlInputDevices = getChild("voice_input_device"); + mCtrlOutputDevices = getChild("voice_output_device"); + + mCtrlInputDevices->setCommitCallback( + boost::bind(&LLPanelVoiceDeviceSettings::onCommitInputDevice, this)); + mCtrlOutputDevices->setCommitCallback( + boost::bind(&LLPanelVoiceDeviceSettings::onCommitOutputDevice, this)); + + mLocalizedDeviceNames[DEFAULT_DEVICE] = getString("default_text"); + mLocalizedDeviceNames["No Device"] = getString("name_no_device"); + mLocalizedDeviceNames["Default System Device"] = getString("name_default_system_device"); + + return TRUE; +} + +// virtual +void LLPanelVoiceDeviceSettings::handleVisibilityChange ( BOOL new_visibility ) +{ + if (new_visibility) + { + initialize(); + } + else + { + cleanup(); + // when closing this window, turn off visiblity control so that + // next time preferences is opened we don't suspend voice + gSavedSettings.setBOOL("ShowDeviceSettings", FALSE); + } +} +void LLPanelVoiceDeviceSettings::draw() +{ + refresh(); + + // let user know that volume indicator is not yet available + bool is_in_tuning_mode = LLVoiceClient::getInstance()->inTuningMode(); + getChildView("wait_text")->setVisible( !is_in_tuning_mode && mUseTuningMode); + + LLPanel::draw(); + + if (is_in_tuning_mode) + { + const S32 num_bars = 5; + F32 voice_power = LLVoiceClient::getInstance()->tuningGetEnergy() / LLVoiceClient::OVERDRIVEN_POWER_LEVEL; + S32 discrete_power = llmin(num_bars, llfloor(voice_power * (F32)num_bars + 0.1f)); + + for(S32 power_bar_idx = 0; power_bar_idx < num_bars; power_bar_idx++) + { + std::string view_name = llformat("%s%d", "bar", power_bar_idx); + LLView* bar_view = getChild(view_name); + if (bar_view) + { + gl_rect_2d(bar_view->getRect(), LLColor4::grey, TRUE); + + LLColor4 color; + if (power_bar_idx < discrete_power) + { + color = (power_bar_idx >= 3) ? gSavedSettings.getColor4("OverdrivenColor") : gSavedSettings.getColor4("SpeakingColor"); + } + else + { + color = LLUI::sColorsGroup->getColor("FocusBackgroundColor"); + } + + LLRect color_rect = bar_view->getRect(); + color_rect.stretch(-1); + gl_rect_2d(color_rect, color, TRUE); + } + } + } +} + +void LLPanelVoiceDeviceSettings::apply() +{ + std::string s; + if(mCtrlInputDevices) + { + s = mCtrlInputDevices->getValue().asString(); + gSavedSettings.setString("VoiceInputAudioDevice", s); + mInputDevice = s; + } + + if(mCtrlOutputDevices) + { + s = mCtrlOutputDevices->getValue().asString(); + gSavedSettings.setString("VoiceOutputAudioDevice", s); + mOutputDevice = s; + } + + // assume we are being destroyed by closing our embedding window + LLSlider* volume_slider = getChild("mic_volume_slider"); + if(volume_slider) + { + F32 slider_value = (F32)volume_slider->getValue().asReal(); + gSavedSettings.setF32("AudioLevelMic", slider_value); + mMicVolume = slider_value; + } +} + +void LLPanelVoiceDeviceSettings::cancel() +{ + gSavedSettings.setString("VoiceInputAudioDevice", mInputDevice); + gSavedSettings.setString("VoiceOutputAudioDevice", mOutputDevice); + + if(mCtrlInputDevices) + mCtrlInputDevices->setValue(mInputDevice); + + if(mCtrlOutputDevices) + mCtrlOutputDevices->setValue(mOutputDevice); + + gSavedSettings.setF32("AudioLevelMic", mMicVolume); + LLSlider* volume_slider = getChild("mic_volume_slider"); + if(volume_slider) + { + volume_slider->setValue(mMicVolume); + } +} + +void LLPanelVoiceDeviceSettings::refresh() +{ + //grab current volume + LLSlider* volume_slider = getChild("mic_volume_slider"); + // set mic volume tuning slider based on last mic volume setting + F32 current_volume = (F32)volume_slider->getValue().asReal(); + LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume); + + // Fill in popup menus + bool device_settings_available = LLVoiceClient::getInstance()->deviceSettingsAvailable(); + + if (mCtrlInputDevices) + { + mCtrlInputDevices->setEnabled(device_settings_available); + } + + if (mCtrlOutputDevices) + { + mCtrlOutputDevices->setEnabled(device_settings_available); + } + + getChild("mic_volume_slider")->setEnabled(device_settings_available); + + if(!device_settings_available) + { + // The combo boxes are disabled, since we can't get the device settings from the daemon just now. + // Put the currently set default (ONLY) in the box, and select it. + if(mCtrlInputDevices) + { + mCtrlInputDevices->removeall(); + mCtrlInputDevices->add(getLocalizedDeviceName(mInputDevice), mInputDevice, ADD_BOTTOM); + mCtrlInputDevices->setValue(mInputDevice); + } + if(mCtrlOutputDevices) + { + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->add(getLocalizedDeviceName(mOutputDevice), mOutputDevice, ADD_BOTTOM); + mCtrlOutputDevices->setValue(mOutputDevice); + } + mDevicesUpdated = FALSE; + } + else if (!mDevicesUpdated) + { + LLVoiceDeviceList::const_iterator iter; + + if(mCtrlInputDevices) + { + mCtrlInputDevices->removeall(); + mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + + for(iter=LLVoiceClient::getInstance()->getCaptureDevices().begin(); + iter != LLVoiceClient::getInstance()->getCaptureDevices().end(); + iter++) + { + mCtrlInputDevices->add(getLocalizedDeviceName(*iter), *iter, ADD_BOTTOM); + } + + // Fix invalid input audio device preference. + if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE)) + { + mCtrlInputDevices->setValue(DEFAULT_DEVICE); + gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE); + mInputDevice = DEFAULT_DEVICE; + } + } + + if(mCtrlOutputDevices) + { + mCtrlOutputDevices->removeall(); + mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM); + + for(iter= LLVoiceClient::getInstance()->getRenderDevices().begin(); + iter != LLVoiceClient::getInstance()->getRenderDevices().end(); iter++) + { + mCtrlOutputDevices->add(getLocalizedDeviceName(*iter), *iter, ADD_BOTTOM); + } + + // Fix invalid output audio device preference. + if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE)) + { + mCtrlOutputDevices->setValue(DEFAULT_DEVICE); + gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE); + mOutputDevice = DEFAULT_DEVICE; + } + } + mDevicesUpdated = TRUE; + } +} + +void LLPanelVoiceDeviceSettings::initialize() +{ + mInputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + mOutputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + mMicVolume = gSavedSettings.getF32("AudioLevelMic"); + mDevicesUpdated = FALSE; + + // ask for new device enumeration + LLVoiceClient::getInstance()->refreshDeviceLists(); + + // put voice client in "tuning" mode + if (mUseTuningMode) + { + LLVoiceClient::getInstance()->tuningStart(); + LLVoiceChannel::suspend(); + } +} + +void LLPanelVoiceDeviceSettings::cleanup() +{ + if (mUseTuningMode) + { + LLVoiceClient::getInstance()->tuningStop(); + LLVoiceChannel::resume(); + } +} + +// returns English name if no translation found +std::string LLPanelVoiceDeviceSettings::getLocalizedDeviceName(const std::string& en_dev_name) +{ + std::map::const_iterator it = mLocalizedDeviceNames.find(en_dev_name); + return it != mLocalizedDeviceNames.end() ? it->second : en_dev_name; +} + +void LLPanelVoiceDeviceSettings::onCommitInputDevice() +{ + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->setCaptureDevice( + mCtrlInputDevices->getValue().asString()); + } +} + +void LLPanelVoiceDeviceSettings::onCommitOutputDevice() +{ + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->setRenderDevice( + mCtrlInputDevices->getValue().asString()); + } +} diff --git a/indra/newview/llpanelvoicedevicesettings.h b/indra/newview/llpanelvoicedevicesettings.h new file mode 100644 index 000000000..4b4e55b47 --- /dev/null +++ b/indra/newview/llpanelvoicedevicesettings.h @@ -0,0 +1,67 @@ +/** + * @file llpanelvoicedevicesettings.h + * @author Richard Nelson + * @brief Voice communication set-up wizard + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLPANELVOICEDEVICESETTINGS_H +#define LL_LLPANELVOICEDEVICESETTINGS_H + +#include "llpanel.h" + +class LLPanelVoiceDeviceSettings : public LLPanel +{ +public: + LLPanelVoiceDeviceSettings(); + ~LLPanelVoiceDeviceSettings(); + + /*virtual*/ void draw(); + /*virtual*/ BOOL postBuild(); + void apply(); + void cancel(); + void refresh(); + void initialize(); + void cleanup(); + + /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); + + void setUseTuningMode(bool use) { mUseTuningMode = use; }; + +protected: + std::string getLocalizedDeviceName(const std::string& en_dev_name); + + void onCommitInputDevice(); + void onCommitOutputDevice(); + + F32 mMicVolume; + std::string mInputDevice; + std::string mOutputDevice; + class LLComboBox *mCtrlInputDevices; + class LLComboBox *mCtrlOutputDevices; + BOOL mDevicesUpdated; + bool mUseTuningMode; + std::map mLocalizedDeviceNames; +}; + +#endif // LL_LLPANELVOICEDEVICESETTINGS_H diff --git a/indra/newview/llpanelvoiceeffect.cpp b/indra/newview/llpanelvoiceeffect.cpp new file mode 100755 index 000000000..f80a58a93 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.cpp @@ -0,0 +1,163 @@ +/** + * @file llpanelvoiceeffect.cpp + * @author Aimee + * @brief Panel to select Voice Morphs. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llpanelvoiceeffect.h" + +#include "llcombobox.h" +#include "llfloatervoiceeffect.h" +#include "lltrans.h" + +LLPanelVoiceEffect::LLPanelVoiceEffect() + : mVoiceEffectCombo(NULL) +{ + mCommitCallbackRegistrar.add("Voice.CommitVoiceEffect", boost::bind(&LLPanelVoiceEffect::onCommitVoiceEffect, this)); +} + +LLPanelVoiceEffect::~LLPanelVoiceEffect() +{ + /* + LLView* combo_list_view = mVoiceEffectCombo->getChildView("ComboBox"); + LLTransientFloaterMgr::getInstance()->removeControlView(combo_list_view); + */ + + if(LLVoiceClient::instanceExists()) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->removeObserver(this); + } + } +} + +// virtual +BOOL LLPanelVoiceEffect::postBuild() +{ + mVoiceEffectCombo = getChild("voice_effect"); + + /* + // Need to tell LLTransientFloaterMgr about the combo list, otherwise it can't + // be clicked while in a docked floater as it extends outside the floater area. + LLView* combo_list_view = mVoiceEffectCombo->getChildView("ComboBox"); + LLTransientFloaterMgr::getInstance()->addControlView(combo_list_view); + */ + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->addObserver(this); + } + + update(true); + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////// +/// PRIVATE SECTION +////////////////////////////////////////////////////////////////////////// + +void LLPanelVoiceEffect::onCommitVoiceEffect() +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) + { + mVoiceEffectCombo->setEnabled(false); + return; + } + + LLSD value = mVoiceEffectCombo->getValue(); + if (value.asInteger() == PREVIEW_VOICE_EFFECTS) + { + // Open the Voice Morph preview floater + LLFloaterVoiceEffect::showInstance(); + } + else if (value.asInteger() == GET_VOICE_EFFECTS) + { + // Open the voice morphing info web page + LLWeb::loadURL(LLTrans::getString("voice_morphing_url")); + } + else + { + effect_interface->setVoiceEffect(value.asUUID()); + } + + mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect()); +} + +// virtual +void LLPanelVoiceEffect::onVoiceEffectChanged(bool effect_list_updated) +{ + update(effect_list_updated); +} + +void LLPanelVoiceEffect::update(bool list_updated) +{ + if (mVoiceEffectCombo) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) return; + if (list_updated) + { + // Add the default "No Voice Morph" entry. + mVoiceEffectCombo->removeall(); + mVoiceEffectCombo->add(getString("no_voice_effect"), LLUUID::null); + mVoiceEffectCombo->addSeparator(); + + // Add entries for each Voice Morph. + const voice_effect_list_t& effect_list = effect_interface->getVoiceEffectList(); + if (!effect_list.empty()) + { + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + mVoiceEffectCombo->add(it->first, it->second, ADD_BOTTOM); + } + + mVoiceEffectCombo->addSeparator(); + } + + // Add the fixed entries to go to the preview floater or marketing page. + mVoiceEffectCombo->add(getString("preview_voice_effects"), PREVIEW_VOICE_EFFECTS); + mVoiceEffectCombo->add(getString("get_voice_effects"), GET_VOICE_EFFECTS); + } + + if (effect_interface && LLVoiceClient::instance().isVoiceWorking()) + { + // Select the current Voice Morph. + mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect()); + mVoiceEffectCombo->setEnabled(true); + } + else + { + // If voice isn't working or Voice Effects are not supported disable the control. + mVoiceEffectCombo->setValue(LLUUID::null); + mVoiceEffectCombo->setEnabled(false); + } + } +} diff --git a/indra/newview/llpanelvoiceeffect.h b/indra/newview/llpanelvoiceeffect.h new file mode 100755 index 000000000..a6cbac181 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.h @@ -0,0 +1,68 @@ +/** + * @file llpanelvoiceeffect.h + * @author Aimee + * @brief Panel to select Voice Effects. + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_PANELVOICEEFFECT_H +#define LL_PANELVOICEEFFECT_H + +#include "llpanel.h" +#include "llvoiceclient.h" +#include "lllayoutstack.h" + +class LLComboBox; + +class LLPanelVoiceEffect + : public LLLayoutPanel + , public LLVoiceEffectObserver +{ +public: + LOG_CLASS(LLPanelVoiceEffect); + + LLPanelVoiceEffect(); + virtual ~LLPanelVoiceEffect(); + + virtual BOOL postBuild(); + +private: + void onCommitVoiceEffect(); + void update(bool list_updated); + + /// Called by voice effect provider when voice effect list is changed. + virtual void onVoiceEffectChanged(bool effect_list_updated); + + // Fixed entries in the Voice Morph list + typedef enum e_voice_effect_combo_items + { + NO_VOICE_EFFECT = 0, + PREVIEW_VOICE_EFFECTS = 1, + GET_VOICE_EFFECTS = 2 + } EVoiceEffectComboItems; + + LLComboBox* mVoiceEffectCombo; +}; + + +#endif //LL_PANELVOICEEFFECT_H diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index b8d97ef17..adf752e31 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -3,10 +3,9 @@ * @brief Object editing (position, scale, etc.) in the tools floater * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -117,16 +116,16 @@ BOOL LLPanelVolume::postBuild() childSetCommitCallback("Light Checkbox Ctrl",onCommitIsLight,this); LLColorSwatchCtrl* LightColorSwatch = getChild("colorswatch"); if(LightColorSwatch){ - LightColorSwatch->setOnCancelCallback(onLightCancelColor); - LightColorSwatch->setOnSelectCallback(onLightSelectColor); + LightColorSwatch->setOnCancelCallback(boost::bind(&LLPanelVolume::onLightCancelColor, this, _2)); + LightColorSwatch->setOnSelectCallback(boost::bind(&LLPanelVolume::onLightSelectColor, this, _2)); childSetCommitCallback("colorswatch",onCommitLight,this); } LLTextureCtrl* LightTexPicker = getChild("light texture control"); if (LightTexPicker) { - LightTexPicker->setOnCancelCallback(onLightCancelTexture); - LightTexPicker->setOnSelectCallback(onLightSelectTexture); + LightTexPicker->setOnCancelCallback(boost::bind(&LLPanelVolume::onLightCancelTexture, this, _2)); + LightTexPicker->setOnSelectCallback(boost::bind(&LLPanelVolume::onLightSelectTexture, this, _2)); childSetCommitCallback("light texture control", onCommitLight, this); } @@ -650,31 +649,28 @@ void LLPanelVolume::refreshCost() } } -void LLPanelVolume::onLightCancelColor(LLUICtrl* ctrl, void* userdata) +void LLPanelVolume::onLightCancelColor(const LLSD& data) { - LLPanelVolume* self = (LLPanelVolume*) userdata; - LLColorSwatchCtrl* LightColorSwatch = self->getChild("colorswatch"); + LLColorSwatchCtrl* LightColorSwatch = getChild("colorswatch"); if(LightColorSwatch) { - LightColorSwatch->setColor(self->mLightSavedColor); + LightColorSwatch->setColor(mLightSavedColor); } - onLightSelectColor(NULL, userdata); + onLightSelectColor(data); } -void LLPanelVolume::onLightCancelTexture(LLUICtrl* ctrl, void* userdata) +void LLPanelVolume::onLightCancelTexture(const LLSD& data) { - LLPanelVolume* self = (LLPanelVolume*) userdata; - LLTextureCtrl* LightTextureCtrl = self->getChild("light texture control"); + LLTextureCtrl* LightTextureCtrl = getChild("light texture control"); if (LightTextureCtrl) { - LightTextureCtrl->setImageAssetID(self->mLightSavedTexture); + LightTextureCtrl->setImageAssetID(mLightSavedTexture); } } -void LLPanelVolume::onLightSelectColor(LLUICtrl* ctrl, void* userdata) +void LLPanelVolume::onLightSelectColor(const LLSD& data) { - LLPanelVolume* self = (LLPanelVolume*) userdata; - LLViewerObject* objectp = self->mObject; + LLViewerObject* objectp = mObject; if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME)) { return; @@ -682,32 +678,31 @@ void LLPanelVolume::onLightSelectColor(LLUICtrl* ctrl, void* userdata) LLVOVolume *volobjp = (LLVOVolume *)objectp; - LLColorSwatchCtrl* LightColorSwatch = self->getChild("colorswatch"); + LLColorSwatchCtrl* LightColorSwatch = getChild("colorswatch"); if(LightColorSwatch) { LLColor4 clr = LightColorSwatch->get(); LLColor3 clr3( clr ); volobjp->setLightColor(clr3); - self->mLightSavedColor = clr; + mLightSavedColor = clr; } } -void LLPanelVolume::onLightSelectTexture(LLUICtrl* ctrl, void* userdata) +void LLPanelVolume::onLightSelectTexture(const LLSD& data) { - LLPanelVolume* self = (LLPanelVolume*) userdata; - LLVOVolume *volobjp = (LLVOVolume*)self->mObject.get(); - if (!volobjp || (volobjp->getPCode() != LL_PCODE_VOLUME)) + if (mObject.isNull() || (mObject->getPCode() != LL_PCODE_VOLUME)) { return; } + LLVOVolume *volobjp = (LLVOVolume *) mObject.get(); - LLTextureCtrl* LightTextureCtrl = self->getChild("light texture control"); + LLTextureCtrl* LightTextureCtrl = getChild("light texture control"); if(LightTextureCtrl) { LLUUID id = LightTextureCtrl->getImageAssetID(); volobjp->setLightTextureID(id); - self->mLightSavedTexture = id; + mLightSavedTexture = id; } } diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h index f0acff99b..b82fc3916 100644 --- a/indra/newview/llpanelvolume.h +++ b/indra/newview/llpanelvolume.h @@ -3,10 +3,9 @@ * @brief Object editing (position, scale, etc.) in the tools floater * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -45,7 +44,6 @@ class LLUICtrl; class LLButton; class LLViewerObject; class LLComboBox; -class LLPanelObjectInventory; class LLColorSwatchCtrl; class LLPanelVolume : public LLPanel @@ -72,11 +70,11 @@ public: static void onCommitFlexible( LLUICtrl* ctrl, void* userdata); static void onCommitPhysicsParam( LLUICtrl* ctrl, void* userdata); - static void onLightCancelColor(LLUICtrl* ctrl, void* userdata); - static void onLightSelectColor(LLUICtrl* ctrl, void* userdata); + void onLightCancelColor(const LLSD& data); + void onLightSelectColor(const LLSD& data); - static void onLightCancelTexture(LLUICtrl* ctrl, void* userdata); - static void onLightSelectTexture(LLUICtrl* ctrl, void* userdata); + void onLightCancelTexture(const LLSD& data); + void onLightSelectTexture(const LLSD& data); protected: diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp new file mode 100644 index 000000000..ee27ead48 --- /dev/null +++ b/indra/newview/llparticipantlist.cpp @@ -0,0 +1,597 @@ +/** + * @file llparticipantlist.cpp + * @brief LLParticipantList intended to update view(LLAvatarList) according to incoming messages + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llavataractions.h" +#include "llagent.h" +#include "llmutelist.h" +#include "llparticipantlist.h" +#include "llscrolllistctrl.h" +#include "llspeakers.h" +#include "llviewerwindow.h" +#include "llvoiceclient.h" +#include "llworld.h" // Edit: For ghost detection +// [RLVa:KB] +#include "rlvhandler.h" +// [/RLVa:KB] + +LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, + bool show_text_chatters) : + mSpeakerMgr(data_source), + mAvatarList(NULL), + mShowTextChatters(show_text_chatters), + mValidateSpeakerCallback(NULL) +{ + setMouseOpaque(false); + + /* Singu TODO: Avaline? + mAvalineUpdater = new LLAvalineUpdater(boost::bind(&LLParticipantList::onAvalineCallerFound, this, _1), + boost::bind(&LLParticipantList::onAvalineCallerRemoved, this, _1));*/ + + mSpeakerAddListener = new SpeakerAddListener(*this); + mSpeakerRemoveListener = new SpeakerRemoveListener(*this); + mSpeakerClearListener = new SpeakerClearListener(*this); + //mSpeakerModeratorListener = new SpeakerModeratorUpdateListener(*this); + mSpeakerMuteListener = new SpeakerMuteListener(*this); + + mSpeakerMgr->addListener(mSpeakerAddListener, "add"); + mSpeakerMgr->addListener(mSpeakerRemoveListener, "remove"); + mSpeakerMgr->addListener(mSpeakerClearListener, "clear"); + //mSpeakerMgr->addListener(mSpeakerModeratorListener, "update_moderator"); +} + +BOOL LLParticipantList::postBuild() +{ + mAvatarList = getChild("speakers_list"); + + mAvatarList->sortByColumn(gSavedSettings.getString("FloaterActiveSpeakersSortColumn"), gSavedSettings.getBOOL("FloaterActiveSpeakersSortAscending")); + mAvatarList->setDoubleClickCallback(boost::bind(&LLParticipantList::onAvatarListDoubleClicked, this)); + mAvatarList->setCommitOnSelectionChange(true); + mAvatarList->setCommitCallback(boost::bind(&LLParticipantList::handleSpeakerSelect, this)); + mAvatarList->setSortChangedCallback(boost::bind(&LLParticipantList::onSortChanged, this)); + + if (LLUICtrl* ctrl = findChild("mute_text_btn")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::toggleMuteText, this)); + if (LLUICtrl* ctrl = findChild("mute_btn")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::toggleMuteVoice, this)); + if (LLUICtrl* ctrl = findChild("speaker_volume")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::onVolumeChange, this, _2)); + if (LLUICtrl* ctrl = findChild("profile_btn")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::onClickProfile, this)); + if (LLUICtrl* ctrl = findChild("moderator_allow_voice")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::moderateVoiceParticipant, this, _2)); + if (LLUICtrl* ctrl = findChild("moderator_allow_text")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::toggleAllowTextChat, this, _2)); + if (LLUICtrl* ctrl = findChild("moderator_mode")) + ctrl->setCommitCallback(boost::bind(&LLParticipantList::moderateVoiceAllParticipants, this, _2)); + + // update speaker UI + handleSpeakerSelect(); + + return true; +} + +LLParticipantList::~LLParticipantList() +{ + /* Singu TODO? + mAvatarListDoubleClickConnection.disconnect(); + mAvatarListRefreshConnection.disconnect(); + mAvatarListReturnConnection.disconnect(); + mAvatarListToggleIconsConnection.disconnect(); + + // It is possible Participant List will be re-created from LLCallFloater::onCurrentChannelChanged() + // See ticket EXT-3427 + // hide menu before deleting it to stop enable and check handlers from triggering. + if(mParticipantListMenu && !LLApp::isExiting()) + { + mParticipantListMenu->hide(); + } + + if (mParticipantListMenu) + { + delete mParticipantListMenu; + mParticipantListMenu = NULL; + } + + mAvatarList->setContextMenu(NULL); + mAvatarList->setComparator(NULL); + + delete mAvalineUpdater; + */ +} + +/*void LLParticipantList::setSpeakingIndicatorsVisible(BOOL visible) +{ + mAvatarList->setSpeakingIndicatorsVisible(visible); +}*/ + + +void LLParticipantList::onAvatarListDoubleClicked() +{ + LLAvatarActions::startIM(mAvatarList->getValue().asUUID()); +} + +void LLParticipantList::handleSpeakerSelect() +{ + const LLUUID& speaker_id = mAvatarList->getValue().asUUID(); + LLPointer selected_speakerp = mSpeakerMgr->findSpeaker(speaker_id); + if (speaker_id.isNull() || selected_speakerp.isNull()) + { + // Disable normal controls + if (LLView* view = findChild("mute_btn")) + view->setEnabled(false); + if (LLView* view = findChild("mute_text_btn")) + view->setEnabled(false); + if (LLView* view = findChild("speaker_volume")) + view->setEnabled(false); + // Hide moderator controls + if (LLView* view = findChild("moderation_mode_panel")) + view->setVisible(false); + if (LLView* view = findChild("moderator_controls")) + view->setVisible(false); + // Clear the name + if (LLUICtrl* ctrl = findChild("resident_name")) + ctrl->setValue(LLStringUtil::null); + return; + } + + mSpeakerMuteListener->clearDispatchers(); + + bool valid_speaker = selected_speakerp->mType == LLSpeaker::SPEAKER_AGENT || selected_speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL; + bool can_mute = valid_speaker && LLAvatarActions::canBlock(speaker_id); + bool voice_enabled = valid_speaker && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id); + // since setting these values is delayed by a round trip to the Vivox servers + // update them only when selecting a new speaker or + // asynchronously when an update arrives + if (LLUICtrl* ctrl = findChild("moderator_allow_voice")) + { + ctrl->setEnabled(voice_enabled); + ctrl->setValue(!selected_speakerp->mModeratorMutedVoice); + } + if (LLUICtrl* ctrl = findChild("moderator_allow_text")) + { + ctrl->setEnabled(true); + ctrl->setValue(!selected_speakerp->mModeratorMutedText); + } + if (LLView* view = findChild("moderator_controls_label")) + { + view->setEnabled(true); + } + // update UI for selected participant + if (LLUICtrl* ctrl = findChild("mute_btn")) + { + ctrl->setValue(LLAvatarActions::isVoiceMuted(speaker_id)); + ctrl->setEnabled(can_mute && voice_enabled); + } + if (LLUICtrl* ctrl = findChild("mute_text_btn")) + { + ctrl->setValue(LLAvatarActions::isBlocked(speaker_id)); + ctrl->setEnabled(can_mute); + } + if (LLUICtrl* ctrl = findChild("speaker_volume")) + { + // Singu Note: Allow modifying own voice volume during a voice session (Which is valued at half of what it should be) + ctrl->setValue(gAgentID == speaker_id ? gSavedSettings.getF32("AudioLevelMic")/2 : LLVoiceClient::getInstance()->getUserVolume(speaker_id)); + ctrl->setEnabled(valid_speaker && voice_enabled); + } + if (LLView* view = findChild("profile_btn")) + { + view->setEnabled(selected_speakerp->mType != LLSpeaker::SPEAKER_EXTERNAL); + } + // show selected user name in large font + if (LLUICtrl* ctrl = findChild("resident_name")) + { + ctrl->setValue(selected_speakerp->mDisplayName); + } + selected_speakerp->addListener(mSpeakerMuteListener); + + //update moderator capabilities + LLPointer self_speakerp = mSpeakerMgr->findSpeaker(gAgentID); + if (self_speakerp.isNull()) return; + + if (LLView* view = findChild("moderation_mode_panel")) + { + view->setVisible(self_speakerp->mIsModerator && mSpeakerMgr->isVoiceActive()); + } + if (LLView* view = findChild("moderator_controls")) + { + view->setVisible(self_speakerp->mIsModerator); + } +} + +void LLParticipantList::refreshSpeakers() +{ + // store off current selection and scroll state to preserve across list rebuilds + const S32 scroll_pos = mAvatarList->getScrollInterface()->getScrollPos(); + + // decide whether it's ok to resort the list then update the speaker manager appropriately. + // rapid resorting by activity makes it hard to interact with speakers in the list + // so we freeze the sorting while the user appears to be interacting with the control. + // we assume this is the case whenever the mouse pointer is within the active speaker + // panel and hasn't been motionless for more than a few seconds. see DEV-6655 -MG + LLRect screen_rect; + localRectToScreen(getLocalRect(), &screen_rect); + mSpeakerMgr->update(!(screen_rect.pointInRect(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY()) && gMouseIdleTimer.getElapsedTimeF32() < 5.f)); + + std::vector items = mAvatarList->getAllData(); + for (std::vector::iterator item_it = items.begin(); item_it != items.end(); ++item_it) + { + LLScrollListItem* itemp = (*item_it); + LLPointer speakerp = mSpeakerMgr->findSpeaker(itemp->getUUID()); + if (speakerp.isNull()) continue; + + if (LLScrollListCell* icon_cell = itemp->getColumn(0)) + { + if (speakerp->mStatus == LLSpeaker::STATUS_MUTED) + { + icon_cell->setValue("mute_icon.tga"); + static const LLCachedControl sAscentMutedColor("AscentMutedColor"); + icon_cell->setColor(speakerp->mModeratorMutedVoice ? /*LLColor4::grey*/sAscentMutedColor : LLColor4(1.f, 71.f / 255.f, 71.f / 255.f, 1.f)); + } + else + { + switch(llmin(2, llfloor((speakerp->mSpeechVolume / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f))) + { + case 0: + icon_cell->setValue("icn_active-speakers-dot-lvl0.tga"); + break; + case 1: + icon_cell->setValue("icn_active-speakers-dot-lvl1.tga"); + break; + case 2: + icon_cell->setValue("icn_active-speakers-dot-lvl2.tga"); + break; + } + // non voice speakers have hidden icons, render as transparent + icon_cell->setColor(speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE ? LLColor4::transparent : speakerp->mDotColor); + } + } + // update name column + if (LLScrollListCell* name_cell = itemp->getColumn(1)) + { + if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL) + { + // draw inactive speakers in different color + static const LLCachedControl sSpeakersInactive(gColors, "SpeakersInactive"); + name_cell->setColor(sSpeakersInactive); + } + else + { + // + bool found = mShowTextChatters || speakerp->mID == gAgentID; + const LLWorld::region_list_t& regions = LLWorld::getInstance()->getRegionList(); + for (LLWorld::region_list_t::const_iterator iter = regions.begin(); !found && iter != regions.end(); ++iter) + { + // Are they in this sim? + if (const LLViewerRegion* regionp = *iter) + if (regionp->mMapAvatarIDs.find(speakerp->mID) != -1) + found = true; + } + if (!found) + { + static const LLCachedControl sSpeakersGhost(gColors, "SpeakersGhost"); + name_cell->setColor(sSpeakersGhost); + } + else + // + { + static const LLCachedControl sDefaultListText(gColors, "DefaultListText"); + name_cell->setColor(sDefaultListText); + } + } + + std::string speaker_name = speakerp->mDisplayName.empty() ? LLCacheName::getDefaultName() : speakerp->mDisplayName; + if (speakerp->mIsModerator) + speaker_name += " " + getString("moderator_label"); + name_cell->setValue(speaker_name); + static_cast(name_cell)->setFontStyle(speakerp->mIsModerator ? LLFontGL::BOLD : LLFontGL::NORMAL); + } + // update speaking order column + if (LLScrollListCell* speaking_status_cell = itemp->getColumn(2)) + { + // since we are forced to sort by text, encode sort order as string + // print speaking ordinal in a text-sorting friendly manner + speaking_status_cell->setValue(llformat("%010d", speakerp->mSortIndex)); + } + } + + // we potentially modified the sort order by touching the list items + mAvatarList->setNeedsSort(); + + // keep scroll value stable + mAvatarList->getScrollInterface()->setScrollPos(scroll_pos); +} + +void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t cb) +{ + mValidateSpeakerCallback = cb; +} + +bool LLParticipantList::onAddItemEvent(LLPointer event, const LLSD& userdata) +{ + LLUUID uu_id = event->getValue().asUUID(); + + if (mValidateSpeakerCallback && !mValidateSpeakerCallback(uu_id)) + { + return true; + } + + addAvatarIDExceptAgent(uu_id); + return true; +} + +bool LLParticipantList::onRemoveItemEvent(LLPointer event, const LLSD& userdata) +{ + const S32 pos = mAvatarList->getItemIndex(event->getValue().asUUID()); + if (pos != -1) + { + mAvatarList->deleteSingleItem(pos); + } + return true; +} + +bool LLParticipantList::onClearListEvent(LLPointer event, const LLSD& userdata) +{ + mAvatarList->clearRows(); + return true; +} + +bool LLParticipantList::onSpeakerMuteEvent(LLPointer event, const LLSD& userdata) +{ + LLPointer speakerp = (LLSpeaker*)event->getSource(); + if (speakerp.isNull()) return false; + + // update UI on confirmation of moderator mutes + if (event->getValue().asString() == "voice") + { + childSetValue("moderator_allow_voice", !speakerp->mModeratorMutedVoice); + } + else if (event->getValue().asString() == "text") + { + childSetValue("moderator_allow_text", !speakerp->mModeratorMutedText); + } + return true; +} + +void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) +{ + //if (mExcludeAgent && gAgent.getID() == avatar_id) return; + if (mAvatarList->getItemIndex(avatar_id) != -1) return; + + /* Singu TODO: Avaline? + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(avatar_id); + + if (is_avatar) + { + mAvatarList->getIDs().push_back(avatar_id); + mAvatarList->setDirty(); + } + else + { + std::string display_name = LLVoiceClient::getInstance()->getDisplayName(avatar_id); + mAvatarList->addAvalineItem(avatar_id, mSpeakerMgr->getSessionID(), display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name); + mAvalineUpdater->watchAvalineCaller(avatar_id); + }*/ + adjustParticipant(avatar_id); +} + +void LLParticipantList::adjustParticipant(const LLUUID& speaker_id) +{ + LLPointer speakerp = mSpeakerMgr->findSpeaker(speaker_id); + if (speakerp.isNull()) return; + + LLSD row; + row["id"] = speaker_id; + LLSD& columns = row["columns"]; + columns[0]["column"] = "icon_speaking_status"; + columns[0]["type"] = "icon"; + columns[0]["value"] = "icn_active-speakers-dot-lvl0.tga"; + + const std::string& display_name = LLVoiceClient::getInstance()->getDisplayName(speaker_id); + columns[1]["column"] = "speaker_name"; + columns[1]["type"] = "text"; + columns[1]["value"] = display_name.empty() ? LLCacheName::getDefaultName() : display_name; + + columns[2]["column"] = "speaking_status"; + columns[2]["type"] = "text"; + columns[2]["value"] = llformat("%010d", speakerp->mSortIndex); // print speaking ordinal in a text-sorting friendly manner + mAvatarList->addElement(row); + + // add listener to process moderation changes + speakerp->addListener(mSpeakerMuteListener); +} + +// +// LLParticipantList::SpeakerAddListener +// +bool LLParticipantList::SpeakerAddListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + /** + * We need to filter speaking objects. These objects shouldn't appear in the list + * @see LLFloaterChat::addChat() in llviewermessage.cpp to get detailed call hierarchy + */ + const LLUUID& speaker_id = event->getValue().asUUID(); + LLPointer speaker = mParent.mSpeakerMgr->findSpeaker(speaker_id); + if(speaker.isNull() || speaker->mType == LLSpeaker::SPEAKER_OBJECT) + { + return false; + } + return mParent.onAddItemEvent(event, userdata); +} + +// +// LLParticipantList::SpeakerRemoveListener +// +bool LLParticipantList::SpeakerRemoveListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + return mParent.onRemoveItemEvent(event, userdata); +} + +// +// LLParticipantList::SpeakerClearListener +// +bool LLParticipantList::SpeakerClearListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + return mParent.onClearListEvent(event, userdata); +} + +// +// LLParticipantList::SpeakerMuteListener +// +bool LLParticipantList::SpeakerMuteListener::handleEvent(LLPointer event, const LLSD& userdata) +{ + return mParent.onSpeakerMuteEvent(event, userdata); +} + +// Singu Note: The following functions are actually of the LLParticipantListMenu class, but we haven't married lists with menus yet. +void LLParticipantList::toggleAllowTextChat(const LLSD& userdata) +{ + if (!gAgent.getRegion()) return; + LLIMSpeakerMgr* mgr = dynamic_cast(mSpeakerMgr); + if (mgr) + { + const LLUUID speaker_id = mAvatarList->getValue(); + mgr->toggleAllowTextChat(speaker_id); + } +} + +void LLParticipantList::toggleMute(const LLSD& userdata, U32 flags) +{ + const LLUUID speaker_id = userdata.asUUID(); //mUUIDs.front(); + BOOL is_muted = LLMuteList::getInstance()->isMuted(speaker_id, flags); + std::string name; + + //fill in name using voice client's copy of name cache + LLPointer speakerp = mSpeakerMgr->findSpeaker(speaker_id); + if (speakerp.isNull()) + { + LL_WARNS("Speakers") << "Speaker " << speaker_id << " not found" << llendl; + return; + } + + name = speakerp->mDisplayName; + + LLMute::EType mute_type; + switch (speakerp->mType) + { + case LLSpeaker::SPEAKER_AGENT: + mute_type = LLMute::AGENT; + break; + case LLSpeaker::SPEAKER_OBJECT: + mute_type = LLMute::OBJECT; + break; + case LLSpeaker::SPEAKER_EXTERNAL: + default: + mute_type = LLMute::EXTERNAL; + break; + } + LLMute mute(speaker_id, name, mute_type); + + if (!is_muted) + { + LLMuteList::getInstance()->add(mute, flags); + } + else + { + LLMuteList::getInstance()->remove(mute, flags); + } +} + +void LLParticipantList::toggleMuteText() +{ + toggleMute(mAvatarList->getValue(), LLMute::flagTextChat); +} + +void LLParticipantList::toggleMuteVoice() +{ + toggleMute(mAvatarList->getValue(), LLMute::flagVoiceChat); +} + +void LLParticipantList::moderateVoiceParticipant(const LLSD& param) +{ + if (!gAgent.getRegion()) return; + LLIMSpeakerMgr* mgr = dynamic_cast(mSpeakerMgr); + if (mgr) + { + mgr->moderateVoiceParticipant(mAvatarList->getValue(), param); + } +} + +void LLParticipantList::moderateVoiceAllParticipants(const LLSD& param) +{ + if (!gAgent.getRegion()) return; + // Singu Note: moderateVoiceAllParticipants ends up flipping the boolean passed to it before the actual post + if (LLIMSpeakerMgr* speaker_manager = dynamic_cast(mSpeakerMgr)) + { + if (param.asString() == "unmoderated") + { + speaker_manager->moderateVoiceAllParticipants(true); + } + else if (param.asString() == "moderated") + { + speaker_manager->moderateVoiceAllParticipants(false); + } + } +} + +// Singu Note: The following callbacks are not found upstream +void LLParticipantList::onVolumeChange(const LLSD& param) +{ + // Singu Note: Allow modifying own voice volume during a voice session (Which is valued at half of what it should be) + const LLUUID& speaker_id = mAvatarList->getValue().asUUID(); + if (gAgentID == speaker_id) + { + gSavedSettings.setF32("AudioLevelMic", param.asFloat()*2); + } + else + { + LLVoiceClient::getInstance()->setUserVolume(speaker_id, param.asFloat()); + } +} + +void LLParticipantList::onClickProfile() +{ +// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) return; +// [/RLVa:KB] + LLAvatarActions::showProfile(mAvatarList->getValue().asUUID()); +} + +void LLParticipantList::onSortChanged() +{ + gSavedSettings.setString("FloaterActiveSpeakersSortColumn", mAvatarList->getSortColumnName()); + gSavedSettings.setBOOL("FloaterActiveSpeakersSortAscending", mAvatarList->getSortAscending()); +} + +void LLParticipantList::setVoiceModerationCtrlMode(const bool& moderated_voice) +{ + if (LLUICtrl* voice_moderation_ctrl = findChild("moderation_mode")) + { + voice_moderation_ctrl->setValue(moderated_voice ? "moderated" : "unmoderated"); + } +} + diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h new file mode 100644 index 000000000..1177b778a --- /dev/null +++ b/indra/newview/llparticipantlist.h @@ -0,0 +1,210 @@ +/** + * @file llparticipantlist.h + * @brief LLParticipantList intended to update view(LLAvatarList) according to incoming messages + * + * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_PARTICIPANTLIST_H +#define LL_PARTICIPANTLIST_H + +#include "lllayoutstack.h" + +class LLSpeakerMgr; +class LLScrollListCtrl; +class LLUICtrl; + +class LLParticipantList : public LLLayoutPanel +{ + LOG_CLASS(LLParticipantList); +public: + + typedef boost::function validate_speaker_callback_t; + + LLParticipantList(LLSpeakerMgr* data_source, bool show_text_chatters); + /*virtual*/ BOOL postBuild(); + ~LLParticipantList(); + + /** + * Adds specified avatar ID to the existing list if it is not Agent's ID + * + * @param[in] avatar_id - Avatar UUID to be added into the list + */ + void addAvatarIDExceptAgent(const LLUUID& avatar_id); + + /** + * Refreshes the participant list. + */ + void refreshSpeakers(); + + /** + * Set a callback to be called before adding a speaker. Invalid speakers will not be added. + * + * If the callback is unset all speakers are considered as valid. + * + * @see onAddItemEvent() + */ + void setValidateSpeakerCallback(validate_speaker_callback_t cb); + + void setVoiceModerationCtrlMode(const bool& moderated_voice); + +protected: + /** + * LLSpeakerMgr event handlers + */ + bool onAddItemEvent(LLPointer event, const LLSD& userdata); + bool onRemoveItemEvent(LLPointer event, const LLSD& userdata); + bool onClearListEvent(LLPointer event, const LLSD& userdata); + //bool onModeratorUpdateEvent(LLPointer event, const LLSD& userdata); + bool onSpeakerMuteEvent(LLPointer event, const LLSD& userdata); + + /** + * List of listeners implementing LLOldEvents::LLSimpleListener. + * There is no way to handle all the events in one listener as LLSpeakerMgr registers + * listeners in such a way that one listener can handle only one type of event + **/ + class BaseSpeakerListener : public LLOldEvents::LLSimpleListener + { + public: + BaseSpeakerListener(LLParticipantList& parent) : mParent(parent) {} + protected: + LLParticipantList& mParent; + }; + + class SpeakerAddListener : public BaseSpeakerListener + { + public: + SpeakerAddListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + + class SpeakerRemoveListener : public BaseSpeakerListener + { + public: + SpeakerRemoveListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + + class SpeakerClearListener : public BaseSpeakerListener + { + public: + SpeakerClearListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + + /*class SpeakerModeratorUpdateListener : public BaseSpeakerListener + { + public: + SpeakerModeratorUpdateListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + *virtual* bool handleEvent(LLPointer event, const LLSD& userdata); + };*/ + + class SpeakerMuteListener : public BaseSpeakerListener + { + public: + SpeakerMuteListener(LLParticipantList& parent) : BaseSpeakerListener(parent) {} + + /*virtual*/ bool handleEvent(LLPointer event, const LLSD& userdata); + }; + + /** + * Menu used in the participant list. + class LLParticipantListMenu : public LLListContextMenu + { + */ + private: + void toggleAllowTextChat(const LLSD& userdata); + void toggleMute(const LLSD& userdata, U32 flags); + void toggleMuteText(); + void toggleMuteVoice(); + + /** + * Processes Voice moderation menu items. + * + * It calls either moderateVoiceParticipant() or moderateVoiceParticipant() depend on + * passed parameter. + * + * @param userdata can be "selected" or "others". + * + * @see moderateVoiceParticipant() + * @see moderateVoiceAllParticipants() + */ + void moderateVoice(const LLSD& userdata); + + /** + * Mutes/Unmutes avatar for current group voice chat. + * + * It only marks avatar as muted for session and does not use local Agent's Block list. + * It does not mute Agent itself. + * + * @param[in] avatar_id UUID of avatar to be processed + * @param[in] unmute if true - specified avatar will be muted, otherwise - unmuted. + * + * @see moderateVoiceAllParticipants() + */ + void moderateVoiceParticipant(const LLSD& param); + + /** + * Mutes/Unmutes all avatars for current group voice chat. + * + * It only marks avatars as muted for session and does not use local Agent's Block list. + * + * @param[in] unmute if true - avatars will be muted, otherwise - unmuted. + * + * @see moderateVoiceParticipant() + */ + void moderateVoiceAllParticipants(const LLSD& param); + + //static void confirmMuteAllCallback(const LLSD& notification, const LLSD& response); + //}; + +private: + void onAvatarListDoubleClicked(); + + /** + * Adjusts passed participant to work properly. + * + * Adds SpeakerMuteListener to process moderation actions. + */ + void adjustParticipant(const LLUUID& speaker_id); + + // Singu Note: The following callbacks are not found upstream + void handleSpeakerSelect(); + void onClickProfile(); + void onSortChanged(); + void onVolumeChange(const LLSD& param); + + LLSpeakerMgr* mSpeakerMgr; + LLScrollListCtrl* mAvatarList; + bool mShowTextChatters; + + LLPointer mSpeakerAddListener; + LLPointer mSpeakerRemoveListener; + LLPointer mSpeakerClearListener; + //LLPointer mSpeakerModeratorListener; + LLPointer mSpeakerMuteListener; + + validate_speaker_callback_t mValidateSpeakerCallback; +}; + +#endif // LL_PARTICIPANTLIST_H + diff --git a/indra/newview/llpathfindingmanager.cpp b/indra/newview/llpathfindingmanager.cpp index 783c728c0..7e5bec546 100644 --- a/indra/newview/llpathfindingmanager.cpp +++ b/indra/newview/llpathfindingmanager.cpp @@ -165,6 +165,7 @@ public: /*virtual*/ void result(const LLSD &pContent); /*virtual*/ void error(U32 pStatus, const std::string& pReason); + /*virtual*/ bool followRedir(void) const { return true; } /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return agentStateResponder_timeout; } /*virtual*/ char const* getName(void) const { return "AgentStateResponder"; } diff --git a/indra/newview/llprefsvoice.cpp b/indra/newview/llprefsvoice.cpp index b009dff6d..b2f4342d4 100644 --- a/indra/newview/llprefsvoice.cpp +++ b/indra/newview/llprefsvoice.cpp @@ -37,11 +37,10 @@ #include "floatervoicelicense.h" #include "llcheckboxctrl.h" -#include "llfloatervoicedevicesettings.h" #include "llfocusmgr.h" #include "llkeyboard.h" #include "llmodaldialog.h" -#include "llviewercontrol.h" +#include "llpanelvoicedevicesettings.h" #include "lluictrlfactory.h" @@ -97,12 +96,22 @@ void LLVoiceSetKeyDialog::onCancel(void* user_data) self->close(); } +namespace +{ + void* createDevicePanel(void*) + { + return new LLPanelVoiceDeviceSettings; + } +} + //-------------------------------------------------------------------- //LLPrefsVoice LLPrefsVoice::LLPrefsVoice() : LLPanel(std::string("Voice Chat Panel")) -{ - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_voice.xml"); +{ + mFactoryMap["device_settings_panel"] = LLCallbackMap(createDevicePanel, NULL); + + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_voice.xml", &getFactoryMap()); } LLPrefsVoice::~LLPrefsVoice() @@ -114,7 +123,6 @@ BOOL LLPrefsVoice::postBuild() childSetCommitCallback("enable_voice_check", onCommitEnableVoiceChat, this); childSetAction("set_voice_hotkey_button", onClickSetKey, this); childSetAction("set_voice_middlemouse_button", onClickSetMiddleMouse, this); - childSetAction("device_settings_btn", onClickVoiceDeviceSettings, this); BOOL voice_disabled = gSavedSettings.getBOOL("CmdLineDisableVoice"); childSetVisible("voice_unavailable", voice_disabled); @@ -132,6 +140,8 @@ BOOL LLPrefsVoice::postBuild() childSetValue("ear_location", gSavedSettings.getS32("VoiceEarLocation")); childSetValue("enable_lip_sync_check", gSavedSettings.getBOOL("LipSyncEnabled")); + gSavedSettings.getControl("ShowDeviceSettings")->getSignal()->connect(boost::bind(&LLPanel::childSetVisible, this, "device_settings_panel", _2)); // Singu TODO: visibility_control + return TRUE; } @@ -144,8 +154,7 @@ void LLPrefsVoice::apply() gSavedSettings.setS32("VoiceEarLocation", childGetValue("ear_location")); gSavedSettings.setBOOL("LipSyncEnabled", childGetValue("enable_lip_sync_check")); - LLFloaterVoiceDeviceSettings* voice_device_settings = LLFloaterVoiceDeviceSettings::getInstance(); - if(voice_device_settings) + if (LLPanelVoiceDeviceSettings* voice_device_settings = getChild("device_settings_panel")) { voice_device_settings->apply(); } @@ -165,8 +174,7 @@ void LLPrefsVoice::apply() void LLPrefsVoice::cancel() { - LLFloaterVoiceDeviceSettings* voice_device_settings = LLFloaterVoiceDeviceSettings::getInstance(); - if(voice_device_settings) + if (LLPanelVoiceDeviceSettings* voice_device_settings = getChild("device_settings_panel")) { voice_device_settings->cancel(); } @@ -186,7 +194,6 @@ void LLPrefsVoice::onCommitEnableVoiceChat(LLUICtrl* ctrl, void* user_data) bool enable = enable_voice_chat->getValue(); self->childSetEnabled("modifier_combo", enable); - //self->childSetEnabled("friends_only_check", enable); self->childSetEnabled("push_to_talk_label", enable); self->childSetEnabled("voice_call_friends_only_check", enable); self->childSetEnabled("auto_disengage_mic_check", enable); @@ -196,6 +203,7 @@ void LLPrefsVoice::onCommitEnableVoiceChat(LLUICtrl* ctrl, void* user_data) self->childSetEnabled("set_voice_hotkey_button", enable); self->childSetEnabled("set_voice_middlemouse_button", enable); self->childSetEnabled("device_settings_btn", enable); + self->childSetEnabled("device_settings_panel", enable); } //static @@ -213,15 +221,3 @@ void LLPrefsVoice::onClickSetMiddleMouse(void* user_data) self->childSetValue("modifier_combo", "MiddleMouse"); } -//static -void LLPrefsVoice::onClickVoiceDeviceSettings(void* user_data) -{ - LLPrefsVoice* voice_prefs = (LLPrefsVoice*)user_data; - LLFloaterVoiceDeviceSettings* device_settings_floater = LLFloaterVoiceDeviceSettings::showInstance(); - LLFloater* parent_floater = gFloaterView->getParentFloater(voice_prefs); - if(parent_floater) - { - parent_floater->addDependentFloater(device_settings_floater, FALSE); - } -} - diff --git a/indra/newview/llprefsvoice.h b/indra/newview/llprefsvoice.h index 49f603595..a1cd8c933 100644 --- a/indra/newview/llprefsvoice.h +++ b/indra/newview/llprefsvoice.h @@ -52,7 +52,6 @@ private: static void onCommitEnableVoiceChat(LLUICtrl* ctrl, void* user_data); static void onClickSetKey(void* user_data); static void onClickSetMiddleMouse(void* user_data); - static void onClickVoiceDeviceSettings(void* user_data); }; #endif // LLPREFSVOICE_H diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index c8b3a4b1f..de4598d15 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -54,6 +54,7 @@ #include "llagent.h" #include "llvoavatarself.h" #include "llselectmgr.h" +#include "lltrans.h" #include "llviewerinventory.h" #include "llviewerassettype.h" @@ -550,7 +551,7 @@ void LLPreview::handleReshape(const LLRect& new_rect, bool by_user) // LLMultiPreview // -LLMultiPreview::LLMultiPreview(const LLRect& rect) : LLMultiFloater(std::string("Preview"), rect) +LLMultiPreview::LLMultiPreview(const LLRect& rect) : LLMultiFloater(LLTrans::getString("MultiPreviewTitle"), rect) { setCanResize(TRUE); } @@ -558,7 +559,7 @@ LLMultiPreview::LLMultiPreview(const LLRect& rect) : LLMultiFloater(std::string( void LLMultiPreview::open() /*Flawfinder: ignore*/ { LLMultiFloater::open(); /*Flawfinder: ignore*/ - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); + LLPreview* frontmost_preview = dynamic_cast(mTabContainer->getCurrentPanel()); if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { frontmost_preview->loadAsset(); @@ -571,7 +572,7 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) { if(new_rect.getWidth() != getRect().getWidth() || new_rect.getHeight() != getRect().getHeight()) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); + LLPreview* frontmost_preview = dynamic_cast(mTabContainer->getCurrentPanel()); if (frontmost_preview) frontmost_preview->userResized(); } LLFloater::handleReshape(new_rect, by_user); @@ -580,7 +581,7 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) void LLMultiPreview::tabOpen(LLFloater* opened_floater) { - LLPreview* opened_preview = (LLPreview*)opened_floater; + LLPreview* opened_preview = dynamic_cast(opened_floater); if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { opened_preview->loadAsset(); diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp index c31f53538..c5ffefffe 100644 --- a/indra/newview/llpreviewgesture.cpp +++ b/indra/newview/llpreviewgesture.cpp @@ -414,7 +414,6 @@ BOOL LLPreviewGesture::postBuild() edit->setKeystrokeCallback(boost::bind(&onKeystrokeCommit,_1,this)); edit->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitSetDirty,this)); edit->setCommitOnFocusLost(TRUE); - edit->setIgnoreTab(TRUE); mReplaceEditor = edit; @@ -479,10 +478,8 @@ BOOL LLPreviewGesture::postBuild() edit = getChild("chat_editor"); edit->setVisible(FALSE); edit->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitChat,this)); - //edit->setKeystrokeCallback(onKeystrokeCommit); - //edit->setCallbackUserData(this); + //edit->setKeystrokeCallback(onKeystrokeCommit, this); edit->setCommitOnFocusLost(TRUE); - edit->setIgnoreTab(TRUE); mChatEditor = edit; @@ -500,11 +497,9 @@ BOOL LLPreviewGesture::postBuild() edit->setEnabled(FALSE); edit->setVisible(FALSE); edit->setPrevalidate(LLLineEditor::prevalidateFloat); -// edit->setKeystrokeCallback(onKeystrokeCommit); - //edit->setCallbackUserData(this); +// edit->setKeystrokeCallback(onKeystrokeCommit, this); edit->setCommitOnFocusLost(TRUE); edit->setCommitCallback(boost::bind(&LLPreviewGesture::onCommitWaitTime,this)); - edit->setIgnoreTab(TRUE); mWaitTimeEditor = edit; @@ -528,7 +523,6 @@ BOOL LLPreviewGesture::postBuild() addAnimations(); addSounds(); - const LLInventoryItem* item = getItem(); if (item) diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 8306a0040..6613a0055 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -1186,7 +1186,8 @@ void LLScriptEdCore::selectFirstError() { // Select the first item; mErrorList->selectFirstItem(); - onErrorList(mErrorList, this); + if (gSavedSettings.getBOOL("LiruScriptErrorsStealFocus")) + onErrorList(mErrorList, this); } diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index 2d09e2356..d2929d7a6 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -35,6 +35,7 @@ #include "llpreviewtexture.h" #include "llagent.h" +#include "llavataractions.h" #include "llbutton.h" #include "llcombobox.h" #include "statemachine/aifilepicker.h" @@ -47,12 +48,10 @@ #include "lltrans.h" #include "lltextbox.h" #include "lltextureview.h" -#include "llui.h" #include "llviewertexturelist.h" #include "lluictrlfactory.h" #include "llviewerwindow.h" #include "lllineeditor.h" -#include "llfloateravatarinfo.h" const S32 PREVIEW_TEXTURE_MIN_WIDTH = 300; const S32 PREVIEW_TEXTURE_MIN_HEIGHT = 120; @@ -672,8 +671,7 @@ bool LLPreviewTexture::setAspectRatio(const F32 width, const F32 height) void LLPreviewTexture::onClickProfile(void* userdata) { LLPreviewTexture* self = (LLPreviewTexture*) userdata; - LLUUID key = self->mCreatorKey; - if (!key.isNull()) LLFloaterAvatarInfo::showFromDirectory(key); + LLAvatarActions::showProfile(self->mCreatorKey); } void LLPreviewTexture::onAspectRatioCommit(LLUICtrl* ctrl, void* userdata) diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp new file mode 100644 index 000000000..db1042031 --- /dev/null +++ b/indra/newview/llspeakers.cpp @@ -0,0 +1,960 @@ +/** + * @file llspeakers.cpp + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llspeakers.h" + +#include "llagent.h" +#include "llavatarnamecache.h" +#include "llimpanel.h" // For LLFloaterIMPanel +#include "llimview.h" +#include "llsdutil.h" +#include "llui.h" +#include "llviewerobjectlist.h" +#include "llvoavatar.h" +#include "llvoicechannel.h" +#include "llworld.h" + +#include "rlvhandler.h" + +extern AIHTTPTimeoutPolicy moderationResponder_timeout; + +const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); +const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); + +LLSpeaker::LLSpeaker(const LLUUID& id, const std::string& name, const ESpeakerType type) : + mStatus(LLSpeaker::STATUS_TEXT_ONLY), + mLastSpokeTime(0.f), + mSpeechVolume(0.f), + mHasSpoken(FALSE), + mHasLeftCurrentCall(FALSE), + mDotColor(LLColor4::white), + mID(id), + mTyping(FALSE), + mSortIndex(0), + mType(type), + mIsModerator(FALSE), + mModeratorMutedVoice(FALSE), + mModeratorMutedText(FALSE) +{ + // Make sure we also get the display name if SLIM or some other external voice client is used and not whatever is provided. + if ((name.empty() && type == SPEAKER_AGENT) || type == SPEAKER_EXTERNAL) + { + lookupName(); + } + else + { + mDisplayName = name; + } +} + + +void LLSpeaker::lookupName() +{ + if (mDisplayName.empty()) + { +// [RLVa:KB] - Checked: 2009-07-10 (RLVa-1.0.0g) | Added: RLVa-1.0.0g + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES) && gAgentID != mID) + mDisplayName = RlvStrings::getAnonym(mDisplayName); + else +// [/RLVa:KB] + LLAvatarNameCache::get(mID, boost::bind(&LLSpeaker::onNameCache, this, _2)); + } +} + +void LLSpeaker::onNameCache(const LLAvatarName& full_name) +{ + LLAvatarNameCache::getPNSName(full_name, mDisplayName); +} + +bool LLSpeaker::isInVoiceChannel() +{ + return mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || mStatus == LLSpeaker::STATUS_MUTED; +} + +LLSpeakerUpdateModeratorEvent::LLSpeakerUpdateModeratorEvent(LLSpeaker* source) +: LLEvent(source, "Speaker add moderator event"), + mSpeakerID (source->mID), + mIsModerator (source->mIsModerator) +{ +} + +LLSD LLSpeakerUpdateModeratorEvent::getValue() +{ + LLSD ret; + ret["id"] = mSpeakerID; + ret["is_moderator"] = mIsModerator; + return ret; +} + + +LLSpeakerTextModerationEvent::LLSpeakerTextModerationEvent(LLSpeaker* source) +: LLEvent(source, "Speaker text moderation event") +{ +} + +LLSD LLSpeakerTextModerationEvent::getValue() +{ + return std::string("text"); +} + + +LLSpeakerVoiceModerationEvent::LLSpeakerVoiceModerationEvent(LLSpeaker* source) +: LLEvent(source, "Speaker voice moderation event") +{ +} + +LLSD LLSpeakerVoiceModerationEvent::getValue() +{ + return std::string("voice"); +} + +LLSpeakerListChangeEvent::LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id) +: LLEvent(source, "Speaker added/removed from speaker mgr"), + mSpeakerID(speaker_id) +{ +} + +LLSD LLSpeakerListChangeEvent::getValue() +{ + return mSpeakerID; +} + +// helper sort class +struct LLSortRecentSpeakers +{ + bool operator()(const LLPointer lhs, const LLPointer rhs) const; +}; + +bool LLSortRecentSpeakers::operator()(const LLPointer lhs, const LLPointer rhs) const +{ + // Sort first on status + if (lhs->mStatus != rhs->mStatus) + { + return (lhs->mStatus < rhs->mStatus); + } + + // and then on last speaking time + if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) + { + return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); + } + + // and finally (only if those are both equal), on name. + return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); +} + +LLSpeakerActionTimer::LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id) +: LLEventTimer(action_period) +, mActionCallback(action_cb) +, mSpeakerId(speaker_id) +{ +} + +BOOL LLSpeakerActionTimer::tick() +{ + if (mActionCallback) + { + return (BOOL)mActionCallback(mSpeakerId); + } + return TRUE; +} + +void LLSpeakerActionTimer::unset() +{ + mActionCallback = 0; +} + +LLSpeakersDelayActionsStorage::LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay) +: mActionCallback(action_cb) +, mActionDelay(action_delay) +{ +} + +LLSpeakersDelayActionsStorage::~LLSpeakersDelayActionsStorage() +{ + removeAllTimers(); +} + +void LLSpeakersDelayActionsStorage::setActionTimer(const LLUUID& speaker_id) +{ + bool not_found = true; + if (mActionTimersMap.size() > 0) + { + not_found = mActionTimersMap.find(speaker_id) == mActionTimersMap.end(); + } + + // If there is already a started timer for the passed UUID don't do anything. + if (not_found) + { + // Starting a timer to remove an participant after delay is completed + mActionTimersMap.insert(LLSpeakerActionTimer::action_value_t(speaker_id, + new LLSpeakerActionTimer( + boost::bind(&LLSpeakersDelayActionsStorage::onTimerActionCallback, this, _1), + mActionDelay, speaker_id))); + } +} + +void LLSpeakersDelayActionsStorage::unsetActionTimer(const LLUUID& speaker_id) +{ + if (mActionTimersMap.size() == 0) return; + + LLSpeakerActionTimer::action_timer_iter_t it_speaker = mActionTimersMap.find(speaker_id); + + if (it_speaker != mActionTimersMap.end()) + { + it_speaker->second->unset(); + mActionTimersMap.erase(it_speaker); + } +} + +void LLSpeakersDelayActionsStorage::removeAllTimers() +{ + LLSpeakerActionTimer::action_timer_iter_t iter = mActionTimersMap.begin(); + for (; iter != mActionTimersMap.end(); ++iter) + { + delete iter->second; + } + mActionTimersMap.clear(); +} + +bool LLSpeakersDelayActionsStorage::onTimerActionCallback(const LLUUID& speaker_id) +{ + unsetActionTimer(speaker_id); + + if (mActionCallback) + { + mActionCallback(speaker_id); + } + + return true; +} + + +// +// LLSpeakerMgr +// + +LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : + mVoiceChannel(channelp) +, mVoiceModerated(false) +, mModerateModeHandledFirstTime(false) +{ + static LLUICachedControl remove_delay ("SpeakerParticipantRemoveDelay", 10.0); + + mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLSpeakerMgr::removeSpeaker, this, _1), remove_delay); +} + +LLSpeakerMgr::~LLSpeakerMgr() +{ + delete mSpeakerDelayRemover; +} + +LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::string& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) +{ + if (id.isNull()) return NULL; + + LLPointer speakerp; + if (mSpeakers.find(id) == mSpeakers.end()) + { + speakerp = new LLSpeaker(id, name, type); + speakerp->mStatus = status; + mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); + mSpeakersSorted.push_back(speakerp); + LL_DEBUGS("Speakers") << "Added speaker " << id << llendl; + fireEvent(new LLSpeakerListChangeEvent(this, speakerp->mID), "add"); + } + else + { + speakerp = findSpeaker(id); + if (speakerp.notNull()) + { + // keep highest priority status (lowest value) instead of overriding current value + speakerp->mStatus = llmin(speakerp->mStatus, status); + // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id + // we need to override speakers that we think are objects when we find out they are really + // residents + if (type == LLSpeaker::SPEAKER_AGENT) + { + speakerp->mType = LLSpeaker::SPEAKER_AGENT; + speakerp->lookupName(); + } + } + else + { + LL_WARNS("Speakers") << "Speaker " << id << " not found" << llendl; + } + } + + mSpeakerDelayRemover->unsetActionTimer(speakerp->mID); + return speakerp; +} + +// *TODO: Once way to request the current voice channel moderation mode is implemented +// this method with related code should be removed. +/* + Initializes "moderate_mode" of voice session on first join. + + This is WORKAROUND because a way to request the current voice channel moderation mode exists + but is not implemented in viewer yet. See EXT-6937. +*/ +void LLSpeakerMgr::initVoiceModerateMode() +{ + if (!mModerateModeHandledFirstTime && (mVoiceChannel && mVoiceChannel->isActive())) + { + LLPointer speakerp; + + if (mSpeakers.find(gAgentID) != mSpeakers.end()) + { + speakerp = mSpeakers[gAgentID]; + } + + if (speakerp.notNull()) + { + mVoiceModerated = speakerp->mModeratorMutedVoice; + mModerateModeHandledFirstTime = true; + } + } +} + +void LLSpeakerMgr::update(BOOL resort_ok) +{ + if (!LLVoiceClient::instanceExists()) + { + return; + } + + LLColor4 speaking_color = gSavedSettings.getColor4("SpeakingColor"); + LLColor4 overdriven_color = gSavedSettings.getColor4("OverdrivenColor"); + + if(resort_ok) // only allow list changes when user is not interacting with it + { + updateSpeakerList(); + } + + // update status of all current speakers + BOOL voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + + speaker_it++; + + if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id)) + { + speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id); + BOOL moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id); + if (moderator_muted_voice != speakerp->mModeratorMutedVoice) + { + speakerp->mModeratorMutedVoice = moderator_muted_voice; + LL_DEBUGS("Speakers") << (speakerp->mModeratorMutedVoice? "Muted" : "Umuted") << " speaker " << speaker_id<< llendl; + speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp)); + } + + if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) + { + speakerp->mStatus = LLSpeaker::STATUS_MUTED; + } + else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id)) + { + // reset inactivity expiration + if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } + speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; + // interpolate between active color and full speaking color based on power of speech output + speakerp->mDotColor = speaking_color; + if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) + { + speakerp->mDotColor = overdriven_color; + } + } + else + { + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + + if (speakerp->mHasSpoken) + { + // have spoken once, not currently speaking + speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; + } + else + { + // default state for being in voice channel + speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; + } + } + } + // speaker no longer registered in voice channel, demote to text only + else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) + { + if(speakerp->mType == LLSpeaker::SPEAKER_EXTERNAL) + { + // external speakers should be timed out when they leave the voice channel (since they only exist via SLVoice) + setSpeakerNotInChannel(speakerp); // Singu Note: Don't just flag, call the flagging function and get them on the removal timer! + } + else + { + speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + } + } + } + + if(resort_ok) // only allow list changes when user is not interacting with it + { + // sort by status then time last spoken + std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); + } + + // for recent speakers who are not currently speaking, show "recent" color dot for most recent + // fading to "active" color + + S32 recent_speaker_count = 0; + S32 sort_index = 0; + speaker_list_t::iterator sorted_speaker_it; + for(sorted_speaker_it = mSpeakersSorted.begin(); + sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) + { + LLPointer speakerp = *sorted_speaker_it; + + // color code recent speakers who are not currently speaking + if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) + { + speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); + recent_speaker_count++; + } + + // stuff sort ordinal into speaker so the ui can sort by this value + speakerp->mSortIndex = sort_index++; + } +} + +void LLSpeakerMgr::updateSpeakerList() +{ + // are we bound to the currently active voice channel? + if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) + { + std::set participants; + LLVoiceClient::getInstance()->getParticipantList(participants); + // add new participants to our list of known speakers + for (std::set::iterator participant_it = participants.begin(); + participant_it != participants.end(); + ++participant_it) + { + setSpeaker(*participant_it, + LLVoiceClient::getInstance()->getDisplayName(*participant_it), + LLSpeaker::STATUS_VOICE_ACTIVE, + (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); + + + } + } +} + +void LLSpeakerMgr::setSpeakerNotInChannel(LLSpeaker* speakerp) +{ + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + mSpeakerDelayRemover->setActionTimer(speakerp->mID); +} + +bool LLSpeakerMgr::removeSpeaker(const LLUUID& speaker_id) +{ + mSpeakers.erase(speaker_id); + + speaker_list_t::iterator sorted_speaker_it = mSpeakersSorted.begin(); + + for(; sorted_speaker_it != mSpeakersSorted.end(); ++sorted_speaker_it) + { + if (speaker_id == (*sorted_speaker_it)->mID) + { + mSpeakersSorted.erase(sorted_speaker_it); + break; + } + } + + LL_DEBUGS("Speakers") << "Removed speaker " << speaker_id << llendl; + fireEvent(new LLSpeakerListChangeEvent(this, speaker_id), "remove"); + + update(TRUE); + + return false; +} + +LLPointer LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) +{ + //In some conditions map causes crash if it is empty(Windows only), adding check (EK) + if (mSpeakers.size() == 0) + return NULL; + speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); + if (found_it == mSpeakers.end()) + { + return NULL; + } + return found_it->second; +} + +void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text) +{ + speaker_list->clear(); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLPointer speakerp = speaker_it->second; + // what about text only muted or inactive? + if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) + { + speaker_list->push_back(speakerp); + } + } +} + +const LLUUID LLSpeakerMgr::getSessionID() +{ + return mVoiceChannel->getSessionID(); +} + + +void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mTyping = typing; + } +} + +// speaker has chatted via either text or voice +void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } +} + +BOOL LLSpeakerMgr::isVoiceActive() +{ + // mVoiceChannel = NULL means current voice channel, whatever it is + return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); +} + + +// +// LLIMSpeakerMgr +// +LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) +{ +} + +void LLIMSpeakerMgr::updateSpeakerList() +{ + // don't do normal updates which are pulled from voice channel + // rely on user list reported by sim + + // We need to do this to allow PSTN callers into group chats to show in the list. + LLSpeakerMgr::updateSpeakerList(); + + return; +} + +void LLIMSpeakerMgr::setSpeakers(const LLSD& speakers) +{ + if ( !speakers.isMap() ) return; + + if ( speakers.has("agent_info") && speakers["agent_info"].isMap() ) + { + LLSD::map_const_iterator speaker_it; + for(speaker_it = speakers["agent_info"].beginMap(); + speaker_it != speakers["agent_info"].endMap(); + ++speaker_it) + { + LLUUID agent_id(speaker_it->first); + + LLPointer speakerp = setSpeaker( + agent_id, + LLStringUtil::null, + LLSpeaker::STATUS_TEXT_ONLY); + + if ( speaker_it->second.isMap() ) + { + BOOL is_moderator = speakerp->mIsModerator; + speakerp->mIsModerator = speaker_it->second["is_moderator"]; + speakerp->mModeratorMutedText = + speaker_it->second["mutes"]["text"]; + // Fire event only if moderator changed + if ( is_moderator != speakerp->mIsModerator ) + { + LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << llendl; + fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); + } + } + } + } + else if ( speakers.has("agents" ) && speakers["agents"].isArray() ) + { + //older, more decprecated way. Need here for + //using older version of servers + LLSD::array_const_iterator speaker_it; + for(speaker_it = speakers["agents"].beginArray(); + speaker_it != speakers["agents"].endArray(); + ++speaker_it) + { + const LLUUID agent_id = (*speaker_it).asUUID(); + + LLPointer speakerp = setSpeaker( + agent_id, + LLStringUtil::null, + LLSpeaker::STATUS_TEXT_ONLY); + } + } +} + +void LLIMSpeakerMgr::updateSpeakers(const LLSD& update) +{ + if ( !update.isMap() ) return; + + if ( update.has("agent_updates") && update["agent_updates"].isMap() ) + { + LLSD::map_const_iterator update_it; + for( + update_it = update["agent_updates"].beginMap(); + update_it != update["agent_updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLPointer speakerp = findSpeaker(agent_id); + + LLSD agent_data = update_it->second; + + if (agent_data.isMap() && agent_data.has("transition")) + { + if (agent_data["transition"].asString() == "LEAVE" && speakerp.notNull()) + { + setSpeakerNotInChannel(speakerp); + } + else if (agent_data["transition"].asString() == "ENTER") + { + // add or update speaker + speakerp = setSpeaker(agent_id); + } + else + { + llwarns << "bad membership list update " << ll_print_sd(agent_data["transition"]) << llendl; + } + } + + if (speakerp.isNull()) continue; + + // should have a valid speaker from this point on + if (agent_data.isMap() && agent_data.has("info")) + { + LLSD agent_info = agent_data["info"]; + + if (agent_info.has("is_moderator")) + { + BOOL is_moderator = speakerp->mIsModerator; + speakerp->mIsModerator = agent_info["is_moderator"]; + // Fire event only if moderator changed + if ( is_moderator != speakerp->mIsModerator ) + { + LL_DEBUGS("Speakers") << "Speaker " << agent_id << (is_moderator ? "is now" : "no longer is") << " a moderator" << llendl; + fireEvent(new LLSpeakerUpdateModeratorEvent(speakerp), "update_moderator"); + } + } + + if (agent_info.has("mutes")) + { + speakerp->mModeratorMutedText = agent_info["mutes"]["text"]; + } + } + } + } + else if ( update.has("updates") && update["updates"].isMap() ) + { + LLSD::map_const_iterator update_it; + for ( + update_it = update["updates"].beginMap(); + update_it != update["updates"].endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + LLPointer speakerp = findSpeaker(agent_id); + + std::string agent_transition = update_it->second.asString(); + if (agent_transition == "LEAVE" && speakerp.notNull()) + { + setSpeakerNotInChannel(speakerp); + } + else if ( agent_transition == "ENTER") + { + // add or update speaker + speakerp = setSpeaker(agent_id); + } + else + { + llwarns << "bad membership list update " + << agent_transition << llendl; + } + } + } +} + +class ModerationResponder : public LLHTTPClient::ResponderIgnoreBody +{ +public: + ModerationResponder(const LLUUID& session_id) + { + mSessionID = session_id; + } + + /*virtual*/ void error(U32 status, const std::string& reason) + { + llwarns << "ModerationResponder error [status:" << status << "]: " << reason << llendl; + + if ( gIMMgr ) + { + LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(mSessionID); + if (!floaterp) return; + + //403 == you're not a mod + //should be disabled if you're not a moderator + if ( 403 == status ) + { + floaterp->showSessionEventError( + "mute", + "not_a_mod_error"); + } + else + { + floaterp->showSessionEventError( + "mute", + "generic_request_error"); + } + } + } + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return moderationResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "ModerationResponder"; } + +private: + LLUUID mSessionID; +}; + +void LLIMSpeakerMgr::toggleAllowTextChat(const LLUUID& speaker_id) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (!speakerp) return; + + std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); + LLSD data; + data["method"] = "mute update"; + data["session-id"] = getSessionID(); + data["params"] = LLSD::emptyMap(); + data["params"]["agent_id"] = speaker_id; + data["params"]["mute_info"] = LLSD::emptyMap(); + //current value represents ability to type, so invert + data["params"]["mute_info"]["text"] = !speakerp->mModeratorMutedText; + + LLHTTPClient::post(url, data, new ModerationResponder(getSessionID())); +} + +void LLIMSpeakerMgr::moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute) +{ + LLPointer speakerp = findSpeaker(avatar_id); + if (!speakerp) return; + + // *NOTE: mantipov: probably this condition will be incorrect when avatar will be blocked for + // text chat via moderation (LLSpeaker::mModeratorMutedText == TRUE) + bool is_in_voice = speakerp->mStatus <= LLSpeaker::STATUS_VOICE_ACTIVE || speakerp->mStatus == LLSpeaker::STATUS_MUTED; + + // do not send voice moderation changes for avatars not in voice channel + if (!is_in_voice) return; + + std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); + LLSD data; + data["method"] = "mute update"; + data["session-id"] = getSessionID(); + data["params"] = LLSD::emptyMap(); + data["params"]["agent_id"] = avatar_id; + data["params"]["mute_info"] = LLSD::emptyMap(); + data["params"]["mute_info"]["voice"] = !unmute; + + LLHTTPClient::post( + url, + data, + new ModerationResponder(getSessionID())); +} + +void LLIMSpeakerMgr::moderateVoiceAllParticipants( bool unmute_everyone ) +{ + if (mVoiceModerated == !unmute_everyone) + { + // session already in requested state. Just force participants which do not match it. + forceVoiceModeratedMode(mVoiceModerated); + } + else + { + // otherwise set moderated mode for a whole session. + moderateVoiceSession(getSessionID(), !unmute_everyone); + } +} + +void LLIMSpeakerMgr::processSessionUpdate(const LLSD& session_update) +{ + if (session_update.has("moderated_mode") && + session_update["moderated_mode"].has("voice")) + { + mVoiceModerated = session_update["moderated_mode"]["voice"]; + } +} + +void LLIMSpeakerMgr::moderateVoiceSession(const LLUUID& session_id, bool disallow_voice) +{ + std::string url = gAgent.getRegion()->getCapability("ChatSessionRequest"); + LLSD data; + data["method"] = "session update"; + data["session-id"] = session_id; + data["params"] = LLSD::emptyMap(); + + data["params"]["update_info"] = LLSD::emptyMap(); + + data["params"]["update_info"]["moderated_mode"] = LLSD::emptyMap(); + data["params"]["update_info"]["moderated_mode"]["voice"] = disallow_voice; + + LLHTTPClient::post(url, data, new ModerationResponder(session_id)); +} + +void LLIMSpeakerMgr::forceVoiceModeratedMode(bool should_be_muted) +{ + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + + // participant does not match requested state + if (should_be_muted != (bool)speakerp->mModeratorMutedVoice) + { + moderateVoiceParticipant(speaker_id, !should_be_muted); + } + } +} + +// +// LLActiveSpeakerMgr +// + +LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) +{ +} + +void LLActiveSpeakerMgr::updateSpeakerList() +{ + // point to whatever the current voice channel is + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + + // always populate from active voice channel + if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) //MA: seems this is always false + { + LL_DEBUGS("Speakers") << "Removed all speakers" << llendl; + fireEvent(new LLSpeakerListChangeEvent(this, LLUUID::null), "clear"); + mSpeakers.clear(); + mSpeakersSorted.clear(); + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + mSpeakerDelayRemover->removeAllTimers(); + } + LLSpeakerMgr::updateSpeakerList(); + + // clean up text only speakers + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + // automatically flag text only speakers for removal + setSpeakerNotInChannel(speakerp); // Singu Note: Don't just flag, call the flagging function and get them on the removal timer! + } + } + +} + + + +// +// LLLocalSpeakerMgr +// + +LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) +{ +} + +LLLocalSpeakerMgr::~LLLocalSpeakerMgr () +{ +} + +void LLLocalSpeakerMgr::updateSpeakerList() +{ + // pull speakers from voice channel + LLSpeakerMgr::updateSpeakerList(); + + if (gDisconnected)//the world is cleared. + { + return ; + } + + // pick up non-voice speakers in chat range + uuid_vec_t avatar_ids; + std::vector positions; + LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), CHAT_NORMAL_RADIUS); + for(U32 i=0; ifirst; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + LLVOAvatar* avatarp = gObjectList.findAvatar(speaker_id); + if (!avatarp || dist_vec_squared(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS_SQUARED) + { + setSpeakerNotInChannel(speakerp); + } + } + } +} + diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h new file mode 100644 index 000000000..1d58d1b1d --- /dev/null +++ b/indra/newview/llspeakers.h @@ -0,0 +1,347 @@ +/** + * @file llspeakers.h + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_LLSPEAKERS_H +#define LL_LLSPEAKERS_H + +#include "llevent.h" +#include "lleventtimer.h" +#include "llhandle.h" + +class LLAvatarName; +class LLSpeakerMgr; +class LLVoiceChannel; + +// data for a given participant in a voice channel +class LLSpeaker : public LLRefCount, public LLOldEvents::LLObservable, public LLHandleProvider, public boost::signals2::trackable +{ +public: + typedef enum e_speaker_type + { + SPEAKER_AGENT, + SPEAKER_OBJECT, + SPEAKER_EXTERNAL // Speaker that doesn't map to an avatar or object (i.e. PSTN caller in a group) + } ESpeakerType; + + typedef enum e_speaker_status + { + STATUS_SPEAKING, + STATUS_HAS_SPOKEN, + STATUS_VOICE_ACTIVE, + STATUS_TEXT_ONLY, + STATUS_NOT_IN_CHANNEL, + STATUS_MUTED + } ESpeakerStatus; + + + LLSpeaker(const LLUUID& id, const std::string& name = LLStringUtil::null, const ESpeakerType type = SPEAKER_AGENT); + ~LLSpeaker() {}; + void lookupName(); + + void onNameCache(const LLAvatarName& full_name); + + bool isInVoiceChannel(); + + ESpeakerStatus mStatus; // current activity status in speech group + F32 mLastSpokeTime; // timestamp when this speaker last spoke + F32 mSpeechVolume; // current speech amplitude (timea average rms amplitude?) + std::string mDisplayName; // cache user name for this speaker + BOOL mHasSpoken; // has this speaker said anything this session? + BOOL mHasLeftCurrentCall; // has this speaker left the current voice call? + LLColor4 mDotColor; + LLUUID mID; + BOOL mTyping; + S32 mSortIndex; + ESpeakerType mType; + BOOL mIsModerator; + BOOL mModeratorMutedVoice; + BOOL mModeratorMutedText; +}; + +class LLSpeakerUpdateModeratorEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerUpdateModeratorEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +private: + const LLUUID& mSpeakerID; + BOOL mIsModerator; +}; + +class LLSpeakerTextModerationEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerTextModerationEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +}; + +class LLSpeakerVoiceModerationEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerVoiceModerationEvent(LLSpeaker* source); + /*virtual*/ LLSD getValue(); +}; + +class LLSpeakerListChangeEvent : public LLOldEvents::LLEvent +{ +public: + LLSpeakerListChangeEvent(LLSpeakerMgr* source, const LLUUID& speaker_id); + /*virtual*/ LLSD getValue(); + +private: + const LLUUID& mSpeakerID; +}; + +/** + * class LLSpeakerActionTimer + * + * Implements a timer that calls stored callback action for stored speaker after passed period. + * + * Action is called until callback returns "true". + * In this case the timer will be removed via LLEventTimer::updateClass(). + * Otherwise it should be deleted manually in place where it is used. + * If action callback is not set timer will tick only once and deleted. + */ +class LLSpeakerActionTimer : public LLEventTimer +{ +public: + typedef boost::function action_callback_t; + typedef std::map action_timers_map_t; + typedef action_timers_map_t::value_type action_value_t; + typedef action_timers_map_t::const_iterator action_timer_const_iter_t; + typedef action_timers_map_t::iterator action_timer_iter_t; + + /** + * Constructor. + * + * @param action_cb - callback which will be called each time after passed action period. + * @param action_period - time in seconds timer should tick. + * @param speaker_id - LLUUID of speaker which will be passed into action callback. + */ + LLSpeakerActionTimer(action_callback_t action_cb, F32 action_period, const LLUUID& speaker_id); + virtual ~LLSpeakerActionTimer() {}; + + /** + * Implements timer "tick". + * + * If action callback is not specified returns true. Instance will be deleted by LLEventTimer::updateClass(). + */ + virtual BOOL tick(); + + /** + * Clears the callback. + * + * Use this instead of deleteing this object. + * The next call to tick() will return true and that will destroy this object. + */ + void unset(); +private: + action_callback_t mActionCallback; + LLUUID mSpeakerId; +}; + +/** + * Represents a functionality to store actions for speakers with delay. + * Is based on LLSpeakerActionTimer. + */ +class LLSpeakersDelayActionsStorage +{ +public: + LLSpeakersDelayActionsStorage(LLSpeakerActionTimer::action_callback_t action_cb, F32 action_delay); + ~LLSpeakersDelayActionsStorage(); + + /** + * Sets new LLSpeakerActionTimer with passed speaker UUID. + */ + void setActionTimer(const LLUUID& speaker_id); + + /** + * Removes stored LLSpeakerActionTimer for passed speaker UUID from internal map and optionally deletes it. + * + * @see onTimerActionCallback() + */ + void unsetActionTimer(const LLUUID& speaker_id); + + void removeAllTimers(); +private: + /** + * Callback of the each instance of LLSpeakerActionTimer. + * + * Unsets an appropriate timer instance and calls action callback for specified speacker_id. + * + * @see unsetActionTimer() + */ + bool onTimerActionCallback(const LLUUID& speaker_id); + + LLSpeakerActionTimer::action_timers_map_t mActionTimersMap; + LLSpeakerActionTimer::action_callback_t mActionCallback; + + /** + * Delay to call action callback for speakers after timer was set. + */ + F32 mActionDelay; + +}; + + +class LLSpeakerMgr : public LLOldEvents::LLObservable +{ + LOG_CLASS(LLSpeakerMgr); + +public: + LLSpeakerMgr(LLVoiceChannel* channelp); + virtual ~LLSpeakerMgr(); + + LLPointer findSpeaker(const LLUUID& avatar_id); + void update(BOOL resort_ok); + void setSpeakerTyping(const LLUUID& speaker_id, BOOL typing); + void speakerChatted(const LLUUID& speaker_id); + LLPointer setSpeaker(const LLUUID& id, + const std::string& name = LLStringUtil::null, + LLSpeaker::ESpeakerStatus status = LLSpeaker::STATUS_TEXT_ONLY, + LLSpeaker::ESpeakerType = LLSpeaker::SPEAKER_AGENT); + + BOOL isVoiceActive(); + + typedef std::vector > speaker_list_t; + void getSpeakerList(speaker_list_t* speaker_list, BOOL include_text); + LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } + const LLUUID getSessionID(); + + /** + * Removes avaline speaker. + * + * This is a HACK due to server does not send information that Avaline caller ends call. + * It can be removed when server is updated. See EXT-4301 for details + */ + bool removeAvalineSpeaker(const LLUUID& speaker_id) { return removeSpeaker(speaker_id); } + + /** + * Initializes mVoiceModerated depend on LLSpeaker::mModeratorMutedVoice of agent's participant. + * + * Is used only to implement workaround to initialize mVoiceModerated on first join to group chat. See EXT-6937 + */ + void initVoiceModerateMode(); + +protected: + virtual void updateSpeakerList(); + void setSpeakerNotInChannel(LLSpeaker* speackerp); + bool removeSpeaker(const LLUUID& speaker_id); + + typedef std::map > speaker_map_t; + speaker_map_t mSpeakers; + + speaker_list_t mSpeakersSorted; + LLFrameTimer mSpeechTimer; + LLVoiceChannel* mVoiceChannel; + + /** + * time out speakers when they are not part of current session + */ + LLSpeakersDelayActionsStorage* mSpeakerDelayRemover; + + // *TODO: should be moved back into LLIMSpeakerMgr when a way to request the current voice channel + // moderation mode is implemented: See EXT-6937 + bool mVoiceModerated; + + // *TODO: To be removed when a way to request the current voice channel + // moderation mode is implemented: See EXT-6937 + bool mModerateModeHandledFirstTime; +}; + +class LLIMSpeakerMgr : public LLSpeakerMgr +{ + LOG_CLASS(LLIMSpeakerMgr); + +public: + LLIMSpeakerMgr(LLVoiceChannel* channel); + + void updateSpeakers(const LLSD& update); + void setSpeakers(const LLSD& speakers); + + void toggleAllowTextChat(const LLUUID& speaker_id); + + /** + * Mutes/Unmutes avatar for current group voice chat. + * + * It only marks avatar as muted for session and does not use local Agent's Block list. + * It does not mute Agent itself. + * + * @param[in] avatar_id UUID of avatar to be processed + * @param[in] unmute if false - specified avatar will be muted, otherwise - unmuted. + * + * @see moderateVoiceAllParticipants() + */ + void moderateVoiceParticipant(const LLUUID& avatar_id, bool unmute); + + /** + * Mutes/Unmutes all avatars for current group voice chat. + * + * It only marks avatars as muted for session and does not use local Agent's Block list. + * It calls forceVoiceModeratedMode() in case of session is already in requested state. + * + * @param[in] unmute_everyone if false - avatars will be muted, otherwise - unmuted. + * + * @see moderateVoiceParticipant() + */ + void moderateVoiceAllParticipants(bool unmute_everyone); + + void processSessionUpdate(const LLSD& session_update); + +protected: + virtual void updateSpeakerList(); + + void moderateVoiceSession(const LLUUID& session_id, bool disallow_voice); + + /** + * Process all participants to mute/unmute them according to passed voice session state. + */ + void forceVoiceModeratedMode(bool should_be_muted); + +}; + +class LLActiveSpeakerMgr : public LLSpeakerMgr, public LLSingleton +{ + LOG_CLASS(LLActiveSpeakerMgr); + +public: + LLActiveSpeakerMgr(); +protected: + virtual void updateSpeakerList(); +}; + +class LLLocalSpeakerMgr : public LLSpeakerMgr, public LLSingleton +{ + LOG_CLASS(LLLocalSpeakerMgr); +public: + LLLocalSpeakerMgr(); + ~LLLocalSpeakerMgr (); +protected: + virtual void updateSpeakerList(); +}; + +#endif // LL_LLSPEAKERS_H + diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index e539c358f..2f3b7e33a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -211,6 +211,7 @@ #include "llwearable.h" #include "llinventorybridge.h" #include "llappearancemgr.h" +#include "llvoicechannel.h" #include "jcfloaterareasearch.h" #include "generichandlers.h" @@ -1282,7 +1283,7 @@ bool idle_startup() std::string grid_uri = vl->getCurrentGridURI(); if(!redirect_uri.empty()) grid_uri = redirect_uri; - redirect_uri.clear(); + //redirect_uri.clear(); //Should this be cleared immediately after consumption? Doing this will break retrying on http error. llinfos << "Authenticating with " << grid_uri << llendl; @@ -1411,10 +1412,17 @@ bool idle_startup() { progress += 0.01f; auth_message = message_response; - set_startup_status(progress, auth_desc, auth_message); - auth_method = response["next_method"].asString(); redirect_uri = response["next_url"].asString(); + if(auth_method.substr(0, 5) == "login") + { + auth_desc = LLTrans::getString("LoginAuthenticating"); + } + else + { + auth_desc = LLTrans::getString("LoginMaintenance"); + } + set_startup_status(progress, auth_desc, auth_message); LLStartUp::setStartupState(STATE_XMLRPC_LEGACY_LOGIN ); return false; } @@ -1543,8 +1551,10 @@ bool idle_startup() name += " " + lastname; } gViewerWindow->getWindow()->setTitle(LLAppViewer::instance()->getWindowTitle() + "- " + name); - // Pass the user information to the voice chat server interface. - gVoiceClient->userAuthorized(firstname, lastname, gAgentID); + // Pass the user information to the voice chat server interface. + LLVoiceClient::getInstance()->userAuthorized(name, gAgentID); + // create the default proximal channel + LLVoiceChannel::initClass(); LLStartUp::setStartupState( STATE_WORLD_INIT ); } else @@ -1826,6 +1836,11 @@ bool idle_startup() LLStartUp::initNameCache(); display_startup(); + // update the voice settings *after* gCacheName initialization + // so that we can construct voice UI that relies on the name cache + LLVoiceClient::getInstance()->updateSettings(); + display_startup(); + // *Note: this is where gWorldMap used to be initialized. // register null callbacks for audio until the audio system is initialized @@ -2108,41 +2123,6 @@ bool idle_startup() } display_startup(); - // testing adding a local inventory folder... - if (gSavedSettings.getBOOL("AscentUseSystemFolder")) - { - LLViewerInventoryCategory* system_folder = new LLViewerInventoryCategory(gAgent.getID()); - system_folder->rename(std::string("System Inventory")); - LLUUID system_folder_id = LLUUID("00000000-0000-0000-0000-000000000001");//"FFFFFFFF-0000-F113-7357-000000000001"); - system_folder->setUUID(system_folder_id); - gSystemFolderRoot = system_folder_id; - system_folder->setParent(LLUUID::null); - system_folder->setPreferredType(LLFolderType::FT_NONE); - gInventory.addCategory(system_folder); - - LLViewerInventoryCategory* settings_folder = new LLViewerInventoryCategory(gAgent.getID()); - settings_folder->rename(std::string("Settings")); - LLUUID settings_folder_id; - settings_folder_id.generate(); - settings_folder->setUUID(settings_folder_id); - gSystemFolderSettings = settings_folder_id; - settings_folder->setParent(gSystemFolderRoot); - settings_folder->setPreferredType(LLFolderType::FT_NONE); - gInventory.addCategory(settings_folder); - - LLViewerInventoryCategory* assets_folder = new LLViewerInventoryCategory(gAgent.getID()); - assets_folder->rename(std::string("Assets")); - LLUUID assets_folder_id; - assets_folder_id.generate(); - assets_folder->setUUID(assets_folder_id); - gSystemFolderAssets = assets_folder_id; - assets_folder->setParent(gSystemFolderRoot); - assets_folder->setPreferredType(LLFolderType::FT_NONE); - gInventory.addCategory(assets_folder); - } - display_startup(); - // - LLSD buddy_list = response["buddy-list"]; if(buddy_list.isDefined()) { @@ -4226,4 +4206,4 @@ void transition_back_to_login_panel(const std::string& emsg) // Bounce back to the login screen. reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); gSavedSettings.setBOOL("AutoLogin", FALSE); -} \ No newline at end of file +} diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 73aaa6194..e5f14641e 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -73,7 +73,6 @@ #include "llviewerparcelmgr.h" #include "llviewerthrottle.h" #include "lluictrlfactory.h" -#include "llvoiceclient.h" // for gVoiceClient #include "llagentui.h" #include "lltoolmgr.h" diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index f125d2ca5..18155eb58 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -4,10 +4,9 @@ * @brief LLTextureCtrl class implementation including related functions * * $LicenseInfo:firstyear=2002&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2002-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -71,7 +70,6 @@ #include "lltrans.h" // #include "llmenugl.h" -#include "lllocalinventory.h" // // tag: vaa emerald local_asset_browser [begin] @@ -186,7 +184,7 @@ public: static void onBtnRemove( void* userdata ); static void onBtnBrowser( void* userdata ); - static void onLocalScrollCommit ( LLUICtrl* ctrl, void *userdata ); + void onLocalScrollCommit(); // tag: vaa emerald local_asset_browser [end] protected: @@ -285,12 +283,11 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id) } else { - LLInventoryItem* itemp = gInventory.getItem(image_id); if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID())) { // no copy texture - childSetValue("apply_immediate_check", FALSE); + getChild("apply_immediate_check")->setValue(FALSE); mNoCopyTextureSelected = TRUE; } mInventoryPanel->setSelection(item_id, TAKE_FOCUS_NO); @@ -300,7 +297,7 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id) void LLFloaterTexturePicker::setActive( BOOL active ) { - if (!active && childGetValue("Pipette").asBoolean()) + if (!active && getChild("Pipette")->getValue().asBoolean()) { stopUsingPipette(); } @@ -312,7 +309,7 @@ void LLFloaterTexturePicker::setCanApplyImmediately(BOOL b) mCanApplyImmediately = b; if (!mCanApplyImmediately) { - childSetValue("apply_immediate_check", FALSE); + getChild("apply_immediate_check")->setValue(FALSE); } updateFilterPermMask(); } @@ -486,13 +483,12 @@ BOOL LLFloaterTexturePicker::postBuild() childSetAction("Browser", LLFloaterTexturePicker::onBtnBrowser, this); mLocalScrollCtrl = getChild("local_name_list"); - mLocalScrollCtrl->setCallbackUserData(this); - mLocalScrollCtrl->setCommitCallback(onLocalScrollCommit); + mLocalScrollCtrl->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onLocalScrollCommit, this)); LocalAssetBrowser::UpdateTextureCtrlList( mLocalScrollCtrl ); // tag: vaa emerald local_asset_browser [end] childSetCommitCallback("show_folders_check", onShowFolders, this); - childSetVisible("show_folders_check", FALSE); + getChildView("show_folders_check")->setVisible( FALSE); mFilterEdit = getChild("inventory search editor"); mFilterEdit->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onFilterEdit, this, _2)); @@ -530,12 +526,12 @@ BOOL LLFloaterTexturePicker::postBuild() mNoCopyTextureSelected = FALSE; - childSetValue("apply_immediate_check", gSavedSettings.getBOOL("ApplyTextureImmediately")); + getChild("apply_immediate_check")->setValue(gSavedSettings.getBOOL("ApplyTextureImmediately")); childSetCommitCallback("apply_immediate_check", onApplyImmediateCheck, this); if (!mCanApplyImmediately) { - childSetEnabled("show_folders_check", FALSE); + getChildView("show_folders_check")->setEnabled(FALSE); } getChild("Pipette")->setCommitCallback( boost::bind(&LLFloaterTexturePicker::onBtnPipette, this)); @@ -609,10 +605,10 @@ void LLFloaterTexturePicker::draw() updateImageStats(); // if we're inactive, gray out "apply immediate" checkbox - childSetEnabled("show_folders_check", mActive && mCanApplyImmediately && !mNoCopyTextureSelected); - childSetEnabled("Select", mActive); - childSetEnabled("Pipette", mActive); - childSetValue("Pipette", LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); + getChildView("show_folders_check")->setEnabled(mActive && mCanApplyImmediately && !mNoCopyTextureSelected); + getChildView("Select")->setEnabled(mActive); + getChildView("Pipette")->setEnabled(mActive); + getChild("Pipette")->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); //RN: reset search bar to reflect actual search query (all caps, for example) mFilterEdit->setText(mInventoryPanel->getFilterSubString()); @@ -635,11 +631,11 @@ void LLFloaterTexturePicker::draw() mTentativeLabel->setVisible( FALSE ); } - childSetEnabled("Default", mImageAssetID != mOwner->getDefaultImageAssetID()); - childSetEnabled("Blank", mImageAssetID != mWhiteImageAssetID ); - childSetEnabled("None", mOwner->getAllowNoTexture() && !mImageAssetID.isNull() ); - childSetEnabled("Invisible", mOwner->getAllowInvisibleTexture() && mImageAssetID != mInvisibleImageAssetID ); - childSetEnabled("Alpha", mImageAssetID != mAlphaImageAssetID ); + getChildView("Default")->setEnabled(mImageAssetID != mOwner->getDefaultImageAssetID()); + getChildView("Blank")->setEnabled(mImageAssetID != mWhiteImageAssetID); + getChildView("None")->setEnabled(mOwner->getAllowNoTexture() && !mImageAssetID.isNull() ); + getChildView("Invisible")->setEnabled(mOwner->getAllowInvisibleTexture() && mImageAssetID != mInvisibleImageAssetID); + getChildView("Alpha")->setEnabled(mImageAssetID != mAlphaImageAssetID); LLFloater::draw(); @@ -765,14 +761,14 @@ const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL co PermissionMask LLFloaterTexturePicker::getFilterPermMask() { - bool apply_immediate = childGetValue("apply_immediate_check").asBoolean(); + bool apply_immediate = getChild("apply_immediate_check")->getValue().asBoolean(); return apply_immediate ? mImmediateFilterPermMask : mNonImmediateFilterPermMask; } void LLFloaterTexturePicker::commitIfImmediateSet() { - bool apply_immediate = childGetValue("apply_immediate_check").asBoolean(); - if (!mNoCopyTextureSelected && apply_immediate && mOwner) + bool apply_immediate = getChild("apply_immediate_check")->getValue().asBoolean(); + if (!mNoCopyTextureSelected && mOwner && apply_immediate) { mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE); } @@ -909,15 +905,14 @@ void LLFloaterTexturePicker::onBtnBrowser(void *userdata) FloaterLocalAssetBrowser::show(NULL); } -// static, reacts to user clicking a valid field in the local scroll list. -void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl *ctrl, void *userdata) +// reacts to user clicking a valid field in the local scroll list. +void LLFloaterTexturePicker::onLocalScrollCommit() { - LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; - LLUUID id = (LLUUID)self->mLocalScrollCtrl->getSelectedItemLabel( LOCALLIST_COL_ID ); + LLUUID id(mLocalScrollCtrl->getSelectedItemLabel(LOCALLIST_COL_ID)); - self->mOwner->setImageAssetID( id ); - if ( self->childGetValue("apply_immediate_check").asBoolean() ) - { self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE, id); } // calls an overridden function. + mOwner->setImageAssetID(id); + if (childGetValue("apply_immediate_check").asBoolean()) + mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE, id); // calls an overridden function. } // tag: vaa emerald local_asset_browser [end] @@ -1077,7 +1072,7 @@ void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te ) } else { - LLToolPipette::getInstance()->setResult(FALSE, "You do not have a copy this \nof texture in your inventory"); + LLToolPipette::getInstance()->setResult(FALSE, LLTrans::getString("InventoryNoTexture")); } } @@ -1275,7 +1270,6 @@ void LLTextureCtrl::setEnabled( BOOL enabled ) mEnable = enabled; LLView::setEnabled( enabled ); - } void LLTextureCtrl::setValid(BOOL valid ) @@ -1423,11 +1417,11 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op) lldebugs << "mImageAssetID: " << mImageAssetID << llendl; if (op == TEXTURE_SELECT && mOnSelectCallback) { - mOnSelectCallback(this, mCallbackUserData); + mOnSelectCallback( this, LLSD() ); } else if (op == TEXTURE_CANCEL && mOnCancelCallback) { - mOnCancelCallback(this, mCallbackUserData); + mOnCancelCallback( this, LLSD() ); } else { @@ -1456,11 +1450,11 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) if (op == TEXTURE_SELECT && mOnSelectCallback) { - mOnSelectCallback(this, mCallbackUserData); + mOnSelectCallback(this, LLSD()); } else if (op == TEXTURE_CANCEL && mOnCancelCallback) { - mOnCancelCallback(this, mCallbackUserData); + mOnCancelCallback(this, LLSD()); } else { @@ -1576,7 +1570,6 @@ void LLTextureCtrl::draw() mTentativeLabel->setVisible( !mTexturep.isNull() && getTentative() ); - // Show "Loading..." string on the top left corner while this texture is loading. // Using the discard level, do not show the string if the texture is almost but not // fully loaded. @@ -1613,13 +1606,12 @@ BOOL LLTextureCtrl::allowDrop(LLInventoryItem* item) // PermissionMask filter_perm_mask = mCanApplyImmediately ? commented out due to no-copy texture loss. // mImmediateFilterPermMask : mNonImmediateFilterPermMask; - PermissionMask filter_perm_mask = mImmediateFilterPermMask; if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask ) { if(mDragCallback) { - return mDragCallback(this, item, mCallbackUserData); + return mDragCallback(this, item); } else { @@ -1639,7 +1631,7 @@ BOOL LLTextureCtrl::doDrop(LLInventoryItem* item) { // if it returns TRUE, we return TRUE, and therefore the // commit is called above. - return mDropCallback(this, item, mCallbackUserData); + return mDropCallback(this, item); } // no callback installed, so just set the image ids and carry on. @@ -1669,64 +1661,6 @@ LLSD LLTextureCtrl::getValue() const } -///////////////////////////////////////////////////////////////////////////////// -// LLToolTexEyedropper - -class LLToolTexEyedropper : public LLTool -{ -public: - LLToolTexEyedropper( void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ), void* userdata ); - virtual ~LLToolTexEyedropper(); - virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); - -protected: - void (*mCallback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ); - void* mCallbackUserData; -}; -LLToolTexEyedropper::LLToolTexEyedropper( - void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ), - void* userdata ) - : LLTool(std::string("texeyedropper")), - mCallback( callback ), - mCallbackUserData( userdata ) -{ -} - -LLToolTexEyedropper::~LLToolTexEyedropper() -{ -} - - -BOOL LLToolTexEyedropper::handleMouseDown(S32 x, S32 y, MASK mask) -{ - // this will affect framerate on mouse down - const LLPickInfo& pick = gViewerWindow->pickImmediate(x, y, FALSE); - LLViewerObject* hit_obj = pick.getObject(); - if (hit_obj && - !hit_obj->isAvatar()) - { - if( (0 <= pick.mObjectFace) && (pick.mObjectFace < hit_obj->getNumTEs()) ) - { - LLViewerTexture* image = hit_obj->getTEImage( pick.mObjectFace ); - if( image ) - { - if( mCallback ) - { - mCallback( hit_obj->getID(), image->getID(), mCallbackUserData ); - } - } - } - } - return TRUE; -} - -BOOL LLToolTexEyedropper::handleHover(S32 x, S32 y, MASK mask) -{ - lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolTexEyedropper" << llendl; - gViewerWindow->getWindow()->setCursor(UI_CURSOR_CROSS); // TODO: better cursor - return TRUE; -} diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 35a5e9c8f..27afeba3d 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -4,10 +4,9 @@ * @brief LLTextureCtrl class header file including related functions * * $LicenseInfo:firstyear=2002&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2002-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -48,7 +47,8 @@ class LLViewBorder; class LLViewerFetchedTexture; // used for setting drag & drop callbacks. -typedef BOOL (*drag_n_drop_callback)(LLUICtrl*, LLInventoryItem*, void*); +typedef boost::function drag_n_drop_callback; + ////////////////////////////////////////////////////////////////////////////////////////// // LLTextureCtrl @@ -154,9 +154,9 @@ public: // necessariliy any other change. void setDropCallback(drag_n_drop_callback cb) { mDropCallback = cb; } - void setOnCancelCallback(LLUICtrlCallback cb) { mOnCancelCallback = cb; } + void setOnCancelCallback(commit_callback_t cb) { mOnCancelCallback = cb; } - void setOnSelectCallback(LLUICtrlCallback cb) { mOnSelectCallback = cb; } + void setOnSelectCallback(commit_callback_t cb) { mOnSelectCallback = cb; } void setShowLoadingPlaceholder(BOOL showLoadingPlaceholder); @@ -170,8 +170,8 @@ private: private: drag_n_drop_callback mDragCallback; drag_n_drop_callback mDropCallback; - LLUICtrlCallback mOnCancelCallback; - LLUICtrlCallback mOnSelectCallback; + commit_callback_t mOnCancelCallback; + commit_callback_t mOnSelectCallback; LLPointer mTexturep; LLColor4 mBorderColor; LLUUID mImageItemID; diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp index 897b2237c..791f8f248 100644 --- a/indra/newview/lltoolbar.cpp +++ b/indra/newview/lltoolbar.cpp @@ -84,10 +84,9 @@ class LLFakeResizeHandle : public LLResizeHandle { public: - LLFakeResizeHandle(const std::string& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner = RIGHT_BOTTOM ) - : LLResizeHandle(name, rect, min_width, min_height, corner ) - { - + LLFakeResizeHandle(const LLResizeHandle::Params& p) + : LLResizeHandle(p) + { } virtual BOOL handleHover(S32 x, S32 y, MASK mask) { return FALSE; }; @@ -183,9 +182,13 @@ BOOL LLToolBar::postBuild() #if LL_DARWIN if(mResizeHandle == NULL) { - LLRect rect(0, 0, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT); - mResizeHandle = new LLFakeResizeHandle(std::string(""), rect, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT); - this->addChildInBack(mResizeHandle); + LLResizeHandle::Params p; + p.rect(LLRect(0, 0, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT)); + p.name(std::string("")); + p.min_width(RESIZE_HANDLE_WIDTH); + p.min_height(RESIZE_HANDLE_HEIGHT); + p.corner(LLResizeHandle::RIGHT_BOTTOM); + mResizeHandle = new LLFakeResizeHandle(p); this->addChildInBack(mResizeHandle); LLLayoutStack* toolbar_stack = getChild("toolbar_stack"); toolbar_stack->reshape(toolbar_stack->getRect().getWidth() - RESIZE_HANDLE_WIDTH, toolbar_stack->getRect().getHeight()); } diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 485a96634..fe41db3a6 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -1512,10 +1512,9 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL if(!item || !obj) return ACCEPT_NO; // HACK: downcast LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item; - // - //if(!vitem->isFinished()) return ACCEPT_NO; - if(!vitem->isFinished() && !(gInventory.isObjectDescendentOf(vitem->getUUID(), gSystemFolderRoot))) return ACCEPT_NO; - // + + if(!vitem->isFinished()) return ACCEPT_NO; + if (vitem->getIsLinkType()) return ACCEPT_NO; // No giving away links // deny attempts to drop from an object onto itself. This is to @@ -1980,10 +1979,9 @@ EAcceptance LLToolDragAndDrop::dad3dApplyToObject( LLViewerInventoryItem* item; LLViewerInventoryCategory* cat; locateInventory(item, cat); - // - //if(!item || !item->isFinished()) return ACCEPT_NO; - if( !item || (!item->isFinished() && !(gInventory.isObjectDescendentOf(item->getUUID(), gSystemFolderRoot))) ) return ACCEPT_NO; - // + + if(!item || !item->isFinished()) return ACCEPT_NO; + EAcceptance rv = willObjectAcceptInventory(obj, item); if((mask & MASK_CONTROL)) { diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 7df652c41..fd8915e84 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -182,19 +182,19 @@ void audio_update_volume(bool force_update) LLViewerMedia::setVolume( mute_media ? 0.0f : media_volume ); // Voice - if (gVoiceClient) + if (LLVoiceClient::instanceExists()) { F32 voice_volume = mute_volume * master_volume * audio_level_voice; - gVoiceClient->setVoiceVolume(mute_voice ? 0.f : voice_volume); - gVoiceClient->setMicGain(mute_voice ? 0.f : audio_level_mic); + LLVoiceClient::getInstance()->setVoiceVolume(mute_voice ? 0.f : voice_volume); + LLVoiceClient::getInstance()->setMicGain(mute_voice ? 0.f : audio_level_mic); if (!gViewerWindow->getActive() && mute_when_minimized) { - gVoiceClient->setMuteMic(true); + LLVoiceClient::getInstance()->setMuteMic(true); } else { - gVoiceClient->setMuteMic(false); + LLVoiceClient::getInstance()->setMuteMic(false); } } } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 59958523b..8fd959cf2 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -170,7 +170,7 @@ static bool handleRenderPerfTestChanged(const LLSD& newvalue) LLPipeline::RENDER_TYPE_CLASSIC_CLOUDS, LLPipeline::RENDER_TYPE_HUD_PARTICLES, LLPipeline::END_RENDER_TYPES); - gPipeline.setRenderDebugFeatureControl(LLPipeline::RENDER_DEBUG_FEATURE_UI, false); + gPipeline.setRenderDebugFeatureControl(~(U32)0, false); // Reset all RENDER_DEBUG_FEATURE_* flags. } else { @@ -547,6 +547,12 @@ bool handleEffectColorChanged(const LLSD& newvalue) return true; } +bool handleVoiceClientPrefsChanged(const LLSD& newvalue) +{ + LLVoiceClient::getInstance()->updateSettings(); + return true; +} + bool handleVelocityInterpolate(const LLSD& newvalue) { LLMessageSystem* msg = gMessageSystem; @@ -571,15 +577,6 @@ bool handleVelocityInterpolate(const LLSD& newvalue) return true; } -bool handleVoiceClientPrefsChanged(const LLSD& newvalue) -{ - if(gVoiceClient) - { - gVoiceClient->updateSettings(); - } - return true; -} - bool handleTranslateChatPrefsChanged(const LLSD& newvalue) { LLFloaterChat* floaterp = LLFloaterChat::getInstance(); diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 165e22201..9678a880b 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -293,9 +293,6 @@ void LLViewerInventoryItem::cloneViewerItem(LLPointer& ne void LLViewerInventoryItem::removeFromServer() { - // this check is ghetto - if((mParentUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot))) return; - // lldebugs << "Removing inventory item " << mUUID << " from server." << llendl; @@ -314,9 +311,6 @@ void LLViewerInventoryItem::removeFromServer() void LLViewerInventoryItem::updateServer(BOOL is_new) const { - // - if((mParentUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot))) return; - // if(!mIsComplete) { // *FIX: deal with this better. @@ -492,9 +486,6 @@ bool LLViewerInventoryItem::exportFileLocal(LLFILE* fp) const void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const { - // - if(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)) return; - // LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoveInventoryItem); msg->nextBlockFast(_PREHASH_AgentData); @@ -564,9 +555,6 @@ void LLViewerInventoryCategory::copyViewerCategory(const LLViewerInventoryCatego void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const { - // - if(gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot)) return; - // LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoveInventoryFolder); msg->nextBlockFast(_PREHASH_AgentData); @@ -608,11 +596,13 @@ void LLViewerInventoryCategory::removeFromServer( void ) llinfos << "Removing inventory category " << mUUID << " from server." << llendl; // communicate that change with the server. +#ifndef DELETE_SYSTEM_FOLDERS if(LLFolderType::lookupIsProtectedType(mPreferredType)) { LLNotificationsUtil::add("CannotRemoveProtectedCategories"); return; } +#endif LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1); gInventory.accountForUpdate(up); @@ -629,9 +619,6 @@ void LLViewerInventoryCategory::removeFromServer( void ) bool LLViewerInventoryCategory::fetch() { - // - if((mUUID == gSystemFolderRoot) || (gInventory.isObjectDescendentOf(mUUID, gSystemFolderRoot))) return false; - // if((VERSION_UNKNOWN == getVersion()) && (!mDescendentsRequested.getStarted() || mDescendentsRequested.hasExpired())) //Expired check prevents multiple downloads. diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index c45680685..ee4162dce 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -47,6 +47,7 @@ #include "llevent.h" // LLSimpleListener #include "llfloaterwebcontent.h" // for handling window close requests and geometry change requests in media browser windows. #include "llfocusmgr.h" +#include "llhttpclient.h" #include "llkeyboard.h" #include "llmarketplacefunctions.h" #include "llmediaentry.h" @@ -83,6 +84,8 @@ #include // for SkinFolder listener #include +std::string getProfileURL(const std::string& agent_name); + /*static*/ const char* LLViewerMedia::AUTO_PLAY_MEDIA_SETTING = "ParcelMediaAutoPlayEnable"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING = "MediaShowOnOthers"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING = "MediaShowWithinParcel"; diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 825470f6c..8df0599dd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -63,7 +63,6 @@ #include "llfirstuse.h" #include "llfloaterabout.h" #include "llfloateractivespeakers.h" -#include "llfloateravatarinfo.h" #include "llfloateravatarlist.h" #include "llfloateravatartextures.h" #include "llfloaterbeacons.h" @@ -79,12 +78,9 @@ #include "llfloaterdirectory.h" #include "llfloatereditui.h" #include "llfloaterchatterbox.h" -#include "llfloaterfriends.h" #include "llfloaterfonttest.h" #include "llfloatergesture.h" #include "llfloatergodtools.h" -#include "llfloatergroupinvite.h" -#include "llfloatergroups.h" #include "llfloaterhtmlcurrency.h" #include "llfloaterhud.h" #include "llfloaterinspect.h" @@ -113,6 +109,7 @@ #include "llfloaterteleporthistory.h" #include "llfloatertest.h" #include "llfloatertools.h" +#include "llfloatervoiceeffect.h" #include "llfloaterwater.h" #include "llfloaterwebcontent.h" #include "llfloaterwindlight.h" @@ -120,6 +117,7 @@ #include "llfloatermemleak.h" #include "llframestats.h" #include "llgivemoney.h" +#include "llavataractions.h" #include "llgroupmgr.h" #include "llhoverview.h" #include "llhudeffecttrail.h" @@ -172,6 +170,7 @@ #include "llfloatermessagelog.h" #include "shfloatermediaticker.h" #include "llpacketring.h" +#include "aihttpview.h" // #include "scriptcounter.h" @@ -221,6 +220,8 @@ void handle_test_load_url(void*); // // Evil hackish imported globals +class AIHTTPView; + //extern BOOL gHideSelectedObjects; //extern BOOL gAllowSelectAvatar; //extern BOOL gDebugAvatarRotation; @@ -229,6 +230,7 @@ extern BOOL gDebugWindowProc; extern BOOL gDebugTextEditorTips; extern BOOL gShowOverlayTitle; extern BOOL gOcclusionCull; +extern AIHTTPView* gHttpView; // // Globals // @@ -817,6 +819,13 @@ void init_client_menu(LLMenuGL* menu) '6', MASK_CONTROL|MASK_SHIFT ) ); } + sub->addChild(new LLMenuItemCheckGL("HTTP Console", + &AIHTTPView::toggle_visibility, + NULL, + &get_visibility, + (void*)gHttpView, + '7', MASK_CONTROL|MASK_SHIFT ) ); + sub->addChild(new LLMenuItemCheckGL("Region Debug Console", handle_singleton_toggle, NULL, handle_singleton_check,NULL,'`', MASK_CONTROL|MASK_SHIFT)); sub->addChild(new LLMenuItemCheckGL("Fast Timers", @@ -3754,11 +3763,6 @@ bool LLHaveCallingcard::operator()(LLInventoryCategory* cat, } */ -BOOL is_agent_friend(const LLUUID& agent_id) -{ - return (LLAvatarTracker::instance().getBuddyInfo(agent_id) != NULL); -} - BOOL is_agent_mappable(const LLUUID& agent_id) { const LLRelationship* buddy_info = LLAvatarTracker::instance().getBuddyInfo(agent_id); @@ -3776,9 +3780,9 @@ class LLAvatarEnableAddFriend : public view_listener_t bool handleEvent(LLPointer event, const LLSD& userdata) { LLVOAvatar* avatar = find_avatar_from_object(LLSelectMgr::getInstance()->getSelection()->getPrimaryObject()); -// bool new_value = avatar && !is_agent_friend(avatar->getID()); +// bool new_value = avatar && !LLAvatarActions::isFriend(avatar->getID()); // [RLVa:KB] - Checked: 2010-04-20 (RLVa-1.2.0f) | Modified: RLVa-1.2.0f - bool new_value = avatar && !is_agent_friend(avatar->getID()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)); + bool new_value = avatar && !LLAvatarActions::isFriend(avatar->getID()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)); // [/RLVa:KB] gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value); return true; @@ -3800,7 +3804,7 @@ void request_friendship(const LLUUID& dest_id) } if (!full_name.empty()) { - LLPanelFriends::requestFriendshipDialog(dest_id, full_name); + LLAvatarActions::requestFriendshipDialog(dest_id, full_name); } else { @@ -6080,30 +6084,6 @@ void handle_look_at_selection(const LLSD& param) } } -void callback_invite_to_group(LLUUID group_id, void *user_data) -{ - std::vector agent_ids; - agent_ids.push_back(*(LLUUID *)user_data); - - LLFloaterGroupInvite::showForGroup(group_id, &agent_ids); -} - -void invite_to_group(const LLUUID& dest_id) -{ - LLViewerObject* dest = gObjectList.findObject(dest_id); - if(dest && dest->isAvatar()) - { - LLFloaterGroupPicker* widget; - widget = LLFloaterGroupPicker::showInstance(LLSD(gAgent.getID())); - if (widget) - { - widget->center(); - widget->setPowersMask(GP_MEMBER_INVITE); - widget->setSelectCallback(callback_invite_to_group, (void *)&dest_id); - } - } -} - class LLAvatarInviteToGroup : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -6114,7 +6094,7 @@ class LLAvatarInviteToGroup : public view_listener_t if ( (avatar) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ) // [/RLVa:KB] { - invite_to_group(avatar->getID()); + LLAvatarActions::inviteToGroup(avatar->getID()); } return true; } @@ -6125,9 +6105,9 @@ class LLAvatarAddFriend : public view_listener_t bool handleEvent(LLPointer event, const LLSD& userdata) { LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); -// if(avatar && !is_agent_friend(avatar->getID())) +// if(avatar && !LLAvatarActions::isFriend(avatar->getID())) // [RLVa:KB] - Checked: 2010-04-20 (RLVa-1.2.0f) | Modified: RLVa-1.2.0f - if ( (avatar && !is_agent_friend(avatar->getID())) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ) + if ( (avatar && !LLAvatarActions::isFriend(avatar->getID())) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ) // [/RLVa:KB] { request_friendship(avatar->getID()); @@ -6396,6 +6376,7 @@ struct MenuFloaterDict : public LLSingleton registerFloater ("script info"); registerFloater ("stat bar"); registerFloater ("teleport history"); + registerFloater ("voice effect"); registerFloater ("pathfinding_characters"); registerFloater ("pathfinding_linksets"); @@ -6610,7 +6591,7 @@ class LLShowAgentProfile : public view_listener_t if ( (avatar) && ((!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) || (gAgent.getID() == agent_id)) ) // [/RLVa:KB] { - LLFloaterAvatarInfo::show(avatar->getID()); + LLAvatarActions::showProfile(avatar->getID()); } return true; } @@ -7261,28 +7242,12 @@ class LLAvatarSendIM : public view_listener_t bool handleEvent(LLPointer event, const LLSD& userdata) { LLVOAvatar* avatar = find_avatar_from_object( LLSelectMgr::getInstance()->getSelection()->getPrimaryObject() ); -// [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) | OK - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - { - return true; - } +// if(avatar) +// [RLVa:KB] - Checked: 2010-06-04 (RLVa-1.2.0d) | Added: RLVa-1.2.0d + if ((avatar) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))) // [/RLVa:KB] - if(avatar) { - std::string name("IM"); - LLNameValue *first = avatar->getNVPair("FirstName"); - LLNameValue *last = avatar->getNVPair("LastName"); - if (first && last) - { - name.assign( first->getString() ); - name.append(" "); - name.append( last->getString() ); - } - - gIMMgr->setFloaterOpen(TRUE); - //EInstantMessage type = have_agent_callingcard(gLastHitObjectID) - // ? IM_SESSION_ADD : IM_SESSION_CARDLESS_START; - gIMMgr->addSession(LLCacheName::cleanFullName(name),IM_NOTHING_SPECIAL,avatar->getID()); + LLAvatarActions::startIM(avatar->getID()); } return true; } diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index 265f77fd3..6d3c2947f 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -74,8 +74,6 @@ BOOL enable_deselect(void*); BOOL enable_undo(void*); BOOL enable_redo(void*); -// returns TRUE if we have a friend relationship with agent_id -BOOL is_agent_friend(const LLUUID& agent_id); BOOL is_agent_mappable(const LLUUID& agent_id); void menu_toggle_control( void* user_data ); diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 7492ea725..46cebc7b5 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -37,7 +37,6 @@ #include "llagent.h" #include "llagentcamera.h" #include "statemachine/aifilepicker.h" -#include "llfloateranimpreview.h" #include "llfloaterbvhpreview.h" #include "llfloaterimagepreview.h" #include "llfloatermodelpreview.h" @@ -217,7 +216,7 @@ bool AIFileUpload::is_valid(std::string const& filename, ELoadFilter type) // No extension LLSD args; args["FILE"] = short_name; - LLNotifications::instance().add("NoFileExtension", args); + LLNotificationsUtil::add("NoFileExtension", args); return false; } else @@ -260,7 +259,7 @@ bool AIFileUpload::is_valid(std::string const& filename, ELoadFilter type) LLSD args; args["EXTENSION"] = ext; args["VALIDS"] = valid_extensions; - LLNotifications::instance().add("InvalidFileExtension", args); + LLNotificationsUtil::add("InvalidFileExtension", args); return false; } }//end else (non-null extension) @@ -279,7 +278,7 @@ bool AIFileUpload::is_valid(std::string const& filename, ELoadFilter type) llinfos << error_msg << ": " << filename << llendl; LLSD args; args["FILE"] = filename; - LLNotifications::instance().add( error_msg, args ); + LLNotificationsUtil::add( error_msg, args ); return false; } }//end if a wave/sound file @@ -331,9 +330,7 @@ class LLFileUploadSound : public view_listener_t, public AIFileUpload // Inherited from AIFileUpload. /*virtual*/ void handle_event(std::string const& filename) { - LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_sound_preview.xml"); - floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() )); + LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterSoundPreview(LLSD(filename)), "floater_sound_preview.xml"); } }; @@ -349,20 +346,34 @@ class LLFileUploadAnim : public view_listener_t, public AIFileUpload // Inherited from AIFileUpload. /*virtual*/ void handle_event(std::string const& filename) { - int len = filename.size(); - if (len >= 5 && filename.substr(len - 5, 5) == ".anim") + if (filename.rfind(".anim") != std::string::npos) { - LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename); - LLUICtrlFactory::getInstance()->buildFloater(floaterp, "floater_animation_anim_preview.xml"); - floaterp->childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%s%d", gHippoGridManager->getConnectedGrid()->getCurrencySymbol().c_str(), LLGlobalEconomy::Singleton::getInstance()->getPriceUpload())); + LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterAnimPreview(LLSD(filename)), "floater_animation_anim_preview.xml"); } else { - LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterBvhPreview(filename), "floater_animation_bvh_preview.xml"); + LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterBvhPreview(LLSD(filename)), "floater_animation_bvh_preview.xml"); } } }; +/* Singu TODO? LL made a class to upload scripts, but never actually hooked everything up, it'd be nice for us to offer such a thing. +class LLFileUploadScript : public view_listener_t, public AIFileUpload +{ + bool handleEvent(LLPointer event, const LLSD& userdata) + { + start_filepicker(FFLOAD_SCRIPT, "script"); + return true; + } + + protected: + // Inherited from AIFileUpload. + virtual void handle_event(std::string const& filename) + { + LLUICtrlFactory::getInstance()->buildFloater(new LLFloaterScriptPreview(LLSD(filename)), "floater_script_preview.xml"); + } +};*/ + class LLFileUploadBulk : public view_listener_t { bool handleEvent(LLPointer event, const LLSD& userdata) @@ -386,13 +397,13 @@ class LLFileUploadBulk : public view_listener_t const char* notification_type = expected_upload_cost ? "BulkTemporaryUpload" : "BulkTemporaryUploadFree"; LLSD args; args["UPLOADCOST"] = gHippoGridManager->getConnectedGrid()->getUploadFee(); - LLNotifications::instance().add(notification_type, args, LLSD(), onConfirmBulkUploadTemp); + LLNotificationsUtil::add(notification_type, args, LLSD(), onConfirmBulkUploadTemp); return true; } static bool onConfirmBulkUploadTemp(const LLSD& notification, const LLSD& response ) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); bool enabled; if (option == 0) // yes enabled = true; @@ -446,7 +457,7 @@ class LLFileUploadBulk : public view_listener_t void upload_error(const std::string& error_message, const std::string& label, const std::string& filename, const LLSD& args) { llwarns << error_message << llendl; - LLNotifications::instance().add(label, args); + LLNotificationsUtil::add(label, args); if(LLFile::remove(filename) == -1) { lldebugs << "unable to remove temp file" << llendl; @@ -1361,6 +1372,7 @@ void init_menu_file() (new LLFileUploadImage())->registerListener(gMenuHolder, "File.UploadImage"); (new LLFileUploadSound())->registerListener(gMenuHolder, "File.UploadSound"); (new LLFileUploadAnim())->registerListener(gMenuHolder, "File.UploadAnim"); + //(new LLFileUploadScript())->registerListener(gMenuHolder, "File.UploadScript"); // Singu TODO? (new LLFileUploadModel())->registerListener(gMenuHolder, "File.UploadModel"); (new LLFileUploadBulk())->registerListener(gMenuHolder, "File.UploadBulk"); (new LLFileCloseWindow())->registerListener(gMenuHolder, "File.CloseWindow"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 673462beb..2a09ccbd7 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -55,13 +55,10 @@ #include "llagentcamera.h" #include "llcallingcard.h" #include "llfirstuse.h" -#include "llfloaterbvhpreview.h" #include "llfloaterbump.h" #include "llfloaterbuycurrency.h" #include "llfloaterbuyland.h" #include "llfloaterchat.h" -#include "llfloatergroupinfo.h" -#include "llfloaterimagepreview.h" #include "llfloaterland.h" #include "llfloaterregioninfo.h" #include "llfloaterlandholdings.h" @@ -69,6 +66,7 @@ #include "llfloaterpostcard.h" #include "llfloaterpreference.h" #include "llfloaterteleporthistory.h" +#include "llgroupactions.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llimpanel.h" @@ -88,7 +86,7 @@ #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" -#include "llfloateractivespeakers.h" +#include "llspeakers.h" #include "lltrans.h" #include "llviewerfoldertype.h" #include "llviewergenericmessage.h" @@ -640,7 +638,7 @@ bool join_group_response(const LLSD& notification, const LLSD& response) if (option == 2 && !group_id.isNull()) { - LLFloaterGroupInfo::showFromUUID(group_id); + LLGroupActions::show(group_id); LLSD args; args["MESSAGE"] = message; LLNotificationsUtil::add("JoinGroup", args, notification["payload"]); @@ -1321,6 +1319,14 @@ void inventory_offer_mute_callback(const LLUUID& blocked_id, gNotifyBoxView->purgeMessagesMatching(OfferMatcher(blocked_id)); } +LLOfferInfo::LLOfferInfo() + : mFromGroup(FALSE) + , mFromObject(FALSE) + , mIM(IM_NOTHING_SPECIAL) + , mType(LLAssetType::AT_NONE) +{ +} + LLOfferInfo::LLOfferInfo(const LLSD& sd) { mIM = (EInstantMessage)sd["im_type"].asInteger(); @@ -1336,6 +1342,21 @@ LLOfferInfo::LLOfferInfo(const LLSD& sd) mHost = LLHost(sd["sender"].asString()); } +LLOfferInfo::LLOfferInfo(const LLOfferInfo& info) +{ + mIM = info.mIM; + mFromID = info.mFromID; + mFromGroup = info.mFromGroup; + mFromObject = info.mFromObject; + mTransactionID = info.mTransactionID; + mFolderID = info.mFolderID; + mObjectID = info.mObjectID; + mType = info.mType; + mFromName = info.mFromName; + mDesc = info.mDesc; + mHost = info.mHost; +} + LLSD LLOfferInfo::asLLSD() { LLSD sd; @@ -1781,7 +1802,7 @@ bool group_vote_callback(const LLSD& notification, const LLSD& response) case 0: // Vote Now // Open up the voting tab - LLFloaterGroupInfo::showFromUUID(group_id, "voting_tab"); + LLGroupActions::showTab(group_id, "voting_tab"); break; default: // Vote Later or @@ -2594,7 +2615,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // Also send down the old path for now. if (IM_GROUP_NOTICE_REQUESTED == dialog) { - LLFloaterGroupInfo::showNotice(subj,mes,group_id,has_inventory,item_name,info); + LLGroupActions::showNotice(subj,mes,group_id,has_inventory,item_name,info); } else { @@ -2741,9 +2762,10 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_INVENTORY_ACCEPTED: { // args["NAME"] = name; -// [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Modified: RLVa-0.2.0b +// [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a + // Only anonymize the name if the agent is nearby, there isn't an open IM session to them and their profile isn't open bool fRlvFilterName = (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(from_id)) && - (!LLFloaterAvatarInfo::getInstance(from_id)); + (!RlvUIEnabler::hasOpenProfile(from_id)) && (!RlvUIEnabler::hasOpenIM(from_id)); args["NAME"] = (!fRlvFilterName) ? name : RlvStrings::getAnonym(name); // [/RLVa:KB] LLNotificationsUtil::add("InventoryAccepted", args); @@ -2752,9 +2774,10 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_INVENTORY_DECLINED: { // args["NAME"] = name; -// [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Modified: RLVa-0.2.0b +// [RLVa:KB] - Checked: 2010-11-02 (RLVa-1.2.2a) | Modified: RLVa-1.2.2a + // Only anonymize the name if the agent is nearby, there isn't an open IM session to them and their profile isn't open bool fRlvFilterName = (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(from_id)) && - (!LLFloaterAvatarInfo::getInstance(from_id)); + (!RlvUIEnabler::hasOpenProfile(from_id)) && (!RlvUIEnabler::hasOpenIM(from_id)); args["NAME"] = (!fRlvFilterName) ? name : RlvStrings::getAnonym(name); // [/RLVa:KB] LLNotificationsUtil::add("InventoryDeclined", args); @@ -2821,7 +2844,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) std::string saved; if(offline == IM_OFFLINE) { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } buffer = separator_string + saved + message.substr(message_offset); gIMMgr->addMessage( @@ -3586,78 +3611,81 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // because I moved it to above //chatter = gObjectList.findObject(from_id); // - if (chatter) + + msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); + + if ((source_temp == CHAT_SOURCE_OBJECT) && (type_temp == CHAT_TYPE_OWNER) && + (mesg.substr(0, 3) == "># ")) { - if ((source_temp == CHAT_SOURCE_OBJECT) && (type_temp == CHAT_TYPE_OWNER) && - (mesg.substr(0, 3) == "># ")) + if (mesg.substr(mesg.size()-3, 3) == " #<"){ + // hello from object + if (from_id.isNull()) return; + char buf[200]; + snprintf(buf, 200, "%s v%d.%d.%d", gVersionChannel, gVersionMajor, gVersionMinor, gVersionPatch); + send_chat_from_viewer(buf, CHAT_TYPE_WHISPER, 427169570); + sChatObjectAuth[from_id] = 1; + return; + } + else if (from_id.isNull() || sChatObjectAuth.find(from_id) != sChatObjectAuth.end()) { - if (mesg.substr(mesg.size()-3, 3) == " #<"){ - // hello from object - if (from_id.isNull()) return; - char buf[200]; - snprintf(buf, 200, "%s v%d.%d.%d", gVersionChannel, gVersionMajor, gVersionMinor, gVersionPatch); - send_chat_from_viewer(buf, CHAT_TYPE_WHISPER, 427169570); - sChatObjectAuth[from_id] = 1; - return; - } - else if (from_id.isNull() || sChatObjectAuth.find(from_id) != sChatObjectAuth.end()) + LLUUID key; + if (key.set(mesg.substr(3, 36),false)) { - LLUUID key; - if (key.set(mesg.substr(3, 36),false)) + // object command found + if (key.isNull() && (mesg.size() == 39)) { - // object command found - if (key.isNull() && (mesg.size() == 39)) + // clear all nameplates + for (int i=0; i(obj)) - { - avatar->clearNameFromChat(); - } - } - } - else - { - if (key.isNull()) - { - llwarns << "Nameplate from chat on NULL avatar (ignored)" << llendl; - return; - } - LLVOAvatar *avatar = gObjectList.findAvatar(key); - if (!avatar) - { - llwarns << "Nameplate from chat on invalid avatar (ignored)" << llendl; - return; - } - if (mesg.size() == 39) + LLViewerObject *obj = gObjectList.getObject(i); + if (LLVOAvatar *avatar = dynamic_cast(obj)) { avatar->clearNameFromChat(); } - else if (mesg[39] == ' ') - { - avatar->setNameFromChat(mesg.substr(40)); - } } - return; } - else if (mesg.substr(2, 9) == " floater ") + else { - HippoFloaterXml::execute(mesg.substr(11)); - return; - } - else if (mesg.substr(2, 6) == " auth ") - { - std::string authUrl = mesg.substr(8); - authUrl += (authUrl.find('?') != std::string::npos)? "&auth=": "?auth="; - authUrl += gAuthString; - LLHTTPClient::get(authUrl, new AuthHandler); - return; + if (key.isNull()) + { + llwarns << "Nameplate from chat on NULL avatar (ignored)" << llendl; + return; + } + LLVOAvatar *avatar = gObjectList.findAvatar(key); + if (!avatar) + { + llwarns << "Nameplate from chat on invalid avatar (ignored)" << llendl; + return; + } + if (mesg.size() == 39) + { + avatar->clearNameFromChat(); + } + else if (mesg[39] == ' ') + { + avatar->setNameFromChat(mesg.substr(40)); + } } + return; + } + else if (mesg.substr(2, 9) == " floater ") + { + HippoFloaterXml::execute(mesg.substr(11)); + return; + } + else if (mesg.substr(2, 6) == " auth ") + { + std::string authUrl = mesg.substr(8); + authUrl += (authUrl.find('?') != std::string::npos)? "&auth=": "?auth="; + authUrl += gAuthString; + LLHTTPClient::get(authUrl, new AuthHandler); + return; } } + } + if (chatter) + { chat.mPosAgent = chatter->getPositionAgent(); // Make swirly things only for talking objects. (not script debug messages, though) @@ -3726,8 +3754,6 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) if (is_audible) { - msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, mesg); - // NaCl - Newline flood protection static LLCachedControl AntiSpamEnabled(gSavedSettings,"AntiSpamEnabled",false); if (AntiSpamEnabled && can_block(from_id)) @@ -6619,9 +6645,6 @@ void process_economy_data(LLMessageSystem *msg, void** /*user_data*/) LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; - LLFloaterImagePreview::setUploadAmount(upload_cost); - LLFloaterBvhPreview::setUploadAmount(upload_cost); - std::string fee = gHippoGridManager->getConnectedGrid()->getUploadFee(); gMenuHolder->childSetLabelArg("Upload Image", "[UPLOADFEE]", fee); gMenuHolder->childSetLabelArg("Upload Sound", "[UPLOADFEE]", fee); diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index fd9745d94..a31b7f486 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -216,11 +216,14 @@ bool highlight_offered_object(const LLUUID& obj_id); void set_dad_inventory_item(LLInventoryItem* inv_item, const LLUUID& into_folder_uuid); void set_dad_inbox_object(const LLUUID& object_id); -struct LLOfferInfo +class LLOfferInfo { - LLOfferInfo() {}; +public: + LLOfferInfo(); LLOfferInfo(const LLSD& sd); + LLOfferInfo(const LLOfferInfo& info); + void forceResponse(InventoryOfferResponse response); EInstantMessage mIM; @@ -243,5 +246,3 @@ struct LLOfferInfo void process_feature_disabled_message(LLMessageSystem* msg, void**); #endif - - diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 32817b9a1..0e8e97f3a 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -809,9 +809,9 @@ void LLViewerParcelMedia::filterMedia(LLParcel* parcel, U32 type) sDeniedMedia.erase(ip); dirty = true; } - if (dirty) + if (dirty && SLFloaterMediaFilter::findInstance()) { - SLFloaterMediaFilter::setDirty(); + SLFloaterMediaFilter::getInstance()->setDirty(); } } @@ -987,7 +987,7 @@ void callback_media_alert(const LLSD ¬ification, const LLSD &response, LLParc } LLViewerParcelMedia::sMediaQueries.erase(domain); - SLFloaterMediaFilter::setDirty(); + if (SLFloaterMediaFilter::findInstance()) SLFloaterMediaFilter::getInstance()->setDirty(); } void LLViewerParcelMedia::saveDomainFilterList() @@ -1018,7 +1018,7 @@ bool LLViewerParcelMedia::loadDomainFilterList() llifstream medialistFile(medialist_filename); LLSDSerialize::fromXML(sMediaFilterList, medialistFile); medialistFile.close(); - SLFloaterMediaFilter::setDirty(); + if (SLFloaterMediaFilter::findInstance()) SLFloaterMediaFilter::getInstance()->setDirty(); return true; } else @@ -1034,7 +1034,7 @@ void LLViewerParcelMedia::clearDomainFilterList() sDeniedMedia.clear(); saveDomainFilterList(); LLNotificationsUtil::add("MediaFiltersCleared"); - SLFloaterMediaFilter::setDirty(); + if (SLFloaterMediaFilter::findInstance()) SLFloaterMediaFilter::getInstance()->setDirty(); } std::string LLViewerParcelMedia::extractDomain(std::string url) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 73ee75104..7e9f8ea5e 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1621,7 +1621,11 @@ void LLViewerRegion::unpackRegionHandshake() msg->addUUID("AgentID", gAgent.getID()); msg->addUUID("SessionID", gAgent.getSessionID()); msg->nextBlock("RegionInfo"); - msg->addU32("Flags", 0x0 ); + + U32 flags = 0; + flags |= REGION_HANDSHAKE_SUPPORTS_SELF_APPEARANCE; + + msg->addU32("Flags", flags ); msg->sendReliable(host); } diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index 821b87a5d..e22fc3907 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -56,6 +56,8 @@ #define WATER 2 const U32 MAX_OBJECT_CACHE_ENTRIES = 50000; +// Region handshake flags +const U32 REGION_HANDSHAKE_SUPPORTS_SELF_APPEARANCE = 1U << 2; class LLEventPoll; class LLVLComposition; diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 587c67dda..9704fe2cb 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -3,10 +3,9 @@ * @brief Text editor widget to let users enter a multi-line document. * * $LicenseInfo:firstyear=2001&license=viewergpl$ - * + * Second Life Viewer Source Code * Copyright (c) 2001-2009, Linden Research, Inc. * - * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement @@ -32,42 +31,39 @@ #include "llviewerprecompiledheaders.h" -#include "llfocusmgr.h" -#include "llaudioengine.h" -#include "llagent.h" -#include "llinventory.h" -#include "llinventorydefines.h" -#include "llinventorymodel.h" -#include "llinventorypanel.h" -#include "llinventorybridge.h" // for landmark prefix string - #include "llviewertexteditor.h" -#include "llfloaterchat.h" +#include "llagent.h" +#include "llaudioengine.h" +#include "llavataractions.h" #include "llfloaterworldmap.h" -#include "llnotify.h" +#include "llfocusmgr.h" +#include "llinventorybridge.h" // for landmark prefix string +#include "llinventorydefines.h" +#include "llinventorymodel.h" +#include "llmemorystream.h" +#include "llmenugl.h" +#include "llnotecard.h" +#include "llnotificationsutil.h" #include "llpreview.h" #include "llpreviewtexture.h" #include "llpreviewnotecard.h" -#include "llpreviewlandmark.h" #include "llscrollbar.h" #include "lltooldraganddrop.h" +#include "lluictrlfactory.h" +#include "llviewerassettype.h" #include "llviewercontrol.h" +#include "llviewerinventory.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" -#include "llviewerinventory.h" -#include "lluictrlfactory.h" -#include "llnotecard.h" -#include "llmemorystream.h" -#include "llmenugl.h" -#include "llviewerassettype.h" - -#include "llappviewer.h" // for gPacificDaylightTime // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] +void open_landmark(LLViewerInventoryItem* inv_item, const std::string& title, BOOL show_keep_discard, const LLUUID& source_id, BOOL take_focus); +extern BOOL gPacificDaylightTime; + static LLRegisterWidget r("text_editor"); ///---------------------------------------------------------------------------- @@ -113,7 +109,7 @@ public: // See if we can bring an existing preview to the front if(!LLPreview::show(item->getUUID(), true)) { - if(!gSavedSettings.getBOOL("ShowNewInventory")) + if (gSavedSettings.getBOOL("ShowNewInventory")) // Singu Note: ShowNewInventory is true, not false, when they want a preview { // There isn't one, so make a new preview S32 left, top; @@ -1407,6 +1403,10 @@ BOOL LLViewerTextEditor::openEmbeddedItem(LLInventoryItem* item, llwchar wc) openEmbeddedLandmark( item, wc ); return TRUE; + case LLAssetType::AT_CALLINGCARD: + openEmbeddedCallingcard( item, wc ); + return TRUE; + case LLAssetType::AT_LSL_TEXT: case LLAssetType::AT_CLOTHING: case LLAssetType::AT_OBJECT: @@ -1483,18 +1483,26 @@ void LLViewerTextEditor::openEmbeddedNotecard( LLInventoryItem* item, llwchar wc copyInventory(item, gInventoryCallbacks.registerCB(mInventoryCallback)); } +void LLViewerTextEditor::openEmbeddedCallingcard( LLInventoryItem* item, llwchar wc ) +{ + if(item && !item->getCreatorUUID().isNull()) + { + LLAvatarActions::showProfile(item->getCreatorUUID()); + } +} + void LLViewerTextEditor::showUnsavedAlertDialog( LLInventoryItem* item ) { LLSD payload; payload["item_id"] = item->getUUID(); payload["notecard_id"] = mNotecardInventoryID; - LLNotifications::instance().add( "ConfirmNotecardSave", LLSD(), payload, LLViewerTextEditor::onNotecardDialog); + LLNotificationsUtil::add( "ConfirmNotecardSave", LLSD(), payload, LLViewerTextEditor::onNotecardDialog); } // static bool LLViewerTextEditor::onNotecardDialog(const LLSD& notification, const LLSD& response ) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if( option == 0 ) { // itemptr is deleted by LLPreview::save @@ -1512,13 +1520,13 @@ void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item, llwchar wc LLUUID item_id = item->getUUID(); payload["item_id"] = item_id; payload["item_wc"] = LLSD::Integer(wc); - LLNotifications::instance().add( "ConfirmItemCopy", LLSD(), payload, + LLNotificationsUtil::add( "ConfirmItemCopy", LLSD(), payload, boost::bind(&LLViewerTextEditor::onCopyToInvDialog, this, _1, _2)); } bool LLViewerTextEditor::onCopyToInvDialog(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if( 0 == option ) { LLUUID item_id = notification["payload"]["item_id"].asUUID(); diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h index 0a364a90f..702b97ca1 100644 --- a/indra/newview/llviewertexteditor.h +++ b/indra/newview/llviewertexteditor.h @@ -117,6 +117,7 @@ private: void openEmbeddedSound( LLInventoryItem* item, llwchar wc ); void openEmbeddedLandmark( LLInventoryItem* item, llwchar wc ); void openEmbeddedNotecard( LLInventoryItem* item, llwchar wc); + void openEmbeddedCallingcard( LLInventoryItem* item, llwchar wc); void showCopyToInvDialog( LLInventoryItem* item, llwchar wc ); void showUnsavedAlertDialog( LLInventoryItem* item ); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 7b1bc5133..6a1cf636a 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -58,7 +58,6 @@ // linden library includes #include "llaudioengine.h" // mute on minimize -#include "indra_constants.h" #include "llassetstorage.h" #include "llfontgl.h" #include "llmousehandler.h" @@ -78,12 +77,9 @@ #include "raytrace.h" // newview includes -#include "llagent.h" #include "llbox.h" #include "llchatbar.h" #include "llconsole.h" -#include "llviewercontrol.h" -#include "llcylinder.h" #include "lldebugview.h" #include "lldir.h" #include "lldrawable.h" @@ -94,20 +90,11 @@ #include "llface.h" #include "llfeaturemanager.h" #include "statemachine/aifilepicker.h" -#include "llfloater.h" -#include "llfloateractivespeakers.h" -#include "llfloaterbuildoptions.h" -#include "llfloaterbuyland.h" #include "llfloatercamera.h" #include "llfloaterchat.h" #include "llfloaterchatterbox.h" #include "llfloatercustomize.h" #include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor -#include "llfloaterland.h" -#include "llfloaterinspect.h" -#include "llfloaterinventory.h" -#include "llfloaternamedesc.h" -#include "llfloaterpreference.h" #include "llfloatersnapshot.h" #include "llfloaterteleporthistory.h" #include "llfloatertools.h" @@ -196,7 +183,6 @@ #include "llnotifications.h" #include "llnotificationsutil.h" -#include "llfloatertest.h" // HACK! #include "llfloaternotificationsconsole.h" #include "llpanelnearbymedia.h" @@ -1077,7 +1063,7 @@ BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK m BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = TRUE; - gVoiceClient->middleMouseState(true); + LLVoiceClient::getInstance()->middleMouseState(true); handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); // Always handled as far as the OS is concerned. @@ -1230,7 +1216,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - gVoiceClient->middleMouseState(false); + LLVoiceClient::getInstance()->middleMouseState(false); handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); // Always handled as far as the OS is concerned. @@ -1372,8 +1358,7 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) { // Let the voice chat code check for its PTT key. Note that this never affects event processing. - if(gVoiceClient) - gVoiceClient->keyDown(key, mask); + LLVoiceClient::getInstance()->keyDown(key, mask); if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME) { @@ -1395,7 +1380,7 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) { // Let the voice chat code check for its PTT key. Note that this never affects event processing. - gVoiceClient->keyUp(key, mask); + LLVoiceClient::getInstance()->keyUp(key, mask); return FALSE; } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 433f2f0bb..10ab24309 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1526,7 +1526,7 @@ void LLVOAvatar::initInstance(void) //VTPause(); // VTune - mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) ); + mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) ); } // virtual @@ -2344,13 +2344,16 @@ void LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) // store off last frame's root position to be consistent with camera position LLVector3 root_pos_last = mRoot->getWorldPosition(); bool detailed_update = updateCharacter(agent); - bool voice_enabled = gVoiceClient->getVoiceEnabled( mID ) && gVoiceClient->inProximalChannel(); if (gNoRender) { return; } + static LLUICachedControl visualizers_in_calls("ShowVoiceVisualizersInCalls", false); + bool voice_enabled = (visualizers_in_calls || LLVoiceClient::getInstance()->inProximalChannel()) && + LLVoiceClient::getInstance()->getVoiceEnabled(mID); + idleUpdateVoiceVisualizer( voice_enabled ); idleUpdateMisc( detailed_update ); idleUpdateAppearanceAnimation(); @@ -2370,10 +2373,10 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) { bool render_visualizer = voice_enabled; - // Don't render the user's own voice visualizer when in mouselook + // Don't render the user's own voice visualizer when in mouselook, or when opening the mic is disabled. if(isSelf()) { - if(gAgentCamera.cameraMouselook()/* || gSavedSettings.getBOOL("VoiceDisableMic")*/) + if(gAgentCamera.cameraMouselook() || gSavedSettings.getBOOL("VoiceDisableMic")) { render_visualizer = false; } @@ -2421,7 +2424,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) // Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been // "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking. //----------------------------------------------------------------------------------------------------------------- - if ( gVoiceClient->getIsSpeaking( mID ) ) + if (LLVoiceClient::getInstance()->getIsSpeaking( mID )) { if ( ! mVoiceVisualizer->getCurrentlySpeaking() ) { @@ -2430,7 +2433,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) //printf( "gAwayTimer.reset();\n" ); } - mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) ); + mVoiceVisualizer->setSpeakingAmplitude( LLVoiceClient::getInstance()->getCurrentPower( mID ) ); if( isSelf() ) { @@ -2682,7 +2685,7 @@ F32 LLVOAvatar::calcMorphAmount() void LLVOAvatar::idleUpdateLipSync(bool voice_enabled) { // Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync - if ( voice_enabled && (gVoiceClient->lipSyncEnabled()) && gVoiceClient->getIsSpeaking( mID ) ) + if ( voice_enabled && (LLVoiceClient::getInstance()->lipSyncEnabled()) && LLVoiceClient::getInstance()->getIsSpeaking( mID ) ) { F32 ooh_morph_amount = 0.0f; F32 aah_morph_amount = 0.0f; diff --git a/indra/newview/llvoicecallhandler.cpp b/indra/newview/llvoicecallhandler.cpp new file mode 100644 index 000000000..0f68f7972 --- /dev/null +++ b/indra/newview/llvoicecallhandler.cpp @@ -0,0 +1,69 @@ + /** + * @file llvoicecallhandler.cpp + * @brief slapp to handle avatar to avatar voice call. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llcommandhandler.h" +#include "llavataractions.h" +//#include "llnotificationsutil.h" +//#include "llui.h" + +class LLVoiceCallAvatarHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLVoiceCallAvatarHandler() : LLCommandHandler("voicecallavatar", UNTRUSTED_THROTTLE) + { + } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + /*if (!LLUI::sSettingGroups["config"]->getBOOL("EnableVoiceCall")) + { + LLNotificationsUtil::add("NoVoiceCall", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + }*/ + + //Make sure we have some parameters + if (params.size() == 0) + { + return false; + } + + //Get the ID + LLUUID id; + if (!id.set( params[0], FALSE )) + { + return false; + } + + //instigate call with this avatar + LLAvatarActions::startCall( id ); + return true; + } +}; + +LLVoiceCallAvatarHandler gVoiceCallAvatarHandler; + diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp new file mode 100644 index 000000000..4ce04b028 --- /dev/null +++ b/indra/newview/llvoicechannel.cpp @@ -0,0 +1,967 @@ +/** + * @file llvoicechannel.cpp + * @brief Voice Channel related classes + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llagent.h" +#include "llhttpclient.h" +#include "llimview.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llvoicechannel.h" + +extern AIHTTPTimeoutPolicy voiceCallCapResponder_timeout; + + +LLVoiceChannel::voice_channel_map_t LLVoiceChannel::sVoiceChannelMap; +LLVoiceChannel::voice_channel_map_uri_t LLVoiceChannel::sVoiceChannelURIMap; +LLVoiceChannel* LLVoiceChannel::sCurrentVoiceChannel = NULL; +LLVoiceChannel* LLVoiceChannel::sSuspendedVoiceChannel = NULL; +LLVoiceChannel::channel_changed_signal_t LLVoiceChannel::sCurrentVoiceChannelChangedSignal; + +BOOL LLVoiceChannel::sSuspended = FALSE; + +// +// Constants +// +const U32 DEFAULT_RETRIES_COUNT = 3; + + +class LLVoiceCallCapResponder : public LLHTTPClient::ResponderWithResult +{ +public: + LLVoiceCallCapResponder(const LLUUID& session_id) : mSessionID(session_id) {}; + + /*virtual*/ void error(U32 status, const std::string& reason); // called with bad status codes + /*virtual*/ void result(const LLSD& content); + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceCallCapResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "LLVoiceCallCapResponder"; } + +private: + LLUUID mSessionID; +}; + + +void LLVoiceCallCapResponder::error(U32 status, const std::string& reason) +{ + LL_WARNS("Voice") << "LLVoiceCallCapResponder error [status:" + << status << "]: " << reason << LL_ENDL; + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if ( channelp ) + { + if ( 403 == status ) + { + //403 == no ability + LLNotificationsUtil::add( + "VoiceNotAllowed", + channelp->getNotifyArgs()); + } + else + { + LLNotificationsUtil::add( + "VoiceCallGenericError", + channelp->getNotifyArgs()); + } + channelp->deactivate(); + } +} + +void LLVoiceCallCapResponder::result(const LLSD& content) +{ + LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID); + if (channelp) + { + // *TODO: DEBUG SPAM + LLSD::map_const_iterator iter; + for(iter = content.beginMap(); iter != content.endMap(); ++iter) + { + LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got " + << iter->first << LL_ENDL; + } + + channelp->setChannelInfo( + content["voice_credentials"]["channel_uri"].asString(), + content["voice_credentials"]["channel_credentials"].asString()); + } +} + +// +// LLVoiceChannel +// +LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& session_name) : + mSessionID(session_id), + mState(STATE_NO_CHANNEL_INFO), + mSessionName(session_name), + mCallDirection(OUTGOING_CALL), + mIgnoreNextSessionLeave(FALSE), + mCallEndedByAgent(false) +{ + mNotifyArgs["VOICE_CHANNEL_NAME"] = mSessionName; + + if (!sVoiceChannelMap.insert(std::make_pair(session_id, this)).second) + { + // a voice channel already exists for this session id, so this instance will be orphaned + // the end result should simply be the failure to make voice calls + LL_WARNS("Voice") << "Duplicate voice channels registered for session_id " << session_id << LL_ENDL; + } +} + +LLVoiceChannel::~LLVoiceChannel() +{ + // Must check instance exists here, the singleton MAY have already been destroyed. + if(LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver(this); + } + + sVoiceChannelMap.erase(mSessionID); + sVoiceChannelURIMap.erase(mURI); +} + +void LLVoiceChannel::setChannelInfo( + const std::string& uri, + const std::string& credentials) +{ + setURI(uri); + + mCredentials = credentials; + + if (mState == STATE_NO_CHANNEL_INFO) + { + if (mURI.empty()) + { + LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); + LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL; + deactivate(); + } + else if (mCredentials.empty()) + { + LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); + LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL; + deactivate(); + } + else + { + setState(STATE_READY); + + // if we are supposed to be active, reconnect + // this will happen on initial connect, as we request credentials on first use + if (sCurrentVoiceChannel == this) + { + // just in case we got new channel info while active + // should move over to new channel + activate(); + } + } + } +} + +void LLVoiceChannel::onChange(EStatusType type, const std::string &channelURI, bool proximal) +{ + if (channelURI != mURI) + { + return; + } + + if (type < BEGIN_ERROR_STATUS) + { + handleStatusChange(type); + } + else + { + handleError(type); + } +} + +void LLVoiceChannel::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_LOGIN_RETRY: + //mLoginNotificationHandle = LLNotifyBox::showXml("VoiceLoginRetry")->getHandle(); + LLNotificationsUtil::add("VoiceLoginRetry"); + break; + case STATUS_LOGGED_IN: + //if (!mLoginNotificationHandle.isDead()) + //{ + // LLNotifyBox* notifyp = (LLNotifyBox*)mLoginNotificationHandle.get(); + // if (notifyp) + // { + // notifyp->close(); + // } + // mLoginNotificationHandle.markDead(); + //} + break; + case STATUS_LEFT_CHANNEL: + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) + { + // if forceably removed from channel + // update the UI and revert to default channel + LLNotificationsUtil::add("VoiceChannelDisconnected", mNotifyArgs); + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + break; + case STATUS_JOINING: + if (callStarted()) + { + setState(STATE_RINGING); + } + break; + case STATUS_JOINED: + if (callStarted()) + { + setState(STATE_CONNECTED); + } + default: + break; + } +} + +// default behavior is to just deactivate channel +// derived classes provide specific error messages +void LLVoiceChannel::handleError(EStatusType type) +{ + deactivate(); + setState(STATE_ERROR); +} + +BOOL LLVoiceChannel::isActive() +{ + // only considered active when currently bound channel matches what our channel + return callStarted() && LLVoiceClient::getInstance()->getCurrentChannel() == mURI; +} + +BOOL LLVoiceChannel::callStarted() +{ + return mState >= STATE_CALL_STARTED; +} + +void LLVoiceChannel::deactivate() +{ + if (mState >= STATE_RINGING) + { + // ignore session leave event + mIgnoreNextSessionLeave = TRUE; + } + + if (callStarted()) + { + setState(STATE_HUNG_UP); + + //Default mic is OFF when leaving voice calls + if (gSavedSettings.getBOOL("AutoDisengageMic") && + sCurrentVoiceChannel == this && + LLVoiceClient::getInstance()->getUserPTTState()) + { + gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); + LLVoiceClient::getInstance()->inputUserControlState(true); + } + } + LLVoiceClient::getInstance()->removeObserver(this); + + if (sCurrentVoiceChannel == this) + { + // default channel is proximal channel + sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); + sCurrentVoiceChannel->activate(); + } +} + +void LLVoiceChannel::activate() +{ + if (callStarted()) + { + return; + } + + // deactivate old channel and mark ourselves as the active one + if (sCurrentVoiceChannel != this) + { + // mark as current before deactivating the old channel to prevent + // activating the proximal channel between IM calls + LLVoiceChannel* old_channel = sCurrentVoiceChannel; + sCurrentVoiceChannel = this; + //mCallDialogPayload["old_channel_name"] = ""; + if (old_channel) + { + //mCallDialogPayload["old_channel_name"] = old_channel->getSessionName(); + old_channel->deactivate(); + } + } + + if (mState == STATE_NO_CHANNEL_INFO) + { + // responsible for setting status to active + getChannelInfo(); + } + else + { + setState(STATE_CALL_STARTED); + } + + LLVoiceClient::getInstance()->addObserver(this); + + //do not send earlier, channel should be initialized, should not be in STATE_NO_CHANNEL_INFO state + sCurrentVoiceChannelChangedSignal(this->mSessionID); +} + +void LLVoiceChannel::getChannelInfo() +{ + // pretend we have everything we need + if (sCurrentVoiceChannel == this) + { + setState(STATE_CALL_STARTED); + } +} + +//static +LLVoiceChannel* LLVoiceChannel::getChannelByID(const LLUUID& session_id) +{ + voice_channel_map_t::iterator found_it = sVoiceChannelMap.find(session_id); + if (found_it == sVoiceChannelMap.end()) + { + return NULL; + } + else + { + return found_it->second; + } +} + +//static +LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri) +{ + voice_channel_map_uri_t::iterator found_it = sVoiceChannelURIMap.find(uri); + if (found_it == sVoiceChannelURIMap.end()) + { + return NULL; + } + else + { + return found_it->second; + } +} + +LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel() +{ + return sCurrentVoiceChannel; +} + +void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id) +{ + sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); + mSessionID = new_session_id; + sVoiceChannelMap.insert(std::make_pair(mSessionID, this)); +} + +void LLVoiceChannel::setURI(std::string uri) +{ + sVoiceChannelURIMap.erase(mURI); + mURI = uri; + sVoiceChannelURIMap.insert(std::make_pair(mURI, this)); +} + +void LLVoiceChannel::setState(EState state) +{ + switch(state) + { + case STATE_RINGING: + gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); + break; + case STATE_CONNECTED: + gIMMgr->addSystemMessage(mSessionID, "connected", mNotifyArgs); + break; + case STATE_HUNG_UP: + gIMMgr->addSystemMessage(mSessionID, "hang_up", mNotifyArgs); + break; + default: + break; + } + + doSetState(state); +} + +void LLVoiceChannel::doSetState(const EState& new_state) +{ + EState old_state = mState; + mState = new_state; + + if (!mStateChangedCallback.empty()) + mStateChangedCallback(old_state, mState, mCallDirection, mCallEndedByAgent); +} + +//static +void LLVoiceChannel::initClass() +{ + sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance(); +} + +//static +void LLVoiceChannel::suspend() +{ + if (!sSuspended) + { + sSuspendedVoiceChannel = sCurrentVoiceChannel; + sSuspended = TRUE; + } +} + +//static +void LLVoiceChannel::resume() +{ + if (sSuspended) + { + if (LLVoiceClient::getInstance()->voiceEnabled()) + { + if (sSuspendedVoiceChannel) + { + sSuspendedVoiceChannel->activate(); + } + else + { + LLVoiceChannelProximal::getInstance()->activate(); + } + } + sSuspended = FALSE; + } +} + +boost::signals2::connection LLVoiceChannel::setCurrentVoiceChannelChangedCallback(channel_changed_callback_t cb, bool at_front) +{ + if (at_front) + { + return sCurrentVoiceChannelChangedSignal.connect(cb, boost::signals2::at_front); + } + else + { + return sCurrentVoiceChannelChangedSignal.connect(cb); + } +} + +// +// LLVoiceChannelGroup +// + +LLVoiceChannelGroup::LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name) : + LLVoiceChannel(session_id, session_name) +{ + mRetries = DEFAULT_RETRIES_COUNT; + mIsRetrying = FALSE; +} + +void LLVoiceChannelGroup::deactivate() +{ + if (callStarted()) + { + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + } + LLVoiceChannel::deactivate(); +} + +void LLVoiceChannelGroup::activate() +{ + if (callStarted()) return; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // we have the channel info, just need to use it now + LLVoiceClient::getInstance()->setNonSpatialChannel( + mURI, + mCredentials); + + /* + if (!gAgent.isInGroup(mSessionID)) // ad-hoc channel + { + LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionID); + // Adding ad-hoc call participants to Recent People List. + // If it's an outgoing ad-hoc, we can use mInitialTargetIDs that holds IDs of people we + // called(both online and offline) as source to get people for recent (STORM-210). + if (session->isOutgoingAdHoc()) + { + for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin(); + it!=session->mInitialTargetIDs.end();++it) + { + const LLUUID id = *it; + LLRecentPeople::instance().add(id); + } + } + // If this ad-hoc is incoming then trying to get ids of people from mInitialTargetIDs + // would lead to EXT-8246. So in this case we get them from speakers list. + else + { + LLIMModel::addSpeakersToRecent(mSessionID); + } + } + */ + + //Mic default state is OFF on initiating/joining Ad-Hoc/Group calls + if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle()) + { + LLVoiceClient::getInstance()->inputUserControlState(true); + } + + } +} + +void LLVoiceChannelGroup::getChannelInfo() +{ + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + std::string url = region->getCapability("ChatSessionRequest"); + LLSD data; + data["method"] = "call"; + data["session-id"] = mSessionID; + LLHTTPClient::post(url, + data, + new LLVoiceCallCapResponder(mSessionID)); + } +} + +void LLVoiceChannelGroup::setChannelInfo( + const std::string& uri, + const std::string& credentials) +{ + setURI(uri); + + mCredentials = credentials; + + if (mState == STATE_NO_CHANNEL_INFO) + { + if(!mURI.empty() && !mCredentials.empty()) + { + setState(STATE_READY); + + // if we are supposed to be active, reconnect + // this will happen on initial connect, as we request credentials on first use + if (sCurrentVoiceChannel == this) + { + // just in case we got new channel info while active + // should move over to new channel + activate(); + } + } + else + { + //*TODO: notify user + LL_WARNS("Voice") << "Received invalid credentials for channel " << mSessionName << LL_ENDL; + deactivate(); + } + } + else if ( mIsRetrying ) + { + // we have the channel info, just need to use it now + LLVoiceClient::getInstance()->setNonSpatialChannel( + mURI, + mCredentials); + } +} + +void LLVoiceChannelGroup::handleStatusChange(EStatusType type) +{ + // status updates + switch(type) + { + case STATUS_JOINED: + mRetries = 3; + mIsRetrying = FALSE; + default: + break; + } + + LLVoiceChannel::handleStatusChange(type); +} + +void LLVoiceChannelGroup::handleError(EStatusType status) +{ + std::string notify; + switch(status) + { + case ERROR_CHANNEL_LOCKED: + case ERROR_CHANNEL_FULL: + notify = "VoiceChannelFull"; + break; + case ERROR_NOT_AVAILABLE: + //clear URI and credentials + //set the state to be no info + //and activate + if ( mRetries > 0 ) + { + mRetries--; + mIsRetrying = TRUE; + mIgnoreNextSessionLeave = TRUE; + + getChannelInfo(); + return; + } + else + { + notify = "VoiceChannelJoinFailed"; + mRetries = DEFAULT_RETRIES_COUNT; + mIsRetrying = FALSE; + } + + break; + + case ERROR_UNKNOWN: + default: + break; + } + + // notification + if (!notify.empty()) + { + LLNotificationPtr notification = LLNotificationsUtil::add(notify, mNotifyArgs); + // echo to im window + gIMMgr->addMessage(mSessionID, LLUUID::null, SYSTEM_FROM, notification->getMessage()); + } + + LLVoiceChannel::handleError(status); +} + +void LLVoiceChannelGroup::setState(EState state) +{ + switch(state) + { + case STATE_RINGING: + if ( !mIsRetrying ) + { + gIMMgr->addSystemMessage(mSessionID, "ringing", mNotifyArgs); + } + + doSetState(state); + break; + default: + LLVoiceChannel::setState(state); + } +} + +// +// LLVoiceChannelProximal +// +LLVoiceChannelProximal::LLVoiceChannelProximal() : + LLVoiceChannel(LLUUID::null, LLStringUtil::null) +{ +} + +BOOL LLVoiceChannelProximal::isActive() +{ + return callStarted() && LLVoiceClient::getInstance()->inProximalChannel(); +} + +void LLVoiceChannelProximal::activate() +{ + if (callStarted()) return; + + if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED)) + { + // we're connected to a non-spatial channel, so disconnect. + LLVoiceClient::getInstance()->leaveNonSpatialChannel(); + } + LLVoiceChannel::activate(); + +} + +void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) +{ + if (!proximal) + { + return; + } + + if (type < BEGIN_ERROR_STATUS) + { + handleStatusChange(type); + } + else + { + handleError(type); + } +} + +void LLVoiceChannelProximal::handleStatusChange(EStatusType status) +{ + // status updates + switch(status) + { + case STATUS_LEFT_CHANNEL: + // do not notify user when leaving proximal channel + return; + case STATUS_VOICE_DISABLED: + //skip showing "Voice not available at your current location" when agent voice is disabled (EXT-4749) + if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking()) + { + gIMMgr->addSystemMessage(LLUUID::null, "unavailable", mNotifyArgs); + } + return; + default: + break; + } + LLVoiceChannel::handleStatusChange(status); +} + + +void LLVoiceChannelProximal::handleError(EStatusType status) +{ + std::string notify; + switch(status) + { + case ERROR_CHANNEL_LOCKED: + case ERROR_CHANNEL_FULL: + notify = "ProximalVoiceChannelFull"; + break; + default: + break; + } + + // notification + if (!notify.empty()) + { + LLNotificationsUtil::add(notify, mNotifyArgs); + } + + LLVoiceChannel::handleError(status); +} + +void LLVoiceChannelProximal::deactivate() +{ + if (callStarted()) + { + setState(STATE_HUNG_UP); + } +} + + +// +// LLVoiceChannelP2P +// +LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id) : + LLVoiceChannelGroup(session_id, session_name), + mOtherUserID(other_user_id), + mReceivedCall(FALSE) +{ + // make sure URI reflects encoded version of other user's agent id + // *NOTE: in case of Avaline call generated SIP URL will be incorrect. + // But it will be overridden in LLVoiceChannelP2P::setSessionHandle() called when agent accepts call + setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); +} + +void LLVoiceChannelP2P::handleStatusChange(EStatusType type) +{ + LL_INFOS("Voice") << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << LL_ENDL; + + // status updates + switch(type) + { + case STATUS_LEFT_CHANNEL: + if (callStarted() && !mIgnoreNextSessionLeave && !sSuspended) + { + // *TODO: use it to show DECLINE voice notification + if (mState == STATE_RINGING) + { + // other user declined call + LLNotificationsUtil::add("P2PCallDeclined", mNotifyArgs); + } + else + { + // other user hung up, so we didn't end the call + LLNotificationsUtil::add("VoiceChannelDisconnectedP2P", mNotifyArgs); + mCallEndedByAgent = false; + } + deactivate(); + } + mIgnoreNextSessionLeave = FALSE; + return; + case STATUS_JOINING: + // because we join session we expect to process session leave event in the future. EXT-7371 + // may be this should be done in the LLVoiceChannel::handleStatusChange. + mIgnoreNextSessionLeave = FALSE; + break; + + default: + break; + } + + LLVoiceChannel::handleStatusChange(type); +} + +void LLVoiceChannelP2P::handleError(EStatusType type) +{ + switch(type) + { + case ERROR_NOT_AVAILABLE: + LLNotificationsUtil::add("P2PCallNoAnswer", mNotifyArgs); + break; + default: + break; + } + + LLVoiceChannel::handleError(type); +} + +void LLVoiceChannelP2P::activate() +{ + if (callStarted()) return; + + //call will be counted as ended by user unless this variable is changed in handleStatusChange() + mCallEndedByAgent = true; + + LLVoiceChannel::activate(); + + if (callStarted()) + { + // no session handle yet, we're starting the call + if (mSessionHandle.empty()) + { + mReceivedCall = FALSE; + LLVoiceClient::getInstance()->callUser(mOtherUserID); + } + // otherwise answering the call + else + { + if (!LLVoiceClient::getInstance()->answerInvite(mSessionHandle)) + { + mCallEndedByAgent = false; + mSessionHandle.clear(); + handleError(ERROR_UNKNOWN); + return; + } + // using the session handle invalidates it. Clear it out here so we can't reuse it by accident. + mSessionHandle.clear(); + } + + // Add the party to the list of people with which we've recently interacted. + //addToTheRecentPeopleList(); + + //Default mic is ON on initiating/joining P2P calls + if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle()) + { + LLVoiceClient::getInstance()->inputUserControlState(true); + } + } +} + +void LLVoiceChannelP2P::getChannelInfo() +{ + // pretend we have everything we need, since P2P doesn't use channel info + if (sCurrentVoiceChannel == this) + { + setState(STATE_CALL_STARTED); + } +} + +// receiving session from other user who initiated call +void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::string &inURI) +{ + BOOL needs_activate = FALSE; + if (callStarted()) + { + // defer to lower agent id when already active + if (mOtherUserID < gAgent.getID()) + { + // pretend we haven't started the call yet, so we can connect to this session instead + deactivate(); + needs_activate = TRUE; + } + else + { + // we are active and have priority, invite the other user again + // under the assumption they will join this new session + mSessionHandle.clear(); + LLVoiceClient::getInstance()->callUser(mOtherUserID); + return; + } + } + + mSessionHandle = handle; + + // The URI of a p2p session should always be the other end's SIP URI. + if(!inURI.empty()) + { + setURI(inURI); + } + else + { + LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL; + // In the case of an incoming AvaLine call, the generated URI will be different from the + // original one. This is because the P2P URI is based on avatar UUID but Avaline is not. + // See LLVoiceClient::sessionAddedEvent() + setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); + } + + mReceivedCall = TRUE; + + if (needs_activate) + { + activate(); + } +} + +void LLVoiceChannelP2P::setState(EState state) +{ + LL_INFOS("Voice") << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << LL_ENDL; + + if (mReceivedCall) // incoming call + { + // you only "answer" voice invites in p2p mode + // so provide a special purpose message here + if (mReceivedCall && state == STATE_RINGING) + { + gIMMgr->addSystemMessage(mSessionID, "answering", mNotifyArgs); + doSetState(state); + return; + } + } + + LLVoiceChannel::setState(state); +} + +/*void LLVoiceChannelP2P::addToTheRecentPeopleList() +{ + bool avaline_call = LLIMModel::getInstance()->findIMSession(mSessionID)->isAvalineSessionType(); + + if (avaline_call) + { + LLSD call_data; + std::string call_number = LLVoiceChannel::getSessionName(); + + call_data["avaline_call"] = true; + call_data["session_id"] = mSessionID; + call_data["call_number"] = call_number; + call_data["date"] = LLDate::now(); + + LLRecentPeople::instance().add(mOtherUserID, call_data); + } + else + { + LLRecentPeople::instance().add(mOtherUserID); + } +}*/ + diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h new file mode 100644 index 000000000..55dcf0f07 --- /dev/null +++ b/indra/newview/llvoicechannel.h @@ -0,0 +1,205 @@ +/** + * @file llvoicechannel.h + * @brief Voice channel related classes + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LL_VOICECHANNEL_H +#define LL_VOICECHANNEL_H + +#include "llvoiceclient.h" + +class LLVoiceChannel : public LLVoiceClientStatusObserver +{ +public: + typedef enum e_voice_channel_state + { + STATE_NO_CHANNEL_INFO, + STATE_ERROR, + STATE_HUNG_UP, + STATE_READY, + STATE_CALL_STARTED, + STATE_RINGING, + STATE_CONNECTED + } EState; + + typedef enum e_voice_channel_direction + { + INCOMING_CALL, + OUTGOING_CALL + } EDirection; + + typedef boost::signals2::signal state_changed_signal_t; + + // on current channel changed signal + typedef boost::function channel_changed_callback_t; + typedef boost::signals2::signal channel_changed_signal_t; + static channel_changed_signal_t sCurrentVoiceChannelChangedSignal; + static boost::signals2::connection setCurrentVoiceChannelChangedCallback(channel_changed_callback_t cb, bool at_front = false); + + + + LLVoiceChannel(const LLUUID& session_id, const std::string& session_name); + virtual ~LLVoiceChannel(); + + /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + + virtual void handleStatusChange(EStatusType status); + virtual void handleError(EStatusType status); + virtual void deactivate(); + virtual void activate(); + virtual void setChannelInfo( + const std::string& uri, + const std::string& credentials); + virtual void getChannelInfo(); + virtual BOOL isActive(); + virtual BOOL callStarted(); + + // Session name is a UI label used for feedback about which person, + // group, or phone number you are talking to + const std::string& getSessionName() const { return mSessionName; } + + boost::signals2::connection setStateChangedCallback(const state_changed_signal_t::slot_type& callback) + { return mStateChangedCallback.connect(callback); } + + const LLUUID getSessionID() { return mSessionID; } + EState getState() { return mState; } + + void updateSessionID(const LLUUID& new_session_id); + const LLSD& getNotifyArgs() { return mNotifyArgs; } + + void setCallDirection(EDirection direction) {mCallDirection = direction;} + EDirection getCallDirection() {return mCallDirection;} + + static LLVoiceChannel* getChannelByID(const LLUUID& session_id); + static LLVoiceChannel* getChannelByURI(std::string uri); + static LLVoiceChannel* getCurrentVoiceChannel(); + + static void initClass(); + + static void suspend(); + static void resume(); + +protected: + virtual void setState(EState state); + /** + * Use this method if you want mStateChangedCallback to be executed while state is changed + */ + void doSetState(const EState& state); + void setURI(std::string uri); + + // there can be two directions INCOMING and OUTGOING + EDirection mCallDirection; + + std::string mURI; + std::string mCredentials; + LLUUID mSessionID; + EState mState; + std::string mSessionName; + LLSD mNotifyArgs; + //LLSD mCallDialogPayload; + // true if call was ended by agent + bool mCallEndedByAgent; + BOOL mIgnoreNextSessionLeave; + LLHandle mLoginNotificationHandle; + + typedef std::map voice_channel_map_t; + static voice_channel_map_t sVoiceChannelMap; + + typedef std::map voice_channel_map_uri_t; + static voice_channel_map_uri_t sVoiceChannelURIMap; + + static LLVoiceChannel* sCurrentVoiceChannel; + static LLVoiceChannel* sSuspendedVoiceChannel; + static BOOL sSuspended; + +private: + state_changed_signal_t mStateChangedCallback; +}; + +class LLVoiceChannelGroup : public LLVoiceChannel +{ +public: + LLVoiceChannelGroup(const LLUUID& session_id, const std::string& session_name); + + /*virtual*/ void handleStatusChange(EStatusType status); + /*virtual*/ void handleError(EStatusType status); + /*virtual*/ void activate(); + /*virtual*/ void deactivate(); + /*vritual*/ void setChannelInfo( + const std::string& uri, + const std::string& credentials); + /*virtual*/ void getChannelInfo(); + +protected: + virtual void setState(EState state); + +private: + U32 mRetries; + BOOL mIsRetrying; +}; + +class LLVoiceChannelProximal : public LLVoiceChannel, public LLSingleton +{ +public: + LLVoiceChannelProximal(); + + /*virtual*/ void onChange(EStatusType status, const std::string &channelURI, bool proximal); + /*virtual*/ void handleStatusChange(EStatusType status); + /*virtual*/ void handleError(EStatusType status); + /*virtual*/ BOOL isActive(); + /*virtual*/ void activate(); + /*virtual*/ void deactivate(); + +}; + +class LLVoiceChannelP2P : public LLVoiceChannelGroup +{ +public: + LLVoiceChannelP2P(const LLUUID& session_id, const std::string& session_name, const LLUUID& other_user_id); + + /*virtual*/ void handleStatusChange(EStatusType status); + /*virtual*/ void handleError(EStatusType status); + /*virtual*/ void activate(); + /*virtual*/ void getChannelInfo(); + + void setSessionHandle(const std::string& handle, const std::string &inURI); + +protected: + virtual void setState(EState state); + +private: + + /** + * Add the caller to the list of people with which we've recently interacted + * + void addToTheRecentPeopleList(); + **/ + + std::string mSessionHandle; + LLUUID mOtherUserID; + BOOL mReceivedCall; +}; + +#endif // LL_VOICECHANNEL_H + diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index f8532d7b0..40cdfa175 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1,1515 +1,46 @@ /** * @file llvoiceclient.cpp - * @brief Implementation of LLVoiceClient class which is the interface to the voice client process. + * @brief Voice client delegation class implementation. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ + #include "llviewerprecompiledheaders.h" - -#if LL_LINUX && defined(LL_STANDALONE) -#include -#endif - #include "llvoiceclient.h" - -#include - -#include "llsdutil.h" - -#include "llvoavatarself.h" -#include "llbufferstream.h" -#include "llfile.h" -#ifdef LL_STANDALONE -# include "expat.h" -#else -# include "expat/expat.h" -#endif -#include "llcallbacklist.h" -#include "llviewerregion.h" -#include "llviewernetwork.h" // for gGridChoice -#include "llbase64.h" #include "llviewercontrol.h" -#include "llkeyboard.h" -#include "llappviewer.h" // for gDisconnected, gDisableVoice -#include "llmutelist.h" // to check for muted avatars -#include "llagent.h" -#include "llcachename.h" -#include "llimview.h" // for LLIMMgr -#include "llimpanel.h" // for LLVoiceChannel -#include "llparcel.h" -#include "llviewerparcelmgr.h" -#include "llfirstuse.h" #include "llviewerwindow.h" -#include "llviewercamera.h" +#include "llvoicevivox.h" +#include "llviewernetwork.h" +#include "llhttpnode.h" #include "llnotificationsutil.h" +#include "llsdserialize.h" +#include "llkeyboard.h" -#include "llfloaterfriends.h" //VIVOX, inorder to refresh communicate panel -#include "llfloaterchat.h" // for LLFloaterChat::addChat() +const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; -// for base64 decoding -#include "apr_base64.h" +const F32 LLVoiceClient::VOLUME_MIN = 0.f; +const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f; +const F32 LLVoiceClient::VOLUME_MAX = 1.0f; -// for SHA1 hash -#include "apr_sha1.h" - -// for MD5 hash -#include "llmd5.h" - -#define USE_SESSION_GROUPS 0 - -class AIHTTPTimeoutPolicy; -extern AIHTTPTimeoutPolicy viewerVoiceAccountProvisionResponder_timeout; -extern AIHTTPTimeoutPolicy voiceClientCapResponder_timeout; - -static bool sConnectingToAgni = false; -F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; - -const F32 SPEAKING_TIMEOUT = 1.f; - -const int VOICE_MAJOR_VERSION = 1; -const int VOICE_MINOR_VERSION = 0; - -LLVoiceClient *gVoiceClient = NULL; - -// Don't retry connecting to the daemon more frequently than this: -const F32 CONNECT_THROTTLE_SECONDS = 1.0f; - -// Don't send positional updates more frequently than this: -const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - -const F32 LOGIN_RETRY_SECONDS = 10.0f; -const int MAX_LOGIN_RETRIES = 12; - -static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) -{ - LLMD5 md5_uuid; - md5_uuid.update((const unsigned char*)str.data(), str.size()); - md5_uuid.finalize(); - md5_uuid.raw_digest(uuid.mData); -} - -static int scale_mic_volume(float volume) -{ - // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. - // Map it as follows: 0.0 -> 40, 1.0 -> 44, 2.0 -> 75 - - volume -= 1.0f; // offset volume to the range [-1.0 ... 1.0], with 0 at the default. - int scaled_volume = 44; // offset scaled_volume by its default level - if(volume < 0.0f) - scaled_volume += ((int)(volume * 4.0f)); // (44 - 40) - else - scaled_volume += ((int)(volume * 31.0f)); // (75 - 44) - - return scaled_volume; -} - -static int scale_speaker_volume(float volume) -{ - // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. - // Map it as follows: 0.0 -> 0, 0.5 -> 62, 1.0 -> 75 - - volume -= 0.5f; // offset volume to the range [-0.5 ... 0.5], with 0 at the default. - int scaled_volume = 62; // offset scaled_volume by its default level - if(volume < 0.0f) - scaled_volume += ((int)(volume * 124.0f)); // (62 - 0) * 2 - else - scaled_volume += ((int)(volume * 26.0f)); // (75 - 62) * 2 - - return scaled_volume; -} - -class LLViewerVoiceAccountProvisionResponder : public LLHTTPClient::ResponderWithResult -{ -public: - LLViewerVoiceAccountProvisionResponder(int retries) - { - mRetries = retries; - } - - /*virtual*/ void error(U32 status, const std::string& reason) - { - if ( mRetries > 0 ) - { - LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying. status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; - if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision( - mRetries - 1); - } - else - { - LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up). status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; - if ( gVoiceClient ) gVoiceClient->giveUp(); - } - } - - /*virtual*/ void result(const LLSD& content) - { - if ( gVoiceClient ) - { - std::string voice_sip_uri_hostname; - std::string voice_account_server_uri; - - LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; - - if(content.has("voice_sip_uri_hostname")) - voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); - - // this key is actually misnamed -- it will be an entire URI, not just a hostname. - if(content.has("voice_account_server_name")) - voice_account_server_uri = content["voice_account_server_name"].asString(); - - gVoiceClient->login( - content["username"].asString(), - content["password"].asString(), - voice_sip_uri_hostname, - voice_account_server_uri); - } - } - - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return viewerVoiceAccountProvisionResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "LLViewerVoiceAccountProvisionResponder"; } - -private: - int mRetries; -}; - -/** - * @class LLVivoxProtocolParser - * @brief This class helps construct new LLIOPipe specializations - * @see LLIOPipe - * - * THOROUGH_DESCRIPTION - */ -class LLVivoxProtocolParser : public LLIOPipe -{ - LOG_CLASS(LLVivoxProtocolParser); -public: - LLVivoxProtocolParser(); - virtual ~LLVivoxProtocolParser(); - -protected: - /* @name LLIOPipe virtual implementations - */ - //@{ - /** - * @brief Process the data in buffer - */ - virtual EStatus process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump); - //@} - - std::string mInput; - - // Expat control members - XML_Parser parser; - int responseDepth; - bool ignoringTags; - bool isEvent; - int ignoreDepth; - - // Members for processing responses. The values are transient and only valid within a call to processResponse(). - bool squelchDebugOutput; - int returnCode; - int statusCode; - std::string statusString; - std::string requestId; - std::string actionString; - std::string connectorHandle; - std::string versionID; - std::string accountHandle; - std::string sessionHandle; - std::string sessionGroupHandle; - std::string alias; - std::string applicationString; - - // Members for processing events. The values are transient and only valid within a call to processResponse(). - std::string eventTypeString; - int state; - std::string uriString; - bool isChannel; - bool incoming; - bool enabled; - std::string nameString; - std::string audioMediaString; - std::string displayNameString; - int participantType; - bool isLocallyMuted; - bool isModeratorMuted; - bool isSpeaking; - int volume; - F32 energy; - std::string messageHeader; - std::string messageBody; - std::string notificationType; - bool hasText; - bool hasAudio; - bool hasVideo; - bool terminated; - std::string blockMask; - std::string presenceOnly; - std::string autoAcceptMask; - std::string autoAddAsBuddy; - int numberOfAliases; - std::string subscriptionHandle; - std::string subscriptionType; - - - // Members for processing text between tags - std::string textBuffer; - bool accumulateText; - - void reset(); - - void processResponse(std::string tag); - -static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); -static void XMLCALL ExpatEndTag(void *data, const char *el); -static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); - - void StartTag(const char *tag, const char **attr); - void EndTag(const char *tag); - void CharData(const char *buffer, int length); - -}; - -LLVivoxProtocolParser::LLVivoxProtocolParser() -{ - parser = NULL; - parser = XML_ParserCreate(NULL); - - reset(); -} - -void LLVivoxProtocolParser::reset() -{ - responseDepth = 0; - ignoringTags = false; - accumulateText = false; - energy = 0.f; - ignoreDepth = 0; - isChannel = false; - isEvent = false; - isLocallyMuted = false; - isModeratorMuted = false; - isSpeaking = false; - participantType = 0; - squelchDebugOutput = false; - returnCode = -1; - state = 0; - statusCode = 0; - volume = 0; - textBuffer.clear(); - alias.clear(); - numberOfAliases = 0; - applicationString.clear(); -} - -//virtual -LLVivoxProtocolParser::~LLVivoxProtocolParser() -{ - if (parser) - XML_ParserFree(parser); -} - -// virtual -LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( - const LLChannelDescriptors& channels, - buffer_ptr_t& buffer, - bool& eos, - LLSD& context, - LLPumpIO* pump) -{ - LLBufferStream istr(channels, buffer.get()); - std::ostringstream ostr; - while (istr.good()) - { - char buf[1024]; - istr.read(buf, sizeof(buf)); - mInput.append(buf, istr.gcount()); - } - - // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. - int start = 0; - int delim; - while((delim = mInput.find("\n\n\n", start)) != std::string::npos) - { - - // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) - reset(); - - XML_ParserReset(parser, NULL); - XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); - XML_SetCharacterDataHandler(parser, ExpatCharHandler); - XML_SetUserData(parser, this); - XML_Parse(parser, mInput.data() + start, delim - start, false); - - // If this message isn't set to be squelched, output the raw XML received. - if(!squelchDebugOutput) - { - LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; - } - - start = delim + 3; - } - - if(start != 0) - mInput = mInput.substr(start); - - LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; - - if(!gVoiceClient->mConnected) - { - // If voice has been disabled, we just want to close the socket. This does so. - LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; - return STATUS_STOP; - } - - return STATUS_OK; -} - -void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) -{ - if (data) - { - LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; - object->StartTag(el, attr); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el) -{ - if (data) - { - LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; - object->EndTag(el); - } -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) -{ - if (data) - { - LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; - object->CharData(s, len); - } -} - -// -------------------------------------------------------------------------------- - - -void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) -{ - // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags - textBuffer.clear(); - // only accumulate text if we're not ignoring tags. - accumulateText = !ignoringTags; - - if (responseDepth == 0) - { - isEvent = !stricmp("Event", tag); - - if (!stricmp("Response", tag) || isEvent) - { - // Grab the attributes - while (*attr) - { - const char *key = *attr++; - const char *value = *attr++; - - if (!stricmp("requestId", key)) - { - requestId = value; - } - else if (!stricmp("action", key)) - { - actionString = value; - } - else if (!stricmp("type", key)) - { - eventTypeString = value; - } - } - } - LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - } - else - { - if (ignoringTags) - { - LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - else - { - LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; - - // Ignore the InputXml stuff so we don't get confused - if (!stricmp("InputXml", tag)) - { - ignoringTags = true; - ignoreDepth = responseDepth; - accumulateText = false; - - LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; - } - else if (!stricmp("CaptureDevices", tag)) - { - gVoiceClient->clearCaptureDevices(); - } - else if (!stricmp("RenderDevices", tag)) - { - gVoiceClient->clearRenderDevices(); - } - else if (!stricmp("Buddies", tag)) - { - gVoiceClient->deleteAllBuddies(); - } - else if (!stricmp("BlockRules", tag)) - { - gVoiceClient->deleteAllBlockRules(); - } - else if (!stricmp("AutoAcceptRules", tag)) - { - gVoiceClient->deleteAllAutoAcceptRules(); - } - - } - } - responseDepth++; -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::EndTag(const char *tag) -{ - const std::string& string = textBuffer; - bool clearbuffer = true; - - responseDepth--; - - if (ignoringTags) - { - if (ignoreDepth == responseDepth) - { - LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL; - ignoringTags = false; - } - else - { - LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - } - } - - if (!ignoringTags) - { - LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - - // Closing a tag. Finalize the text we've accumulated and reset - if (!stricmp("ReturnCode", tag)) - returnCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("SessionHandle", tag)) - sessionHandle = string; - else if (!stricmp("SessionGroupHandle", tag)) - sessionGroupHandle = string; - else if (!stricmp("StatusCode", tag)) - statusCode = strtol(string.c_str(), NULL, 10); - else if (!stricmp("StatusString", tag)) - statusString = string; - else if (!stricmp("ParticipantURI", tag)) - uriString = string; - else if (!stricmp("Volume", tag)) - volume = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Energy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("IsModeratorMuted", tag)) - isModeratorMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("IsSpeaking", tag)) - isSpeaking = !stricmp(string.c_str(), "true"); - else if (!stricmp("Alias", tag)) - alias = string; - else if (!stricmp("NumberOfAliases", tag)) - numberOfAliases = strtol(string.c_str(), NULL, 10); - else if (!stricmp("Application", tag)) - applicationString = string; - else if (!stricmp("ConnectorHandle", tag)) - connectorHandle = string; - else if (!stricmp("VersionID", tag)) - versionID = string; - else if (!stricmp("AccountHandle", tag)) - accountHandle = string; - else if (!stricmp("State", tag)) - state = strtol(string.c_str(), NULL, 10); - else if (!stricmp("URI", tag)) - uriString = string; - else if (!stricmp("IsChannel", tag)) - isChannel = !stricmp(string.c_str(), "true"); - else if (!stricmp("Incoming", tag)) - incoming = !stricmp(string.c_str(), "true"); - else if (!stricmp("Enabled", tag)) - enabled = !stricmp(string.c_str(), "true"); - else if (!stricmp("Name", tag)) - nameString = string; - else if (!stricmp("AudioMedia", tag)) - audioMediaString = string; - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("DisplayName", tag)) - displayNameString = string; - else if (!stricmp("AccountName", tag)) - nameString = string; - else if (!stricmp("ParticipantType", tag)) - participantType = strtol(string.c_str(), NULL, 10); - else if (!stricmp("IsLocallyMuted", tag)) - isLocallyMuted = !stricmp(string.c_str(), "true"); - else if (!stricmp("MicEnergy", tag)) - energy = (F32)strtod(string.c_str(), NULL); - else if (!stricmp("ChannelName", tag)) - nameString = string; - else if (!stricmp("ChannelURI", tag)) - uriString = string; - else if (!stricmp("BuddyURI", tag)) - uriString = string; - else if (!stricmp("Presence", tag)) - statusString = string; - else if (!stricmp("Device", tag)) - { - // This closing tag shouldn't clear the accumulated text. - clearbuffer = false; - } - else if (!stricmp("CaptureDevice", tag)) - { - gVoiceClient->addCaptureDevice(textBuffer); - } - else if (!stricmp("RenderDevice", tag)) - { - gVoiceClient->addRenderDevice(textBuffer); - } - else if (!stricmp("Buddy", tag)) - { - gVoiceClient->processBuddyListEntry(uriString, displayNameString); - } - else if (!stricmp("BlockRule", tag)) - { - gVoiceClient->addBlockRule(blockMask, presenceOnly); - } - else if (!stricmp("BlockMask", tag)) - blockMask = string; - else if (!stricmp("PresenceOnly", tag)) - presenceOnly = string; - else if (!stricmp("AutoAcceptRule", tag)) - { - gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy); - } - else if (!stricmp("AutoAcceptMask", tag)) - autoAcceptMask = string; - else if (!stricmp("AutoAddAsBuddy", tag)) - autoAddAsBuddy = string; - else if (!stricmp("MessageHeader", tag)) - messageHeader = string; - else if (!stricmp("MessageBody", tag)) - messageBody = string; - else if (!stricmp("NotificationType", tag)) - notificationType = string; - else if (!stricmp("HasText", tag)) - hasText = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasAudio", tag)) - hasAudio = !stricmp(string.c_str(), "true"); - else if (!stricmp("HasVideo", tag)) - hasVideo = !stricmp(string.c_str(), "true"); - else if (!stricmp("Terminated", tag)) - terminated = !stricmp(string.c_str(), "true"); - else if (!stricmp("SubscriptionHandle", tag)) - subscriptionHandle = string; - else if (!stricmp("SubscriptionType", tag)) - subscriptionType = string; - - - if(clearbuffer) - { - textBuffer.clear(); - accumulateText= false; - } - - if (responseDepth == 0) - { - // We finished all of the XML, process the data - processResponse(tag); - } - } -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::CharData(const char *buffer, int length) -{ - /* - This method is called for anything that isn't a tag, which can be text you - want that lies between tags, and a lot of stuff you don't want like file formatting - (tabs, spaces, CR/LF, etc). - - Only copy text if we are in accumulate mode... - */ - if (accumulateText) - textBuffer.append(buffer, length); -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::processResponse(std::string tag) -{ - LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; - - // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs. - // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", - // so I believe this will give correct behavior. - - if(returnCode == 0) - statusCode = 0; - - if (isEvent) - { - const char *eventTypeCstr = eventTypeString.c_str(); - if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) - { - gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); - } - else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-1408789@bhr.vivox.com - true - false - - - */ - gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) - { - gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) - { - gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle); - } - else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - 200 - OK - 2 - false - - */ - gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); - } - else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==1 - true - 1 - true - - */ - gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); - } - else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com - xI5auBZ60SJWIk606-1JGRQ== - - 0 - - */ - gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); - } - else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 - sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com - xtx7YNV-3SGiG7rA1fo5Ndw== - - */ - gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); - } - else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com - false - true - 44 - 0.0879437 - - */ - - // These happen so often that logging them is pretty useless. - squelchDebugOutput = true; - - gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); - } - else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) - { - gVoiceClient->auxAudioPropertiesEvent(energy); - } - else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) - { - gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString); - } - else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent")) - { - // The buddy list was updated during parsing. - // Need to recheck against the friends list. - gVoiceClient->buddyListChanged(); - } - else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw== - sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com - Monroe Tester - - 0 - Set - - */ - // TODO: Question: Do we need to process this at all? - } - else if (!stricmp(eventTypeCstr, "MessageEvent")) - { - gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); - } - else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) - { - gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType); - } - else if (!stricmp(eventTypeCstr, "SubscriptionEvent")) - { - gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType); - } - else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 - sip:confctl-9@bhd.vivox.com - 0 - 50 - 1 - 0 - 000 - 0 - - */ - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - - else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) - { - /* - - c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 - - */ - // We don't need to process this, but we also shouldn't warn on it, since that confuses people. - } - else - { - LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; - } - } - else - { - const char *actionCstr = actionString.c_str(); - if (!stricmp(actionCstr, "Connector.Create.1")) - { - gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); - } - else if (!stricmp(actionCstr, "Account.Login.1")) - { - gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); - } - else if (!stricmp(actionCstr, "Session.Create.1")) - { - gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) - { - gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); - } - else if (!stricmp(actionCstr, "Session.Connect.1")) - { - gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.Logout.1")) - { - gVoiceClient->logoutResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) - { - gVoiceClient->connectorShutdownResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.ListBlockRules.1")) - { - gVoiceClient->accountListBlockRulesResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1")) - { - gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) - { - // We don't need to process these, but they're so spammy we don't want to log them. - squelchDebugOutput = true; - } -/* - else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) - { - gVoiceClient->channelGetListResponse(statusCode, statusString); - } - else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) - { - - } - else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) - { - - } - else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) - { - - } - else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) - { - - } - else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) - { - - } -*/ - } -} - -/////////////////////////////////////////////////////////////////////////////////////////////// - -class LLVoiceClientMuteListObserver : public LLMuteListObserver -{ - /* virtual */ void onChange() { gVoiceClient->muteListChanged();} -}; - -class LLVoiceClientFriendsObserver : public LLFriendObserver -{ -public: - /* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);} -}; - -static LLVoiceClientMuteListObserver mutelist_listener; -static bool sMuteListListener_listening = false; - -static LLVoiceClientFriendsObserver *friendslist_listener = NULL; - -/////////////////////////////////////////////////////////////////////////////////////////////// -class LLVoiceClientCapResponder : public LLHTTPClient::ResponderWithResult -{ -public: - LLVoiceClientCapResponder(void){}; - - /*virtual*/ void error(U32 status, const std::string& reason); // called with bad status codes - /*virtual*/ void result(const LLSD& content); - /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return voiceClientCapResponder_timeout; } - /*virtual*/ char const* getName(void) const { return "LLVoiceClientCapResponder"; } - -private: -}; - -void LLVoiceClientCapResponder::error(U32 status, const std::string& reason) -{ - LL_WARNS("Voice") << "LLVoiceClientCapResponder::error(" - << status << ": " << reason << ")" - << LL_ENDL; -} - -void LLVoiceClientCapResponder::result(const LLSD& content) -{ - LLSD::map_const_iterator iter; - - LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; - - if ( content.has("voice_credentials") ) - { - LLSD voice_credentials = content["voice_credentials"]; - std::string uri; - std::string credentials; - - if ( voice_credentials.has("channel_uri") ) - { - uri = voice_credentials["channel_uri"].asString(); - } - if ( voice_credentials.has("channel_credentials") ) - { - credentials = - voice_credentials["channel_credentials"].asString(); - } - - gVoiceClient->setSpatialChannel(uri, credentials); - } -} - - - -#if LL_WINDOWS -static HANDLE sGatewayHandle = 0; - -static bool isGatewayRunning() -{ - bool result = false; - if(sGatewayHandle != 0) - { - DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); - if(waitresult != WAIT_OBJECT_0) - { - result = true; - } - } - return result; -} -static void killGateway() -{ - if(sGatewayHandle != 0) - { - TerminateProcess(sGatewayHandle,0); - } -} - -#else // Mac and linux - -static pid_t sGatewayPID = 0; -static bool isGatewayRunning() -{ - bool result = false; - if(sGatewayPID != 0) - { - // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists. - if(kill(sGatewayPID, 0) == 0) - { - result = true; - } - } - return result; -} - -static void killGateway() -{ - if(sGatewayPID != 0) - { - kill(sGatewayPID, SIGTERM); - } -} - -#endif - -/////////////////////////////////////////////////////////////////////////////////////////////// - -LLVoiceClient::LLVoiceClient() -{ - gVoiceClient = this; - mWriteInProgress = false; - mAreaVoiceDisabled = false; - mPTT = false; - mUserPTTState = false; - mMuteMic = false; - mSessionTerminateRequested = false; - mRelogRequested = false; - mConnected = false; - mCommandCookie = 0; - mCurrentParcelLocalID = 0; - mLoginRetryCount = 0; - - mSpeakerVolume = 0; - mMicVolume = 0; - - mNextAudioSession = NULL; - mAudioSession = NULL; - mAudioSessionChanged = false; - - // Initial dirty state - mSpatialCoordsDirty = false; - mPTTDirty = true; - mFriendsListDirty = true; - mSpeakerVolumeDirty = true; - mMicVolumeDirty = true; - mBuddyListMapPopulated = false; - mBlockRulesListReceived = false; - mAutoAcceptRulesListReceived = false; - mCaptureDeviceDirty = false; - mRenderDeviceDirty = false; - - // Use default values for everything then call updateSettings() after preferences are loaded - mVoiceEnabled = false; - mUsePTT = true; - mPTTIsToggle = false; - mEarLocation = 0; - mLipSyncEnabled = false; - - mTuningMode = false; - mTuningEnergy = 0.0f; - mTuningMicVolume = 0; - mTuningMicVolumeDirty = true; - mTuningSpeakerVolume = 0; - mTuningSpeakerVolumeDirty = true; - - // gMuteListp isn't set up at this point, so we defer this until later. -// gMuteListp->addObserver(&mutelist_listener); - - // stash the pump for later use - // This now happens when init() is called instead. - mPump = NULL; - -#if LL_DARWIN || LL_LINUX || LL_SOLARIS - // HACK: THIS DOES NOT BELONG HERE - // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. - // This should cause us to ignore SIGPIPE and handle the error through proper channels. - // This should really be set up elsewhere. Where should it go? - signal(SIGPIPE, SIG_IGN); - - // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. - // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. - signal(SIGCHLD, SIG_IGN); -#endif - - // set up state machine - setState(stateDisabled); - - gIdleCallbacks.addFunction(idle, this); -} - -//--------------------------------------------------- - -LLVoiceClient::~LLVoiceClient() -{ -} - -//---------------------------------------------- - -void LLVoiceClient::init(LLPumpIO *pump) -{ - // constructor will set up gVoiceClient - LLVoiceClient::getInstance()->mPump = pump; - LLVoiceClient::getInstance()->updateSettings(); -} - -void LLVoiceClient::terminate() -{ - if(gVoiceClient) - { -// gVoiceClient->leaveAudioSession(); - gVoiceClient->logout(); - // As of SDK version 4885, this should no longer be necessary. It will linger after the socket close if it needs to. - // ms_sleep(2000); - gVoiceClient->connectorShutdown(); - gVoiceClient->closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. - - // This will do unpleasant things on windows. -// killGateway(); - - // Don't do this anymore -- LLSingleton will take care of deleting the object. -// delete gVoiceClient; - - // Hint to other code not to access the voice client anymore. - gVoiceClient = NULL; - } -} - -//--------------------------------------------------- - -void LLVoiceClient::updateSettings() -{ - setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); - setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); - std::string keyString = gSavedSettings.getString("PushToTalkButton"); - setPTTKey(keyString); - setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); - setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); - - std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); - setCaptureDevice(inputDevice); - std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); - setRenderDevice(outputDevice); - F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); - setMicGain(mic_level); - setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); -} - -///////////////////////////// -// utility functions - -bool LLVoiceClient::writeString(const std::string &str) -{ - bool result = false; - if(mConnected) - { - apr_status_t err; - apr_size_t size = (apr_size_t)str.size(); - apr_size_t written = size; - - //MARK: Turn this on to log outgoing XML -// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; - - // check return code - sockets will fail (broken, etc.) - err = apr_socket_send( - mSocket->getSocket(), - (const char*)str.data(), - &written); - - if(err == 0) - { - // Success. - result = true; - } - // TODO: handle partial writes (written is number of bytes written) - // Need to set socket to non-blocking before this will work. -// else if(APR_STATUS_IS_EAGAIN(err)) -// { -// // -// } - else - { - // Assume any socket error means something bad. For now, just close the socket. - char buf[MAX_STRING]; - LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; - daemonDied(); - } - } - - return result; -} - - -///////////////////////////// -// session control messages -void LLVoiceClient::connectorCreate() -{ - std::ostringstream stream; - std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); - std::string loglevel = "0"; - - // Transition to stateConnectorStarted when the connector handle comes back. - setState(stateConnectorStarting); - - std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); - - if(savedLogLevel != "-1") - { - LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; - loglevel = "10"; - } - - stream - << "" - << "V2 SDK" - << "" << mVoiceAccountServerURI << "" - << "Normal"; - - if (gSavedSettings.getBOOL("VoiceMultiInstance")) - { - stream - << "30000" - << "50000"; - } - - stream - << "" - << "" << logpath << "" - << "Connector" - << ".log" - << "" << loglevel << "" - << "" - << "SecondLifeViewer.1" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::connectorShutdown() -{ - setState(stateConnectorStopping); - - if(!mConnectorHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mConnectorHandle << "" - << "" - << "\n\n\n"; - - mConnectorHandle.clear(); - - writeString(stream.str()); - } -} - -void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID) -{ - mAccountFirstName = firstName; - mAccountLastName = lastName; - - mAccountDisplayName = firstName; - mAccountDisplayName += " "; - mAccountDisplayName += lastName; - - LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - - sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid(); - - mAccountName = nameFromID(agentID); -} - -void LLVoiceClient::requestVoiceAccountProvision(S32 retries) -{ - if ( gAgent.getRegion() && mVoiceEnabled ) - { - std::string url = - gAgent.getRegion()->getCapability( - "ProvisionVoiceAccountRequest"); - - if ( url == "" ) return; - - LLHTTPClient::post( - url, - LLSD(), - new LLViewerVoiceAccountProvisionResponder(retries)); - } -} - -void LLVoiceClient::login( - const std::string& account_name, - const std::string& password, - const std::string& voice_sip_uri_hostname, - const std::string& voice_account_server_uri) -{ - mVoiceSIPURIHostName = voice_sip_uri_hostname; - mVoiceAccountServerURI = voice_account_server_uri; - - if(!mAccountHandle.empty()) - { - // Already logged in. - LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; - - // Don't process another login. - return; - } - else if ( account_name != mAccountName ) - { - //TODO: error? - LL_WARNS("Voice") << "Wrong account name! " << account_name - << " instead of " << mAccountName << LL_ENDL; - } - else - { - mAccountPassword = password; - } - - std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); - - if( !debugSIPURIHostName.empty() ) - { - mVoiceSIPURIHostName = debugSIPURIHostName; - } - - if( mVoiceSIPURIHostName.empty() ) - { - // we have an empty account server name - // so we fall back to hardcoded defaults - - if(sConnectingToAgni) - { - // Use the release account server - mVoiceSIPURIHostName = "bhr.vivox.com"; - } - else - { - // Use the development account server - mVoiceSIPURIHostName = "bhd.vivox.com"; - } - } - - std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); - - if( !debugAccountServerURI.empty() ) - { - mVoiceAccountServerURI = debugAccountServerURI; - } - - if( mVoiceAccountServerURI.empty() ) - { - // If the account server URI isn't specified, construct it from the SIP URI hostname - mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/"; - } -} - -void LLVoiceClient::idle(void* user_data) -{ - LLVoiceClient* self = (LLVoiceClient*)user_data; - self->stateMachine(); -} - -std::string LLVoiceClient::state2string(LLVoiceClient::state inState) -{ - std::string result = "UNKNOWN"; - - // Prevent copy-paste errors when updating this list... -#define CASE(x) case x: result = #x; break - - switch(inState) - { - CASE(stateDisableCleanup); - CASE(stateDisabled); - CASE(stateStart); - CASE(stateDaemonLaunched); - CASE(stateConnecting); - CASE(stateConnected); - CASE(stateIdle); - CASE(stateMicTuningStart); - CASE(stateMicTuningRunning); - CASE(stateMicTuningStop); - CASE(stateConnectorStart); - CASE(stateConnectorStarting); - CASE(stateConnectorStarted); - CASE(stateLoginRetry); - CASE(stateLoginRetryWait); - CASE(stateNeedsLogin); - CASE(stateLoggingIn); - CASE(stateLoggedIn); - CASE(stateCreatingSessionGroup); - CASE(stateNoChannel); - CASE(stateJoiningSession); - CASE(stateSessionJoined); - CASE(stateRunning); - CASE(stateLeavingSession); - CASE(stateSessionTerminated); - CASE(stateLoggingOut); - CASE(stateLoggedOut); - CASE(stateConnectorStopping); - CASE(stateConnectorStopped); - CASE(stateConnectorFailed); - CASE(stateConnectorFailedWaiting); - CASE(stateLoginFailed); - CASE(stateLoginFailedWaiting); - CASE(stateJoinSessionFailed); - CASE(stateJoinSessionFailedWaiting); - CASE(stateJail); - } - -#undef CASE - - return result; -} std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus) { @@ -1536,3695 +67,292 @@ std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserv } #undef CASE - + return result; } -void LLVoiceClient::setState(state inState) + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +LLVoiceClient::LLVoiceClient() + : + mVoiceModule(NULL), + m_servicePump(NULL), + mVoiceEffectEnabled(LLCachedControl(gSavedSettings, "VoiceMorphingEnabled")), + mVoiceEffectDefault(LLCachedControl(gSavedPerAccountSettings, "VoiceEffectDefault")), + mPTTDirty(true), + mPTT(true), + mUsePTT(true), + mPTTIsMiddleMouse(false), + mPTTKey(0), + mPTTIsToggle(false), + mUserPTTState(false), + mMuteMic(false), + mDisableMic(false) { - LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; - - mState = inState; + updateSettings(); } -void LLVoiceClient::stateMachine() +//--------------------------------------------------- +// Basic setup/shutdown + +LLVoiceClient::~LLVoiceClient() { - if(gDisconnected) +} + +void LLVoiceClient::init(LLPumpIO *pump) +{ + // Initialize all of the voice modules + m_servicePump = pump; +} + +void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +{ + // In the future, we should change this to allow voice module registration + // with a table lookup of sorts. + std::string voice_server = gSavedSettings.getString("VoiceServerType"); + LL_DEBUGS("Voice") << "voice server type " << voice_server << LL_ENDL; + if(voice_server == "vivox") { - // The viewer has been disconnected from the sim. Disable voice. - setVoiceEnabled(false); - } - - if(mVoiceEnabled) - { - updatePosition(); - } - else if(mTuningMode) - { - // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. + mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance(); } else { - if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) - { - // User turned off voice support. Send the cleanup messages, close the socket, and reset. - if(!mConnected) - { - // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. - LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; - killGateway(); - } - - logout(); - connectorShutdown(); - - setState(stateDisableCleanup); - } + mVoiceModule = NULL; + return; } - - // Check for parcel boundary crossing - if(mVoiceEnabled) + mVoiceModule->init(m_servicePump); + mVoiceModule->userAuthorized(user_id, agentID); +} + + +void LLVoiceClient::terminate() +{ + if (mVoiceModule) mVoiceModule->terminate(); + mVoiceModule = NULL; +} + +const LLVoiceVersionInfo LLVoiceClient::getVersion() +{ + if (mVoiceModule) { - LLViewerRegion *region = gAgent.getRegion(); - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(region && parcel && region->capabilitiesReceived()) - { - S32 parcelLocalID = parcel->getLocalID(); - std::string regionName = region->getName(); - std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); - -// LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; - - // The region name starts out empty and gets filled in later. - // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. - // If either is empty, wait for the next time around. - if(!regionName.empty()) - { - if(!capURI.empty()) - { - if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) - { - // We have changed parcels. Initiate a parcel channel lookup. - mCurrentParcelLocalID = parcelLocalID; - mCurrentRegionName = regionName; - - parcelChanged(); - } - } - else - { - LL_DEBUGS("Voice") << "region doesn't have ParcelVoiceInfoRequest capability. This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; - } - } - } - } - - switch(getState()) - { - //MARK: stateDisableCleanup - case stateDisableCleanup: - // Clean up and reset everything. - closeSocket(); - deleteAllSessions(); - deleteAllBuddies(); - - mConnectorHandle.clear(); - mAccountHandle.clear(); - mAccountPassword.clear(); - mVoiceAccountServerURI.clear(); - - setState(stateDisabled); - break; - - //MARK: stateDisabled - case stateDisabled: - if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) - { - setState(stateStart); - } - break; - - //MARK: stateStart - case stateStart: - if(gSavedSettings.getBOOL("CmdLineDisableVoice")) - { - // Voice is locked out, we must not launch the vivox daemon. - setState(stateJail); - } - else if(!isGatewayRunning()) - { - if(true) - { - // Launch the voice daemon - -#if LL_LINUX && defined(LL_STANDALONE) - // Look for the vivox daemon in the executable path list - // using glib first. - char *voice_path = g_find_program_in_path ("SLVoice"); - std::string exe_path; - if (voice_path) { - exe_path = llformat("%s", voice_path); - free(voice_path); - } else { - exe_path = gDirUtilp->getExecutableDir() + - gDirUtilp->getDirDelimiter() + "SLVoice"; - } -#else - // *FIX:Mani - Using the executable dir instead - // of mAppRODataDir, the working directory from which the - // app is launched. - //std::string exe_path = gDirUtilp->getAppRODataDir(); - std::string exe_path = gDirUtilp->getExecutableDir(); - exe_path += gDirUtilp->getDirDelimiter(); -#if LL_WINDOWS - exe_path += "SLVoice.exe"; -#elif LL_DARWIN - exe_path += "../Resources/SLVoice"; -#else - exe_path += "SLVoice"; -#endif -#endif - // See if the vivox executable exists - llstat s; - if(!LLFile::stat(exe_path, &s)) - { - // vivox executable exists. Build the command line and launch the daemon. - // SLIM SDK: these arguments are no longer necessary. -// std::string args = " -p tcp -h -c"; - std::string args; - std::string cmd; - std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); - - // If we allow multiple instances of the viewer to start the voice - // daemon, set TEMPORARY random voice port - if (gSavedSettings.getBOOL("VoiceMultiInstance")) - { - LLControlVariable* voice_port = gSavedSettings.getControl("VoicePort"); - if (voice_port) - { - const BOOL DO_NOT_PERSIST = FALSE; - S32 port_nr = 30000 + ll_rand(20000); - voice_port->setValue(LLSD(port_nr), DO_NOT_PERSIST); - } - } - - if(loglevel.empty()) - { - loglevel = "-1"; // turn logging off completely - } - - args += " -ll "; - args += loglevel; - - // Tell voice gateway to listen to a specific port - if (gSavedSettings.getBOOL("VoiceMultiInstance")) - { - args += llformat(" -i 127.0.0.1:%u", gSavedSettings.getU32("VoicePort")); - } - - LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL; - -#if LL_WINDOWS - PROCESS_INFORMATION pinfo; - STARTUPINFOA sinfo; - memset(&sinfo, 0, sizeof(sinfo)); - std::string exe_dir = gDirUtilp->getAppRODataDir(); - cmd = "SLVoice.exe"; - cmd += args; - - // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string... - char *args2 = new char[args.size() + 1]; - strcpy(args2, args.c_str()); - - if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo)) - { -// DWORD dwErr = GetLastError(); - } - else - { - // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on - // CloseHandle(pinfo.hProcess); // stops leaks - nothing else - sGatewayHandle = pinfo.hProcess; - CloseHandle(pinfo.hThread); // stops leaks - nothing else - } - - delete[] args2; -#else // LL_WINDOWS - // This should be the same for mac and linux - { - std::vector arglist; - arglist.push_back(exe_path); - - // Split the argument string into separate strings for each argument - typedef boost::tokenizer > tokenizer; - boost::char_separator sep(" "); - tokenizer tokens(args, sep); - tokenizer::iterator token_iter; - - for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - arglist.push_back(*token_iter); - } - - // create an argv vector for the child process - char **fakeargv = new char*[arglist.size() + 1]; - int i; - for(i=0; i < arglist.size(); i++) - fakeargv[i] = const_cast(arglist[i].c_str()); - - fakeargv[i] = NULL; - - fflush(NULL); // flush all buffers before the child inherits them - pid_t id = vfork(); - if(id == 0) - { - // child - execv(exe_path.c_str(), fakeargv); - - // If we reach this point, the exec failed. - // Use _exit() instead of exit() per the vfork man page. - _exit(0); - } - - // parent - delete[] fakeargv; - sGatewayPID = id; - } -#endif // LL_WINDOWS - mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort")); - } - else - { - LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; - } - } - else - { - // SLIM SDK: port changed from 44124 to 44125. - // We can connect to a client gateway running on another host. This is useful for testing. - // To do this, launch the gateway on a nearby host like this: - // vivox-gw.exe -p tcp -i 0.0.0.0:44125 - // and put that host's IP address here. - mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort")); - } - - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); - - setState(stateDaemonLaunched); - - // Dirty the states we'll need to sync with the daemon when it comes up. - mPTTDirty = true; - mMicVolumeDirty = true; - mSpeakerVolumeDirty = true; - mSpeakerMuteDirty = true; - // These only need to be set if they're not default (i.e. empty string). - mCaptureDeviceDirty = !mCaptureDevice.empty(); - mRenderDeviceDirty = !mRenderDevice.empty(); - - mMainSessionGroupHandle.clear(); - } - break; - - //MARK: stateDaemonLaunched - case stateDaemonLaunched: - if(mUpdateTimer.hasExpired()) - { - LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL; - - mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); - - if(!mSocket) - { - mSocket = LLSocket::create(LLSocket::STREAM_TCP); - } - - mConnected = mSocket->blockingConnect(mDaemonHost); - if(mConnected) - { - setState(stateConnecting); - } - else - { - // If the connect failed, the socket may have been put into a bad state. Delete it. - closeSocket(); - } - } - break; - - //MARK: stateConnecting - case stateConnecting: - // Can't do this until we have the pump available. - if(mPump) - { - // MBW -- Note to self: pumps and pipes examples in - // indra/test/io.cpp - // indra/test/llpipeutil.{cpp|h} - - // Attach the pumps and pipes - - LLPumpIO::chain_t readChain; - - readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); - readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); - - mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); - - setState(stateConnected); - } - - break; - - //MARK: stateConnected - case stateConnected: - // Initial devices query - getCaptureDevicesSendMessage(); - getRenderDevicesSendMessage(); - - mLoginRetryCount = 0; - - setState(stateIdle); - break; - - //MARK: stateIdle - case stateIdle: - // This is the idle state where we're connected to the daemon but haven't set up a connector yet. - if(mTuningMode) - { - mTuningExitState = stateIdle; - setState(stateMicTuningStart); - } - else if(!mVoiceEnabled) - { - // We never started up the connector. This will shut down the daemon. - setState(stateConnectorStopped); - } - else if(!mAccountName.empty()) - { - LLViewerRegion *region = gAgent.getRegion(); - - if(region && region->capabilitiesReceived()) - { - if ( region->getCapability("ProvisionVoiceAccountRequest") != "" ) - { - if ( mAccountPassword.empty() ) - { - requestVoiceAccountProvision(); - } - setState(stateConnectorStart); - } - else - { - LL_DEBUGS("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL; - } - } - } - break; - - //MARK: stateMicTuningStart - case stateMicTuningStart: - if(mUpdateTimer.hasExpired()) - { - if(mCaptureDeviceDirty || mRenderDeviceDirty) - { - // These can't be changed while in tuning mode. Set them before starting. - std::ostringstream stream; - - buildSetCaptureDevice(stream); - buildSetRenderDevice(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - - // This will come around again in the same state and start the capture, after the timer expires. - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - } - else - { - // duration parameter is currently unused, per Mike S. - tuningCaptureStartSendMessage(10000); - - setState(stateMicTuningRunning); - } - } - - break; - - //MARK: stateMicTuningRunning - case stateMicTuningRunning: - if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) - { - // All of these conditions make us leave tuning mode. - setState(stateMicTuningStop); - } - else - { - // process mic/speaker volume changes - if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) - { - std::ostringstream stream; - - if(mTuningMicVolumeDirty) - { - LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; - stream - << "" - << "" << mTuningMicVolume << "" - << "\n\n\n"; - } - - if(mTuningSpeakerVolumeDirty) - { - stream - << "" - << "" << mTuningSpeakerVolume << "" - << "\n\n\n"; - } - - mTuningMicVolumeDirty = false; - mTuningSpeakerVolumeDirty = false; - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - } - } - break; - - //MARK: stateMicTuningStop - case stateMicTuningStop: - { - // transition out of mic tuning - tuningCaptureStopSendMessage(); - - setState(mTuningExitState); - - // if we exited just to change devices, this will keep us from re-entering too fast. - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - - } - break; - - //MARK: stateConnectorStart - case stateConnectorStart: - if(!mVoiceEnabled) - { - // We were never logged in. This will shut down the connector. - setState(stateLoggedOut); - } - else if(!mVoiceAccountServerURI.empty()) - { - connectorCreate(); - } - break; - - //MARK: stateConnectorStarting - case stateConnectorStarting: // waiting for connector handle - // connectorCreateResponse() will transition from here to stateConnectorStarted. - break; - - //MARK: stateConnectorStarted - case stateConnectorStarted: // connector handle received - if(!mVoiceEnabled) - { - // We were never logged in. This will shut down the connector. - setState(stateLoggedOut); - } - else - { - // The connector is started. Send a login message. - setState(stateNeedsLogin); - } - break; - - //MARK: stateLoginRetry - case stateLoginRetry: - if(mLoginRetryCount == 0) - { - // First retry -- display a message to the user - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); - } - - mLoginRetryCount++; - - if(mLoginRetryCount > MAX_LOGIN_RETRIES) - { - LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; - setState(stateLoginFailed); - } - else - { - LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); - setState(stateLoginRetryWait); - } - break; - - //MARK: stateLoginRetryWait - case stateLoginRetryWait: - if(mUpdateTimer.hasExpired()) - { - setState(stateNeedsLogin); - } - break; - - //MARK: stateNeedsLogin - case stateNeedsLogin: - if(!mAccountPassword.empty()) - { - setState(stateLoggingIn); - loginSendMessage(); - } - break; - - //MARK: stateLoggingIn - case stateLoggingIn: // waiting for account handle - // loginResponse() will transition from here to stateLoggedIn. - break; - - //MARK: stateLoggedIn - case stateLoggedIn: // account handle received - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - - // request the current set of block rules (we'll need them when updating the friends list) - accountListBlockRulesSendMessage(); - - // request the current set of auto-accept rules - accountListAutoAcceptRulesSendMessage(); - - // Set up the mute list observer if it hasn't been set up already. - if((!sMuteListListener_listening)) - { - LLMuteList::getInstance()->addObserver(&mutelist_listener); - sMuteListListener_listening = true; - } - - // Set up the friends list observer if it hasn't been set up already. - if(friendslist_listener == NULL) - { - friendslist_listener = new LLVoiceClientFriendsObserver; - LLAvatarTracker::instance().addObserver(friendslist_listener); - } - - // Set the initial state of mic mute, local speaker volume, etc. - { - std::ostringstream stream; - - buildLocalAudioUpdates(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - } - -#if USE_SESSION_GROUPS - // create the main session group - sessionGroupCreateSendMessage(); - - setState(stateCreatingSessionGroup); -#else - // Not using session groups -- skip the stateCreatingSessionGroup state. - setState(stateNoChannel); - - // Initial kick-off of channel lookup logic - parcelChanged(); -#endif - break; - - //MARK: stateCreatingSessionGroup - case stateCreatingSessionGroup: - if(mSessionTerminateRequested || !mVoiceEnabled) - { - // TODO: Question: is this the right way out of this state - setState(stateSessionTerminated); - } - else if(!mMainSessionGroupHandle.empty()) - { - setState(stateNoChannel); - - // Start looped recording (needed for "panic button" anti-griefing tool) - recordingLoopStart(); - - // Initial kick-off of channel lookup logic - parcelChanged(); - } - break; - - //MARK: stateNoChannel - case stateNoChannel: - // Do this here as well as inside sendPositionalUpdate(). - // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync. - sendFriendsListUpdates(); - - if(mSessionTerminateRequested || !mVoiceEnabled) - { - // TODO: Question: Is this the right way out of this state? - setState(stateSessionTerminated); - } - else if(mTuningMode) - { - mTuningExitState = stateNoChannel; - setState(stateMicTuningStart); - } - else if(sessionNeedsRelog(mNextAudioSession)) - { - requestRelog(); - setState(stateSessionTerminated); - } - else if(mNextAudioSession) - { - sessionState *oldSession = mAudioSession; - - mAudioSession = mNextAudioSession; - if(!mAudioSession->mReconnect) - { - mNextAudioSession = NULL; - } - - // The old session may now need to be deleted. - reapSession(oldSession); - - if(!mAudioSession->mHandle.empty()) - { - // Connect to a session by session handle - - sessionMediaConnectSendMessage(mAudioSession); - } - else - { - // Connect to a session by URI - sessionCreateSendMessage(mAudioSession, true, false); - } - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); - setState(stateJoiningSession); - } - else if(!mSpatialSessionURI.empty()) - { - // If we're not headed elsewhere and have a spatial URI, return to spatial. - switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); - } - break; - - //MARK: stateJoiningSession - case stateJoiningSession: // waiting for session handle - // joinedAudioSession() will transition from here to stateSessionJoined. - if(!mVoiceEnabled) - { - // User bailed out during connect -- jump straight to teardown. - setState(stateSessionTerminated); - } - else if(mSessionTerminateRequested) - { - if(mAudioSession && !mAudioSession->mHandle.empty()) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. - if(mAudioSession->mIsP2P) - { - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateSessionTerminated); - } - } - } - break; - - //MARK: stateSessionJoined - case stateSessionJoined: // session handle received - // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 - // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. - // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. - // This is a cheap way to make sure both have happened before proceeding. - if(mAudioSession && mAudioSession->mVoiceEnabled) - { - // Dirty state that may need to be sync'ed with the daemon. - mPTTDirty = true; - mSpeakerVolumeDirty = true; - mSpatialCoordsDirty = true; - - setState(stateRunning); - - // Start the throttle timer - mUpdateTimer.start(); - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - - // Events that need to happen when a session is joined could go here. - // Maybe send initial spatial data? - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - - } - else if(!mVoiceEnabled) - { - // User bailed out during connect -- jump straight to teardown. - setState(stateSessionTerminated); - } - else if(mSessionTerminateRequested) - { - // Only allow direct exits from this state in p2p calls (for cancelling an invite). - // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. - if(mAudioSession && mAudioSession->mIsP2P) - { - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateSessionTerminated); - } - } - break; - - //MARK: stateRunning - case stateRunning: // steady state - // Disabling voice or disconnect requested. - if(!mVoiceEnabled || mSessionTerminateRequested) - { - leaveAudioSession(); - } - else - { - - // Figure out whether the PTT state needs to change - { - bool newPTT; - if(mUsePTT) - { - // If configured to use PTT, track the user state. - newPTT = mUserPTTState; - } - else - { - // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak). - newPTT = true; - } - - if(mMuteMic) - { - // This always overrides any other PTT setting. - newPTT = false; - } - - // Dirty if state changed. - if(newPTT != mPTT) - { - mPTT = newPTT; - mPTTDirty = true; - } - } - - if(!inSpatialChannel()) - { - // When in a non-spatial channel, never send positional updates. - mSpatialCoordsDirty = false; - } - else - { - // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) - enforceTether(); - } - - // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast) - // or every 10hz, whichever is sooner. - if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired()) - { - mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - sendPositionalUpdate(); - } - } - break; - - //MARK: stateLeavingSession - case stateLeavingSession: // waiting for terminate session response - // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. - break; - - //MARK: stateSessionTerminated - case stateSessionTerminated: - - // Must do this first, since it uses mAudioSession. - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); - - if(mAudioSession) - { - sessionState *oldSession = mAudioSession; - - mAudioSession = NULL; - // We just notified status observers about this change. Don't do it again. - mAudioSessionChanged = false; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - else - { - LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; - } - - // Always reset the terminate request flag when we get here. - mSessionTerminateRequested = false; - - if(mVoiceEnabled && !mRelogRequested) - { - // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). - setState(stateNoChannel); - } - else - { - // Shutting down voice, continue with disconnecting. - logout(); - - // The state machine will take it from here - mRelogRequested = false; - } - - break; - - //MARK: stateLoggingOut - case stateLoggingOut: // waiting for logout response - // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. - break; - - //MARK: stateLoggedOut - case stateLoggedOut: // logout response received - - // Once we're logged out, all these things are invalid. - mAccountHandle.clear(); - deleteAllSessions(); - deleteAllBuddies(); - - if(mVoiceEnabled && !mRelogRequested) - { - // User was logged out, but wants to be logged in. Send a new login request. - setState(stateNeedsLogin); - } - else - { - // shut down the connector - connectorShutdown(); - } - break; - - //MARK: stateConnectorStopping - case stateConnectorStopping: // waiting for connector stop - // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. - break; - - //MARK: stateConnectorStopped - case stateConnectorStopped: // connector stop received - setState(stateDisableCleanup); - break; - - //MARK: stateConnectorFailed - case stateConnectorFailed: - setState(stateConnectorFailedWaiting); - break; - //MARK: stateConnectorFailedWaiting - case stateConnectorFailedWaiting: - if(!mVoiceEnabled) - { - setState(stateDisableCleanup); - } - break; - - //MARK: stateLoginFailed - case stateLoginFailed: - setState(stateLoginFailedWaiting); - break; - //MARK: stateLoginFailedWaiting - case stateLoginFailedWaiting: - if(!mVoiceEnabled) - { - setState(stateDisableCleanup); - } - break; - - //MARK: stateJoinSessionFailed - case stateJoinSessionFailed: - // Transition to error state. Send out any notifications here. - if(mAudioSession) - { - LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; - } - else - { - LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; - } - - notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); - setState(stateJoinSessionFailedWaiting); - break; - - //MARK: stateJoinSessionFailedWaiting - case stateJoinSessionFailedWaiting: - // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. - // Region crossings may leave this state and try the join again. - if(mSessionTerminateRequested) - { - setState(stateSessionTerminated); - } - break; - - //MARK: stateJail - case stateJail: - // We have given up. Do nothing. - break; - - } - - if(mAudioSession && mAudioSession->mParticipantsChanged) - { - mAudioSession->mParticipantsChanged = false; - mAudioSessionChanged = true; - } - - if(mAudioSessionChanged) - { - mAudioSessionChanged = false; - notifyParticipantObservers(); - } -} - -void LLVoiceClient::closeSocket(void) -{ - mSocket.reset(); - mConnected = false; -} - -void LLVoiceClient::loginSendMessage() -{ - std::ostringstream stream; - - bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps"); - - stream - << "" - << "" << mConnectorHandle << "" - << "" << mAccountName << "" - << "" << mAccountPassword << "" - << "VerifyAnswer" - << "true" - << "Application" - << "5" - << (autoPostCrashDumps?"true":"") - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::logout() -{ - // Ensure that we'll re-request provisioning before logging in again - mAccountPassword.clear(); - mVoiceAccountServerURI.clear(); - - setState(stateLoggingOut); - logoutSendMessage(); -} - -void LLVoiceClient::logoutSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - mAccountHandle.clear(); - - writeString(stream.str()); - } -} - -void LLVoiceClient::accountListBlockRulesSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::accountListAutoAcceptRulesSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::sessionGroupCreateSendMessage() -{ - if(!mAccountHandle.empty()) - { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; - - stream - << "" - << "" << mAccountHandle << "" - << "Normal" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) -{ - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"Session.Create.1\">" - << "" << mAccountHandle << "" - << "" << session->mSIPURI << ""; - - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~"; - - if(!session->mHash.empty()) - { - stream - << "" << LLURI::escape(session->mHash, allowed_chars) << "" - << "SHA1UserName"; - } - - stream - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << mChannelName << "" - << "\n\n\n"; - writeString(stream.str()); -} - -void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) -{ - LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; - - session->mCreateInProgress = true; - if(startAudio) - { - session->mMediaConnectInProgress = true; - } - - std::string password; - if(!session->mHash.empty()) - { - static const std::string allowed_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~" - ; - password = LLURI::escape(session->mHash, allowed_chars); - } - - std::ostringstream stream; - stream - << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mSIPURI << "" - << "" << mChannelName << "" - << "" << (startAudio?"true":"false") << "" - << "" << (startText?"true":"false") << "" - << "" << password << "" - << "SHA1UserName" - << "\n\n\n" - ; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session) -{ - LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; - - session->mMediaConnectInProgress = true; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.MediaConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session) -{ - LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; - - std::ostringstream stream; - - stream - << "mHandle << "\" action=\"Session.TextConnect.1\">" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionTerminate() -{ - mSessionTerminateRequested = true; -} - -void LLVoiceClient::requestRelog() -{ - mSessionTerminateRequested = true; - mRelogRequested = true; -} - - -void LLVoiceClient::leaveAudioSession() -{ - if(mAudioSession) - { - LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - - switch(getState()) - { - case stateNoChannel: - // In this case, we want to pretend the join failed so our state machine doesn't get stuck. - // Skip the join failed transition state so we don't send out error notifications. - setState(stateJoinSessionFailedWaiting); - break; - case stateJoiningSession: - case stateSessionJoined: - case stateRunning: - if(!mAudioSession->mHandle.empty()) - { - -#if RECORD_EVERYTHING - // HACK: for testing only - // Save looped recording - std::string savepath("/tmp/vivoxrecording"); - { - time_t now = time(NULL); - const size_t BUF_SIZE = 64; - char time_str[BUF_SIZE]; /* Flawfinder: ignore */ - - strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); - savepath += time_str; - } - recordingLoopSave(savepath); -#endif - - sessionMediaDisconnectSendMessage(mAudioSession); - setState(stateLeavingSession); - } - else - { - LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; - setState(stateSessionTerminated); - } - break; - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - setState(stateSessionTerminated); - break; - - default: - LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; - break; - } + return mVoiceModule->getVersion(); } else { - LL_WARNS("Voice") << "called with no active session" << LL_ENDL; - setState(stateSessionTerminated); + LLVoiceVersionInfo result; + result.serverVersion = std::string(); + result.serverType = std::string(); + return result; } } -void LLVoiceClient::sessionTerminateSendMessage(sessionState *session) +void LLVoiceClient::updateSettings() { - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); + setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); + std::string keyString = gSavedSettings.getString("PushToTalkButton"); + setPTTKey(keyString); + setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); + mDisableMic = gSavedSettings.getBOOL("VoiceDisableMic"); + + updateMicMuteLogic(); + + if (mVoiceModule) mVoiceModule->updateSettings(); } -void LLVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "Audio" - << "\n\n\n"; - - writeString(stream.str()); - -} - -void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) -{ - std::ostringstream stream; - - LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL; - stream - << "" - << "" << session->mGroupHandle << "" - << "" << session->mHandle << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::getCaptureDevicesSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::getRenderDevicesSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::clearCaptureDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mCaptureDevices.clear(); -} - -void LLVoiceClient::addCaptureDevice(const std::string& name) -{ - LL_DEBUGS("Voice") << name << LL_ENDL; - - mCaptureDevices.push_back(name); -} - -LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices() -{ - return &mCaptureDevices; -} - -void LLVoiceClient::setCaptureDevice(const std::string& name) -{ - if(name == "Default") - { - if(!mCaptureDevice.empty()) - { - mCaptureDevice.clear(); - mCaptureDeviceDirty = true; - } - } - else - { - if(mCaptureDevice != name) - { - mCaptureDevice = name; - mCaptureDeviceDirty = true; - } - } -} - -void LLVoiceClient::clearRenderDevices() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - mRenderDevices.clear(); -} - -void LLVoiceClient::addRenderDevice(const std::string& name) -{ - LL_DEBUGS("Voice") << name << LL_ENDL; - mRenderDevices.push_back(name); -} - -LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices() -{ - return &mRenderDevices; -} - -void LLVoiceClient::setRenderDevice(const std::string& name) -{ - if(name == "Default") - { - if(!mRenderDevice.empty()) - { - mRenderDevice.clear(); - mRenderDeviceDirty = true; - } - } - else - { - if(mRenderDevice != name) - { - mRenderDevice = name; - mRenderDeviceDirty = true; - } - } - -} +//-------------------------------------------------- +// tuning void LLVoiceClient::tuningStart() { - mTuningMode = true; - if(getState() >= stateNoChannel) - { - sessionTerminate(); - } + if (mVoiceModule) mVoiceModule->tuningStart(); } void LLVoiceClient::tuningStop() { - mTuningMode = false; + if (mVoiceModule) mVoiceModule->tuningStop(); } bool LLVoiceClient::inTuningMode() { - bool result = false; - switch(getState()) + if (mVoiceModule) { - case stateMicTuningRunning: - result = true; - break; - default: - break; + return mVoiceModule->inTuningMode(); + } + else + { + return false; } - return result; -} - -void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{ - mTuningAudioFile = name; - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "" << (loop?"1":"0") << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::tuningRenderStopSendMessage() -{ - std::ostringstream stream; - stream - << "" - << "" << mTuningAudioFile << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::tuningCaptureStartSendMessage(int duration) -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "" << duration << "" - << "\n\n\n"; - - writeString(stream.str()); -} - -void LLVoiceClient::tuningCaptureStopSendMessage() -{ - LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; - - std::ostringstream stream; - stream - << "" - << "\n\n\n"; - - writeString(stream.str()); - - mTuningEnergy = 0.0f; } void LLVoiceClient::tuningSetMicVolume(float volume) { - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mTuningMicVolume) - { - mTuningMicVolume = scaled_volume; - mTuningMicVolumeDirty = true; - } + if (mVoiceModule) mVoiceModule->tuningSetMicVolume(volume); } void LLVoiceClient::tuningSetSpeakerVolume(float volume) { - int scaled_volume = scale_speaker_volume(volume); - - if(scaled_volume != mTuningSpeakerVolume) - { - mTuningSpeakerVolume = scaled_volume; - mTuningSpeakerVolumeDirty = true; - } + if (mVoiceModule) mVoiceModule->tuningSetSpeakerVolume(volume); } - + float LLVoiceClient::tuningGetEnergy(void) { - return mTuningEnergy; + if (mVoiceModule) + { + return mVoiceModule->tuningGetEnergy(); + } + else + { + return 0.0; + } } + +//------------------------------------------------ +// devices + bool LLVoiceClient::deviceSettingsAvailable() { - bool result = true; - - if(!mConnected) - result = false; - - if(mRenderDevices.empty()) - result = false; - - return result; + if (mVoiceModule) + { + return mVoiceModule->deviceSettingsAvailable(); + } + else + { + return false; + } } void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) { - if(clearCurrentList) - { - clearCaptureDevices(); - clearRenderDevices(); - } - getCaptureDevicesSendMessage(); - getRenderDevicesSendMessage(); + if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList); } -void LLVoiceClient::daemonDied() +void LLVoiceClient::setCaptureDevice(const std::string& name) { - // The daemon died, so the connection is gone. Reset everything and start over. - LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; + if (mVoiceModule) mVoiceModule->setCaptureDevice(name); - // Try to relaunch the daemon - setState(stateDisableCleanup); } -void LLVoiceClient::giveUp() +void LLVoiceClient::setRenderDevice(const std::string& name) { - // All has failed. Clean up and stop trying. - closeSocket(); - deleteAllSessions(); - deleteAllBuddies(); - - setState(stateJail); + if (mVoiceModule) mVoiceModule->setRenderDevice(name); } -static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) +const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices() { - F32 nat[3], nup[3], nl[3]; // the new at, up, left vectors and the new position and velocity -// F32 nvel[3]; - F64 npos[3]; - - // The original XML command was sent like this: - /* - << "" - << "" << pos[VX] << "" - << "" << pos[VZ] << "" - << "" << pos[VY] << "" - << "" - << "" - << "" << mAvatarVelocity[VX] << "" - << "" << mAvatarVelocity[VZ] << "" - << "" << mAvatarVelocity[VY] << "" - << "" - << "" - << "" << l.mV[VX] << "" - << "" << u.mV[VX] << "" - << "" << a.mV[VX] << "" - << "" - << "" - << "" << l.mV[VZ] << "" - << "" << u.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VY] << "" - << "" << u.mV [VZ] << "" - << "" << a.mV [VY] << "" - << ""; - */ - -#if 1 - // This was the original transform done when building the XML command - nat[0] = left.mV[VX]; - nat[1] = up.mV[VX]; - nat[2] = at.mV[VX]; - - nup[0] = left.mV[VZ]; - nup[1] = up.mV[VY]; - nup[2] = at.mV[VZ]; - - nl[0] = left.mV[VY]; - nl[1] = up.mV[VZ]; - nl[2] = at.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY]; - -// nvel[0] = vel.mV[VX]; -// nvel[1] = vel.mV[VZ]; -// nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - - // This was the original transform done in the SDK - nat[0] = at.mV[2]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * left.mV[2]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = at.mV[0]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[0]; - - npos[2] = pos.mdV[2] * -1.0; - npos[1] = pos.mdV[1]; - npos[0] = pos.mdV[0]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } -#else - // This is the compose of the two transforms (at least, that's what I'm trying for) - nat[0] = at.mV[VX]; - nat[1] = 0; // y component of at vector is always 0, this was up[2] - nat[2] = -1 * up.mV[VZ]; - - // We override whatever the application gives us - nup[0] = 0; // x component of up vector is always 0 - nup[1] = 1; // y component of up vector is always 1 - nup[2] = 0; // z component of up vector is always 0 - - nl[0] = left.mV[VX]; - nl[1] = 0; // y component of left vector is always zero, this was up[0] - nl[2] = -1 * left.mV[VY]; - - npos[0] = pos.mdV[VX]; - npos[1] = pos.mdV[VZ]; - npos[2] = pos.mdV[VY] * -1.0; - - nvel[0] = vel.mV[VX]; - nvel[1] = vel.mV[VZ]; - nvel[2] = vel.mV[VY]; - - for(int i=0;i<3;++i) { - at.mV[i] = nat[i]; - up.mV[i] = nup[i]; - left.mV[i] = nl[i]; - pos.mdV[i] = npos[i]; - } - -#endif -} - -void LLVoiceClient::sendPositionalUpdate(void) -{ - std::ostringstream stream; - - if(mSpatialCoordsDirty) + static LLVoiceDeviceList nullCaptureDevices; + if (mVoiceModule) { - LLVector3 l, u, a, vel; - LLVector3d pos; - - mSpatialCoordsDirty = false; - - // Always send both speaker and listener positions together. - stream << "" - << "" << getAudioSessionHandle() << ""; - - stream << ""; - -// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; - l = mAvatarRot.getLeftRow(); - u = mAvatarRot.getUpRow(); - a = mAvatarRot.getFwdRow(); - pos = mAvatarPosition; - vel = mAvatarVelocity; - - // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. - // The old transform is replicated by this function. - oldSDKTransform(l, u, a, pos, vel); - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << ""; - - stream << ""; - - stream << ""; - - LLVector3d earPosition; - LLVector3 earVelocity; - LLMatrix3 earRot; - - switch(mEarLocation) - { - case earLocCamera: - default: - earPosition = mCameraPosition; - earVelocity = mCameraVelocity; - earRot = mCameraRot; - break; - - case earLocAvatar: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = mAvatarRot; - break; - - case earLocMixed: - earPosition = mAvatarPosition; - earVelocity = mAvatarVelocity; - earRot = mCameraRot; - break; - } - - l = earRot.getLeftRow(); - u = earRot.getUpRow(); - a = earRot.getFwdRow(); - pos = earPosition; - vel = earVelocity; - -// LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; - - oldSDKTransform(l, u, a, pos, vel); - - stream - << "" - << "" << pos.mdV[VX] << "" - << "" << pos.mdV[VY] << "" - << "" << pos.mdV[VZ] << "" - << "" - << "" - << "" << vel.mV[VX] << "" - << "" << vel.mV[VY] << "" - << "" << vel.mV[VZ] << "" - << "" - << "" - << "" << a.mV[VX] << "" - << "" << a.mV[VY] << "" - << "" << a.mV[VZ] << "" - << "" - << "" - << "" << u.mV[VX] << "" - << "" << u.mV[VY] << "" - << "" << u.mV[VZ] << "" - << "" - << "" - << "" << l.mV [VX] << "" - << "" << l.mV [VY] << "" - << "" << l.mV [VZ] << "" - << ""; - - - stream << ""; - - stream << "\n\n\n"; - } - - if(mAudioSession && mAudioSession->mVolumeDirty) - { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - - mAudioSession->mVolumeDirty = false; - - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) - { - participantState *p = iter->second; - - if(p->mVolumeDirty) - { - // Can't set volume/mute for yourself - if(!p->mIsSelf) - { - int volume = 56; // nominal default value - bool mute = p->mOnMuteList; - - if(p->mUserVolume != -1) - { - // scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal") - if(p->mUserVolume < 100) - volume = (p->mUserVolume * 56) / 100; - else - volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56; - } - else if(p->mVolume != -1) - { - // Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent) - volume = p->mVolume; - } - - - if(mute) - { - // SetParticipantMuteForMe doesn't work in p2p sessions. - // If we want the user to be muted, set their volume to 0 as well. - // This isn't perfect, but it will at least reduce their volume to a minimum. - volume = 0; - } - - if(volume == 0) - mute = true; - - LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; - - // SLIM SDK: Send both volume and mute commands. - - // Send a "volume for me" command for the user. - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << volume << "" - << "\n\n\n"; - - // Send a "mute for me" command for the user - stream << "" - << "" << getAudioSessionHandle() << "" - << "" << p->mURI << "" - << "" << (mute?"1":"0") << "" - << "\n\n\n"; - } - - p->mVolumeDirty = false; - } - } - } - - buildLocalAudioUpdates(stream); - - if(!stream.str().empty()) - { - writeString(stream.str()); - } - - // Friends list updates can be huge, especially on the first voice login of an account with lots of friends. - // Batching them all together can choke SLVoice, so send them in separate writes. - sendFriendsListUpdates(); -} - -void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) -{ - if(mCaptureDeviceDirty) - { - LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; - - stream - << "" - << "" << mCaptureDevice << "" - << "" - << "\n\n\n"; - - mCaptureDeviceDirty = false; - } -} - -void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream) -{ - if(mRenderDeviceDirty) - { - LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; - - stream - << "" - << "" << mRenderDevice << "" - << "" - << "\n\n\n"; - mRenderDeviceDirty = false; - } -} - -void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) -{ - buildSetCaptureDevice(stream); - - buildSetRenderDevice(stream); - - if(mPTTDirty) - { - mPTTDirty = false; - - // Send a local mute command. - // NOTE that the state of "PTT" is the inverse of "local mute". - // (i.e. when PTT is true, we send a mute command with "false", and vice versa) - - LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << (mPTT?"false":"true") << "" - << "\n\n\n"; - - } - - if(mSpeakerMuteDirty) - { - const char *muteval = ((mSpeakerVolume == 0)?"true":"false"); - - mSpeakerMuteDirty = false; - - LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << muteval << "" - << "\n\n\n"; - - } - - if(mSpeakerVolumeDirty) - { - mSpeakerVolumeDirty = false; - - LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << mSpeakerVolume << "" - << "\n\n\n"; - - } - - if(mMicVolumeDirty) - { - mMicVolumeDirty = false; - - LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; - - stream << "" - << "" << mConnectorHandle << "" - << "" << mMicVolume << "" - << "\n\n\n"; - } - - -} - -void LLVoiceClient::checkFriend(const LLUUID& id) -{ - std::string name; - buddyListEntry *buddy = findBuddy(id); - - // Make sure we don't add a name before it's been looked up. - if(gCacheName->getFullName(id, name)) - { - - const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); - bool canSeeMeOnline = false; - if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) - canSeeMeOnline = true; - - // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary. - - if(buddy) - { - // This buddy is already in both lists. - - if(name != buddy->mDisplayName) - { - // The buddy is in the list with the wrong name. Update it with the correct name. - LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; - buddy->mDisplayName = name; - buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent. - } - } - else - { - // This buddy was not in the vivox list, needs to be added. - buddy = addBuddy(sipURIFromID(id), name); - buddy->mUUID = id; - } - - // In all the above cases, the buddy is in the SL friends list (which is how we got here). - buddy->mInSLFriends = true; - buddy->mCanSeeMeOnline = canSeeMeOnline; - buddy->mNameResolved = true; - + return mVoiceModule->getCaptureDevices(); } else { - // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has. - if(buddy) - { - buddy->mNameResolved = false; - } - - // Initiate a lookup. - // The "lookup completed" callback will ensure that the friends list is rechecked after it completes. - lookupName(id); + return nullCaptureDevices; } } -void LLVoiceClient::clearAllLists() + +const LLVoiceDeviceList& LLVoiceClient::getRenderDevices() { - // FOR TESTING ONLY - - // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about. - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) + static LLVoiceDeviceList nullRenderDevices; + if (mVoiceModule) { - buddyListEntry *buddy = buddy_it->second; - buddy_it++; - - std::ostringstream stream; - - if(buddy->mInVivoxBuddies) - { - // delete this entry from the vivox buddy list - buddy->mInVivoxBuddies = false; - LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - - if(buddy->mHasBlockListEntry) - { - // Delete the associated block list entry (so the block list doesn't fill up with junk) - buddy->mHasBlockListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - if(buddy->mHasAutoAcceptListEntry) - { - // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk) - buddy->mHasAutoAcceptListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - - writeString(stream.str()); - - } -} - -void LLVoiceClient::sendFriendsListUpdates() -{ - if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty) - { - mFriendsListDirty = false; - - if(0) - { - // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries. - clearAllLists(); - return; - } - - LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL; - - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) - { - // reset the temp flags in the local buddy list - buddy_it->second->mInSLFriends = false; - } - - // correlate with the friends list - { - LLCollectAllBuddies collect; - LLAvatarTracker::instance().applyFunctor(collect); - LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); - LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); - - for ( ; it != end; ++it) - { - checkFriend(it->second); - } - it = collect.mOffline.begin(); - end = collect.mOffline.end(); - for ( ; it != end; ++it) - { - checkFriend(it->second); - } - } - - LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL; - - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) - { - buddyListEntry *buddy = buddy_it->second; - buddy_it++; - - // Ignore entries that aren't resolved yet. - if(buddy->mNameResolved) - { - std::ostringstream stream; - - if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) - { - if(mNumberOfAliases > 0) - { - // Add (or update) this entry in the vivox buddy list - buddy->mInVivoxBuddies = true; - buddy->mNeedsNameUpdate = false; - LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; - stream - << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "" << buddy->mDisplayName << "" - << "" // Without this, SLVoice doesn't seem to parse the command. - << "0" - << "\n\n\n"; - } - } - else if(!buddy->mInSLFriends) - { - // This entry no longer exists in your SL friends list. Remove all traces of it from the Vivox buddy list. - if(buddy->mInVivoxBuddies) - { - // delete this entry from the vivox buddy list - buddy->mInVivoxBuddies = false; - LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - - if(buddy->mHasBlockListEntry) - { - // Delete the associated block list entry, if any - buddy->mHasBlockListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - if(buddy->mHasAutoAcceptListEntry) - { - // Delete the associated auto-accept list entry, if any - buddy->mHasAutoAcceptListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - } - } - - if(buddy->mInSLFriends) - { - - if(buddy->mCanSeeMeOnline) - { - // Buddy should not be blocked. - - // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. - - // If the buddy has a block list entry, delete it. - if(buddy->mHasBlockListEntry) - { - buddy->mHasBlockListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - - - // If we just deleted a block list entry, add an auto-accept entry. - if(!buddy->mHasAutoAcceptListEntry) - { - buddy->mHasAutoAcceptListEntry = true; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "0" - << "\n\n\n"; - } - } - } - else - { - // Buddy should be blocked. - - // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. - - // If this buddy has an autoaccept entry, delete it - if(buddy->mHasAutoAcceptListEntry) - { - buddy->mHasAutoAcceptListEntry = false; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "\n\n\n"; - - // If we just deleted an auto-accept entry, add a block list entry. - if(!buddy->mHasBlockListEntry) - { - buddy->mHasBlockListEntry = true; - stream << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "1" - << "\n\n\n"; - } - } - } - - if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies) - { - // Delete this entry from the local buddy list. This should NOT invalidate the iterator, - // since it has already been incremented to the next entry. - deleteBuddy(buddy->mURI); - } - - } - writeString(stream.str()); - } - } - } -} - -///////////////////////////// -// Response/Event handlers - -void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; - setState(stateConnectorFailed); + return mVoiceModule->getRenderDevices(); } else { - // Connector created, move forward. - LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; - mConnectorHandle = connectorHandle; - if(getState() == stateConnectorStarting) - { - setState(stateConnectorStarted); - } + return nullRenderDevices; } } -void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) -{ - LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; - - // Status code of 20200 means "bad password". We may want to special-case that at some point. - - if ( statusCode == 401 ) + +//-------------------------------------------------- +// participants + +void LLVoiceClient::getParticipantList(std::set &participants) +{ + if (mVoiceModule) { - // Login failure which is probably caused by the delay after a user's password being updated. - LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - setState(stateLoginRetry); - } - else if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; - setState(stateLoginFailed); + mVoiceModule->getParticipantList(participants); } else { - // Login succeeded, move forward. - mAccountHandle = accountHandle; - mNumberOfAliases = numberOfAliases; - // This needs to wait until the AccountLoginStateChangeEvent is received. -// if(getState() == stateLoggingIn) -// { -// setState(stateLoggedIn); -// } + participants = std::set(); } } -void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{ - sessionState *session = findSessionBeingCreatedByURI(requestId); - - if(session) +bool LLVoiceClient::isParticipant(const LLUUID &speaker_id) +{ + if(mVoiceModule) { - session->mCreateInProgress = false; + return mVoiceModule->isParticipant(speaker_id); } - - if(statusCode != 0) + return false; +} + + +//-------------------------------------------------- +// text chat + + +BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id) +{ + if (mVoiceModule) { - LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - { - setState(stateJoinSessionFailed); - } - else - { - reapSession(session); - } - } + return mVoiceModule->isSessionTextIMPossible(id); } else { - LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } + return FALSE; } } -void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{ - sessionState *session = findSessionBeingCreatedByURI(requestId); - - if(session) +BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id) +{ + if (mVoiceModule) { - session->mCreateInProgress = false; - } - - if(statusCode != 0) - { - LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - { - setState(stateJoinSessionFailed); - } - else - { - reapSession(session); - } - } + return mVoiceModule->isSessionCallBackPossible(id); } else { - LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; - if(session) - { - setSessionHandle(session, sessionHandle); - } + return FALSE; } } -void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) { - sessionState *session = findSession(requestId); - if(statusCode != 0) + if (mVoiceModule) { - LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; - if(session) - { - session->mMediaConnectInProgress = false; - session->mErrorStatusCode = statusCode; - session->mErrorStatusString = statusString; - if(session == mAudioSession) - setState(stateJoinSessionFailed); - } + return mVoiceModule->sendTextMessage(participant_id, message); } else { - LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; + return FALSE; } } -void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString) -{ - if(statusCode != 0) - { - LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } -} - -void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +void LLVoiceClient::endUserIMSession(const LLUUID& participant_id) { - if(statusCode != 0) + if (mVoiceModule) { - LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; - // Should this ever fail? do we care if it does? - } - - mConnected = false; - - if(getState() == stateConnectorStopping) - { - setState(stateConnectorStopped); + mVoiceModule->endUserIMSession(participant_id); } } -void LLVoiceClient::sessionAddedEvent( - std::string &uriString, - std::string &alias, - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool isChannel, - bool incoming, - std::string &nameString, - std::string &applicationString) +//---------------------------------------------- +// channels + +bool LLVoiceClient::inProximalChannel() { - sessionState *session = NULL; - - LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; - - session = addSession(uriString, sessionHandle); - if(session) + if (mVoiceModule) { - session->mGroupHandle = sessionGroupHandle; - session->mIsChannel = isChannel; - session->mIncoming = incoming; - session->mAlias = alias; - - // Generate a caller UUID -- don't need to do this for channels - if(!session->mIsChannel) - { - if(IDFromName(session->mSIPURI, session->mCallerID)) - { - // Normal URI(base64-encoded UUID) - } - else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) - { - // Wrong URI, but an alias is available. Stash the incoming URI as an alternate - session->mAlternateSIPURI = session->mSIPURI; - - // and generate a proper URI from the ID. - setSessionURI(session, sipURIFromID(session->mCallerID)); - } - else - { - LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; - setUUIDFromStringHash(session->mCallerID, session->mSIPURI); - session->mSynthesizedCallerID = true; - - // Can't look up the name in this case -- we have to extract it from the URI. - std::string namePortion = nameFromsipURI(session->mSIPURI); - if(namePortion.empty()) - { - // Didn't seem to be a SIP URI, just use the whole provided name. - namePortion = nameString; - } - - // Some incoming names may be separated with an underscore instead of a space. Fix this. - LLStringUtil::replaceChar(namePortion, '_', ' '); - - // Act like we just finished resolving the name (this stores it in all the right places) - avatarNameResolved(session->mCallerID, namePortion); - } - - LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; - - if(!session->mSynthesizedCallerID) - { - // If we got here, we don't have a proper name. Initiate a lookup. - lookupName(session->mCallerID); - } - } - } -} - -void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) -{ - LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; - -#if USE_SESSION_GROUPS - if(mMainSessionGroupHandle.empty()) - { - // This is the first (i.e. "main") session group. Save its handle. - mMainSessionGroupHandle = sessionGroupHandle; + return mVoiceModule->inProximalChannel(); } else { - LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; - } -#endif -} - -void LLVoiceClient::joinedAudioSession(sessionState *session) -{ - if(mAudioSession != session) - { - sessionState *oldSession = mAudioSession; - - mAudioSession = session; - mAudioSessionChanged = true; - - // The old session may now need to be deleted. - reapSession(oldSession); - } - - // This is the session we're joining. - if(getState() == stateJoiningSession) - { - setState(stateSessionJoined); - - // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. - // Add the current user as a participant here. - participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); - if(participant) - { - participant->mIsSelf = true; - lookupName(participant->mAvatarID); - - LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - } - - if(!session->mIsChannel) - { - // this is a p2p session. Make sure the other end is added as a participant. - participantState *participant = session->addParticipant(session->mSIPURI); - if(participant) - { - if(participant->mAvatarIDValid) - { - lookupName(participant->mAvatarID); - } - else if(!session->mName.empty()) - { - participant->mDisplayName = session->mName; - avatarNameResolved(participant->mAvatarID, session->mName); - } - - // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? - LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - } - } - } -} - -void LLVoiceClient::sessionRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle) -{ - LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; - - sessionState *session = findSession(sessionHandle); - if(session) - { - leftAudioSession(session); - - // This message invalidates the session's handle. Set it to empty. - setSessionHandle(session); - - // This also means that the session's session group is now empty. - // Terminate the session group so it doesn't leak. - sessionGroupTerminateSendMessage(session); - - // Reset the media state (we now have no info) - session->mMediaStreamState = streamStateUnknown; - session->mTextStreamState = streamStateUnknown; - - // Conditionally delete the session - reapSession(session); - } - else - { - LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; - } -} - -void LLVoiceClient::reapSession(sessionState *session) -{ - if(session) - { - if(!session->mHandle.empty()) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; - } - else if(session->mCreateInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; - } - else if(session->mMediaConnectInProgress) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; - } - else if(session == mAudioSession) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; - } - else if(session == mNextAudioSession) - { - LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; - } - else - { - // TODO: Question: Should we check for queued text messages here? - // We don't have a reason to keep tracking this session, so just delete it. - LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; - deleteSession(session); - session = NULL; - } - } - else - { -// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; - } -} - -// Returns true if the session seems to indicate we've moved to a region on a different voice server -bool LLVoiceClient::sessionNeedsRelog(sessionState *session) -{ - bool result = false; - - if(session != NULL) - { - // Only make this check for spatial channels (so it won't happen for group or p2p calls) - if(session->mIsSpatial) - { - std::string::size_type atsign; - - atsign = session->mSIPURI.find("@"); - - if(atsign != std::string::npos) - { - std::string urihost = session->mSIPURI.substr(atsign + 1); - if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) - { - // The hostname in this URI is different from what we expect. This probably means we need to relog. - - // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of - // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. - - result = true; - } - } - } - } - - return result; -} - -void LLVoiceClient::leftAudioSession( - sessionState *session) -{ - if(mAudioSession == session) - { - switch(getState()) - { - case stateJoiningSession: - case stateSessionJoined: - case stateRunning: - case stateLeavingSession: - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - // normal transition - LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; - setState(stateSessionTerminated); - break; - - case stateSessionTerminated: - // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. - LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; - break; - - default: - LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; - setState(stateSessionTerminated); - break; - } - } -} - -void LLVoiceClient::accountLoginStateChangeEvent( - std::string &accountHandle, - int statusCode, - std::string &statusString, - int state) -{ - LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; - /* - According to Mike S., status codes for this event are: - login_state_logged_out=0, - login_state_logged_in = 1, - login_state_logging_in = 2, - login_state_logging_out = 3, - login_state_resetting = 4, - login_state_error=100 - */ - - switch(state) - { - case 1: - if(getState() == stateLoggingIn) - { - setState(stateLoggedIn); - } - break; - - case 3: - // The user is in the process of logging out. - setState(stateLoggingOut); - break; - - case 0: - // The user has been logged out. - setState(stateLoggedOut); - break; - - default: - //Used to be a commented out warning - LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; - break; - } -} - -void LLVoiceClient::mediaStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - int statusCode, - std::string &statusString, - int state, - bool incoming) -{ - sessionState *session = findSession(sessionHandle); - - LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; - - if(session) - { - // We know about this session - - // Save the state for later use - session->mMediaStreamState = state; - - switch(statusCode) - { - case 0: - case 200: - // generic success - // Don't change the saved error code (it may have been set elsewhere) - break; - default: - // save the status code for later - session->mErrorStatusCode = statusCode; - break; - } - - switch(state) - { - case streamStateIdle: - // Standard "left audio session" - session->mVoiceEnabled = false; - session->mMediaConnectInProgress = false; - leftAudioSession(session); - break; - - case streamStateConnected: - session->mVoiceEnabled = true; - session->mMediaConnectInProgress = false; - joinedAudioSession(session); - break; - - case streamStateRinging: - if(incoming) - { - // Send the voice chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? - session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); - session->mVoiceInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } - - } - else - { - LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; - } -} - -void LLVoiceClient::textStreamUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - bool enabled, - int state, - bool incoming) -{ - sessionState *session = findSession(sessionHandle); - - if(session) - { - // Save the state for later use - session->mTextStreamState = state; - - // We know about this session - switch(state) - { - case 0: // We see this when the text stream closes - LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; - break; - - case 1: // We see this on an incoming call from the Connector - // Try to send any text messages queued for this session. - sendQueuedTextMessages(session); - - // Send the text chat invite to the GUI layer - // TODO: Question: Should we correlate with the mute list here? - session->mTextInvitePending = true; - if(session->mName.empty()) - { - lookupName(session->mCallerID); - } - else - { - // Act like we just finished resolving the name - avatarNameResolved(session->mCallerID, session->mName); - } - break; - - default: - LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; - break; - - } - } -} - -void LLVoiceClient::participantAddedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString, - std::string &displayNameString, - int participantType) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - participantState *participant = session->addParticipant(uriString); - if(participant) - { - participant->mAccountName = nameString; - - LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName - << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; - - if(participant->mAvatarIDValid) - { - // Initiate a lookup - lookupName(participant->mAvatarID); - } - else - { - // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. - std::string namePortion = nameFromsipURI(uriString); - if(namePortion.empty()) - { - // Problem with the SIP URI, fall back to the display name - namePortion = displayNameString; - } - if(namePortion.empty()) - { - // Problems with both of the above, fall back to the account name - namePortion = nameString; - } - - // Set the display name (which is a hint to the active speakers window not to do its own lookup) - participant->mDisplayName = namePortion; - avatarNameResolved(participant->mAvatarID, namePortion); - } - } - } -} - -void LLVoiceClient::participantRemovedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - std::string &nameString) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - participantState *participant = session->findParticipant(uriString); - if(participant) - { - session->removeParticipant(participant); - } - else - { - LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - - -void LLVoiceClient::participantUpdatedEvent( - std::string &sessionHandle, - std::string &sessionGroupHandle, - std::string &uriString, - std::string &alias, - bool isModeratorMuted, - bool isSpeaking, - int volume, - F32 energy) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - participantState *participant = session->findParticipant(uriString); - - if(participant) - { - participant->mIsSpeaking = isSpeaking; - participant->mIsModeratorMuted = isModeratorMuted; - - // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false - if (isSpeaking) - { - participant->mSpeakingTimeout.reset(); - participant->mPower = energy; - } - else - { - participant->mPower = 0.0f; - } - participant->mVolume = volume; - } - else - { - LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; - } - } - else - { - LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; - } -} - -void LLVoiceClient::buddyPresenceEvent( - std::string &uriString, - std::string &alias, - std::string &statusString, - std::string &applicationString) -{ - buddyListEntry *buddy = findBuddy(uriString); - - if(buddy) - { - LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL; - LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; - - if(applicationString.empty()) - { - // This presence event is from a client that doesn't set up the Application string. Do things the old-skool way. - // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet. - - if ( stricmp("Unknown", statusString.c_str())== 0) - { - // User went offline with a non-SLim-enabled viewer. - buddy->mOnlineSL = false; - } - else if ( stricmp("Online", statusString.c_str())== 0) - { - // User came online with a non-SLim-enabled viewer. - buddy->mOnlineSL = true; - } - else - { - // If the user is online through SLim, their status will be "Online-slc", "Away", or something else. - // NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string. - buddy->mOnlineSLim = true; - } - } - else if(applicationString.find("SecondLifeViewer") != std::string::npos) - { - // This presence event is from a viewer that sets the application string - if ( stricmp("Unknown", statusString.c_str())== 0) - { - // Viewer says they're offline - buddy->mOnlineSL = false; - } - else - { - // Viewer says they're online - buddy->mOnlineSL = true; - } - } - else - { - // This presence event is from something which is NOT the SL viewer (assume it's SLim). - if ( stricmp("Unknown", statusString.c_str())== 0) - { - // SLim says they're offline - buddy->mOnlineSLim = false; - } - else - { - // SLim says they're online - buddy->mOnlineSLim = true; - } - } - - LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; - - // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change. - LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID)); - - notifyFriendObservers(); - } - else - { - LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL; - } -} - -void LLVoiceClient::messageEvent( - std::string &sessionHandle, - std::string &uriString, - std::string &alias, - std::string &messageHeader, - std::string &messageBody, - std::string &applicationString) -{ - LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; -// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; - - if(messageHeader.find("text/html") != std::string::npos) - { - std::string rawMessage; - - { - const std::string startMarker = ", try looking for a instead. - start = messageBody.find(startSpan); - start = messageBody.find(startMarker2, start); - end = messageBody.find(endSpan); - - if(start != std::string::npos) - { - start += startMarker2.size(); - - if(end != std::string::npos) - end -= start; - - rawMessage.assign(messageBody, start, end); - } - } - } - -// LL_DEBUGS("Voice") << " raw message = \n" << rawMessage << LL_ENDL; - - // strip formatting tags - { - std::string::size_type start; - std::string::size_type end; - - while((start = rawMessage.find('<')) != std::string::npos) - { - if((end = rawMessage.find('>', start + 1)) != std::string::npos) - { - // Strip out the tag - rawMessage.erase(start, (end + 1) - start); - } - else - { - // Avoid an infinite loop - break; - } - } - } - - // Decode ampersand-escaped chars - { - std::string::size_type mark = 0; - - // The text may contain text encoded with <, >, and & - mark = 0; - while((mark = rawMessage.find("<", mark)) != std::string::npos) - { - rawMessage.replace(mark, 4, "<"); - mark += 1; - } - - mark = 0; - while((mark = rawMessage.find(">", mark)) != std::string::npos) - { - rawMessage.replace(mark, 4, ">"); - mark += 1; - } - - mark = 0; - while((mark = rawMessage.find("&", mark)) != std::string::npos) - { - rawMessage.replace(mark, 5, "&"); - mark += 1; - } - } - - // strip leading/trailing whitespace (since we always seem to get a couple newlines) - LLStringUtil::trim(rawMessage); - -// LL_DEBUGS("Voice") << " stripped message = \n" << rawMessage << LL_ENDL; - - sessionState *session = findSession(sessionHandle); - if(session) - { - bool is_busy = gAgent.getBusy(); - bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); - bool is_linden = LLMuteList::getInstance()->isLinden(session->mName); - bool quiet_chat = false; - LLChat chat; - - chat.mMuted = is_muted && !is_linden; - - if(!chat.mMuted) - { - chat.mFromID = session->mCallerID; - chat.mFromName = session->mName; - chat.mSourceType = CHAT_SOURCE_AGENT; - - if(is_busy && !is_linden) - { - quiet_chat = true; - // TODO: Question: Return busy mode response here? Or maybe when session is started instead? - } - - std::string fullMessage = std::string(": ") + rawMessage; - - LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; - gIMMgr->addMessage(session->mIMSessionID, - session->mCallerID, - session->mName.c_str(), - fullMessage.c_str(), - LLStringUtil::null, // default arg - IM_NOTHING_SPECIAL, // default arg - 0, // default arg - LLUUID::null, // default arg - LLVector3::zero, // default arg - true); // prepend name and make it a link to the user's profile - - chat.mText = std::string("IM: ") + session->mName + std::string(": ") + rawMessage; - // If the chat should come in quietly (i.e. we're in busy mode), pretend it's from a local agent. - LLFloaterChat::addChat( chat, TRUE, quiet_chat ); - } - } - } -} - -void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) -{ - sessionState *session = findSession(sessionHandle); - - if(session) - { - participantState *participant = session->findParticipant(uriString); - if(participant) - { - if (!stricmp(notificationType.c_str(), "Typing")) - { - // Other end started typing - // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). - // It requires an LLIMInfo for the message, which we don't have here. - } - else if (!stricmp(notificationType.c_str(), "NotTyping")) - { - // Other end stopped typing - // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). - // It requires an LLIMInfo for the message, which we don't have here. - } - else - { - LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; - } - } - else - { - LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; - } -} - -void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType) -{ - buddyListEntry *buddy = findBuddy(buddyURI); - - if(!buddy) - { - // Couldn't find buddy by URI, try converting the alias... - if(!alias.empty()) - { - LLUUID id; - if(IDFromName(alias, id)) - { - buddy = findBuddy(id); - } - } - } - - if(buddy) - { - std::ostringstream stream; - - if(buddy->mCanSeeMeOnline) - { - // Sending the response will create an auto-accept rule - buddy->mHasAutoAcceptListEntry = true; - } - else - { - // Sending the response will create a block rule - buddy->mHasBlockListEntry = true; - } - - if(buddy->mInSLFriends) - { - buddy->mInVivoxBuddies = true; - } - - stream - << "" - << "" << mAccountHandle << "" - << "" << buddy->mURI << "" - << "" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "" - << ""<< (buddy->mInSLFriends?"1":"0")<< "" - << "" << subscriptionHandle << "" - << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::auxAudioPropertiesEvent(F32 energy) -{ - LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL; - mTuningEnergy = energy; -} - -void LLVoiceClient::buddyListChanged() -{ - // This is called after we receive a BuddyAndGroupListChangedEvent. - mBuddyListMapPopulated = true; - mFriendsListDirty = true; -} - -void LLVoiceClient::muteListChanged() -{ - // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. - if(mAudioSession) - { - participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - - for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) - { - participantState *p = iter->second; - - // Check to see if this participant is on the mute list already - if(p->updateMuteState()) - mAudioSession->mVolumeDirty = true; - } - } -} - -void LLVoiceClient::updateFriends(U32 mask) -{ - if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS)) - { - // Just resend the whole friend list to the daemon - mFriendsListDirty = true; - } -} - -///////////////////////////// -// Managing list of participants -LLVoiceClient::participantState::participantState(const std::string &uri) : - mURI(uri), - mPTT(false), - mIsSpeaking(false), - mIsModeratorMuted(false), - mLastSpokeTimestamp(0.f), - mPower(0.f), - mVolume(-1), - mOnMuteList(false), - mUserVolume(-1), - mVolumeDirty(false), - mAvatarIDValid(false), - mIsSelf(false) -{ -} - -LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri) -{ - participantState *result = NULL; - bool useAlternateURI = false; - - // Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it - // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. - { - participantMap::iterator iter = mParticipantsByURI.find(&uri); - - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Use mSIPURI instead, since it will be properly encoded. - iter = mParticipantsByURI.find(&(mSIPURI)); - useAlternateURI = true; - } - } - - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } - } - - if(!result) - { - // participant isn't already in one list or the other. - result = new participantState(useAlternateURI?mSIPURI:uri); - mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result)); - mParticipantsChanged = true; - - // Try to do a reverse transform on the URI to get the GUID back. - { - LLUUID id; - if(IDFromName(result->mURI, id)) - { - result->mAvatarIDValid = true; - result->mAvatarID = id; - - if(result->updateMuteState()) - mVolumeDirty = true; - } - else - { - // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. - // This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache. - setUUIDFromStringHash(result->mAvatarID, uri); - } - } - - mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result)); - - LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; - } - - return result; -} - -bool LLVoiceClient::participantState::updateMuteState() -{ - bool result = false; - - if(mAvatarIDValid) - { - bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); - if(mOnMuteList != isMuted) - { - mOnMuteList = isMuted; - mVolumeDirty = true; - result = true; - } - } - return result; -} - -bool LLVoiceClient::participantState::isAvatar() -{ - return mAvatarIDValid; -} - -void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant) -{ - if(participant) - { - participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI)); - participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID)); - - LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; - - if(iter == mParticipantsByURI.end()) - { - LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; - } - else if(iter2 == mParticipantsByUUID.end()) - { - LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; - } - else if(iter->second != iter2->second) - { - LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; - } - else - { - mParticipantsByURI.erase(iter); - mParticipantsByUUID.erase(iter2); - - delete participant; - mParticipantsChanged = true; - } - } -} - -void LLVoiceClient::sessionState::removeAllParticipants() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - - while(!mParticipantsByURI.empty()) - { - removeParticipant(mParticipantsByURI.begin()->second); - } - - if(!mParticipantsByUUID.empty()) - { - LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; - } -} - -LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void) -{ - participantMap *result = NULL; - if(mAudioSession) - { - result = &(mAudioSession->mParticipantsByURI); - } - return result; -} - - -LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri) -{ - participantState *result = NULL; - - participantMap::iterator iter = mParticipantsByURI.find(&uri); - - if(iter == mParticipantsByURI.end()) - { - if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) - { - // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. - // Look up the other URI - iter = mParticipantsByURI.find(&(mSIPURI)); - } - } - - if(iter != mParticipantsByURI.end()) - { - result = iter->second; - } - - return result; -} - -LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id) -{ - participantState * result = NULL; - participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id); - - if(iter != mParticipantsByUUID.end()) - { - result = iter->second; - } - - return result; -} - -LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id) -{ - participantState * result = NULL; - - if(mAudioSession) - { - result = mAudioSession->findParticipantByID(id); - } - - return result; -} - - -void LLVoiceClient::parcelChanged() -{ - if(getState() >= stateNoChannel) - { - // If the user is logged in, start a channel lookup. - LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - - std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); - LLSD data; - LLHTTPClient::post( - url, - data, - new LLVoiceClientCapResponder); - } - else - { - // The transition to stateNoChannel needs to kick this off again. - LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; - } -} - -void LLVoiceClient::switchChannel( - std::string uri, - bool spatial, - bool no_reconnect, - bool is_p2p, - std::string hash) -{ - bool needsSwitch = false; - - LL_DEBUGS("Voice") - << "called in state " << state2string(getState()) - << " with uri \"" << uri << "\"" - << (spatial?", spatial is true":", spatial is false") - << LL_ENDL; - - switch(getState()) - { - case stateJoinSessionFailed: - case stateJoinSessionFailedWaiting: - case stateNoChannel: - // Always switch to the new URI from these states. - needsSwitch = true; - break; - - default: - if(mSessionTerminateRequested) - { - // If a terminate has been requested, we need to compare against where the URI we're already headed to. - if(mNextAudioSession) - { - if(mNextAudioSession->mSIPURI != uri) - needsSwitch = true; - } - else - { - // mNextAudioSession is null -- this probably means we're on our way back to spatial. - if(!uri.empty()) - { - // We do want to process a switch in this case. - needsSwitch = true; - } - } - } - else - { - // Otherwise, compare against the URI we're in now. - if(mAudioSession) - { - if(mAudioSession->mSIPURI != uri) - { - needsSwitch = true; - } - } - else - { - if(!uri.empty()) - { - // mAudioSession is null -- it's not clear what case would cause this. - // For now, log it as a warning and see if it ever crops up. - LL_WARNS("Voice") << "No current audio session." << LL_ENDL; - } - } - } - break; - } - - if(needsSwitch) - { - if(uri.empty()) - { - // Leave any channel we may be in - LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; - - sessionState *oldSession = mNextAudioSession; - mNextAudioSession = NULL; - - // The old session may now need to be deleted. - reapSession(oldSession); - - notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); - } - else - { - LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; - - mNextAudioSession = addSession(uri); - mNextAudioSession->mHash = hash; - mNextAudioSession->mIsSpatial = spatial; - mNextAudioSession->mReconnect = !no_reconnect; - mNextAudioSession->mIsP2P = is_p2p; - } - - if(getState() <= stateNoChannel) - { - // We're already set up to join a channel, just needed to fill in the session URI - } - else - { - // State machine will come around and rejoin if uri/handle is not empty. - sessionTerminate(); - } - } -} - -void LLVoiceClient::joinSession(sessionState *session) -{ - mNextAudioSession = session; - - if(getState() <= stateNoChannel) - { - // We're already set up to join a channel, just needed to fill in the session handle - } - else - { - // State machine will come around and rejoin if uri/handle is not empty. - sessionTerminate(); + return false; } } @@ -5232,608 +360,163 @@ void LLVoiceClient::setNonSpatialChannel( const std::string &uri, const std::string &credentials) { - switchChannel(uri, false, false, false, credentials); + if (mVoiceModule) mVoiceModule->setNonSpatialChannel(uri, credentials); } void LLVoiceClient::setSpatialChannel( const std::string &uri, const std::string &credentials) { - mSpatialSessionURI = uri; - mSpatialSessionCredentials = credentials; - mAreaVoiceDisabled = mSpatialSessionURI.empty(); - - LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; - - if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) - { - // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. - LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; - } - else - { - switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); - } -} - -void LLVoiceClient::callUser(const LLUUID &uuid) -{ - std::string userURI = sipURIFromID(uuid); - - switchChannel(userURI, false, true, true); -} - -LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid) -{ - // Figure out if a session with the user already exists - sessionState *session = findSession(uuid); - if(!session) - { - // No session with user, need to start one. - std::string uri = sipURIFromID(uuid); - session = addSession(uri); - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; - session->mCallerID = uuid; - } - - if(session) - { - if(session->mHandle.empty()) - { - // Session isn't active -- start it up. - sessionCreateSendMessage(session, false, true); - } - else - { - // Session is already active -- start up text. - sessionTextConnectSendMessage(session); - } - } - - return session; -} - -bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) -{ - bool result = false; - - // Attempt to locate the indicated session - sessionState *session = startUserIMSession(participant_id); - if(session) - { - // found the session, attempt to send the message - session->mTextMsgQueue.push(message); - - // Try to send queued messages (will do nothing if the session is not open yet) - sendQueuedTextMessages(session); - - // The message is queued, so we succeed. - result = true; - } - else - { - LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; - } - - return result; -} - -void LLVoiceClient::sendQueuedTextMessages(sessionState *session) -{ - if(session->mTextStreamState == 1) - { - if(!session->mTextMsgQueue.empty()) - { - std::ostringstream stream; - - while(!session->mTextMsgQueue.empty()) - { - std::string message = session->mTextMsgQueue.front(); - session->mTextMsgQueue.pop(); - stream - << "" - << "" << session->mHandle << "" - << "text/HTML" - << "" << message << "" - << "" - << "\n\n\n"; - } - writeString(stream.str()); - } - } - else - { - // Session isn't connected yet, defer until later. - } -} - -void LLVoiceClient::endUserIMSession(const LLUUID &uuid) -{ - // Figure out if a session with the user exists - sessionState *session = findSession(uuid); - if(session) - { - // found the session - if(!session->mHandle.empty()) - { - sessionTextDisconnectSendMessage(session); - } - } - else - { - LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; - } -} - -bool LLVoiceClient::answerInvite(std::string &sessionHandle) -{ - // this is only ever used to answer incoming p2p call invites. - - sessionState *session = findSession(sessionHandle); - if(session) - { - session->mIsSpatial = false; - session->mReconnect = false; - session->mIsP2P = true; - - joinSession(session); - return true; - } - - return false; -} - -bool LLVoiceClient::isOnlineSIP(const LLUUID &id) -{ - bool result = false; - buddyListEntry *buddy = findBuddy(id); - if(buddy) - { - result = buddy->mOnlineSLim; - LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL; - } - - if(!result) - { - // This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM. - sessionState *session = findSession(id); - if(session && !session->mHandle.empty()) - { - if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle)) - { - LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL; - // we have a p2p text session open with this user, so by definition they're online. - result = true; - } - } - } - - return result; -} - -// Returns true if the indicated participant in the current audio session is really an SL avatar. -// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. -bool LLVoiceClient::isParticipantAvatar(const LLUUID &id) -{ - bool result = true; - sessionState *session = findSession(id); - - if(session != NULL) - { - // this is a p2p session with the indicated caller, or the session with the specified UUID. - if(session->mSynthesizedCallerID) - result = false; - } - else - { - // Didn't find a matching session -- check the current audio session for a matching participant - if(mAudioSession != NULL) - { - participantState *participant = findParticipantByID(id); - if(participant != NULL) - { - result = participant->isAvatar(); - } - } - } - - return result; -} - -// Returns true if calling back the session URI after the session has closed is possible. -// Currently this will be false only for PSTN P2P calls. -bool LLVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) -{ - bool result = true; - sessionState *session = findSession(session_id); - - if(session != NULL) - { - result = session->isCallBackPossible(); - } - - return result; -} - -// Returns true if the session can accepte text IM's. -// Currently this will be false only for PSTN P2P calls. -bool LLVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) -{ - bool result = true; - sessionState *session = findSession(session_id); - - if(session != NULL) - { - result = session->isTextIMPossible(); - } - - return result; -} - - -void LLVoiceClient::declineInvite(std::string &sessionHandle) -{ - sessionState *session = findSession(sessionHandle); - if(session) - { - sessionMediaDisconnectSendMessage(session); - } + if (mVoiceModule) mVoiceModule->setSpatialChannel(uri, credentials); } void LLVoiceClient::leaveNonSpatialChannel() { - LL_DEBUGS("Voice") - << "called in state " << state2string(getState()) - << LL_ENDL; - - // Make sure we don't rejoin the current session. - sessionState *oldNextSession = mNextAudioSession; - mNextAudioSession = NULL; - - // Most likely this will still be the current session at this point, but check it anyway. - reapSession(oldNextSession); - - verifySessionState(); - - sessionTerminate(); -} - -std::string LLVoiceClient::getCurrentChannel() -{ - std::string result; - - if((getState() == stateRunning) && !mSessionTerminateRequested) - { - result = getAudioSessionURI(); - } - - return result; -} - -bool LLVoiceClient::inProximalChannel() -{ - bool result = false; - - if((getState() == stateRunning) && !mSessionTerminateRequested) - { - result = inSpatialChannel(); - } - - return result; -} - -std::string LLVoiceClient::sipURIFromID(const LLUUID &id) -{ - std::string result; - result = "sip:"; - result += nameFromID(id); - result += "@"; - result += mVoiceSIPURIHostName; - - return result; -} - -std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) -{ - std::string result; - if(avatar) - { - result = "sip:"; - result += nameFromID(avatar->getID()); - result += "@"; - result += mVoiceSIPURIHostName; - } - - return result; -} - -std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar) -{ - std::string result; - if(avatar) - { - result = nameFromID(avatar->getID()); - } - return result; -} - -std::string LLVoiceClient::nameFromID(const LLUUID &uuid) -{ - std::string result; - - if (uuid.isNull()) { - //VIVOX, the uuid emtpy look for the mURIString and return that instead. - //result.assign(uuid.mURIStringName); - LLStringUtil::replaceChar(result, '_', ' '); - return result; - } - // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. - result = "x"; - - // Base64 encode and replace the pieces of base64 that are less compatible - // with e-mail local-parts. - // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" - result += LLBase64::encode(uuid.mData, UUID_BYTES); - LLStringUtil::replaceChar(result, '+', '-'); - LLStringUtil::replaceChar(result, '/', '_'); - - // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: - // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') - - // The reverse transform can be done with: - // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p - - return result; -} - -bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) -{ - bool result = false; - - // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com" - // If it is, convert to a bare name before doing the transform. - std::string name = nameFromsipURI(inName); - - // Doesn't look like a SIP URI, assume it's an actual name. - if(name.empty()) - name = inName; - - // This will only work if the name is of the proper form. - // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: - // "xFnPP04IpREWNkuw1cOXlhw==" - - if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) - { - // The name appears to have the right form. - - // Reverse the transforms done by nameFromID - std::string temp = name; - LLStringUtil::replaceChar(temp, '-', '+'); - LLStringUtil::replaceChar(temp, '_', '/'); - - U8 rawuuid[UUID_BYTES + 1]; - int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); - if(len == UUID_BYTES) - { - // The decode succeeded. Stuff the bits into the result's UUID - memcpy(uuid.mData, rawuuid, UUID_BYTES); - result = true; - } - } - - if(!result) - { - // VIVOX: not a standard account name, just copy the URI name mURIString field - // and hope for the best. bpj - uuid.setNull(); // VIVOX, set the uuid field to nulls - } - - return result; -} - -std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) -{ - return avatar->getFullname(); -} - -std::string LLVoiceClient::sipURIFromName(std::string &name) -{ - std::string result; - result = "sip:"; - result += name; - result += "@"; - result += mVoiceSIPURIHostName; - -// LLStringUtil::toLower(result); - - return result; -} - -std::string LLVoiceClient::nameFromsipURI(const std::string &uri) -{ - std::string result; - - std::string::size_type sipOffset, atOffset; - sipOffset = uri.find("sip:"); - atOffset = uri.find("@"); - if((sipOffset != std::string::npos) && (atOffset != std::string::npos)) - { - result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); - } - - return result; -} - -bool LLVoiceClient::inSpatialChannel(void) -{ - bool result = false; - - if(mAudioSession) - result = mAudioSession->mIsSpatial; - - return result; -} - -std::string LLVoiceClient::getAudioSessionURI() -{ - std::string result; - - if(mAudioSession) - result = mAudioSession->mSIPURI; - - return result; -} - -std::string LLVoiceClient::getAudioSessionHandle() -{ - std::string result; - - if(mAudioSession) - result = mAudioSession->mHandle; - - return result; -} - - -///////////////////////////// -// Sending updates of current state - -void LLVoiceClient::enforceTether(void) -{ - LLVector3d tethered = mCameraRequestedPosition; - - // constrain 'tethered' to within 50m of mAvatarPosition. - { - F32 max_dist = 50.0f; - LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; - F32 camera_distance = (F32)camera_offset.magVec(); - if(camera_distance > max_dist) - { - tethered = mAvatarPosition + - (max_dist / camera_distance) * camera_offset; - } - } - - if(dist_vec(mCameraPosition, tethered) > 0.1) - { - mCameraPosition = tethered; - mSpatialCoordsDirty = true; - } -} - -void LLVoiceClient::updatePosition(void) -{ - - if(gVoiceClient) - { - LLVOAvatar *agent = gAgentAvatarp; - LLViewerRegion *region = gAgent.getRegion(); - if(region && agent) - { - LLMatrix3 rot; - LLVector3d pos; - - // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... - // They're currently always set to zero. - - // Send the current camera position to the voice code - rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); - pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); - - gVoiceClient->setCameraPosition( - pos, // position - LLVector3::zero, // velocity - rot); // rotation matrix - - // Send the current avatar position to the voice code - rot = agent->getRootJoint()->getWorldRotation().getMatrix3(); - - pos = agent->getPositionGlobal(); - // TODO: Can we get the head offset from outside the LLVOAvatar? -// pos += LLVector3d(mHeadOffset); - pos += LLVector3d(0.f, 0.f, 1.f); - - gVoiceClient->setAvatarPosition( - pos, // position - LLVector3::zero, // velocity - rot); // rotation matrix - } - } -} - -void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) -{ - mCameraRequestedPosition = position; - - if(mCameraVelocity != velocity) - { - mCameraVelocity = velocity; - mSpatialCoordsDirty = true; - } - - if(mCameraRot != rot) - { - mCameraRot = rot; - mSpatialCoordsDirty = true; - } -} - -void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) -{ - if(dist_vec(mAvatarPosition, position) > 0.1) - { - mAvatarPosition = position; - mSpatialCoordsDirty = true; - } - - if(mAvatarVelocity != velocity) - { - mAvatarVelocity = velocity; - mSpatialCoordsDirty = true; - } - - if(mAvatarRot != rot) - { - mAvatarRot = rot; - mSpatialCoordsDirty = true; - } -} - -bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) -{ - bool result = false; - - if(region) - { - name = region->getName(); - } - - if(!name.empty()) - result = true; - - return result; + if (mVoiceModule) mVoiceModule->leaveNonSpatialChannel(); } void LLVoiceClient::leaveChannel(void) { - if(getState() == stateRunning) + if (mVoiceModule) mVoiceModule->leaveChannel(); +} + +std::string LLVoiceClient::getCurrentChannel() +{ + if (mVoiceModule) { - LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; - mChannelName.clear(); - sessionTerminate(); + return mVoiceModule->getCurrentChannel(); + } + else + { + return std::string(); + } +} + + +//--------------------------------------- +// invitations + +void LLVoiceClient::callUser(const LLUUID &uuid) +{ + if (mVoiceModule) mVoiceModule->callUser(uuid); +} + +bool LLVoiceClient::isValidChannel(std::string &session_handle) +{ + if (mVoiceModule) + { + return mVoiceModule->isValidChannel(session_handle); + } + else + { + return false; + } +} + +bool LLVoiceClient::answerInvite(std::string &channelHandle) +{ + if (mVoiceModule) + { + return mVoiceModule->answerInvite(channelHandle); + } + else + { + return false; + } +} + +void LLVoiceClient::declineInvite(std::string &channelHandle) +{ + if (mVoiceModule) mVoiceModule->declineInvite(channelHandle); +} + + +//------------------------------------------ +// Volume/gain + + +void LLVoiceClient::setVoiceVolume(F32 volume) +{ + if (mVoiceModule) mVoiceModule->setVoiceVolume(volume); +} + +void LLVoiceClient::setMicGain(F32 volume) +{ + if (mVoiceModule) mVoiceModule->setMicGain(volume); +} + + +//------------------------------------------ +// enable/disable voice features + +bool LLVoiceClient::voiceEnabled() +{ + if (mVoiceModule) + { + return mVoiceModule->voiceEnabled(); + } + else + { + return false; + } +} + +void LLVoiceClient::setVoiceEnabled(bool enabled) +{ + if (mVoiceModule) mVoiceModule->setVoiceEnabled(enabled); +} + +void LLVoiceClient::updateMicMuteLogic() +{ + // If not configured to use PTT, the mic should be open (otherwise the user will be unable to speak). + bool new_mic_mute = false; + + if(mUsePTT) + { + // If configured to use PTT, track the user state. + new_mic_mute = !mUserPTTState; + } + + if(mMuteMic || mDisableMic) + { + // Either of these always overrides any other PTT setting. + new_mic_mute = true; + } + + if (mVoiceModule) mVoiceModule->setMuteMic(new_mic_mute); +} + +void LLVoiceClient::setLipSyncEnabled(BOOL enabled) +{ + if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled); +} + +BOOL LLVoiceClient::lipSyncEnabled() +{ + if (mVoiceModule) + { + return mVoiceModule->lipSyncEnabled(); + } + else + { + return false; } } void LLVoiceClient::setMuteMic(bool muted) { mMuteMic = muted; + updateMicMuteLogic(); } + +// ---------------------------------------------- +// PTT + void LLVoiceClient::setUserPTTState(bool ptt) { mUserPTTState = ptt; + updateMicMuteLogic(); } bool LLVoiceClient::getUserPTTState() @@ -5841,53 +524,6 @@ bool LLVoiceClient::getUserPTTState() return mUserPTTState; } -void LLVoiceClient::toggleUserPTTState(void) -{ - mUserPTTState = !mUserPTTState; -} - -void LLVoiceClient::setVoiceEnabled(bool enabled) -{ - if (enabled != mVoiceEnabled) - { - mVoiceEnabled = enabled; - if (enabled) - { - LLVoiceChannel::getCurrentVoiceChannel()->activate(); - } - else - { - // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. - LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); - } - } -} - -bool LLVoiceClient::voiceEnabled() -{ - static const LLCachedControl enable_voice_chat("EnableVoiceChat",true); - static const LLCachedControl cmdline_disable_voce("CmdLineDisableVoice",false); - return enable_voice_chat && !cmdline_disable_voce; -} - -void LLVoiceClient::setLipSyncEnabled(BOOL enabled) -{ - mLipSyncEnabled = enabled; -} - -BOOL LLVoiceClient::lipSyncEnabled() -{ - - if ( mVoiceEnabled && stateDisabled != getState() ) - { - return mLipSyncEnabled; - } - else - { - return FALSE; - } -} - void LLVoiceClient::setUsePTT(bool usePTT) { if(usePTT && !mUsePTT) @@ -5896,6 +532,8 @@ void LLVoiceClient::setUsePTT(bool usePTT) mUserPTTState = false; } mUsePTT = usePTT; + + updateMicMuteLogic(); } void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle) @@ -5907,8 +545,14 @@ void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle) } mPTTIsToggle = PTTIsToggle; + + updateMicMuteLogic(); } +bool LLVoiceClient::getPTTIsToggle() +{ + return mPTTIsToggle; +} void LLVoiceClient::setPTTKey(std::string &key) { @@ -5927,48 +571,28 @@ void LLVoiceClient::setPTTKey(std::string &key) } } -void LLVoiceClient::setEarLocation(S32 loc) +void LLVoiceClient::inputUserControlState(bool down) { - if(mEarLocation != loc) + if(mPTTIsToggle) { - LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; - - mEarLocation = loc; - mSpatialCoordsDirty = true; - } -} - -void LLVoiceClient::setVoiceVolume(F32 volume) -{ - int scaled_volume = scale_speaker_volume(volume); - - if(scaled_volume != mSpeakerVolume) - { - if((scaled_volume == 0) || (mSpeakerVolume == 0)) + if(down) // toggle open-mic state on 'down' { - mSpeakerMuteDirty = true; + toggleUserPTTState(); } - - mSpeakerVolume = scaled_volume; - mSpeakerVolumeDirty = true; + } + else // set open-mic state as an absolute + { + setUserPTTState(down); } } -void LLVoiceClient::setMicGain(F32 volume) +void LLVoiceClient::toggleUserPTTState(void) { - int scaled_volume = scale_mic_volume(volume); - - if(scaled_volume != mMicVolume) - { - mMicVolume = scaled_volume; - mMicVolumeDirty = true; - } + setUserPTTState(!getUserPTTState()); } void LLVoiceClient::keyDown(KEY key, MASK mask) -{ -// LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL; - +{ if (gKeyboard->getKeyRepeated(key)) { // ignore auto-repeat keys @@ -5977,1061 +601,248 @@ void LLVoiceClient::keyDown(KEY key, MASK mask) if(!mPTTIsMiddleMouse) { - if(mPTTIsToggle) - { - if(key == mPTTKey) - { - toggleUserPTTState(); - } - } - else if(mPTTKey != KEY_NONE) - { - setUserPTTState(gKeyboard->getKeyDown(mPTTKey)); - } + bool down = (mPTTKey != KEY_NONE) + && gKeyboard->getKeyDown(mPTTKey); + inputUserControlState(down); } + } void LLVoiceClient::keyUp(KEY key, MASK mask) { if(!mPTTIsMiddleMouse) { - if(!mPTTIsToggle && (mPTTKey != KEY_NONE)) - { - setUserPTTState(gKeyboard->getKeyDown(mPTTKey)); - } + bool down = (mPTTKey != KEY_NONE) + && gKeyboard->getKeyDown(mPTTKey); + inputUserControlState(down); } } void LLVoiceClient::middleMouseState(bool down) { if(mPTTIsMiddleMouse) { - if(mPTTIsToggle) + if(mPTTIsMiddleMouse) { - if(down) - { - toggleUserPTTState(); - } - } - else - { - setUserPTTState(down); + inputUserControlState(down); } } } -///////////////////////////// -// Accessors for data related to nearby speakers + +//------------------------------------------- +// nearby speaker accessors + BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) { - BOOL result = FALSE; - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - // I'm not sure what the semantics of this should be. - // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. - result = TRUE; + return mVoiceModule->getVoiceEnabled(id); + } + else + { + return FALSE; + } +} + +std::string LLVoiceClient::getDisplayName(const LLUUID& id) +{ + if (mVoiceModule) + { + return mVoiceModule->getDisplayName(id); + } + else + { + return std::string(); + } +} + +bool LLVoiceClient::isVoiceWorking() const +{ + if (mVoiceModule) + { + return mVoiceModule->isVoiceWorking(); + } + return false; +} + +BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id) +{ + if (mVoiceModule) + { + return mVoiceModule->isParticipantAvatar(id); + } + else + { + return FALSE; + } +} + +BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id) +{ + if (mVoiceModule) + { + return mVoiceModule->isOnlineSIP(id); + } + else + { + return FALSE; } - - return result; } BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) { - BOOL result = FALSE; - - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) - { - participant->mIsSpeaking = FALSE; - } - result = participant->mIsSpeaking; + return mVoiceModule->getIsSpeaking(id); + } + else + { + return FALSE; } - - return result; } BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) { - BOOL result = FALSE; - - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - result = participant->mIsModeratorMuted; + return mVoiceModule->getIsModeratorMuted(id); + } + else + { + return FALSE; } - - return result; } F32 LLVoiceClient::getCurrentPower(const LLUUID& id) -{ - F32 result = 0; - participantState *participant = findParticipantByID(id); - if(participant) - { - result = participant->mPower; - } - - return result; -} - - -std::string LLVoiceClient::getDisplayName(const LLUUID& id) { - std::string result; - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - result = participant->mDisplayName; + return mVoiceModule->getCurrentPower(id); } - - return result; -} - - -BOOL LLVoiceClient::getUsingPTT(const LLUUID& id) -{ - BOOL result = FALSE; - - participantState *participant = findParticipantByID(id); - if(participant) + else { - // I'm not sure what the semantics of this should be. - // Does "using PTT" mean they're configured with a push-to-talk button? - // For now, we know there's no PTT mechanism in place, so nobody is using it. + return 0.0; } - - return result; } BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) { - BOOL result = FALSE; - - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - result = participant->mOnMuteList; + return mVoiceModule->getOnMuteList(id); + } + else + { + return FALSE; } - - return result; } -// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100 -// internal = 400 * external^2 F32 LLVoiceClient::getUserVolume(const LLUUID& id) { - F32 result = 0.0f; - - participantState *participant = findParticipantByID(id); - if(participant) + if (mVoiceModule) { - S32 ires = 100; // nominal default volume - - if(participant->mIsSelf) - { - // Always make it look like the user's own volume is set at the default. - } - else if(participant->mUserVolume != -1) - { - // Use the internal volume - ires = participant->mUserVolume; - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. -// LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL; - } - else if(participant->mVolume != -1) - { - // Map backwards from vivox volume - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. -// LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL; - - if(participant->mVolume < 56) - { - ires = (participant->mVolume * 100) / 56; - } - else - { - ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100; - } - } - result = sqrtf(((F32)ires) / 400.f); + return mVoiceModule->getUserVolume(id); + } + else + { + return 0.0; } - - // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. -// LL_DEBUGS("Voice") << "returning " << result << LL_ENDL; - - return result; } void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) { - if(mAudioSession) - { - participantState *participant = findParticipantByID(id); - if (participant) - { - // volume can amplify by as much as 4x! - S32 ivol = (S32)(400.f * volume * volume); - participant->mUserVolume = llclamp(ivol, 0, 400); - participant->mVolumeDirty = TRUE; - mAudioSession->mVolumeDirty = TRUE; - } - } + if (mVoiceModule) mVoiceModule->setUserVolume(id, volume); } -std::string LLVoiceClient::getGroupID(const LLUUID& id) -{ - std::string result; - - participantState *participant = findParticipantByID(id); - if(participant) - { - result = participant->mGroupID; - } - - return result; -} - -BOOL LLVoiceClient::getAreaVoiceDisabled() -{ - return mAreaVoiceDisabled; -} - -void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; - - if(!mMainSessionGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << deltaFramesPerControlFrame << "" - << "" << "" << "" - << "false" - << "" << seconds << "" - << "\n\n\n"; - - - writeString(stream.str()); - } -} - -void LLVoiceClient::recordingLoopSave(const std::string& filename) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Flush" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::recordingStop() -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::filePlaybackStart(const std::string& filename) -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Start" - << "" << filename << "" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::filePlaybackStop() -{ -// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; - - if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) - { - std::ostringstream stream; - stream - << "" - << "" << mMainSessionGroupHandle << "" - << "Stop" - << "\n\n\n"; - - writeString(stream.str()); - } -} - -void LLVoiceClient::filePlaybackSetPaused(bool paused) -{ - // TODO: Implement once Vivox gives me a sample -} - -void LLVoiceClient::filePlaybackSetMode(bool vox, float speed) -{ - // TODO: Implement once Vivox gives me a sample -} - -LLVoiceClient::sessionState::sessionState() : - mMediaStreamState(streamStateUnknown), - mTextStreamState(streamStateUnknown), - mCreateInProgress(false), - mMediaConnectInProgress(false), - mVoiceInvitePending(false), - mTextInvitePending(false), - mSynthesizedCallerID(false), - mIsChannel(false), - mIsSpatial(false), - mIsP2P(false), - mIncoming(false), - mVoiceEnabled(false), - mReconnect(false), - mVolumeDirty(false), - mParticipantsChanged(false) -{ -} - -LLVoiceClient::sessionState::~sessionState() -{ - removeAllParticipants(); -} - -bool LLVoiceClient::sessionState::isCallBackPossible() -{ - // This may change to be explicitly specified by vivox in the future... - // Currently, only PSTN P2P calls cannot be returned. - // Conveniently, this is also the only case where we synthesize a caller UUID. - return !mSynthesizedCallerID; -} - -bool LLVoiceClient::sessionState::isTextIMPossible() -{ - // This may change to be explicitly specified by vivox in the future... - return !mSynthesizedCallerID; -} - - -LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void) -{ - return mSessions.begin(); -} - -LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void) -{ - return mSessions.end(); -} - - -LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle) -{ - sessionState *result = NULL; - sessionMap::iterator iter = mSessionsByHandle.find(&handle); - if(iter != mSessionsByHandle.end()) - { - result = iter->second; - } - - return result; -} - -LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) -{ - sessionState *result = NULL; - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - if(session->mCreateInProgress && (session->mSIPURI == uri)) - { - result = session; - break; - } - } - - return result; -} - -LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id) -{ - sessionState *result = NULL; - - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) - { - result = session; - break; - } - } - - return result; -} - -LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle) -{ - sessionState *result = NULL; - - if(handle.empty()) - { - // No handle supplied. - // Check whether there's already a session with this URI - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *s = *iter; - if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) - { - // TODO: I need to think about this logic... it's possible that this case should raise an internal error. - result = s; - break; - } - } - } - else // (!handle.empty()) - { - // Check for an existing session with this handle - sessionMap::iterator iter = mSessionsByHandle.find(&handle); - - if(iter != mSessionsByHandle.end()) - { - result = iter->second; - } - } - - if(!result) - { - // No existing session found. - - LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; - result = new sessionState(); - result->mSIPURI = uri; - result->mHandle = handle; - - mSessions.insert(result); - - if(!result->mHandle.empty()) - { - mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result)); - } - } - else - { - // Found an existing session - - if(uri != result->mSIPURI) - { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; - setSessionURI(result, uri); - } - - if(handle != result->mHandle) - { - if(handle.empty()) - { - // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. - LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; - } - else - { - // TODO: Should this be an internal error? - LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; - setSessionHandle(result, handle); - } - } - - LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; - } - - verifySessionState(); - - return result; -} - -void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) -{ - // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. - - if(!session->mHandle.empty()) - { - // Remove session from the map if it should have been there. - sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle)); - if(iter != mSessionsByHandle.end()) - { - if(iter->second != session) - { - LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; - } - - mSessionsByHandle.erase(iter); - } - else - { - LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; - } - } - - session->mHandle = handle; - - if(!handle.empty()) - { - mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session)); - } - - verifySessionState(); -} - -void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri) -{ - // There used to be a map of session URIs to sessions, which made this complex.... - session->mSIPURI = uri; - - verifySessionState(); -} - -void LLVoiceClient::deleteSession(sessionState *session) -{ - // Remove the session from the handle map - if(!session->mHandle.empty()) - { - sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle)); - if(iter != mSessionsByHandle.end()) - { - if(iter->second != session) - { - LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; - } - mSessionsByHandle.erase(iter); - } - } - - // Remove the session from the URI map - mSessions.erase(session); - - // At this point, the session should be unhooked from all lists and all state should be consistent. - verifySessionState(); - - // If this is the current audio session, clean up the pointer which will soon be dangling. - if(mAudioSession == session) - { - mAudioSession = NULL; - mAudioSessionChanged = true; - } - - // ditto for the next audio session - if(mNextAudioSession == session) - { - mNextAudioSession = NULL; - } - - // delete the session - delete session; -} - -void LLVoiceClient::deleteAllSessions() -{ - LL_DEBUGS("Voice") << "called" << LL_ENDL; - - while(!mSessions.empty()) - { - deleteSession(*(sessionsBegin())); - } - - if(!mSessionsByHandle.empty()) - { - LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; - } -} - -void LLVoiceClient::verifySessionState(void) -{ - // This is mostly intended for debugging problems with session state management. - LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; - - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - - LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; - - if(!session->mHandle.empty()) - { - // every session with a non-empty handle needs to be in the handle map - sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle)); - if(i2 == mSessionsByHandle.end()) - { - LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; - } - else - { - if(i2->second != session) - { - LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; - } - } - } - } - - // check that every entry in the handle map points to a valid session in the session set - for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) - { - sessionState *session = iter->second; - sessionIterator i2 = mSessions.find(session); - if(i2 == mSessions.end()) - { - LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; - } - else - { - if(session->mHandle != (*i2)->mHandle) - { - LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; - } - } - } -} - -LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : - mURI(uri) -{ - mOnlineSL = false; - mOnlineSLim = false; - mCanSeeMeOnline = true; - mHasBlockListEntry = false; - mHasAutoAcceptListEntry = false; - mNameResolved = false; - mInVivoxBuddies = false; - mInSLFriends = false; - mNeedsNameUpdate = false; -} - -void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) -{ - buddyListEntry *buddy = addBuddy(uri, displayName); - buddy->mInVivoxBuddies = true; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri) -{ - std::string empty; - buddyListEntry *buddy = addBuddy(uri, empty); - if(buddy->mDisplayName.empty()) - { - buddy->mNameResolved = false; - } - return buddy; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName) -{ - buddyListEntry *result = NULL; - buddyListMap::iterator iter = mBuddyListMap.find(&uri); - - if(iter != mBuddyListMap.end()) - { - // Found a matching buddy already in the map. - LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; - result = iter->second; - } - - if(!result) - { - // participant isn't already in one list or the other. - LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; - result = new buddyListEntry(uri); - result->mDisplayName = displayName; - - if(IDFromName(uri, result->mUUID)) - { - // Extracted UUID from name successfully. - } - else - { - LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; - } - - mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result)); - } - - return result; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri) -{ - buddyListEntry *result = NULL; - buddyListMap::iterator iter = mBuddyListMap.find(&uri); - if(iter != mBuddyListMap.end()) - { - result = iter->second; - } - - return result; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id) -{ - buddyListEntry *result = NULL; - buddyListMap::iterator iter; - - for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) - { - if(iter->second->mUUID == id) - { - result = iter->second; - break; - } - } - - return result; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name) -{ - buddyListEntry *result = NULL; - buddyListMap::iterator iter; - - for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) - { - if(iter->second->mDisplayName == name) - { - result = iter->second; - break; - } - } - - return result; -} - -void LLVoiceClient::deleteBuddy(const std::string &uri) -{ - buddyListMap::iterator iter = mBuddyListMap.find(&uri); - if(iter != mBuddyListMap.end()) - { - LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL; - buddyListEntry *buddy = iter->second; - mBuddyListMap.erase(iter); - delete buddy; - } - else - { - LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL; - } - -} - -void LLVoiceClient::deleteAllBuddies(void) -{ - while(!mBuddyListMap.empty()) - { - deleteBuddy(*(mBuddyListMap.begin()->first)); - } - - // Don't want to correlate with friends list when we've emptied the buddy list. - mBuddyListMapPopulated = false; - - // Don't want to correlate with friends list when we've reset the block rules. - mBlockRulesListReceived = false; - mAutoAcceptRulesListReceived = false; -} - -void LLVoiceClient::deleteAllBlockRules(void) -{ - // Clear the block list entry flags from all local buddy list entries - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) - { - buddy_it->second->mHasBlockListEntry = false; - } -} - -void LLVoiceClient::deleteAllAutoAcceptRules(void) -{ - // Clear the auto-accept list entry flags from all local buddy list entries - buddyListMap::iterator buddy_it; - for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) - { - buddy_it->second->mHasAutoAcceptListEntry = false; - } -} - -void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly) -{ - buddyListEntry *buddy = NULL; - - // blockMask is the SIP URI of a friends list entry - buddyListMap::iterator iter = mBuddyListMap.find(&blockMask); - if(iter != mBuddyListMap.end()) - { - LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL; - buddy = iter->second; - } - - if(buddy == NULL) - { - LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL; - buddy = addBuddy(blockMask); - } - - if(buddy != NULL) - { - buddy->mHasBlockListEntry = true; - } -} - -void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy) -{ - buddyListEntry *buddy = NULL; - - // blockMask is the SIP URI of a friends list entry - buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask); - if(iter != mBuddyListMap.end()) - { - LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL; - buddy = iter->second; - } - - if(buddy == NULL) - { - LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL; - buddy = addBuddy(autoAcceptMask); - } - - if(buddy != NULL) - { - buddy->mHasAutoAcceptListEntry = true; - } -} - -void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) -{ - // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. - mBlockRulesListReceived = true; -} - -void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString) -{ - // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. - mAutoAcceptRulesListReceived = true; -} - -void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) -{ - mParticipantObservers.insert(observer); -} - -void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) -{ - mParticipantObservers.erase(observer); -} - -void LLVoiceClient::notifyParticipantObservers() -{ - for (observer_set_t::iterator it = mParticipantObservers.begin(); - it != mParticipantObservers.end(); - ) - { - LLVoiceClientParticipantObserver* observer = *it; - observer->onChange(); - // In case onChange() deleted an entry. - it = mParticipantObservers.upper_bound(observer); - } -} +//-------------------------------------------------- +// status observers void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) { - mStatusObservers.insert(observer); + if (mVoiceModule) mVoiceModule->addObserver(observer); } void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) { - mStatusObservers.erase(observer); -} - -void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) -{ - if(mAudioSession) - { - if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) - { - switch(mAudioSession->mErrorStatusCode) - { - case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; - case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; - case 20715: - //invalid channel, we may be using a set of poorly cached - //info - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - case 1009: - //invalid username and password - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - break; - } - - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - } - else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) - { - switch(mAudioSession->mErrorStatusCode) - { - case 404: // NOT_FOUND - case 480: // TEMPORARILY_UNAVAILABLE - case 408: // REQUEST_TIMEOUT - // call failed because other user was not available - // treat this as an error case - status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - - // Reset the error code to make sure it won't be reused later by accident. - mAudioSession->mErrorStatusCode = 0; - break; - } - } - } - - LL_DEBUGS("Voice") - << " " << LLVoiceClientStatusObserver::status2string(status) - << ", session URI " << getAudioSessionURI() - << (inSpatialChannel()?", proximal is true":", proximal is false") - << LL_ENDL; - - for (status_observer_set_t::iterator it = mStatusObservers.begin(); - it != mStatusObservers.end(); - ) - { - LLVoiceClientStatusObserver* observer = *it; - observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); - // In case onError() deleted an entry. - it = mStatusObservers.upper_bound(observer); - } - + if (mVoiceModule) mVoiceModule->removeObserver(observer); } void LLVoiceClient::addObserver(LLFriendObserver* observer) { - mFriendObservers.insert(observer); + if (mVoiceModule) mVoiceModule->addObserver(observer); } void LLVoiceClient::removeObserver(LLFriendObserver* observer) { - mFriendObservers.erase(observer); + if (mVoiceModule) mVoiceModule->removeObserver(observer); } -void LLVoiceClient::notifyFriendObservers() +void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) { - for (friend_observer_set_t::iterator it = mFriendObservers.begin(); - it != mFriendObservers.end(); - ) + if (mVoiceModule) mVoiceModule->addObserver(observer); +} + +void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +{ + if (mVoiceModule) mVoiceModule->removeObserver(observer); +} + +std::string LLVoiceClient::sipURIFromID(const LLUUID &id) +{ + if (mVoiceModule) { - LLFriendObserver* observer = *it; - it++; - // The only friend-related thing we notify on is online/offline transitions. - observer->changed(LLFriendObserver::ONLINE); + return mVoiceModule->sipURIFromID(id); + } + else + { + return std::string(); } } -void LLVoiceClient::lookupName(const LLUUID &id) +LLVoiceEffectInterface* LLVoiceClient::getVoiceEffectInterface() const { - gCacheName->get(id, false, boost::bind(&LLVoiceClient::onAvatarNameLookup,_1,_2)); + return getVoiceEffectEnabled() ? dynamic_cast(mVoiceModule) : NULL; } -//static -void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& full_name) -{ - if(gVoiceClient) - { - gVoiceClient->avatarNameResolved(id, full_name); - } -} +/////////////////// +// version checking -void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +class LLViewerRequiredVoiceVersion : public LLHTTPNode { - // If the avatar whose name just resolved is on our friends list, resync the friends list. - if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL) + static BOOL sAlertedUser; + virtual void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const { - mFriendsListDirty = true; - } - - // Iterate over all sessions. - for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) - { - sessionState *session = *iter; - - // Check for this user as a participant in this session - participantState *participant = session->findParticipantByID(id); - if(participant) + //You received this messsage (most likely on region cross or + //teleport) + if ( input.has("body") && input["body"].has("major_version") ) { - // Found -- fill in the name - participant->mAccountName = name; - // and post a "participants updated" message to listeners later. - session->mParticipantsChanged = true; - } - - // Check whether this is a p2p session whose caller name just resolved - if(session->mCallerID == id) - { - // this session's "caller ID" just resolved. Fill in the name. - session->mName = name; - if(session->mTextInvitePending) - { - session->mTextInvitePending = false; + int major_voice_version = + input["body"]["major_version"].asInteger(); + // int minor_voice_version = + // input["body"]["minor_version"].asInteger(); + LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion(); - // We don't need to call gIMMgr->addP2PSession() here. The first incoming message will create the panel. - } - if(session->mVoiceInvitePending) + if (major_voice_version > 1) { - session->mVoiceInvitePending = false; - - gIMMgr->inviteToSession( - session->mIMSessionID, - session->mName, - session->mCallerID, - session->mName, - IM_SESSION_P2P_INVITE, - LLIMMgr::INVITATION_TYPE_VOICE, - session->mHandle, - session->mSIPURI); + if (!sAlertedUser) + { + //sAlertedUser = TRUE; + LLNotificationsUtil::add("VoiceVersionMismatch"); + gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener + } } - } } -} +}; class LLViewerParcelVoiceInfo : public LLHTTPNode { virtual void post( - LLHTTPNode::ResponsePtr response, - const LLSD& context, - const LLSD& input) const + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const { //the parcel you are in has changed something about its //voice information @@ -7069,42 +880,159 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode voice_credentials["channel_credentials"].asString(); } - gVoiceClient->setSpatialChannel(uri, credentials); + LLVoiceClient::getInstance()->setSpatialChannel(uri, credentials); } } } }; -class LLViewerRequiredVoiceVersion : public LLHTTPNode +const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml"; + +LLSpeakerVolumeStorage::LLSpeakerVolumeStorage() { - static BOOL sAlertedUser; - virtual void post( - LLHTTPNode::ResponsePtr response, - const LLSD& context, - const LLSD& input) const - { - //You received this messsage (most likely on region cross or - //teleport) - if ( input.has("body") && input["body"].has("major_version") ) - { - int major_voice_version = - input["body"]["major_version"].asInteger(); -// int minor_voice_version = -// input["body"]["minor_version"].asInteger(); + load(); +} - if (gVoiceClient && - (major_voice_version > VOICE_MAJOR_VERSION) ) - { - if (!sAlertedUser) - { - //sAlertedUser = TRUE; - LLNotificationsUtil::add("VoiceVersionMismatch"); - gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener - } - } - } +LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage() +{ + save(); +} + +void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume) +{ + if ((volume >= LLVoiceClient::VOLUME_MIN) && (volume <= LLVoiceClient::VOLUME_MAX)) + { + mSpeakersData[speaker_id] = volume; + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. + // LL_DEBUGS("Voice") << "Stored volume = " << volume << " for " << id << LL_ENDL; } -}; + else + { + LL_WARNS("Voice") << "Attempted to store out of range volume " << volume << " for " << speaker_id << LL_ENDL; + llassert(0); + } +} + +bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume) +{ + speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id); + + if (it != mSpeakersData.end()) + { + volume = it->second; + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. + // LL_DEBUGS("Voice") << "Retrieved stored volume = " << volume << " for " << id << LL_ENDL; + + return true; + } + + return false; +} + +void LLSpeakerVolumeStorage::removeSpeakerVolume(const LLUUID& speaker_id) +{ + mSpeakersData.erase(speaker_id); + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. + // LL_DEBUGS("Voice") << "Removing stored volume for " << id << LL_ENDL; +} + +/* static */ F32 LLSpeakerVolumeStorage::transformFromLegacyVolume(F32 volume_in) +{ + // Convert to linear-logarithmic [0.0..1.0] with 0.5 = 0dB + // from legacy characteristic composed of two square-curves + // that intersect at volume_in = 0.5, volume_out = 0.56 + + F32 volume_out = 0.f; + volume_in = llclamp(volume_in, 0.f, 1.0f); + + if (volume_in <= 0.5f) + { + volume_out = volume_in * volume_in * 4.f * 0.56f; + } + else + { + volume_out = (1.f - 0.56f) * (4.f * volume_in * volume_in - 1.f) / 3.f + 0.56f; + } + + return volume_out; +} + +/* static */ F32 LLSpeakerVolumeStorage::transformToLegacyVolume(F32 volume_in) +{ + // Convert from linear-logarithmic [0.0..1.0] with 0.5 = 0dB + // to legacy characteristic composed of two square-curves + // that intersect at volume_in = 0.56, volume_out = 0.5 + + F32 volume_out = 0.f; + volume_in = llclamp(volume_in, 0.f, 1.0f); + + if (volume_in <= 0.56f) + { + volume_out = sqrt(volume_in / (4.f * 0.56f)); + } + else + { + volume_out = sqrt((3.f * (volume_in - 0.56f) / (1.f - 0.56f) + 1.f) / 4.f); + } + + return volume_out; +} + +void LLSpeakerVolumeStorage::load() +{ + // load per-resident voice volume information + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); + + LL_INFOS("Voice") << "Loading stored speaker volumes from: " << filename << LL_ENDL; + + LLSD settings_llsd; + llifstream file; + file.open(filename); + if (file.is_open()) + { + LLSDSerialize::fromXML(settings_llsd, file); + } + + for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); + iter != settings_llsd.endMap(); ++iter) + { + // Maintain compatibility with 1.23 non-linear saved volume levels + F32 volume = transformFromLegacyVolume((F32)iter->second.asReal()); + + storeSpeakerVolume(LLUUID(iter->first), volume); + } +} + +void LLSpeakerVolumeStorage::save() +{ + // If we quit from the login screen we will not have an SL account + // name. Don't try to save, otherwise we'll dump a file in + // C:\Program Files\SecondLife\ or similar. JC + std::string user_dir = gDirUtilp->getLindenUserDir(true); + if (!user_dir.empty()) + { + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); + LLSD settings_llsd; + + LL_INFOS("Voice") << "Saving stored speaker volumes to: " << filename << LL_ENDL; + + for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter) + { + // Maintain compatibility with 1.23 non-linear saved volume levels + F32 volume = transformToLegacyVolume(iter->second); + + settings_llsd[iter->first.asString()] = volume; + } + + llofstream file; + file.open(filename); + LLSDSerialize::toPrettyXML(settings_llsd, file); + } +} + BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE; LLHTTPRegistration diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index e5c4af6a3..761a1a768 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -2,38 +2,31 @@ * @file llvoiceclient.h * @brief Declaration of LLVoiceClient class which is the interface to the voice client process. * - * $LicenseInfo:firstyear=2001&license=viewergpl$ - * - * Copyright (c) 2001-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LL_VOICE_CLIENT_H #define LL_VOICE_CLIENT_H class LLVOAvatar; -class LLVivoxProtocolParser; #include "lliopipe.h" #include "llpumpio.h" @@ -43,14 +36,23 @@ class LLVivoxProtocolParser; #include "llframetimer.h" #include "llviewerregion.h" #include "llcallingcard.h" // for LLFriendObserver +#include "llcontrol.h" + +// devices + +typedef std::vector LLVoiceDeviceList; + class LLVoiceClientParticipantObserver { public: virtual ~LLVoiceClientParticipantObserver() { } - virtual void onChange() = 0; + virtual void onParticipantsChanged() = 0; }; + +/////////////////////////////////// +/// @class LLVoiceClientStatusObserver class LLVoiceClientStatusObserver { public: @@ -64,6 +66,7 @@ public: STATUS_JOINED, STATUS_LEFT_CHANNEL, STATUS_VOICE_DISABLED, + STATUS_VOICE_ENABLED, BEGIN_ERROR_STATUS, ERROR_CHANNEL_FULL, ERROR_CHANNEL_LOCKED, @@ -77,678 +80,444 @@ public: static std::string status2string(EStatusType inStatus); }; +struct LLVoiceVersionInfo +{ + std::string serverType; + std::string serverVersion; +}; + +////////////////////////////////// +/// @class LLVoiceModuleInterface +/// @brief Voice module interface +/// +/// Voice modules should provide an implementation for this interface. +///////////////////////////////// + +class LLVoiceModuleInterface +{ +public: + LLVoiceModuleInterface() {} + virtual ~LLVoiceModuleInterface() {} + + virtual void init(LLPumpIO *pump)=0; // Call this once at application startup (creates connector) + virtual void terminate()=0; // Call this to clean up during shutdown + + virtual void updateSettings()=0; // call after loading settings and whenever they change + + virtual bool isVoiceWorking() const = 0; // connected to a voice server and voice channel + + virtual const LLVoiceVersionInfo& getVersion()=0; + + ///////////////////// + /// @name Tuning + //@{ + virtual void tuningStart()=0; + virtual void tuningStop()=0; + virtual bool inTuningMode()=0; + + virtual void tuningSetMicVolume(float volume)=0; + virtual void tuningSetSpeakerVolume(float volume)=0; + virtual float tuningGetEnergy(void)=0; + //@} + + ///////////////////// + /// @name Devices + //@{ + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + virtual bool deviceSettingsAvailable()=0; + + // Requery the vivox daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + virtual void refreshDeviceLists(bool clearCurrentList = true)=0; + + virtual void setCaptureDevice(const std::string& name)=0; + virtual void setRenderDevice(const std::string& name)=0; + + virtual LLVoiceDeviceList& getCaptureDevices()=0; + virtual LLVoiceDeviceList& getRenderDevices()=0; + + virtual void getParticipantList(std::set &participants)=0; + virtual bool isParticipant(const LLUUID& speaker_id)=0; + //@} + + //////////////////////////// + /// @ name Channel stuff + //@{ + // returns true iff the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + virtual bool inProximalChannel()=0; + + virtual void setNonSpatialChannel(const std::string &uri, + const std::string &credentials)=0; + + virtual void setSpatialChannel(const std::string &uri, + const std::string &credentials)=0; + + virtual void leaveNonSpatialChannel()=0; + + virtual void leaveChannel(void)=0; + + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + virtual std::string getCurrentChannel()=0; + //@} + + + ////////////////////////// + /// @name invitations + //@{ + // start a voice channel with the specified user + virtual void callUser(const LLUUID &uuid)=0; + virtual bool isValidChannel(std::string& channelHandle)=0; + virtual bool answerInvite(std::string &channelHandle)=0; + virtual void declineInvite(std::string &channelHandle)=0; + //@} + + ///////////////////////// + /// @name Volume/gain + //@{ + virtual void setVoiceVolume(F32 volume)=0; + virtual void setMicGain(F32 volume)=0; + //@} + + ///////////////////////// + /// @name enable disable voice and features + //@{ + virtual bool voiceEnabled()=0; + virtual void setVoiceEnabled(bool enabled)=0; + virtual void setLipSyncEnabled(BOOL enabled)=0; + virtual BOOL lipSyncEnabled()=0; + virtual void setMuteMic(bool muted)=0; // Set the mute state of the local mic. + //@} + + ////////////////////////// + /// @name nearby speaker accessors + //@{ + virtual BOOL getVoiceEnabled(const LLUUID& id)=0; // true if we've received data for this avatar + virtual std::string getDisplayName(const LLUUID& id)=0; + virtual BOOL isOnlineSIP(const LLUUID &id)=0; + virtual BOOL isParticipantAvatar(const LLUUID &id)=0; + virtual BOOL getIsSpeaking(const LLUUID& id)=0; + virtual BOOL getIsModeratorMuted(const LLUUID& id)=0; + virtual F32 getCurrentPower(const LLUUID& id)=0; // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + virtual BOOL getOnMuteList(const LLUUID& id)=0; + virtual F32 getUserVolume(const LLUUID& id)=0; + virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal) + //@} + + ////////////////////////// + /// @name text chat + //@{ + virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0; + virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0; + virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; + virtual void endUserIMSession(const LLUUID &uuid)=0; + //@} + + // authorize the user + virtual void userAuthorized(const std::string& user_id, + const LLUUID &agentID)=0; + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceClientStatusObserver* observer)=0; + virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0; + virtual void addObserver(LLFriendObserver* observer)=0; + virtual void removeObserver(LLFriendObserver* observer)=0; + virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0; + virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0; + //@} + + virtual std::string sipURIFromID(const LLUUID &id)=0; + //@} + +}; + + +////////////////////////////////// +/// @class LLVoiceEffectObserver +class LLVoiceEffectObserver +{ +public: + virtual ~LLVoiceEffectObserver() { } + virtual void onVoiceEffectChanged(bool effect_list_updated) = 0; +}; + +typedef std::multimap voice_effect_list_t; + +////////////////////////////////// +/// @class LLVoiceEffectInterface +/// @brief Voice effect module interface +/// +/// Voice effect modules should provide an implementation for this interface. +///////////////////////////////// + +class LLVoiceEffectInterface +{ +public: + LLVoiceEffectInterface() {} + virtual ~LLVoiceEffectInterface() {} + + ////////////////////////// + /// @name Accessors + //@{ + virtual bool setVoiceEffect(const LLUUID& id) = 0; + virtual const LLUUID getVoiceEffect() = 0; + virtual LLSD getVoiceEffectProperties(const LLUUID& id) = 0; + + virtual void refreshVoiceEffectLists(bool clear_lists) = 0; + virtual const voice_effect_list_t &getVoiceEffectList() const = 0; + virtual const voice_effect_list_t &getVoiceEffectTemplateList() const = 0; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceEffectObserver* observer) = 0; + virtual void removeObserver(LLVoiceEffectObserver* observer) = 0; + //@} + + ////////////////////////////// + /// @name Preview buffer + //@{ + virtual void enablePreviewBuffer(bool enable) = 0; + virtual void recordPreviewBuffer() = 0; + virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null) = 0; + virtual void stopPreviewBuffer() = 0; + + virtual bool isPreviewRecording() = 0; + virtual bool isPreviewPlaying() = 0; + //@} +}; + + class LLVoiceClient: public LLSingleton { LOG_CLASS(LLVoiceClient); - public: - LLVoiceClient(); - ~LLVoiceClient(); - - public: - static void init(LLPumpIO *pump); // Call this once at application startup (creates connector) - static void terminate(); // Call this to clean up during shutdown - - protected: - bool writeString(const std::string &str); +public: + LLVoiceClient(); + ~LLVoiceClient(); - public: - - static F32 OVERDRIVEN_POWER_LEVEL; + void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + void terminate(); // Call this to clean up during shutdown - void updateSettings(); // call after loading settings and whenever they change - - void getCaptureDevicesSendMessage(); - void getRenderDevicesSendMessage(); - - void clearCaptureDevices(); - void addCaptureDevice(const std::string& name); - void setCaptureDevice(const std::string& name); - - void clearRenderDevices(); - void addRenderDevice(const std::string& name); - void setRenderDevice(const std::string& name); + const LLVoiceVersionInfo getVersion(); - void tuningStart(); - void tuningStop(); - bool inTuningMode(); - bool inTuningStates(); - - void tuningRenderStartSendMessage(const std::string& name, bool loop); - void tuningRenderStopSendMessage(); + static const F32 OVERDRIVEN_POWER_LEVEL; - void tuningCaptureStartSendMessage(int duration); - void tuningCaptureStopSendMessage(); - - void tuningSetMicVolume(float volume); - void tuningSetSpeakerVolume(float volume); - float tuningGetEnergy(void); - - // This returns true when it's safe to bring up the "device settings" dialog in the prefs. - // i.e. when the daemon is running and connected, and the device lists are populated. - bool deviceSettingsAvailable(); - - // Requery the vivox daemon for the current list of input/output devices. - // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed - // (use this if you want to know when it's done). - // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. - void refreshDeviceLists(bool clearCurrentList = true); - - // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. - void daemonDied(); + static const F32 VOLUME_MIN; + static const F32 VOLUME_DEFAULT; + static const F32 VOLUME_MAX; - // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. - void giveUp(); - - ///////////////////////////// - // Response/Event handlers - void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); - void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); - void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); - void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); - void logoutResponse(int statusCode, std::string &statusString); - void connectorShutdownResponse(int statusCode, std::string &statusString); + void updateSettings(); // call after loading settings and whenever they change - void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); - void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); - void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); - void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); - void sessionGroupAddedEvent(std::string &sessionGroupHandle); - void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); - void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); - void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); - void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); - void auxAudioPropertiesEvent(F32 energy); - void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); - void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); - void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); - void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); - - void buddyListChanged(); - void muteListChanged(); - void updateFriends(U32 mask); - - ///////////////////////////// - // Sending updates of current state -static void updatePosition(void); - void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); - void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); - bool channelFromRegion(LLViewerRegion *region, std::string &name); - void leaveChannel(void); // call this on logout or teleport begin + bool isVoiceWorking() const; // connected to a voice server and voice channel - - void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. - void setUserPTTState(bool ptt); - bool getUserPTTState(); - void toggleUserPTTState(void); - void setVoiceEnabled(bool enabled); - static bool voiceEnabled(); - void setUsePTT(bool usePTT); - void setPTTIsToggle(bool PTTIsToggle); - void setPTTKey(std::string &key); - void setEarLocation(S32 loc); - void setVoiceVolume(F32 volume); - void setMicGain(F32 volume); - void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) - void setLipSyncEnabled(BOOL enabled); - BOOL lipSyncEnabled(); + // tuning + void tuningStart(); + void tuningStop(); + bool inTuningMode(); - // PTT key triggering - void keyDown(KEY key, MASK mask); - void keyUp(KEY key, MASK mask); - void middleMouseState(bool down); - - ///////////////////////////// - // Accessors for data related to nearby speakers - BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar - BOOL getIsSpeaking(const LLUUID& id); - BOOL getIsModeratorMuted(const LLUUID& id); - F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... - BOOL getOnMuteList(const LLUUID& id); - F32 getUserVolume(const LLUUID& id); - std::string getDisplayName(const LLUUID& id); - - // MBW -- XXX -- Not sure how to get this data out of the TVC - BOOL getUsingPTT(const LLUUID& id); - std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) + void tuningSetMicVolume(float volume); + void tuningSetSpeakerVolume(float volume); + float tuningGetEnergy(void); - ///////////////////////////// - BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. - // Use this to determine whether to show a "no speech" icon in the menu bar. - - ///////////////////////////// - // Recording controls - void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); - void recordingLoopSave(const std::string& filename); - void recordingStop(); - - // Playback controls - void filePlaybackStart(const std::string& filename); - void filePlaybackStop(); - void filePlaybackSetPaused(bool paused); - void filePlaybackSetMode(bool vox = false, float speed = 1.0f); - - - // This is used by the string-keyed maps below, to avoid storing the string twice. - // The 'const std::string *' in the key points to a string actually stored in the object referenced by the map. - // The add and delete operations for each map allocate and delete in the right order to avoid dangling references. - // The default compare operation would just compare pointers, which is incorrect, so they must use this comparitor instead. - struct stringMapComparitor - { - bool operator()(const std::string* a, const std::string * b) const - { - return a->compare(*b) < 0; - } - }; + // devices - struct uuidMapComparitor - { - bool operator()(const LLUUID* a, const LLUUID * b) const - { - return *a < *b; - } - }; - - struct participantState - { - public: - participantState(const std::string &uri); + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + bool deviceSettingsAvailable(); - bool updateMuteState(); - bool isAvatar(); + // Requery the vivox daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + void refreshDeviceLists(bool clearCurrentList = true); - std::string mURI; - LLUUID mAvatarID; - std::string mAccountName; - std::string mDisplayName; - LLFrameTimer mSpeakingTimeout; - F32 mLastSpokeTimestamp; - F32 mPower; - int mVolume; - std::string mGroupID; - int mUserVolume; - bool mPTT; - bool mIsSpeaking; - bool mIsModeratorMuted; - bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) - bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) - bool mAvatarIDValid; - bool mIsSelf; - }; - typedef std::map participantMap; + void setCaptureDevice(const std::string& name); + void setRenderDevice(const std::string& name); - typedef std::map participantUUIDMap; - - enum streamState - { - streamStateUnknown = 0, - streamStateIdle = 1, - streamStateConnected = 2, - streamStateRinging = 3, - }; - - struct sessionState - { - public: - sessionState(); - ~sessionState(); + const LLVoiceDeviceList& getCaptureDevices(); + const LLVoiceDeviceList& getRenderDevices(); - participantState *addParticipant(const std::string &uri); - // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. - // Take care not to use the pointer again after that. - void removeParticipant(participantState *participant); - void removeAllParticipants(); + //////////////////////////// + // Channel stuff + // - participantState *findParticipant(const std::string &uri); - participantState *findParticipantByID(const LLUUID& id); + // returns true if the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + bool inProximalChannel(); + void setNonSpatialChannel( + const std::string &uri, + const std::string &credentials); + void setSpatialChannel( + const std::string &uri, + const std::string &credentials); + void leaveNonSpatialChannel(); - bool isCallBackPossible(); - bool isTextIMPossible(); + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + std::string getCurrentChannel(); + // start a voice channel with the specified user + void callUser(const LLUUID &uuid); + bool isValidChannel(std::string& channelHandle); + bool answerInvite(std::string &channelHandle); + void declineInvite(std::string &channelHandle); + void leaveChannel(void); // call this on logout or teleport begin - std::string mHandle; - std::string mGroupHandle; - std::string mSIPURI; - std::string mAlias; - std::string mName; - std::string mAlternateSIPURI; - std::string mHash; // Channel password - std::string mErrorStatusString; - std::queue mTextMsgQueue; - - LLUUID mIMSessionID; - LLUUID mCallerID; - int mErrorStatusCode; - int mMediaStreamState; - int mTextStreamState; - bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. - bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. - bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) - bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) - bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. - bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) - bool mIsSpatial; // True for spatial channels - bool mIsP2P; - bool mIncoming; - bool mVoiceEnabled; - bool mReconnect; // Whether we should try to reconnect to this session if it's dropped - // Set to true when the mute state of someone in the participant list changes. - // The code will have to walk the list to find the changed participant(s). - bool mVolumeDirty; - bool mParticipantsChanged; - participantMap mParticipantsByURI; - participantUUIDMap mParticipantsByUUID; - }; + ///////////////////////////// + // Sending updates of current state - participantState *findParticipantByID(const LLUUID& id); - participantMap *getParticipantList(void); - - typedef std::map sessionMap; - typedef std::set sessionSet; - - typedef sessionSet::iterator sessionIterator; - sessionIterator sessionsBegin(void); - sessionIterator sessionsEnd(void); - sessionState *findSession(const std::string &handle); - sessionState *findSessionBeingCreatedByURI(const std::string &uri); - sessionState *findSession(const LLUUID &participant_id); - sessionState *findSessionByCreateID(const std::string &create_id); - - sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); - void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); - void setSessionURI(sessionState *session, const std::string &uri); - void deleteSession(sessionState *session); - void deleteAllSessions(void); + void setVoiceVolume(F32 volume); + void setMicGain(F32 volume); + void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + bool voiceEnabled(); + void setLipSyncEnabled(BOOL enabled); + void setMuteMic(bool muted); // Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. + void setUserPTTState(bool ptt); + bool getUserPTTState(); + void toggleUserPTTState(void); + void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs + void setVoiceEnabled(bool enabled); - void verifySessionState(void); + void setUsePTT(bool usePTT); + void setPTTIsToggle(bool PTTIsToggle); + bool getPTTIsToggle(); + void setPTTKey(std::string &key); - void joinedAudioSession(sessionState *session); - void leftAudioSession(sessionState *session); + void updateMicMuteLogic(); - // This is called in several places where the session _may_ need to be deleted. - // It contains logic for whether to delete the session or keep it around. - void reapSession(sessionState *session); - - // Returns true if the session seems to indicate we've moved to a region on a different voice server - bool sessionNeedsRelog(sessionState *session); - - struct buddyListEntry - { - buddyListEntry(const std::string &uri); - std::string mURI; - std::string mDisplayName; - LLUUID mUUID; - bool mOnlineSL; - bool mOnlineSLim; - bool mCanSeeMeOnline; - bool mHasBlockListEntry; - bool mHasAutoAcceptListEntry; - bool mNameResolved; - bool mInSLFriends; - bool mInVivoxBuddies; - bool mNeedsNameUpdate; - }; + BOOL lipSyncEnabled(); - typedef std::map buddyListMap; - - // This should be called when parsing a buddy list entry sent by SLVoice. - void processBuddyListEntry(const std::string &uri, const std::string &displayName); + // PTT key triggering + void keyDown(KEY key, MASK mask); + void keyUp(KEY key, MASK mask); + void middleMouseState(bool down); - buddyListEntry *addBuddy(const std::string &uri); - buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); - buddyListEntry *findBuddy(const std::string &uri); - buddyListEntry *findBuddy(const LLUUID &id); - buddyListEntry *findBuddyByDisplayName(const std::string &name); - void deleteBuddy(const std::string &uri); - void deleteAllBuddies(void); - void deleteAllBlockRules(void); - void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); - void deleteAllAutoAcceptRules(void); - void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); - void accountListBlockRulesResponse(int statusCode, const std::string &statusString); - void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); - - ///////////////////////////// - // session control messages - void connectorCreate(); - void connectorShutdown(); + ///////////////////////////// + // Accessors for data related to nearby speakers + BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + std::string getDisplayName(const LLUUID& id); + BOOL isOnlineSIP(const LLUUID &id); + BOOL isParticipantAvatar(const LLUUID &id); + BOOL getIsSpeaking(const LLUUID& id); + BOOL getIsModeratorMuted(const LLUUID& id); + F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + BOOL getOnMuteList(const LLUUID& id); + F32 getUserVolume(const LLUUID& id); - void requestVoiceAccountProvision(S32 retries = 3); - void userAuthorized( - const std::string& firstName, - const std::string& lastName, + ///////////////////////////// + BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. + // Use this to determine whether to show a "no speech" icon in the menu bar. + void getParticipantList(std::set &participants); + bool isParticipant(const LLUUID& speaker_id); + + ////////////////////////// + /// @name text chat + //@{ + BOOL isSessionTextIMPossible(const LLUUID& id); + BOOL isSessionCallBackPossible(const LLUUID& id); + BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); + void endUserIMSession(const LLUUID &uuid); + //@} + + + void userAuthorized(const std::string& user_id, const LLUUID &agentID); - void login( - const std::string& account_name, - const std::string& password, - const std::string& voice_sip_uri_hostname, - const std::string& voice_account_server_uri); - void loginSendMessage(); - void logout(); - void logoutSendMessage(); - void accountListBlockRulesSendMessage(); - void accountListAutoAcceptRulesSendMessage(); - - void sessionGroupCreateSendMessage(); - void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); - void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); - void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session - void sessionTextConnectSendMessage(sessionState *session); // just joins the text session - void sessionTerminateSendMessage(sessionState *session); - void sessionGroupTerminateSendMessage(sessionState *session); - void sessionMediaDisconnectSendMessage(sessionState *session); - void sessionTextDisconnectSendMessage(sessionState *session); + void addObserver(LLVoiceClientStatusObserver* observer); + void removeObserver(LLVoiceClientStatusObserver* observer); + void addObserver(LLFriendObserver* observer); + void removeObserver(LLFriendObserver* observer); + void addObserver(LLVoiceClientParticipantObserver* observer); + void removeObserver(LLVoiceClientParticipantObserver* observer); - // Pokes the state machine to leave the audio session next time around. - void sessionTerminate(); - - // Pokes the state machine to shut down the connector and restart it. - void requestRelog(); - - // Does the actual work to get out of the audio session - void leaveAudioSession(); - - void addObserver(LLVoiceClientParticipantObserver* observer); - void removeObserver(LLVoiceClientParticipantObserver* observer); + std::string sipURIFromID(const LLUUID &id); - void addObserver(LLVoiceClientStatusObserver* observer); - void removeObserver(LLVoiceClientStatusObserver* observer); + ////////////////////////// + /// @name Voice effects + //@{ + bool getVoiceEffectEnabled() const { return mVoiceEffectEnabled; }; + LLUUID getVoiceEffectDefault() const { return LLUUID(mVoiceEffectDefault); }; - void addObserver(LLFriendObserver* observer); - void removeObserver(LLFriendObserver* observer); - - void lookupName(const LLUUID &id); - static void onAvatarNameLookup(const LLUUID& id, const std::string& full_name); - void avatarNameResolved(const LLUUID &id, const std::string &name); - - typedef std::vector deviceList; + // Returns NULL if voice effects are not supported, or not enabled. + LLVoiceEffectInterface* getVoiceEffectInterface() const; + //@} - deviceList *getCaptureDevices(); - deviceList *getRenderDevices(); - - void setNonSpatialChannel( - const std::string &uri, - const std::string &credentials); - void setSpatialChannel( - const std::string &uri, - const std::string &credentials); - // start a voice session with the specified user - void callUser(const LLUUID &uuid); - - // Send a text message to the specified user, initiating the session if necessary. - bool sendTextMessage(const LLUUID& participant_id, const std::string& message); - - // close any existing text IM session with the specified user - void endUserIMSession(const LLUUID &uuid); - - bool answerInvite(std::string &sessionHandle); - void declineInvite(std::string &sessionHandle); - void leaveNonSpatialChannel(); +protected: + LLVoiceModuleInterface* mVoiceModule; + LLPumpIO *m_servicePump; - // Returns the URI of the current channel, or an empty string if not currently in a channel. - // NOTE that it will return an empty string if it's in the process of joining a channel. - std::string getCurrentChannel(); - - // returns true iff the user is currently in a proximal (local spatial) channel. - // Note that gestures should only fire if this returns true. - bool inProximalChannel(); + LLCachedControl mVoiceEffectEnabled; + LLCachedControl mVoiceEffectDefault; - std::string sipURIFromID(const LLUUID &id); - - // Returns true if the indicated user is online via SIP presence according to SLVoice. - // Note that we only get SIP presence data for other users that are in our vivox buddy list. - bool isOnlineSIP(const LLUUID &id); + bool mPTTDirty; + bool mPTT; - // Returns true if the indicated participant is really an SL avatar. - // This should be used to control the state of the "profile" button. - // Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. - bool isParticipantAvatar(const LLUUID &id); - - // Returns true if calling back the session URI after the session has closed is possible. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. - bool isSessionCallBackPossible(const LLUUID &session_id); - - // Returns true if the session can accepte text IM's. - // Currently this will be false only for PSTN P2P calls. - // NOTE: this will return true if the session can't be found. - bool isSessionTextIMPossible(const LLUUID &session_id); - - private: - - // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. - // Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string(). - enum state - { - stateDisableCleanup, - stateDisabled, // Voice is turned off. - stateStart, // Class is initialized, socket is created - stateDaemonLaunched, // Daemon has been launched - stateConnecting, // connect() call has been issued - stateConnected, // connection to the daemon has been made, send some initial setup commands. - stateIdle, // socket is connected, ready for messaging - stateMicTuningStart, - stateMicTuningRunning, - stateMicTuningStop, - stateConnectorStart, // connector needs to be started - stateConnectorStarting, // waiting for connector handle - stateConnectorStarted, // connector handle received - stateLoginRetry, // need to retry login (failed due to changing password) - stateLoginRetryWait, // waiting for retry timer - stateNeedsLogin, // send login request - stateLoggingIn, // waiting for account handle - stateLoggedIn, // account handle received - stateCreatingSessionGroup, // Creating the main session group - stateNoChannel, // - stateJoiningSession, // waiting for session handle - stateSessionJoined, // session handle received - stateRunning, // in session, steady state - stateLeavingSession, // waiting for terminate session response - stateSessionTerminated, // waiting for terminate session response - - stateLoggingOut, // waiting for logout response - stateLoggedOut, // logout response received - stateConnectorStopping, // waiting for connector stop - stateConnectorStopped, // connector stop received - - // We go to this state if the login fails because the account needs to be provisioned. - - // error states. No way to recover from these yet. - stateConnectorFailed, - stateConnectorFailedWaiting, - stateLoginFailed, - stateLoginFailedWaiting, - stateJoinSessionFailed, - stateJoinSessionFailedWaiting, - - stateJail // Go here when all else has failed. Nothing will be retried, we're done. - }; - - state mState; - bool mSessionTerminateRequested; - bool mRelogRequested; - - void setState(state inState); - state getState(void) { return mState; }; - static std::string state2string(state inState); - - void stateMachine(); - static void idle(void *user_data); - - LLHost mDaemonHost; - LLSocket::ptr_t mSocket; - bool mConnected; - - void closeSocket(void); - - LLPumpIO *mPump; - friend class LLVivoxProtocolParser; - - std::string mAccountName; - std::string mAccountPassword; - std::string mAccountDisplayName; - std::string mAccountFirstName; - std::string mAccountLastName; - - bool mTuningMode; - float mTuningEnergy; - std::string mTuningAudioFile; - int mTuningMicVolume; - bool mTuningMicVolumeDirty; - int mTuningSpeakerVolume; - bool mTuningSpeakerVolumeDirty; - state mTuningExitState; // state to return to when we leave tuning mode. - - std::string mSpatialSessionURI; - std::string mSpatialSessionCredentials; - - std::string mMainSessionGroupHandle; // handle of the "main" session group. - - std::string mChannelName; // Name of the channel to be looked up - bool mAreaVoiceDisabled; - sessionState *mAudioSession; // Session state for the current audio session - bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. - - sessionState *mNextAudioSession; // Session state for the audio session we're trying to join - -// std::string mSessionURI; // URI of the session we're in. -// std::string mSessionHandle; // returned by ? - - S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings - std::string mCurrentRegionName; // Used to detect parcel boundary crossings - - std::string mConnectorHandle; // returned by "Create Connector" message - std::string mAccountHandle; // returned by login message - int mNumberOfAliases; - U32 mCommandCookie; - - std::string mVoiceAccountServerURI; - std::string mVoiceSIPURIHostName; - - int mLoginRetryCount; - - sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. - sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. - - bool mBuddyListMapPopulated; - bool mBlockRulesListReceived; - bool mAutoAcceptRulesListReceived; - buddyListMap mBuddyListMap; - - deviceList mCaptureDevices; - deviceList mRenderDevices; - - std::string mCaptureDevice; - std::string mRenderDevice; - bool mCaptureDeviceDirty; - bool mRenderDeviceDirty; - - // This should be called when the code detects we have changed parcels. - // It initiates the call to the server that gets the parcel channel. - void parcelChanged(); - - void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); - void joinSession(sessionState *session); - -static std::string nameFromAvatar(LLVOAvatar *avatar); -static std::string nameFromID(const LLUUID &id); -static bool IDFromName(const std::string name, LLUUID &uuid); -static std::string displayNameFromAvatar(LLVOAvatar *avatar); - std::string sipURIFromAvatar(LLVOAvatar *avatar); - std::string sipURIFromName(std::string &name); - - // Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. -static std::string nameFromsipURI(const std::string &uri); - - bool inSpatialChannel(void); - std::string getAudioSessionURI(); - std::string getAudioSessionHandle(); - - void sendPositionalUpdate(void); - - void buildSetCaptureDevice(std::ostringstream &stream); - void buildSetRenderDevice(std::ostringstream &stream); - void buildLocalAudioUpdates(std::ostringstream &stream); - - void clearAllLists(); - void checkFriend(const LLUUID& id); - void sendFriendsListUpdates(); - - // start a text IM session with the specified user - // This will be asynchronous, the session may be established at a future time. - sessionState* startUserIMSession(const LLUUID& uuid); - void sendQueuedTextMessages(sessionState *session); - - void enforceTether(void); - - bool mSpatialCoordsDirty; - - LLVector3d mCameraPosition; - LLVector3d mCameraRequestedPosition; - LLVector3 mCameraVelocity; - LLMatrix3 mCameraRot; - - LLVector3d mAvatarPosition; - LLVector3 mAvatarVelocity; - LLMatrix3 mAvatarRot; - - bool mPTTDirty; - bool mPTT; - - bool mUsePTT; - bool mPTTIsMiddleMouse; - KEY mPTTKey; - bool mPTTIsToggle; - bool mUserPTTState; - bool mMuteMic; - - // Set to true when the friends list is known to have changed. - bool mFriendsListDirty; - - enum - { - earLocCamera = 0, // ear at camera - earLocAvatar, // ear at avatar - earLocMixed // ear at avatar location/camera direction - }; - - S32 mEarLocation; - - bool mSpeakerVolumeDirty; - bool mSpeakerMuteDirty; - int mSpeakerVolume; - - int mMicVolume; - bool mMicVolumeDirty; - - bool mVoiceEnabled; - bool mWriteInProgress; - std::string mWriteString; - size_t mWriteOffset; - - LLTimer mUpdateTimer; - - BOOL mLipSyncEnabled; - - typedef std::set observer_set_t; - observer_set_t mParticipantObservers; - - void notifyParticipantObservers(); - - typedef std::set status_observer_set_t; - status_observer_set_t mStatusObservers; - - void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); - - typedef std::set friend_observer_set_t; - friend_observer_set_t mFriendObservers; - void notifyFriendObservers(); + bool mUsePTT; + bool mPTTIsMiddleMouse; + KEY mPTTKey; + bool mPTTIsToggle; + bool mUserPTTState; + bool mMuteMic; + bool mDisableMic; }; -extern LLVoiceClient *gVoiceClient; +/** + * Speaker volume storage helper class + **/ +class LLSpeakerVolumeStorage : public LLSingleton +{ + LOG_CLASS(LLSpeakerVolumeStorage); +public: + + /** + * Stores volume level for specified user. + * + * @param[in] speaker_id - LLUUID of user to store volume level for. + * @param[in] volume - volume level to be stored for user. + */ + void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume); + + /** + * Gets stored volume level for specified speaker + * + * @param[in] speaker_id - LLUUID of user to retrieve volume level for. + * @param[out] volume - set to stored volume if found, otherwise unmodified. + * @return - true if a stored volume is found. + */ + bool getSpeakerVolume(const LLUUID& speaker_id, F32& volume); + + /** + * Removes stored volume level for specified user. + * + * @param[in] speaker_id - LLUUID of user to remove. + */ + void removeSpeakerVolume(const LLUUID& speaker_id); + +private: + friend class LLSingleton; + LLSpeakerVolumeStorage(); + ~LLSpeakerVolumeStorage(); + + const static std::string SETTINGS_FILE_NAME; + + void load(); + void save(); + + static F32 transformFromLegacyVolume(F32 volume_in); + static F32 transformToLegacyVolume(F32 volume_in); + + typedef std::map speaker_data_map_t; + speaker_data_map_t mSpeakersData; +}; #endif //LL_VOICE_CLIENT_H diff --git a/indra/newview/llvoiceremotectrl.cpp b/indra/newview/llvoiceremotectrl.cpp index 725402b7a..1863020f9 100644 --- a/indra/newview/llvoiceremotectrl.cpp +++ b/indra/newview/llvoiceremotectrl.cpp @@ -37,9 +37,8 @@ #include "llui.h" #include "llbutton.h" #include "lluictrlfactory.h" -#include "llviewercontrol.h" +#include "llvoicechannel.h" #include "llvoiceclient.h" -#include "llimpanel.h" #include "llfloateractivespeakers.h" #include "llfloaterchatterbox.h" #include "lliconctrl.h" @@ -114,15 +113,15 @@ void LLVoiceRemoteCtrl::draw() if (!mTalkBtn->hasMouseCapture()) { // not in push to talk mode, or push to talk is active means I'm talking - mTalkBtn->setToggleState(!ptt_currently_enabled || gVoiceClient->getUserPTTState()); + mTalkBtn->setToggleState(!ptt_currently_enabled || LLVoiceClient::getInstance()->getUserPTTState()); } mSpeakersBtn->setToggleState(LLFloaterActiveSpeakers::instanceVisible(LLSD())); mTalkLockBtn->setToggleState(!ptt_currently_enabled); std::string talk_blip_image; - if (gVoiceClient->getIsSpeaking(gAgent.getID())) + if (LLVoiceClient::getInstance()->getIsSpeaking(gAgent.getID())) { - F32 voice_power = gVoiceClient->getCurrentPower(gAgent.getID()); + F32 voice_power = LLVoiceClient::getInstance()->getCurrentPower(gAgent.getID()); if (voice_power > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) { @@ -130,7 +129,7 @@ void LLVoiceRemoteCtrl::draw() } else { - F32 power = gVoiceClient->getCurrentPower(gAgent.getID()); + F32 power = LLVoiceClient::getInstance()->getCurrentPower(gAgent.getID()); S32 icon_image_idx = llmin(2, llfloor((power / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); switch(icon_image_idx) @@ -159,15 +158,19 @@ void LLVoiceRemoteCtrl::draw() } LLFloater* voice_floater = LLFloaterChatterBox::getInstance()->getCurrentVoiceFloater(); + LLVoiceChannel* current_channel = LLVoiceChannel::getCurrentVoiceChannel(); + if (!voice_floater) // Maybe it's undocked + { + voice_floater = gIMMgr->findFloaterBySession(current_channel->getSessionID()); + } std::string active_channel_name; if (voice_floater) { active_channel_name = voice_floater->getShortTitle(); } - LLVoiceChannel* current_channel = LLVoiceChannel::getCurrentVoiceChannel(); if (LLButton* end_call_btn = findChild("end_call_btn")) - end_call_btn->setEnabled(LLVoiceClient::voiceEnabled() + end_call_btn->setEnabled(LLVoiceClient::getInstance()->voiceEnabled() && current_channel && current_channel->isActive() && current_channel != LLVoiceChannelProximal::getInstance()); @@ -225,7 +228,7 @@ void LLVoiceRemoteCtrl::onBtnTalkClicked() // when in toggle mode, clicking talk button turns mic on/off if (gSavedSettings.getBOOL("PushToTalkToggle")) { - gVoiceClient->toggleUserPTTState(); + LLVoiceClient::getInstance()->toggleUserPTTState(); } } @@ -234,7 +237,7 @@ void LLVoiceRemoteCtrl::onBtnTalkHeld() // when not in toggle mode, holding down talk button turns on mic if (!gSavedSettings.getBOOL("PushToTalkToggle")) { - gVoiceClient->setUserPTTState(true); + LLVoiceClient::getInstance()->setUserPTTState(true); } } @@ -243,7 +246,7 @@ void LLVoiceRemoteCtrl::onBtnTalkReleased() // when not in toggle mode, releasing talk button turns off mic if (!gSavedSettings.getBOOL("PushToTalkToggle")) { - gVoiceClient->setUserPTTState(false); + LLVoiceClient::getInstance()->setUserPTTState(false); } } @@ -291,5 +294,16 @@ void LLVoiceRemoteCtrl::onClickSpeakers() //static void LLVoiceRemoteCtrl::onClickVoiceChannel() { - LLFloaterChatterBox::showInstance(); + if (LLFloater* floater = LLFloaterChatterBox::getInstance()->getCurrentVoiceFloater()) + { + if (LLMultiFloater* mf = floater->getHost()) // Docked + mf->showFloater(floater); + else // Probably only local chat + floater->open(); + } + else if (LLVoiceChannel* chan = LLVoiceChannel::getCurrentVoiceChannel()) // Detached chat floater + { + if (LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(chan->getSessionID())) + floater->open(); + } } diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp new file mode 100644 index 000000000..d74c3a061 --- /dev/null +++ b/indra/newview/llvoicevivox.cpp @@ -0,0 +1,7849 @@ + /** + * @file LLVivoxVoiceClient.cpp + * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llvoicevivox.h" +#if LL_LINUX && defined(LL_STANDALONE) +#include // g_find_program_in_path +#endif + +#include "llsdutil.h" + +// Linden library includes +#include "llavatarnamecache.h" +#include "llvoavatarself.h" +#include "llbufferstream.h" +#include "llcallbacklist.h" +#include "llbase64.h" +#include "llappviewer.h" // for gDisconnected, gDisableVoice + +// Viewer includes +#include "llmutelist.h" // to check for muted avatars +#include "llagent.h" +#include "llimpanel.h" // for LLFloaterIMPanel +#include "llimview.h" // for LLIMMgr +#include "llparcel.h" +#include "llviewerparcelmgr.h" +//#include "llfirstuse.h" +#include "llspeakers.h" +#include "lltrans.h" +#include "llviewercamera.h" + +#include "llviewernetwork.h" +#include "llvoicechannel.h" +#include "llnotificationsutil.h" + +#include "stringize.h" + +// for base64 decoding +#include "apr_base64.h" + +extern AIHTTPTimeoutPolicy vivoxVoiceAccountProvisionResponder_timeout; +extern AIHTTPTimeoutPolicy vivoxVoiceClientCapResponder_timeout; + +#define USE_SESSION_GROUPS 0 + +const F32 VOLUME_SCALE_VIVOX = 0.01f; + +const F32 SPEAKING_TIMEOUT = 1.f; + +static const std::string VOICE_SERVER_TYPE = "Vivox"; + +// Don't retry connecting to the daemon more frequently than this: +const F32 CONNECT_THROTTLE_SECONDS = 1.0f; + +// Don't send positional updates more frequently than this: +const F32 UPDATE_THROTTLE_SECONDS = 0.1f; + +const F32 LOGIN_RETRY_SECONDS = 10.0f; +const int MAX_LOGIN_RETRIES = 12; + +// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() +// which is treated as normal. If this number is exceeded we suspect there is a problem with connection +// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen +// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is +// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. +const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; + +// How often to check for expired voice fonts in seconds +const F32 VOICE_FONT_EXPIRY_INTERVAL = 10.f; +// Time of day at which Vivox expires voice font subscriptions. +// Used to replace the time portion of received expiry timestamps. +static const std::string VOICE_FONT_EXPIRY_TIME = "T05:00:00Z"; + +// Maximum length of capture buffer recordings in seconds. +const F32 CAPTURE_BUFFER_MAX_TIME = 10.f; + + +static int scale_mic_volume(float volume) +{ + // incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. + // Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 + return 30 + (int)(volume * 20.0f); +} + +static int scale_speaker_volume(float volume) +{ + // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. + // Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 + return 30 + (int)(volume * 40.0f); + +} + +class LLVivoxVoiceAccountProvisionResponder : + public LLHTTPClient::ResponderWithResult +{ +public: + LLVivoxVoiceAccountProvisionResponder(int retries) + { + mRetries = retries; + } + + /*virtual*/ void error(U32 status, const std::string& reason) + { + LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, " + << ( (mRetries > 0) ? "retrying" : "too many retries (giving up)" ) + << status << "]: " << reason << LL_ENDL; + + if ( mRetries > 0 ) + { + LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision(mRetries - 1); + } + else + { + LLVivoxVoiceClient::getInstance()->giveUp(); + } + } + + /*virtual*/ void result(const LLSD& content) + { + + std::string voice_sip_uri_hostname; + std::string voice_account_server_uri; + + LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; + + if(content.has("voice_sip_uri_hostname")) + voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); + + // this key is actually misnamed -- it will be an entire URI, not just a hostname. + if(content.has("voice_account_server_name")) + voice_account_server_uri = content["voice_account_server_name"].asString(); + + LLVivoxVoiceClient::getInstance()->login( + content["username"].asString(), + content["password"].asString(), + voice_sip_uri_hostname, + voice_account_server_uri); + + } + + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return vivoxVoiceAccountProvisionResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "LLVivoxVoiceAccountProvisionResponder"; } + +private: + int mRetries; +}; + + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLVivoxVoiceClientMuteListObserver : public LLMuteListObserver +{ + /* virtual */ void onChange() { LLVivoxVoiceClient::getInstance()->muteListChanged();} +}; + +class LLVivoxVoiceClientFriendsObserver : public LLFriendObserver +{ +public: + /* virtual */ void changed(U32 mask) { LLVivoxVoiceClient::getInstance()->updateFriends(mask);} +}; + +static LLVivoxVoiceClientMuteListObserver mutelist_listener; +static bool sMuteListListener_listening = false; + +static LLVivoxVoiceClientFriendsObserver *friendslist_listener = NULL; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLVivoxVoiceClientCapResponder : public LLHTTPClient::ResponderWithResult +{ +public: + LLVivoxVoiceClientCapResponder(LLVivoxVoiceClient::state requesting_state) : mRequestingState(requesting_state) {}; + + /*virtual*/ void error(U32 status, const std::string& reason); // called with bad status codes + /*virtual*/ void result(const LLSD& content); + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return vivoxVoiceClientCapResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "LLVivoxVoiceClientCapResponder"; } + +private: + LLVivoxVoiceClient::state mRequestingState; // state +}; + +void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason) +{ + LL_WARNS("Voice") << "LLVivoxVoiceClientCapResponder error [status:" + << status << "]: " << reason << LL_ENDL; + LLVivoxVoiceClient::getInstance()->sessionTerminate(); +} + +void LLVivoxVoiceClientCapResponder::result(const LLSD& content) +{ + LLSD::map_const_iterator iter; + + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; + + std::string uri; + std::string credentials; + + if ( content.has("voice_credentials") ) + { + LLSD voice_credentials = content["voice_credentials"]; + if ( voice_credentials.has("channel_uri") ) + { + uri = voice_credentials["channel_uri"].asString(); + } + if ( voice_credentials.has("channel_credentials") ) + { + credentials = + voice_credentials["channel_credentials"].asString(); + } + } + + // set the spatial channel. If no voice credentials or uri are + // available, then we simply drop out of voice spatially. + if(LLVivoxVoiceClient::getInstance()->parcelVoiceInfoReceived(mRequestingState)) + { + LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials); + } +} + +#if LL_WINDOWS +static HANDLE sGatewayHandle = 0; + +static bool isGatewayRunning() +{ + bool result = false; + if (sGatewayHandle != 0) + { + DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); + if (waitresult != WAIT_OBJECT_0) + { + result = true; + } + } + return result; +} + +static void killGateway() +{ + if (sGatewayHandle != 0) + { + TerminateProcess(sGatewayHandle,0); + } +} + +#else // Mac and linux + +static pid_t sGatewayPID = 0; + +static bool isGatewayRunning() +{ + bool result = false; + if (sGatewayPID != 0) + { + // A kill with signal number 0 has no effect, just does error checking. It should return an error if the process no longer exists. + if(kill(sGatewayPID, 0) == 0) + { + result = true; + } + } + return result; +} + +static void killGateway() +{ + if (sGatewayPID != 0) + { + kill(sGatewayPID, SIGTERM); + } +} + +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////// + +LLVivoxVoiceClient::LLVivoxVoiceClient() : + mState(stateDisabled), + mSessionTerminateRequested(false), + mRelogRequested(false), + mConnected(false), + mPump(NULL), + mSpatialJoiningNum(0), + + mTuningMode(false), + mTuningEnergy(0.0f), + mTuningMicVolume(0), + mTuningMicVolumeDirty(true), + mTuningSpeakerVolume(0), + mTuningSpeakerVolumeDirty(true), + mTuningExitState(stateDisabled), + + mAreaVoiceDisabled(false), + mAudioSession(NULL), + mAudioSessionChanged(false), + mNextAudioSession(NULL), + + mCurrentParcelLocalID(0), + mNumberOfAliases(0), + mCommandCookie(0), + mLoginRetryCount(0), + + mBuddyListMapPopulated(false), + mBlockRulesListReceived(false), + mAutoAcceptRulesListReceived(false), + + mCaptureDeviceDirty(false), + mRenderDeviceDirty(false), + mSpatialCoordsDirty(false), + + mMuteMic(false), + mMuteMicDirty(false), + mFriendsListDirty(true), + + mEarLocation(0), + mSpeakerVolumeDirty(true), + mSpeakerMuteDirty(true), + mMicVolume(0), + mMicVolumeDirty(true), + + mVoiceEnabled(false), + mWriteInProgress(false), + + mLipSyncEnabled(false), + + mVoiceFontsReceived(false), + mVoiceFontsNew(false), + mVoiceFontListDirty(false), + + mCaptureBufferMode(false), + mCaptureBufferRecording(false), + mCaptureBufferRecorded(false), + mCaptureBufferPlaying(false), + mPlayRequestCount(0) +{ + mSpeakerVolume = scale_speaker_volume(0); + + mVoiceVersion.serverVersion = ""; + mVoiceVersion.serverType = VOICE_SERVER_TYPE; + + // gMuteListp isn't set up at this point, so we defer this until later. +// gMuteListp->addObserver(&mutelist_listener); + + +#if LL_DARWIN || LL_LINUX || LL_SOLARIS + // HACK: THIS DOES NOT BELONG HERE + // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. + // This should cause us to ignore SIGPIPE and handle the error through proper channels. + // This should really be set up elsewhere. Where should it go? + signal(SIGPIPE, SIG_IGN); + + // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. + // Ignoring SIGCHLD should prevent zombies from being created. Alternately, we could use wait(), but I'd rather not do that. + signal(SIGCHLD, SIG_IGN); +#endif + + // set up state machine + setState(stateDisabled); + + gIdleCallbacks.addFunction(idle, this); +} + +//--------------------------------------------------- + +LLVivoxVoiceClient::~LLVivoxVoiceClient() +{ +} + +//--------------------------------------------------- + +void LLVivoxVoiceClient::init(LLPumpIO *pump) +{ + // constructor will set up LLVoiceClient::getInstance() + LLVivoxVoiceClient::getInstance()->mPump = pump; +} + +void LLVivoxVoiceClient::terminate() +{ + if(mConnected) + { + logout(); + connectorShutdown(); + closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. + cleanUp(); + } + else + { + killGateway(); + } +} + +//--------------------------------------------------- + +void LLVivoxVoiceClient::cleanUp() +{ + deleteAllSessions(); + deleteAllBuddies(); + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); +} + +//--------------------------------------------------- + +const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion() +{ + return mVoiceVersion; +} + +//--------------------------------------------------- + +void LLVivoxVoiceClient::updateSettings() +{ + setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); + setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + + std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); + setCaptureDevice(inputDevice); + std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); + setRenderDevice(outputDevice); + F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); + setMicGain(mic_level); + setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); +} + +///////////////////////////// +// utility functions + +bool LLVivoxVoiceClient::writeString(const std::string &str) +{ + bool result = false; + if(mConnected) + { + apr_status_t err; + apr_size_t size = (apr_size_t)str.size(); + apr_size_t written = size; + + //MARK: Turn this on to log outgoing XML +// LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; + + // check return code - sockets will fail (broken, etc.) + err = apr_socket_send( + mSocket->getSocket(), + (const char*)str.data(), + &written); + + if(err == 0) + { + // Success. + result = true; + } + // TODO: handle partial writes (written is number of bytes written) + // Need to set socket to non-blocking before this will work. +// else if(APR_STATUS_IS_EAGAIN(err)) +// { +// // +// } + else + { + // Assume any socket error means something bad. For now, just close the socket. + char buf[MAX_STRING]; + LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; + daemonDied(); + } + } + + return result; +} + + +///////////////////////////// +// session control messages +void LLVivoxVoiceClient::connectorCreate() +{ + std::ostringstream stream; + std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); + std::string loglevel = "0"; + + // Transition to stateConnectorStarted when the connector handle comes back. + setState(stateConnectorStarting); + + std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); + + if(savedLogLevel != "-1") + { + LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; + loglevel = "10"; + } + + stream + << "" + << "V2 SDK" + << "" << mVoiceAccountServerURI << "" + << "Normal"; + + if (gSavedSettings.getBOOL("VoiceMultiInstance")) + { + stream + << "30000" + << "50000"; + } + + stream + << "" + << "" << logpath << "" + << "Connector" + << ".log" + << "" << loglevel << "" + << "" + << "SecondLifeViewer.1" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::connectorShutdown() +{ + setState(stateConnectorStopping); + + if(!mConnectorHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mConnectorHandle << "" + << "" + << "\n\n\n"; + + mConnectorHandle.clear(); + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +{ + + mAccountDisplayName = user_id; + + LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; + + mAccountName = nameFromID(agentID); +} + +void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) +{ + LLViewerRegion *region = gAgent.getRegion(); + + if ( region && mVoiceEnabled ) + { + std::string url = + region->getCapability("ProvisionVoiceAccountRequest"); + + if ( url.empty() ) + { + // we've not received the capability yet, so return. + // the password will remain empty, so we'll remain in + // stateIdle + return; + } + + LLHTTPClient::post( + url, + LLSD(), + new LLVivoxVoiceAccountProvisionResponder(retries)); + + setState(stateConnectorStart); + } +} + +void LLVivoxVoiceClient::login( + const std::string& account_name, + const std::string& password, + const std::string& voice_sip_uri_hostname, + const std::string& voice_account_server_uri) +{ + mVoiceSIPURIHostName = voice_sip_uri_hostname; + mVoiceAccountServerURI = voice_account_server_uri; + + if(!mAccountHandle.empty()) + { + // Already logged in. + LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; + + // Don't process another login. + return; + } + else if ( account_name != mAccountName ) + { + //TODO: error? + LL_WARNS("Voice") << "Wrong account name! " << account_name + << " instead of " << mAccountName << LL_ENDL; + } + else + { + mAccountPassword = password; + } + + std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); + + if( !debugSIPURIHostName.empty() ) + { + mVoiceSIPURIHostName = debugSIPURIHostName; + } + + if( mVoiceSIPURIHostName.empty() ) + { + // we have an empty account server name + // so we fall back to hardcoded defaults + + if(LLViewerLogin::getInstance()->isInProductionGrid()) + { + // Use the release account server + mVoiceSIPURIHostName = "bhr.vivox.com"; + } + else + { + // Use the development account server + mVoiceSIPURIHostName = "bhd.vivox.com"; + } + } + + std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); + + if( !debugAccountServerURI.empty() ) + { + mVoiceAccountServerURI = debugAccountServerURI; + } + + if( mVoiceAccountServerURI.empty() ) + { + // If the account server URI isn't specified, construct it from the SIP URI hostname + mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/"; + } +} + +void LLVivoxVoiceClient::idle(void* user_data) +{ + LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data; + self->stateMachine(); +} + +std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) +{ + std::string result = "UNKNOWN"; + + // Prevent copy-paste errors when updating this list... +#define CASE(x) case x: result = #x; break + + switch(inState) + { + CASE(stateDisableCleanup); + CASE(stateDisabled); + CASE(stateStart); + CASE(stateDaemonLaunched); + CASE(stateConnecting); + CASE(stateConnected); + CASE(stateIdle); + CASE(stateMicTuningStart); + CASE(stateMicTuningRunning); + CASE(stateMicTuningStop); + CASE(stateCaptureBufferPaused); + CASE(stateCaptureBufferRecStart); + CASE(stateCaptureBufferRecording); + CASE(stateCaptureBufferPlayStart); + CASE(stateCaptureBufferPlaying); + CASE(stateConnectorStart); + CASE(stateConnectorStarting); + CASE(stateConnectorStarted); + CASE(stateLoginRetry); + CASE(stateLoginRetryWait); + CASE(stateNeedsLogin); + CASE(stateLoggingIn); + CASE(stateLoggedIn); + CASE(stateVoiceFontsWait); + CASE(stateVoiceFontsReceived); + CASE(stateCreatingSessionGroup); + CASE(stateNoChannel); + CASE(stateRetrievingParcelVoiceInfo); + CASE(stateJoiningSession); + CASE(stateSessionJoined); + CASE(stateRunning); + CASE(stateLeavingSession); + CASE(stateSessionTerminated); + CASE(stateLoggingOut); + CASE(stateLoggedOut); + CASE(stateConnectorStopping); + CASE(stateConnectorStopped); + CASE(stateConnectorFailed); + CASE(stateConnectorFailedWaiting); + CASE(stateLoginFailed); + CASE(stateLoginFailedWaiting); + CASE(stateJoinSessionFailed); + CASE(stateJoinSessionFailedWaiting); + CASE(stateJail); + } + +#undef CASE + + return result; +} + + + +void LLVivoxVoiceClient::setState(state inState) +{ + LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; + + mState = inState; +} + +void LLVivoxVoiceClient::stateMachine() +{ + if(gDisconnected) + { + // The viewer has been disconnected from the sim. Disable voice. + setVoiceEnabled(false); + } + + if(mVoiceEnabled) + { + updatePosition(); + } + else if(mTuningMode) + { + // Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. + } + else + { + if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) + { + // User turned off voice support. Send the cleanup messages, close the socket, and reset. + if(!mConnected) + { + // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. + LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; + killGateway(); + } + + logout(); + connectorShutdown(); + + setState(stateDisableCleanup); + } + } + + + switch(getState()) + { + //MARK: stateDisableCleanup + case stateDisableCleanup: + // Clean up and reset everything. + closeSocket(); + cleanUp(); + + mAccountHandle.clear(); + mAccountPassword.clear(); + mVoiceAccountServerURI.clear(); + + setState(stateDisabled); + break; + + //MARK: stateDisabled + case stateDisabled: + if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) + { + setState(stateStart); + } + break; + + //MARK: stateStart + case stateStart: + if(gSavedSettings.getBOOL("CmdLineDisableVoice")) + { + // Voice is locked out, we must not launch the vivox daemon. + setState(stateJail); + } + else if(!isGatewayRunning()) + { + if (true) // production build, not test + { + // Launch the voice daemon + +#if LL_LINUX && defined(LL_STANDALONE) + // Look for the vivox daemon in the executable path list + // using glib first. + char *voice_path = g_find_program_in_path ("SLVoice"); + std::string exe_path; + if (voice_path) { + exe_path = llformat("%s", voice_path); + free(voice_path); + } else { + exe_path = gDirUtilp->getExecutableDir() + gDirUtilp->getDirDelimiter() + "SLVoice"; + } +#else + // *FIX:Mani - Using the executable dir instead + // of mAppRODataDir, the working directory from which the app + // is launched. + //std::string exe_path = gDirUtilp->getAppRODataDir(); + std::string exe_path = gDirUtilp->getExecutableDir(); + exe_path += gDirUtilp->getDirDelimiter(); +#if LL_WINDOWS + exe_path += "SLVoice.exe"; +#elif LL_DARWIN + exe_path += "../Resources/SLVoice"; +#else + exe_path += "SLVoice"; +#endif +#endif + // See if the vivox executable exists + llstat s; + if (!LLFile::stat(exe_path, &s)) + { + // vivox executable exists. Build the command line and launch the daemon. + std::string args, cmd; + // SLIM SDK: these arguments are no longer necessary. +// std::string args = " -p tcp -h -c"; + std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); + + // If we allow multiple instances of the viewer to start the voice + // daemon, set TEMPORARY random voice port + if (gSavedSettings.getBOOL("VoiceMultiInstance")) + { + LLControlVariable* voice_port = gSavedSettings.getControl("VoicePort"); + if (voice_port) + { + const BOOL DO_NOT_PERSIST = FALSE; + S32 port_nr = 30000 + ll_rand(20000); + voice_port->setValue(LLSD(port_nr), DO_NOT_PERSIST); + } + } + + if(loglevel.empty()) + { + loglevel = "-1"; // turn logging off completely + } + + args += " -ll "; + args += loglevel; + + // Tell voice gateway to listen to a specific port + if (gSavedSettings.getBOOL("VoiceMultiInstance")) + { + args += llformat(" -i 127.0.0.1:%u", gSavedSettings.getU32("VoicePort")); + } + + LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL; + +#if LL_WINDOWS + PROCESS_INFORMATION pinfo; + STARTUPINFOA sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + std::string exe_dir = gDirUtilp->getAppRODataDir(); + cmd = "SLVoice.exe"; + cmd += args; + + // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string... + char *args2 = new char[args.size() + 1]; + strcpy(args2, args.c_str()); + + if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo)) + { +// DWORD dwErr = GetLastError(); + } + else + { + // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on + // CloseHandle(pinfo.hProcess); // stops leaks - nothing else + sGatewayHandle = pinfo.hProcess; + CloseHandle(pinfo.hThread); // stops leaks - nothing else + } + + delete[] args2; +#else // LL_WINDOWS + // This should be the same for mac and linux + { + std::vector arglist; + arglist.push_back(exe_path); + + // Split the argument string into separate strings for each argument + typedef boost::tokenizer > tokenizer; + boost::char_separator sep(" "); + tokenizer tokens(args, sep); + tokenizer::iterator token_iter; + + for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + arglist.push_back(*token_iter); + } + + // create an argv vector for the child process + char **fakeargv = new char*[arglist.size() + 1]; + int i; + for(i=0; i < arglist.size(); i++) + fakeargv[i] = const_cast(arglist[i].c_str()); + + fakeargv[i] = NULL; + + fflush(NULL); // flush all buffers before the child inherits them + pid_t id = vfork(); + if(id == 0) + { + // child + execv(exe_path.c_str(), fakeargv); + + // If we reach this point, the exec failed. + // Use _exit() instead of exit() per the vfork man page. + _exit(0); + } + + // parent + delete[] fakeargv; + sGatewayPID = id; + } +#endif // LL_WINDOWS + + mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort")); + } + else + { + LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; + } + } + else + { + // SLIM SDK: port changed from 44124 to 44125. + // We can connect to a client gateway running on another host. This is useful for testing. + // To do this, launch the gateway on a nearby host like this: + // vivox-gw.exe -p tcp -i 0.0.0.0:44125 + // and put that host's IP address here. + mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort")); + } + + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + + setState(stateDaemonLaunched); + + // Dirty the states we'll need to sync with the daemon when it comes up. + mMuteMicDirty = true; + mMicVolumeDirty = true; + mSpeakerVolumeDirty = true; + mSpeakerMuteDirty = true; + // These only need to be set if they're not default (i.e. empty string). + mCaptureDeviceDirty = !mCaptureDevice.empty(); + mRenderDeviceDirty = !mRenderDevice.empty(); + + mMainSessionGroupHandle.clear(); + } + break; + + //MARK: stateDaemonLaunched + case stateDaemonLaunched: + if(mUpdateTimer.hasExpired()) + { + LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; + + mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + + if(!mSocket) + { + mSocket = LLSocket::create(LLSocket::STREAM_TCP); + } + + mConnected = mSocket->blockingConnect(mDaemonHost); + if(mConnected) + { + setState(stateConnecting); + } + else + { + // If the connect failed, the socket may have been put into a bad state. Delete it. + closeSocket(); + } + } + break; + + //MARK: stateConnecting + case stateConnecting: + // Can't do this until we have the pump available. + if(mPump) + { + // MBW -- Note to self: pumps and pipes examples in + // indra/test/io.cpp + // indra/test/llpipeutil.{cpp|h} + + // Attach the pumps and pipes + + LLPumpIO::chain_t readChain; + + readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); + readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); + + mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); + + setState(stateConnected); + } + + break; + + //MARK: stateConnected + case stateConnected: + // Initial devices query + getCaptureDevicesSendMessage(); + getRenderDevicesSendMessage(); + + mLoginRetryCount = 0; + + setState(stateIdle); + break; + + //MARK: stateIdle + case stateIdle: + // This is the idle state where we're connected to the daemon but haven't set up a connector yet. + if(mTuningMode) + { + mTuningExitState = stateIdle; + setState(stateMicTuningStart); + } + else if(!mVoiceEnabled) + { + // We never started up the connector. This will shut down the daemon. + setState(stateConnectorStopped); + } + else if(!mAccountName.empty()) + { + if ( mAccountPassword.empty() ) + { + requestVoiceAccountProvision(); + } + } + break; + + //MARK: stateMicTuningStart + case stateMicTuningStart: + if(mUpdateTimer.hasExpired()) + { + if(mCaptureDeviceDirty || mRenderDeviceDirty) + { + // These can't be changed while in tuning mode. Set them before starting. + std::ostringstream stream; + + buildSetCaptureDevice(stream); + buildSetRenderDevice(stream); + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + + // This will come around again in the same state and start the capture, after the timer expires. + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + } + else + { + // duration parameter is currently unused, per Mike S. + tuningCaptureStartSendMessage(10000); + + setState(stateMicTuningRunning); + } + } + + break; + + //MARK: stateMicTuningRunning + case stateMicTuningRunning: + if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) + { + // All of these conditions make us leave tuning mode. + setState(stateMicTuningStop); + } + else + { + // process mic/speaker volume changes + if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) + { + std::ostringstream stream; + + if(mTuningMicVolumeDirty) + { + LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; + stream + << "" + << "" << mTuningMicVolume << "" + << "\n\n\n"; + } + + if(mTuningSpeakerVolumeDirty) + { + stream + << "" + << "" << mTuningSpeakerVolume << "" + << "\n\n\n"; + } + + mTuningMicVolumeDirty = false; + mTuningSpeakerVolumeDirty = false; + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + } + } + break; + + //MARK: stateMicTuningStop + case stateMicTuningStop: + { + // transition out of mic tuning + tuningCaptureStopSendMessage(); + + setState(mTuningExitState); + + // if we exited just to change devices, this will keep us from re-entering too fast. + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + + } + break; + + //MARK: stateCaptureBufferPaused + case stateCaptureBufferPaused: + if (!mCaptureBufferMode) + { + // Leaving capture mode. + + mCaptureBufferRecording = false; + mCaptureBufferRecorded = false; + mCaptureBufferPlaying = false; + + // Return to stateNoChannel to trigger reconnection to a channel. + setState(stateNoChannel); + } + else if (mCaptureBufferRecording) + { + setState(stateCaptureBufferRecStart); + } + else if (mCaptureBufferPlaying) + { + setState(stateCaptureBufferPlayStart); + } + break; + + //MARK: stateCaptureBufferRecStart + case stateCaptureBufferRecStart: + captureBufferRecordStartSendMessage(); + + // Flag that something is recorded to allow playback. + mCaptureBufferRecorded = true; + + // Start the timer, recording will be stopped when it expires. + mCaptureTimer.start(); + mCaptureTimer.setTimerExpirySec(CAPTURE_BUFFER_MAX_TIME); + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferRecording); + break; + + //MARK: stateCaptureBufferRecording + case stateCaptureBufferRecording: + if (!mCaptureBufferMode || !mCaptureBufferRecording || + mCaptureBufferPlaying || mCaptureTimer.hasExpired()) + { + // Stop recording + captureBufferRecordStopSendMessage(); + mCaptureBufferRecording = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferPaused); + } + break; + + //MARK: stateCaptureBufferPlayStart + case stateCaptureBufferPlayStart: + captureBufferPlayStartSendMessage(mPreviewVoiceFont); + + // Store the voice font being previewed, so that we know to restart if it changes. + mPreviewVoiceFontLast = mPreviewVoiceFont; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferPlaying); + break; + + //MARK: stateCaptureBufferPlaying + case stateCaptureBufferPlaying: + if (mCaptureBufferPlaying && mPreviewVoiceFont != mPreviewVoiceFontLast) + { + // If the preview voice font changes, restart playing with the new font. + setState(stateCaptureBufferPlayStart); + } + else if (!mCaptureBufferMode || !mCaptureBufferPlaying || mCaptureBufferRecording) + { + // Stop playing. + captureBufferPlayStopSendMessage(); + mCaptureBufferPlaying = false; + + // Update UI, should really use a separate callback. + notifyVoiceFontObservers(); + + setState(stateCaptureBufferPaused); + } + break; + + //MARK: stateConnectorStart + case stateConnectorStart: + if(!mVoiceEnabled) + { + // We were never logged in. This will shut down the connector. + setState(stateLoggedOut); + } + else if(!mVoiceAccountServerURI.empty()) + { + connectorCreate(); + } + break; + + //MARK: stateConnectorStarting + case stateConnectorStarting: // waiting for connector handle + // connectorCreateResponse() will transition from here to stateConnectorStarted. + break; + + //MARK: stateConnectorStarted + case stateConnectorStarted: // connector handle received + if(!mVoiceEnabled) + { + // We were never logged in. This will shut down the connector. + setState(stateLoggedOut); + } + else + { + // The connector is started. Send a login message. + setState(stateNeedsLogin); + } + break; + + //MARK: stateLoginRetry + case stateLoginRetry: + if(mLoginRetryCount == 0) + { + // First retry -- display a message to the user + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); + } + + mLoginRetryCount++; + + if(mLoginRetryCount > MAX_LOGIN_RETRIES) + { + LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; + setState(stateLoginFailed); + LLSD args; + std::stringstream errs; + errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; + args["HOSTID"] = errs.str(); + LLNotificationsUtil::add("NoVoiceConnect", args); + } + else + { + LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); + setState(stateLoginRetryWait); + } + break; + + //MARK: stateLoginRetryWait + case stateLoginRetryWait: + if(mUpdateTimer.hasExpired()) + { + setState(stateNeedsLogin); + } + break; + + //MARK: stateNeedsLogin + case stateNeedsLogin: + if(!mAccountPassword.empty()) + { + setState(stateLoggingIn); + loginSendMessage(); + } + break; + + //MARK: stateLoggingIn + case stateLoggingIn: // waiting for account handle + // loginResponse() will transition from here to stateLoggedIn. + break; + + //MARK: stateLoggedIn + case stateLoggedIn: // account handle received + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + // Request the set of available voice fonts. + setState(stateVoiceFontsWait); + refreshVoiceEffectLists(true); + } + else + { + // If voice effects are disabled, pretend we've received them and carry on. + setState(stateVoiceFontsReceived); + } + + // request the current set of block rules (we'll need them when updating the friends list) + accountListBlockRulesSendMessage(); + + // request the current set of auto-accept rules + accountListAutoAcceptRulesSendMessage(); + + // Set up the mute list observer if it hasn't been set up already. + if((!sMuteListListener_listening)) + { + LLMuteList::getInstance()->addObserver(&mutelist_listener); + sMuteListListener_listening = true; + } + + // Set up the friends list observer if it hasn't been set up already. + if(friendslist_listener == NULL) + { + friendslist_listener = new LLVivoxVoiceClientFriendsObserver; + LLAvatarTracker::instance().addObserver(friendslist_listener); + } + + // Set the initial state of mic mute, local speaker volume, etc. + { + std::ostringstream stream; + + buildLocalAudioUpdates(stream); + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + } + break; + + //MARK: stateVoiceFontsWait + case stateVoiceFontsWait: // Await voice font list + // accountGetSessionFontsResponse() will transition from here to + // stateVoiceFontsReceived, to ensure we have the voice font list + // before attempting to create a session. + break; + + //MARK: stateVoiceFontsReceived + case stateVoiceFontsReceived: // Voice font list received + // Set up the timer to check for expiring voice fonts + mVoiceFontExpiryTimer.start(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + +#if USE_SESSION_GROUPS + // create the main session group + setState(stateCreatingSessionGroup); + sessionGroupCreateSendMessage(); +#else + setState(stateNoChannel); +#endif + break; + + //MARK: stateCreatingSessionGroup + case stateCreatingSessionGroup: + if(mSessionTerminateRequested || !mVoiceEnabled) + { + // *TODO: Question: is this the right way out of this state + setState(stateSessionTerminated); + } + else if(!mMainSessionGroupHandle.empty()) + { + // Start looped recording (needed for "panic button" anti-griefing tool) + recordingLoopStart(); + setState(stateNoChannel); + } + break; + + //MARK: stateRetrievingParcelVoiceInfo + case stateRetrievingParcelVoiceInfo: + // wait until parcel voice info is received. + if(mSessionTerminateRequested || !mVoiceEnabled) + { + // if a terminate request has been received, + // bail and go to the stateSessionTerminated + // state. If the cap request is still pending, + // the responder will check to see if we've moved + // to a new session and won't change any state. + setState(stateSessionTerminated); + } + break; + + + //MARK: stateNoChannel + case stateNoChannel: + LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL; + mSpatialJoiningNum = 0; + // Do this here as well as inside sendPositionalUpdate(). + // Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync. + sendFriendsListUpdates(); + + if(mSessionTerminateRequested || !mVoiceEnabled) + { + // TODO: Question: Is this the right way out of this state? + setState(stateSessionTerminated); + } + else if(mTuningMode) + { + mTuningExitState = stateNoChannel; + setState(stateMicTuningStart); + } + else if(mCaptureBufferMode) + { + setState(stateCaptureBufferPaused); + } + else if(checkParcelChanged() || (mNextAudioSession == NULL)) + { + // the parcel is changed, or we have no pending audio sessions, + // so try to request the parcel voice info + // if we have the cap, we move to the appropriate state + if(requestParcelVoiceInfo()) + { + setState(stateRetrievingParcelVoiceInfo); + } + } + else if(sessionNeedsRelog(mNextAudioSession)) + { + requestRelog(); + setState(stateSessionTerminated); + } + else if(mNextAudioSession) + { + sessionState *oldSession = mAudioSession; + + mAudioSession = mNextAudioSession; + mAudioSessionChanged = true; + if(!mAudioSession->mReconnect) + { + mNextAudioSession = NULL; + } + + // The old session may now need to be deleted. + reapSession(oldSession); + + if(!mAudioSession->mHandle.empty()) + { + // Connect to a session by session handle + + sessionMediaConnectSendMessage(mAudioSession); + } + else + { + // Connect to a session by URI + sessionCreateSendMessage(mAudioSession, true, false); + } + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); + setState(stateJoiningSession); + } + break; + + //MARK: stateJoiningSession + case stateJoiningSession: // waiting for session handle + + // If this is true we have problem with connection to voice server (EXT-4313). + // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM. + if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM) + { + // Notify observers to let them know there is problem with voice + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; + } + + // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for + // example for p2p many times while waiting for response, so it can't be used to detect errors + if(mAudioSession && mAudioSession->mIsSpatial) + { + + mSpatialJoiningNum++; + } + + // joinedAudioSession() will transition from here to stateSessionJoined. + if(!mVoiceEnabled) + { + // User bailed out during connect -- jump straight to teardown. + setState(stateSessionTerminated); + } + else if(mSessionTerminateRequested) + { + if(mAudioSession && !mAudioSession->mHandle.empty()) + { + // Only allow direct exits from this state in p2p calls (for cancelling an invite). + // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. + if(mAudioSession->mIsP2P) + { + sessionMediaDisconnectSendMessage(mAudioSession); + setState(stateSessionTerminated); + } + } + } + break; + + //MARK: stateSessionJoined + case stateSessionJoined: // session handle received + + + mSpatialJoiningNum = 0; + // It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 + // before continuing from this state. They can happen in either order, and if I don't wait for both, things can get stuck. + // For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. + // This is a cheap way to make sure both have happened before proceeding. + if(mAudioSession && mAudioSession->mVoiceEnabled) + { + // Dirty state that may need to be sync'ed with the daemon. + mMuteMicDirty = true; + mSpeakerVolumeDirty = true; + mSpatialCoordsDirty = true; + + setState(stateRunning); + + // Start the throttle timer + mUpdateTimer.start(); + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + + // Events that need to happen when a session is joined could go here. + // Maybe send initial spatial data? + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + + } + else if(!mVoiceEnabled) + { + // User bailed out during connect -- jump straight to teardown. + setState(stateSessionTerminated); + } + else if(mSessionTerminateRequested) + { + // Only allow direct exits from this state in p2p calls (for cancelling an invite). + // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. + if(mAudioSession && mAudioSession->mIsP2P) + { + sessionMediaDisconnectSendMessage(mAudioSession); + setState(stateSessionTerminated); + } + } + break; + + //MARK: stateRunning + case stateRunning: // steady state + // Disabling voice or disconnect requested. + if(!mVoiceEnabled || mSessionTerminateRequested) + { + leaveAudioSession(); + } + else + { + + if(!inSpatialChannel()) + { + // When in a non-spatial channel, never send positional updates. + mSpatialCoordsDirty = false; + } + else + { + if(checkParcelChanged()) + { + // if the parcel has changed, attempted to request the + // cap for the parcel voice info. If we can't request it + // then we don't have the cap URL so we do nothing and will + // recheck next time around + if(requestParcelVoiceInfo()) + { + // we did get the cap, and we made the request, + // so go wait for the response. + setState(stateRetrievingParcelVoiceInfo); + } + } + // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) + enforceTether(); + + } + + // Do notifications for expiring Voice Fonts. + if (mVoiceFontExpiryTimer.hasExpired()) + { + expireVoiceFonts(); + mVoiceFontExpiryTimer.setTimerExpirySec(VOICE_FONT_EXPIRY_INTERVAL); + } + + // Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often + // -- the user can only click so fast) or every 10hz, whichever is sooner. + // Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged. + if((mAudioSession && mAudioSession->mMuteDirty) || mMuteMicDirty || mUpdateTimer.hasExpired()) + { + mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + sendPositionalUpdate(); + } + } + break; + + //MARK: stateLeavingSession + case stateLeavingSession: // waiting for terminate session response + // The handler for the Session.Terminate response will transition from here to stateSessionTerminated. + break; + + //MARK: stateSessionTerminated + case stateSessionTerminated: + + // Must do this first, since it uses mAudioSession. + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); + + if(mAudioSession) + { + sessionState *oldSession = mAudioSession; + + mAudioSession = NULL; + // We just notified status observers about this change. Don't do it again. + mAudioSessionChanged = false; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + else + { + LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; + } + + // Always reset the terminate request flag when we get here. + mSessionTerminateRequested = false; + + if(mVoiceEnabled && !mRelogRequested) + { + // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). + setState(stateNoChannel); + } + else + { + // Shutting down voice, continue with disconnecting. + logout(); + + // The state machine will take it from here + mRelogRequested = false; + } + + break; + + //MARK: stateLoggingOut + case stateLoggingOut: // waiting for logout response + // The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. + break; + + //MARK: stateLoggedOut + case stateLoggedOut: // logout response received + + // Once we're logged out, these things are invalid. + mAccountHandle.clear(); + cleanUp(); + + if(mVoiceEnabled && !mRelogRequested) + { + // User was logged out, but wants to be logged in. Send a new login request. + setState(stateNeedsLogin); + } + else + { + // shut down the connector + connectorShutdown(); + } + break; + + //MARK: stateConnectorStopping + case stateConnectorStopping: // waiting for connector stop + // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. + break; + + //MARK: stateConnectorStopped + case stateConnectorStopped: // connector stop received + setState(stateDisableCleanup); + break; + + //MARK: stateConnectorFailed + case stateConnectorFailed: + setState(stateConnectorFailedWaiting); + break; + //MARK: stateConnectorFailedWaiting + case stateConnectorFailedWaiting: + if(!mVoiceEnabled) + { + setState(stateDisableCleanup); + } + break; + + //MARK: stateLoginFailed + case stateLoginFailed: + setState(stateLoginFailedWaiting); + break; + //MARK: stateLoginFailedWaiting + case stateLoginFailedWaiting: + if(!mVoiceEnabled) + { + setState(stateDisableCleanup); + } + break; + + //MARK: stateJoinSessionFailed + case stateJoinSessionFailed: + // Transition to error state. Send out any notifications here. + if(mAudioSession) + { + LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; + } + else + { + LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; + } + + notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); + setState(stateJoinSessionFailedWaiting); + break; + + //MARK: stateJoinSessionFailedWaiting + case stateJoinSessionFailedWaiting: + // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. + // Region crossings may leave this state and try the join again. + if(mSessionTerminateRequested) + { + setState(stateSessionTerminated); + } + break; + + //MARK: stateJail + case stateJail: + // We have given up. Do nothing. + break; + + } + + if (mAudioSessionChanged) + { + mAudioSessionChanged = false; + notifyParticipantObservers(); + notifyVoiceFontObservers(); + } + else if (mAudioSession && mAudioSession->mParticipantsChanged) + { + mAudioSession->mParticipantsChanged = false; + notifyParticipantObservers(); + } +} + +void LLVivoxVoiceClient::closeSocket(void) +{ + mSocket.reset(); + mConnected = false; + mConnectorHandle.clear(); + mAccountHandle.clear(); +} + +void LLVivoxVoiceClient::loginSendMessage() +{ + std::ostringstream stream; + + bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps"); + + stream + << "" + << "" << mConnectorHandle << "" + << "" << mAccountName << "" + << "" << mAccountPassword << "" + << "VerifyAnswer" + << "true" + << "Application" + << "5" + << (autoPostCrashDumps?"true":"") + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::logout() +{ + // Ensure that we'll re-request provisioning before logging in again + mAccountPassword.clear(); + mVoiceAccountServerURI.clear(); + + setState(stateLoggingOut); + logoutSendMessage(); +} + +void LLVivoxVoiceClient::logoutSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + mAccountHandle.clear(); + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::accountListBlockRulesSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::accountListAutoAcceptRulesSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::sessionGroupCreateSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "Normal" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) +{ + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"Session.Create.1\">" + << "" << mAccountHandle << "" + << "" << session->mSIPURI << ""; + + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~"; + + if(!session->mHash.empty()) + { + stream + << "" << LLURI::escape(session->mHash, allowed_chars) << "" + << "SHA1UserName"; + } + + stream + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << font_index << "" + << "" << mChannelName << "" + << "\n\n\n"; + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) +{ + LL_DEBUGS("Voice") << "Requesting create: " << session->mSIPURI << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + + session->mCreateInProgress = true; + if(startAudio) + { + session->mMediaConnectInProgress = true; + } + + std::string password; + if(!session->mHash.empty()) + { + static const std::string allowed_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~" + ; + password = LLURI::escape(session->mHash, allowed_chars); + } + + std::ostringstream stream; + stream + << "mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mSIPURI << "" + << "" << mChannelName << "" + << "" << (startAudio?"true":"false") << "" + << "" << (startText?"true":"false") << "" + << "" << font_index << "" + << "" << password << "" + << "SHA1UserName" + << "\n\n\n" + ; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) +{ + LL_DEBUGS("Voice") << "Connecting audio to session handle: " << session->mHandle << LL_ENDL; + + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "With voice font: " << session->mVoiceFontID << " (" << font_index << ")" << LL_ENDL; + + session->mMediaConnectInProgress = true; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.MediaConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "" << font_index << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session) +{ + LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "mHandle << "\" action=\"Session.TextConnect.1\">" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionTerminate() +{ + mSessionTerminateRequested = true; +} + +void LLVivoxVoiceClient::requestRelog() +{ + mSessionTerminateRequested = true; + mRelogRequested = true; +} + + +void LLVivoxVoiceClient::leaveAudioSession() +{ + if(mAudioSession) + { + LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; + + switch(getState()) + { + case stateNoChannel: + // In this case, we want to pretend the join failed so our state machine doesn't get stuck. + // Skip the join failed transition state so we don't send out error notifications. + setState(stateJoinSessionFailedWaiting); + break; + case stateJoiningSession: + case stateSessionJoined: + case stateRunning: + if(!mAudioSession->mHandle.empty()) + { + +#if RECORD_EVERYTHING + // HACK: for testing only + // Save looped recording + std::string savepath("/tmp/vivoxrecording"); + { + time_t now = time(NULL); + const size_t BUF_SIZE = 64; + char time_str[BUF_SIZE]; /* Flawfinder: ignore */ + + strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); + savepath += time_str; + } + recordingLoopSave(savepath); +#endif + + sessionMediaDisconnectSendMessage(mAudioSession); + setState(stateLeavingSession); + } + else + { + LL_WARNS("Voice") << "called with no session handle" << LL_ENDL; + setState(stateSessionTerminated); + } + break; + case stateJoinSessionFailed: + case stateJoinSessionFailedWaiting: + setState(stateSessionTerminated); + break; + + default: + LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; + break; + } + } + else + { + LL_WARNS("Voice") << "called with no active session" << LL_ENDL; + setState(stateSessionTerminated); + } +} + +void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "Audio" + << "\n\n\n"; + + writeString(stream.str()); + +} + +void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) +{ + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL; + stream + << "" + << "" << session->mGroupHandle << "" + << "" << session->mHandle << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::getCaptureDevicesSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::getRenderDevicesSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::clearCaptureDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mCaptureDevices.clear(); +} + +void LLVivoxVoiceClient::addCaptureDevice(const std::string& name) +{ + LL_DEBUGS("Voice") << name << LL_ENDL; + + mCaptureDevices.push_back(name); +} + +LLVoiceDeviceList& LLVivoxVoiceClient::getCaptureDevices() +{ + return mCaptureDevices; +} + +void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) +{ + if(name == "Default") + { + if(!mCaptureDevice.empty()) + { + mCaptureDevice.clear(); + mCaptureDeviceDirty = true; + } + } + else + { + if(mCaptureDevice != name) + { + mCaptureDevice = name; + mCaptureDeviceDirty = true; + } + } +} + +void LLVivoxVoiceClient::clearRenderDevices() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + mRenderDevices.clear(); +} + +void LLVivoxVoiceClient::addRenderDevice(const std::string& name) +{ + LL_DEBUGS("Voice") << name << LL_ENDL; + mRenderDevices.push_back(name); +} + +LLVoiceDeviceList& LLVivoxVoiceClient::getRenderDevices() +{ + return mRenderDevices; +} + +void LLVivoxVoiceClient::setRenderDevice(const std::string& name) +{ + if(name == "Default") + { + if(!mRenderDevice.empty()) + { + mRenderDevice.clear(); + mRenderDeviceDirty = true; + } + } + else + { + if(mRenderDevice != name) + { + mRenderDevice = name; + mRenderDeviceDirty = true; + } + } + +} + +void LLVivoxVoiceClient::tuningStart() +{ + mTuningMode = true; + LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL; + if(getState() >= stateNoChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::tuningStop() +{ + mTuningMode = false; +} + +bool LLVivoxVoiceClient::inTuningMode() +{ + bool result = false; + switch(getState()) + { + case stateMicTuningRunning: + result = true; + break; + default: + break; + } + return result; +} + +void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) +{ + mTuningAudioFile = name; + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "" << (loop?"1":"0") << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningRenderStopSendMessage() +{ + std::ostringstream stream; + stream + << "" + << "" << mTuningAudioFile << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration) +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "" << duration << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningCaptureStopSendMessage() +{ + LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; + + std::ostringstream stream; + stream + << "" + << "\n\n\n"; + + writeString(stream.str()); + + mTuningEnergy = 0.0f; +} + +void LLVivoxVoiceClient::tuningSetMicVolume(float volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mTuningMicVolume) + { + mTuningMicVolume = scaled_volume; + mTuningMicVolumeDirty = true; + } +} + +void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mTuningSpeakerVolume) + { + mTuningSpeakerVolume = scaled_volume; + mTuningSpeakerVolumeDirty = true; + } +} + +float LLVivoxVoiceClient::tuningGetEnergy(void) +{ + return mTuningEnergy; +} + +bool LLVivoxVoiceClient::deviceSettingsAvailable() +{ + bool result = true; + + if(!mConnected) + result = false; + + if(mRenderDevices.empty()) + result = false; + + return result; +} + +void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ + if(clearCurrentList) + { + clearCaptureDevices(); + clearRenderDevices(); + } + getCaptureDevicesSendMessage(); + getRenderDevicesSendMessage(); +} + +void LLVivoxVoiceClient::daemonDied() +{ + // The daemon died, so the connection is gone. Reset everything and start over. + LL_WARNS("Voice") << "Connection to vivox daemon lost. Resetting state."<< LL_ENDL; + + // Try to relaunch the daemon + setState(stateDisableCleanup); +} + +void LLVivoxVoiceClient::giveUp() +{ + // All has failed. Clean up and stop trying. + closeSocket(); + cleanUp(); + + setState(stateJail); +} + +static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) +{ + F32 nat[3], nup[3], nl[3]; // the new at, up, left vectors and the new position and velocity +// F32 nvel[3]; + F64 npos[3]; + + // The original XML command was sent like this: + /* + << "" + << "" << pos[VX] << "" + << "" << pos[VZ] << "" + << "" << pos[VY] << "" + << "" + << "" + << "" << mAvatarVelocity[VX] << "" + << "" << mAvatarVelocity[VZ] << "" + << "" << mAvatarVelocity[VY] << "" + << "" + << "" + << "" << l.mV[VX] << "" + << "" << u.mV[VX] << "" + << "" << a.mV[VX] << "" + << "" + << "" + << "" << l.mV[VZ] << "" + << "" << u.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VY] << "" + << "" << u.mV [VZ] << "" + << "" << a.mV [VY] << "" + << ""; + */ + +#if 1 + // This was the original transform done when building the XML command + nat[0] = left.mV[VX]; + nat[1] = up.mV[VX]; + nat[2] = at.mV[VX]; + + nup[0] = left.mV[VZ]; + nup[1] = up.mV[VY]; + nup[2] = at.mV[VZ]; + + nl[0] = left.mV[VY]; + nl[1] = up.mV[VZ]; + nl[2] = at.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY]; + +// nvel[0] = vel.mV[VX]; +// nvel[1] = vel.mV[VZ]; +// nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + + // This was the original transform done in the SDK + nat[0] = at.mV[2]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * left.mV[2]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = at.mV[0]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[0]; + + npos[2] = pos.mdV[2] * -1.0; + npos[1] = pos.mdV[1]; + npos[0] = pos.mdV[0]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } +#else + // This is the compose of the two transforms (at least, that's what I'm trying for) + nat[0] = at.mV[VX]; + nat[1] = 0; // y component of at vector is always 0, this was up[2] + nat[2] = -1 * up.mV[VZ]; + + // We override whatever the application gives us + nup[0] = 0; // x component of up vector is always 0 + nup[1] = 1; // y component of up vector is always 1 + nup[2] = 0; // z component of up vector is always 0 + + nl[0] = left.mV[VX]; + nl[1] = 0; // y component of left vector is always zero, this was up[0] + nl[2] = -1 * left.mV[VY]; + + npos[0] = pos.mdV[VX]; + npos[1] = pos.mdV[VZ]; + npos[2] = pos.mdV[VY] * -1.0; + + nvel[0] = vel.mV[VX]; + nvel[1] = vel.mV[VZ]; + nvel[2] = vel.mV[VY]; + + for(int i=0;i<3;++i) { + at.mV[i] = nat[i]; + up.mV[i] = nup[i]; + left.mV[i] = nl[i]; + pos.mdV[i] = npos[i]; + } + +#endif +} + +void LLVivoxVoiceClient::sendPositionalUpdate(void) +{ + std::ostringstream stream; + + if(mSpatialCoordsDirty) + { + LLVector3 l, u, a, vel; + LLVector3d pos; + + mSpatialCoordsDirty = false; + + // Always send both speaker and listener positions together. + stream << "" + << "" << getAudioSessionHandle() << ""; + + stream << ""; + +// LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; + l = mAvatarRot.getLeftRow(); + u = mAvatarRot.getUpRow(); + a = mAvatarRot.getFwdRow(); + pos = mAvatarPosition; + vel = mAvatarVelocity; + + // SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. + // The old transform is replicated by this function. + oldSDKTransform(l, u, a, pos, vel); + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << ""; + + stream << ""; + + stream << ""; + + LLVector3d earPosition; + LLVector3 earVelocity; + LLMatrix3 earRot; + + switch(mEarLocation) + { + case earLocCamera: + default: + earPosition = mCameraPosition; + earVelocity = mCameraVelocity; + earRot = mCameraRot; + break; + + case earLocAvatar: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = mAvatarRot; + break; + + case earLocMixed: + earPosition = mAvatarPosition; + earVelocity = mAvatarVelocity; + earRot = mCameraRot; + break; + } + + l = earRot.getLeftRow(); + u = earRot.getUpRow(); + a = earRot.getFwdRow(); + pos = earPosition; + vel = earVelocity; + +// LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; + + oldSDKTransform(l, u, a, pos, vel); + + stream + << "" + << "" << pos.mdV[VX] << "" + << "" << pos.mdV[VY] << "" + << "" << pos.mdV[VZ] << "" + << "" + << "" + << "" << vel.mV[VX] << "" + << "" << vel.mV[VY] << "" + << "" << vel.mV[VZ] << "" + << "" + << "" + << "" << a.mV[VX] << "" + << "" << a.mV[VY] << "" + << "" << a.mV[VZ] << "" + << "" + << "" + << "" << u.mV[VX] << "" + << "" << u.mV[VY] << "" + << "" << u.mV[VZ] << "" + << "" + << "" + << "" << l.mV [VX] << "" + << "" << l.mV [VY] << "" + << "" << l.mV [VZ] << "" + << ""; + + + stream << ""; + + stream << "\n\n\n"; + } + + if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) + { + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + mAudioSession->mVolumeDirty = false; + mAudioSession->mMuteDirty = false; + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + { + participantState *p = iter->second; + + if(p->mVolumeDirty) + { + // Can't set volume/mute for yourself + if(!p->mIsSelf) + { + // scale from the range 0.0-1.0 to vivox volume in the range 0-100 + S32 volume = llround(p->mVolume / VOLUME_SCALE_VIVOX); + bool mute = p->mOnMuteList; + + if(mute) + { + // SetParticipantMuteForMe doesn't work in p2p sessions. + // If we want the user to be muted, set their volume to 0 as well. + // This isn't perfect, but it will at least reduce their volume to a minimum. + volume = 0; + // Mark the current volume level as set to prevent incoming events + // changing it to 0, so that we can return to it when unmuting. + p->mVolumeSet = true; + } + + if(volume == 0) + { + mute = true; + } + + LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; + + // SLIM SDK: Send both volume and mute commands. + + // Send a "volume for me" command for the user. + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << volume << "" + << "\n\n\n"; + + if(!mAudioSession->mIsP2P) + { + // Send a "mute for me" command for the user + // Doesn't work in P2P sessions + stream << "" + << "" << getAudioSessionHandle() << "" + << "" << p->mURI << "" + << "" << (mute?"1":"0") << "" + << "Audio" + << "\n\n\n"; + } + } + + p->mVolumeDirty = false; + } + } + } + + buildLocalAudioUpdates(stream); + + if(!stream.str().empty()) + { + writeString(stream.str()); + } + + // Friends list updates can be huge, especially on the first voice login of an account with lots of friends. + // Batching them all together can choke SLVoice, so send them in separate writes. + sendFriendsListUpdates(); +} + +void LLVivoxVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) +{ + if(mCaptureDeviceDirty) + { + LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; + + stream + << "" + << "" << mCaptureDevice << "" + << "" + << "\n\n\n"; + + mCaptureDeviceDirty = false; + } +} + +void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream) +{ + if(mRenderDeviceDirty) + { + LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; + + stream + << "" + << "" << mRenderDevice << "" + << "" + << "\n\n\n"; + mRenderDeviceDirty = false; + } +} + +void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) +{ + buildSetCaptureDevice(stream); + + buildSetRenderDevice(stream); + + if(mMuteMicDirty) + { + mMuteMicDirty = false; + + // Send a local mute command. + + LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mMuteMic?"true":"false") << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << (mMuteMic?"true":"false") << "" + << "\n\n\n"; + + } + + if(mSpeakerMuteDirty) + { + const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false"); + + mSpeakerMuteDirty = false; + + LL_INFOS("Voice") << "Setting speaker mute to " << muteval << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << muteval << "" + << "\n\n\n"; + + } + + if(mSpeakerVolumeDirty) + { + mSpeakerVolumeDirty = false; + + LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << mSpeakerVolume << "" + << "\n\n\n"; + + } + + if(mMicVolumeDirty) + { + mMicVolumeDirty = false; + + LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume << LL_ENDL; + + stream << "" + << "" << mConnectorHandle << "" + << "" << mMicVolume << "" + << "\n\n\n"; + } + + +} + +void LLVivoxVoiceClient::checkFriend(const LLUUID& id) +{ + buddyListEntry *buddy = findBuddy(id); + + // Make sure we don't add a name before it's been looked up. + LLAvatarName av_name; + if(LLAvatarNameCache::get(id, &av_name)) + { + // *NOTE: For now, we feed legacy names to Vivox because I don't know + // if their service can support a mix of new and old clients with + // different sorts of names. + std::string name = av_name.getLegacyName(); + + const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); + bool canSeeMeOnline = false; + if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) + canSeeMeOnline = true; + + // When we get here, mNeedsSend is true and mInSLFriends is false. Change them as necessary. + + if(buddy) + { + // This buddy is already in both lists. + + if(name != buddy->mDisplayName) + { + // The buddy is in the list with the wrong name. Update it with the correct name. + LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; + buddy->mDisplayName = name; + buddy->mNeedsNameUpdate = true; // This will cause the buddy to be resent. + } + } + else + { + // This buddy was not in the vivox list, needs to be added. + buddy = addBuddy(sipURIFromID(id), name); + buddy->mUUID = id; + } + + // In all the above cases, the buddy is in the SL friends list (which is how we got here). + buddy->mInSLFriends = true; + buddy->mCanSeeMeOnline = canSeeMeOnline; + buddy->mNameResolved = true; + + } + else + { + // This name hasn't been looked up yet. Don't do anything with this buddy list entry until it has. + if(buddy) + { + buddy->mNameResolved = false; + } + + // Initiate a lookup. + // The "lookup completed" callback will ensure that the friends list is rechecked after it completes. + lookupName(id); + } +} + +void LLVivoxVoiceClient::clearAllLists() +{ + // FOR TESTING ONLY + + // This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about. + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) + { + buddyListEntry *buddy = buddy_it->second; + buddy_it++; + + std::ostringstream stream; + + if(buddy->mInVivoxBuddies) + { + // delete this entry from the vivox buddy list + buddy->mInVivoxBuddies = false; + LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + + if(buddy->mHasBlockListEntry) + { + // Delete the associated block list entry (so the block list doesn't fill up with junk) + buddy->mHasBlockListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + if(buddy->mHasAutoAcceptListEntry) + { + // Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk) + buddy->mHasAutoAcceptListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + + writeString(stream.str()); + + } +} + +void LLVivoxVoiceClient::sendFriendsListUpdates() +{ + if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty) + { + mFriendsListDirty = false; + + if(0) + { + // FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries. + clearAllLists(); + return; + } + + LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL; + + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + { + // reset the temp flags in the local buddy list + buddy_it->second->mInSLFriends = false; + } + + // correlate with the friends list + { + LLCollectAllBuddies collect; + LLAvatarTracker::instance().applyFunctor(collect); + LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); + LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); + + for ( ; it != end; ++it) + { + checkFriend(it->second); + } + it = collect.mOffline.begin(); + end = collect.mOffline.end(); + for ( ; it != end; ++it) + { + checkFriend(it->second); + } + } + + LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL; + + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) + { + buddyListEntry *buddy = buddy_it->second; + buddy_it++; + + // Ignore entries that aren't resolved yet. + if(buddy->mNameResolved) + { + std::ostringstream stream; + + if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) + { + if(mNumberOfAliases > 0) + { + // Add (or update) this entry in the vivox buddy list + buddy->mInVivoxBuddies = true; + buddy->mNeedsNameUpdate = false; + LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; + stream + << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "" << buddy->mDisplayName << "" + << "" // Without this, SLVoice doesn't seem to parse the command. + << "0" + << "\n\n\n"; + } + } + else if(!buddy->mInSLFriends) + { + // This entry no longer exists in your SL friends list. Remove all traces of it from the Vivox buddy list. + if(buddy->mInVivoxBuddies) + { + // delete this entry from the vivox buddy list + buddy->mInVivoxBuddies = false; + LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + + if(buddy->mHasBlockListEntry) + { + // Delete the associated block list entry, if any + buddy->mHasBlockListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + if(buddy->mHasAutoAcceptListEntry) + { + // Delete the associated auto-accept list entry, if any + buddy->mHasAutoAcceptListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + } + } + + if(buddy->mInSLFriends) + { + + if(buddy->mCanSeeMeOnline) + { + // Buddy should not be blocked. + + // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. + + // If the buddy has a block list entry, delete it. + if(buddy->mHasBlockListEntry) + { + buddy->mHasBlockListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + + + // If we just deleted a block list entry, add an auto-accept entry. + if(!buddy->mHasAutoAcceptListEntry) + { + buddy->mHasAutoAcceptListEntry = true; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "0" + << "\n\n\n"; + } + } + } + else + { + // Buddy should be blocked. + + // If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. + + // If this buddy has an autoaccept entry, delete it + if(buddy->mHasAutoAcceptListEntry) + { + buddy->mHasAutoAcceptListEntry = false; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "\n\n\n"; + + // If we just deleted an auto-accept entry, add a block list entry. + if(!buddy->mHasBlockListEntry) + { + buddy->mHasBlockListEntry = true; + stream << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "1" + << "\n\n\n"; + } + } + } + + if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies) + { + // Delete this entry from the local buddy list. This should NOT invalidate the iterator, + // since it has already been incremented to the next entry. + deleteBuddy(buddy->mURI); + } + + } + writeString(stream.str()); + } + } + } +} + +///////////////////////////// +// Response/Event handlers + +void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; + setState(stateConnectorFailed); + LLSD args; + std::stringstream errs; + errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; + args["HOSTID"] = errs.str(); + LLNotificationsUtil::add("NoVoiceConnect", args); + } + else + { + // Connector created, move forward. + LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; + mVoiceVersion.serverVersion = versionID; + mConnectorHandle = connectorHandle; + if(getState() == stateConnectorStarting) + { + setState(stateConnectorStarted); + } + } +} + +void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) +{ + LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; + + // Status code of 20200 means "bad password". We may want to special-case that at some point. + + if ( statusCode == 401 ) + { + // Login failure which is probably caused by the delay after a user's password being updated. + LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + setState(stateLoginRetry); + } + else if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; + setState(stateLoginFailed); + } + else + { + // Login succeeded, move forward. + mAccountHandle = accountHandle; + mNumberOfAliases = numberOfAliases; + // This needs to wait until the AccountLoginStateChangeEvent is received. +// if(getState() == stateLoggingIn) +// { +// setState(stateLoggedIn); +// } + } +} + +void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{ + sessionState *session = findSessionBeingCreatedByURI(requestId); + + if(session) + { + session->mCreateInProgress = false; + } + + if(statusCode != 0) + { + LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + { + setState(stateJoinSessionFailed); + } + else + { + reapSession(session); + } + } + } + else + { + LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + } +} + +void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{ + sessionState *session = findSessionBeingCreatedByURI(requestId); + + if(session) + { + session->mCreateInProgress = false; + } + + if(statusCode != 0) + { + LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + { + setState(stateJoinSessionFailed); + } + else + { + reapSession(session); + } + } + } + else + { + LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; + if(session) + { + setSessionHandle(session, sessionHandle); + } + } +} + +void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +{ + sessionState *session = findSession(requestId); + if(statusCode != 0) + { + LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; + if(session) + { + session->mMediaConnectInProgress = false; + session->mErrorStatusCode = statusCode; + session->mErrorStatusString = statusString; + if(session == mAudioSession) + setState(stateJoinSessionFailed); + } + } + else + { + LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } +} + +void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +{ + if(statusCode != 0) + { + LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; + // Should this ever fail? do we care if it does? + } + + mConnected = false; + + if(getState() == stateConnectorStopping) + { + setState(stateConnectorStopped); + } +} + +void LLVivoxVoiceClient::sessionAddedEvent( + std::string &uriString, + std::string &alias, + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool isChannel, + bool incoming, + std::string &nameString, + std::string &applicationString) +{ + sessionState *session = NULL; + + LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; + + session = addSession(uriString, sessionHandle); + if(session) + { + session->mGroupHandle = sessionGroupHandle; + session->mIsChannel = isChannel; + session->mIncoming = incoming; + session->mAlias = alias; + + // Generate a caller UUID -- don't need to do this for channels + if(!session->mIsChannel) + { + if(IDFromName(session->mSIPURI, session->mCallerID)) + { + // Normal URI(base64-encoded UUID) + } + else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) + { + // Wrong URI, but an alias is available. Stash the incoming URI as an alternate + session->mAlternateSIPURI = session->mSIPURI; + + // and generate a proper URI from the ID. + setSessionURI(session, sipURIFromID(session->mCallerID)); + } + else + { + LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; + session->mCallerID.generate(session->mSIPURI); + session->mSynthesizedCallerID = true; + + // Can't look up the name in this case -- we have to extract it from the URI. + std::string namePortion = nameFromsipURI(session->mSIPURI); + if(namePortion.empty()) + { + // Didn't seem to be a SIP URI, just use the whole provided name. + namePortion = nameString; + } + + // Some incoming names may be separated with an underscore instead of a space. Fix this. + LLStringUtil::replaceChar(namePortion, '_', ' '); + + // Act like we just finished resolving the name (this stores it in all the right places) + avatarNameResolved(session->mCallerID, namePortion); + } + + LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; + + if(!session->mSynthesizedCallerID) + { + // If we got here, we don't have a proper name. Initiate a lookup. + lookupName(session->mCallerID); + } + } + } +} + +void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) +{ + LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; + +#if USE_SESSION_GROUPS + if(mMainSessionGroupHandle.empty()) + { + // This is the first (i.e. "main") session group. Save its handle. + mMainSessionGroupHandle = sessionGroupHandle; + } + else + { + LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; + } +#endif +} + +void LLVivoxVoiceClient::joinedAudioSession(sessionState *session) +{ + LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; + if(mAudioSession != session) + { + sessionState *oldSession = mAudioSession; + + mAudioSession = session; + mAudioSessionChanged = true; + + // The old session may now need to be deleted. + reapSession(oldSession); + } + + // This is the session we're joining. + if(getState() == stateJoiningSession) + { + setState(stateSessionJoined); + + // SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. + // Add the current user as a participant here. + participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); + if(participant) + { + participant->mIsSelf = true; + lookupName(participant->mAvatarID); + + LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + } + + if(!session->mIsChannel) + { + // this is a p2p session. Make sure the other end is added as a participant. + participantState *participant = session->addParticipant(session->mSIPURI); + if(participant) + { + if(participant->mAvatarIDValid) + { + lookupName(participant->mAvatarID); + } + else if(!session->mName.empty()) + { + participant->mDisplayName = session->mName; + avatarNameResolved(participant->mAvatarID, session->mName); + } + + // TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? + LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + } + } + } +} + +void LLVivoxVoiceClient::sessionRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle) +{ + LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; + + sessionState *session = findSession(sessionHandle); + if(session) + { + leftAudioSession(session); + + // This message invalidates the session's handle. Set it to empty. + setSessionHandle(session); + + // This also means that the session's session group is now empty. + // Terminate the session group so it doesn't leak. + sessionGroupTerminateSendMessage(session); + + // Reset the media state (we now have no info) + session->mMediaStreamState = streamStateUnknown; + session->mTextStreamState = streamStateUnknown; + + // Conditionally delete the session + reapSession(session); + } + else + { + LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::reapSession(sessionState *session) +{ + if(session) + { + if(!session->mHandle.empty()) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; + } + else if(session->mCreateInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; + } + else if(session->mMediaConnectInProgress) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; + } + else if(session == mAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; + } + else if(session == mNextAudioSession) + { + LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; + } + else + { + // TODO: Question: Should we check for queued text messages here? + // We don't have a reason to keep tracking this session, so just delete it. + LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; + deleteSession(session); + session = NULL; + } + } + else + { +// LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; + } +} + +// Returns true if the session seems to indicate we've moved to a region on a different voice server +bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session) +{ + bool result = false; + + if(session != NULL) + { + // Only make this check for spatial channels (so it won't happen for group or p2p calls) + if(session->mIsSpatial) + { + std::string::size_type atsign; + + atsign = session->mSIPURI.find("@"); + + if(atsign != std::string::npos) + { + std::string urihost = session->mSIPURI.substr(atsign + 1); + if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) + { + // The hostname in this URI is different from what we expect. This probably means we need to relog. + + // We could make a ProvisionVoiceAccountRequest and compare the result with the current values of + // mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. + + result = true; + } + } + } + } + + return result; +} + +void LLVivoxVoiceClient::leftAudioSession( + sessionState *session) +{ + if(mAudioSession == session) + { + switch(getState()) + { + case stateJoiningSession: + case stateSessionJoined: + case stateRunning: + case stateLeavingSession: + case stateJoinSessionFailed: + case stateJoinSessionFailedWaiting: + // normal transition + LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; + setState(stateSessionTerminated); + break; + + case stateSessionTerminated: + // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. + LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; + break; + + default: + LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; + setState(stateSessionTerminated); + break; + } + } +} + +void LLVivoxVoiceClient::accountLoginStateChangeEvent( + std::string &accountHandle, + int statusCode, + std::string &statusString, + int state) +{ + /* + According to Mike S., status codes for this event are: + login_state_logged_out=0, + login_state_logged_in = 1, + login_state_logging_in = 2, + login_state_logging_out = 3, + login_state_resetting = 4, + login_state_error=100 + */ + + LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; + switch(state) + { + case 1: + if(getState() == stateLoggingIn) + { + setState(stateLoggedIn); + } + break; + + case 3: + // The user is in the process of logging out. + setState(stateLoggingOut); + break; + + case 0: + // The user has been logged out. + setState(stateLoggedOut); + break; + + default: + //Used to be a commented out warning + LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; + break; + } +} + +void LLVivoxVoiceClient::mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType) +{ + if (mediaCompletionType == "AuxBufferAudioCapture") + { + mCaptureBufferRecording = false; + } + else if (mediaCompletionType == "AuxBufferAudioRender") + { + // Ignore all but the last stop event + if (--mPlayRequestCount <= 0) + { + mCaptureBufferPlaying = false; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown MediaCompletionType: " << mediaCompletionType << LL_ENDL; + } +} + +void LLVivoxVoiceClient::mediaStreamUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + int statusCode, + std::string &statusString, + int state, + bool incoming) +{ + sessionState *session = findSession(sessionHandle); + + LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; + + if(session) + { + // We know about this session + + // Save the state for later use + session->mMediaStreamState = state; + + switch(statusCode) + { + case 0: + case 200: + // generic success + // Don't change the saved error code (it may have been set elsewhere) + break; + default: + // save the status code for later + session->mErrorStatusCode = statusCode; + break; + } + + switch(state) + { + case streamStateIdle: + // Standard "left audio session" + session->mVoiceEnabled = false; + session->mMediaConnectInProgress = false; + leftAudioSession(session); + break; + + case streamStateConnected: + session->mVoiceEnabled = true; + session->mMediaConnectInProgress = false; + joinedAudioSession(session); + break; + + case streamStateRinging: + if(incoming) + { + // Send the voice chat invite to the GUI layer + // TODO: Question: Should we correlate with the mute list here? + session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); + session->mVoiceInvitePending = true; + if(session->mName.empty()) + { + lookupName(session->mCallerID); + } + else + { + // Act like we just finished resolving the name + avatarNameResolved(session->mCallerID, session->mName); + } + } + break; + + default: + LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; + break; + + } + + } + else + { + LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; + } +} + +void LLVivoxVoiceClient::textStreamUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + bool enabled, + int state, + bool incoming) +{ + sessionState *session = findSession(sessionHandle); + + if(session) + { + // Save the state for later use + session->mTextStreamState = state; + + // We know about this session + switch(state) + { + case 0: // We see this when the text stream closes + LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; + break; + + case 1: // We see this on an incoming call from the Connector + // Try to send any text messages queued for this session. + sendQueuedTextMessages(session); + + // Send the text chat invite to the GUI layer + // TODO: Question: Should we correlate with the mute list here? + session->mTextInvitePending = true; + if(session->mName.empty()) + { + lookupName(session->mCallerID); + } + else + { + // Act like we just finished resolving the name + avatarNameResolved(session->mCallerID, session->mName); + } + break; + + default: + LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; + break; + + } + } +} + +void LLVivoxVoiceClient::participantAddedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString, + std::string &displayNameString, + int participantType) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + participantState *participant = session->addParticipant(uriString); + if(participant) + { + participant->mAccountName = nameString; + + LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName + << "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + + if(participant->mAvatarIDValid) + { + // Initiate a lookup + lookupName(participant->mAvatarID); + } + else + { + // If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. + std::string namePortion = nameFromsipURI(uriString); + if(namePortion.empty()) + { + // Problem with the SIP URI, fall back to the display name + namePortion = displayNameString; + } + if(namePortion.empty()) + { + // Problems with both of the above, fall back to the account name + namePortion = nameString; + } + + // Set the display name (which is a hint to the active speakers window not to do its own lookup) + participant->mDisplayName = namePortion; + avatarNameResolved(participant->mAvatarID, namePortion); + } + } + } +} + +void LLVivoxVoiceClient::participantRemovedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + std::string &nameString) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + participantState *participant = session->findParticipant(uriString); + if(participant) + { + session->removeParticipant(participant); + } + else + { + LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + + +void LLVivoxVoiceClient::participantUpdatedEvent( + std::string &sessionHandle, + std::string &sessionGroupHandle, + std::string &uriString, + std::string &alias, + bool isModeratorMuted, + bool isSpeaking, + int volume, + F32 energy) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + participantState *participant = session->findParticipant(uriString); + + if(participant) + { + participant->mIsSpeaking = isSpeaking; + participant->mIsModeratorMuted = isModeratorMuted; + + // SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false + if (isSpeaking) + { + participant->mSpeakingTimeout.reset(); + participant->mPower = energy; + } + else + { + participant->mPower = 0.0f; + } + + // Ignore incoming volume level if it has been explicitly set, or there + // is a volume or mute change pending. + if ( !participant->mVolumeSet && !participant->mVolumeDirty) + { + participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX; + } + + // *HACK: mantipov: added while working on EXT-3544 + /* + Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE + LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER. + + participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted + Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug. + Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates. + + But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post() + voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager + and event is not fired. + + So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it + in LLCallFloater::draw() + */ + LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); + + // ignore session ID of local chat + if (voice_cnl && voice_cnl->getSessionID().notNull()) + { + /* Singu TODO: LLIMModel::getSpeakerManager + LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); + if (speaker_manager) + */ + if (LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(voice_cnl->getSessionID())) + if (LLSpeakerMgr* speaker_manager = floaterp->getSpeakerManager()) + { + speaker_manager->update(true); + + // also initialize voice moderate_mode depend on Agent's participant. See EXT-6937. + // *TODO: remove once a way to request the current voice channel moderation mode is implemented. + if (gAgent.getID() == participant->mAvatarID) + { + speaker_manager->initVoiceModerateMode(); + } + } + } + } + else + { + LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; + } + } + else + { + LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; + } +} + +void LLVivoxVoiceClient::buddyPresenceEvent( + std::string &uriString, + std::string &alias, + std::string &statusString, + std::string &applicationString) +{ + buddyListEntry *buddy = findBuddy(uriString); + + if(buddy) + { + LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL; + LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; + + if(applicationString.empty()) + { + // This presence event is from a client that doesn't set up the Application string. Do things the old-skool way. + // NOTE: this will be needed to support people who aren't on the 3010-class SDK yet. + + if ( stricmp("Unknown", statusString.c_str())== 0) + { + // User went offline with a non-SLim-enabled viewer. + buddy->mOnlineSL = false; + } + else if ( stricmp("Online", statusString.c_str())== 0) + { + // User came online with a non-SLim-enabled viewer. + buddy->mOnlineSL = true; + } + else + { + // If the user is online through SLim, their status will be "Online-slc", "Away", or something else. + // NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string. + buddy->mOnlineSLim = true; + } + } + else if(applicationString.find("SecondLifeViewer") != std::string::npos) + { + // This presence event is from a viewer that sets the application string + if ( stricmp("Unknown", statusString.c_str())== 0) + { + // Viewer says they're offline + buddy->mOnlineSL = false; + } + else + { + // Viewer says they're online + buddy->mOnlineSL = true; + } + } + else + { + // This presence event is from something which is NOT the SL viewer (assume it's SLim). + if ( stricmp("Unknown", statusString.c_str())== 0) + { + // SLim says they're offline + buddy->mOnlineSLim = false; + } + else + { + // SLim says they're online + buddy->mOnlineSLim = true; + } + } + + LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; + + // HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change. + LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID)); + + notifyFriendObservers(); + } + else + { + LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL; + } +} + +void LLVivoxVoiceClient::messageEvent( + std::string &sessionHandle, + std::string &uriString, + std::string &alias, + std::string &messageHeader, + std::string &messageBody, + std::string &applicationString) +{ + LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; +// LL_DEBUGS("Voice") << " header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; + + if(messageHeader.find("text/html") != std::string::npos) + { + std::string message; + + { + const std::string startMarker = ", try looking for a instead. + start = messageBody.find(startSpan); + start = messageBody.find(startMarker2, start); + end = messageBody.find(endSpan); + + if(start != std::string::npos) + { + start += startMarker2.size(); + + if(end != std::string::npos) + end -= start; + + message.assign(messageBody, start, end); + } + } + } + +// LL_DEBUGS("Voice") << " raw message = \n" << message << LL_ENDL; + + // strip formatting tags + { + std::string::size_type start; + std::string::size_type end; + + while((start = message.find('<')) != std::string::npos) + { + if((end = message.find('>', start + 1)) != std::string::npos) + { + // Strip out the tag + message.erase(start, (end + 1) - start); + } + else + { + // Avoid an infinite loop + break; + } + } + } + + // Decode ampersand-escaped chars + { + std::string::size_type mark = 0; + + // The text may contain text encoded with <, >, and & + mark = 0; + while((mark = message.find("<", mark)) != std::string::npos) + { + message.replace(mark, 4, "<"); + mark += 1; + } + + mark = 0; + while((mark = message.find(">", mark)) != std::string::npos) + { + message.replace(mark, 4, ">"); + mark += 1; + } + + mark = 0; + while((mark = message.find("&", mark)) != std::string::npos) + { + message.replace(mark, 5, "&"); + mark += 1; + } + } + + // strip leading/trailing whitespace (since we always seem to get a couple newlines) + LLStringUtil::trim(message); + +// LL_DEBUGS("Voice") << " stripped message = \n" << message << LL_ENDL; + + sessionState *session = findSession(sessionHandle); + if(session) + { + bool is_busy = gAgent.getBusy(); + bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); + bool is_linden = LLMuteList::getInstance()->isLinden(session->mName); + LLChat chat; + + chat.mMuted = is_muted && !is_linden; + + if(!chat.mMuted) + { + chat.mFromID = session->mCallerID; + chat.mFromName = session->mName; + chat.mSourceType = CHAT_SOURCE_AGENT; + + if(is_busy && !is_linden) + { + // TODO: Question: Return busy mode response here? Or maybe when session is started instead? + } + + LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; + LLIMMgr::getInstance()->addMessage(session->mIMSessionID, + session->mCallerID, + session->mName.c_str(), + message.c_str(), + LLStringUtil::null, // default arg + IM_NOTHING_SPECIAL, // default arg + 0, // default arg + LLUUID::null, // default arg + LLVector3::zero, // default arg + true); // prepend name and make it a link to the user's profile + + } + } + } +} + +void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) +{ + sessionState *session = findSession(sessionHandle); + + if(session) + { + participantState *participant = session->findParticipant(uriString); + if(participant) + { + if (!stricmp(notificationType.c_str(), "Typing")) + { + // Other end started typing + // TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). + // It requires an LLIMInfo for the message, which we don't have here. + } + else if (!stricmp(notificationType.c_str(), "NotTyping")) + { + // Other end stopped typing + // TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). + // It requires an LLIMInfo for the message, which we don't have here. + } + else + { + LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; + } + } + else + { + LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; + } +} + +void LLVivoxVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType) +{ + buddyListEntry *buddy = findBuddy(buddyURI); + + if(!buddy) + { + // Couldn't find buddy by URI, try converting the alias... + if(!alias.empty()) + { + LLUUID id; + if(IDFromName(alias, id)) + { + buddy = findBuddy(id); + } + } + } + + if(buddy) + { + std::ostringstream stream; + + if(buddy->mCanSeeMeOnline) + { + // Sending the response will create an auto-accept rule + buddy->mHasAutoAcceptListEntry = true; + } + else + { + // Sending the response will create a block rule + buddy->mHasBlockListEntry = true; + } + + if(buddy->mInSLFriends) + { + buddy->mInVivoxBuddies = true; + } + + stream + << "" + << "" << mAccountHandle << "" + << "" << buddy->mURI << "" + << "" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "" + << ""<< (buddy->mInSLFriends?"1":"0")<< "" + << "" << subscriptionHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::auxAudioPropertiesEvent(F32 energy) +{ + LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL; + mTuningEnergy = energy; +} + +void LLVivoxVoiceClient::buddyListChanged() +{ + // This is called after we receive a BuddyAndGroupListChangedEvent. + mBuddyListMapPopulated = true; + mFriendsListDirty = true; +} + +void LLVivoxVoiceClient::muteListChanged() +{ + // The user's mute list has been updated. Go through the current participant list and sync it with the mute list. + if(mAudioSession) + { + participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + + for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) + { + participantState *p = iter->second; + + // Check to see if this participant is on the mute list already + if(p->updateMuteState()) + mAudioSession->mVolumeDirty = true; + } + } +} + +void LLVivoxVoiceClient::updateFriends(U32 mask) +{ + if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS)) + { + // Just resend the whole friend list to the daemon + mFriendsListDirty = true; + } +} + +///////////////////////////// +// Managing list of participants +LLVivoxVoiceClient::participantState::participantState(const std::string &uri) : + mURI(uri), + mPTT(false), + mIsSpeaking(false), + mIsModeratorMuted(false), + mLastSpokeTimestamp(0.f), + mPower(0.f), + mVolume(LLVoiceClient::VOLUME_DEFAULT), + mUserVolume(0), + mOnMuteList(false), + mVolumeSet(false), + mVolumeDirty(false), + mAvatarIDValid(false), + mIsSelf(false) +{ +} + +LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri) +{ + participantState *result = NULL; + bool useAlternateURI = false; + + // Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it + // matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. + { + participantMap::iterator iter = mParticipantsByURI.find(uri); + + if(iter == mParticipantsByURI.end()) + { + if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) + { + // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. + // Use mSIPURI instead, since it will be properly encoded. + iter = mParticipantsByURI.find(mSIPURI); + useAlternateURI = true; + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + } + + if(!result) + { + // participant isn't already in one list or the other. + result = new participantState(useAlternateURI?mSIPURI:uri); + mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); + mParticipantsChanged = true; + + // Try to do a reverse transform on the URI to get the GUID back. + { + LLUUID id; + if(LLVivoxVoiceClient::getInstance()->IDFromName(result->mURI, id)) + { + result->mAvatarIDValid = true; + result->mAvatarID = id; + } + else + { + // Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. + // This indicates that the ID will not be in the name cache. + result->mAvatarID.generate(uri); + } + } + + + if(result->updateMuteState()) + { + mMuteDirty = true; + } + + mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); + + if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) + { + result->mVolumeDirty = true; + mVolumeDirty = true; + } + + LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; + } + + return result; +} + +bool LLVivoxVoiceClient::participantState::updateMuteState() +{ + bool result = false; + + + + bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); + if(mOnMuteList != isMuted) + { + mOnMuteList = isMuted; + mVolumeDirty = true; + result = true; + } + return result; +} + +bool LLVivoxVoiceClient::participantState::isAvatar() +{ + return mAvatarIDValid; +} + +void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant) +{ + if(participant) + { + participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); + participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); + + LL_DEBUGS("Voice") << "participant \"" << participant->mURI << "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; + + if(iter == mParticipantsByURI.end()) + { + LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; + } + else if(iter2 == mParticipantsByUUID.end()) + { + LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; + } + else if(iter->second != iter2->second) + { + LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; + } + else + { + mParticipantsByURI.erase(iter); + mParticipantsByUUID.erase(iter2); + + delete participant; + mParticipantsChanged = true; + } + } +} + +void LLVivoxVoiceClient::sessionState::removeAllParticipants() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while(!mParticipantsByURI.empty()) + { + removeParticipant(mParticipantsByURI.begin()->second); + } + + if(!mParticipantsByUUID.empty()) + { + LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::getParticipantList(std::set &participants) +{ + if(mAudioSession) + { + for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); + iter != mAudioSession->mParticipantsByUUID.end(); + iter++) + { + participants.insert(iter->first); + } + } +} + +bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id) +{ + if(mAudioSession) + { + return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); + } + return false; +} + + +LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) +{ + participantState *result = NULL; + + participantMap::iterator iter = mParticipantsByURI.find(uri); + + if(iter == mParticipantsByURI.end()) + { + if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) + { + // This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. + // Look up the other URI + iter = mParticipantsByURI.find(mSIPURI); + } + } + + if(iter != mParticipantsByURI.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +{ + participantState * result = NULL; + participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + + if(iter != mParticipantsByUUID.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) +{ + participantState * result = NULL; + + if(mAudioSession) + { + result = mAudioSession->findParticipantByID(id); + } + + return result; +} + + + +// Check for parcel boundary crossing +bool LLVivoxVoiceClient::checkParcelChanged(bool update) +{ + LLViewerRegion *region = gAgent.getRegion(); + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + if(region && parcel) + { + S32 parcelLocalID = parcel->getLocalID(); + std::string regionName = region->getName(); + + // LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; + + // The region name starts out empty and gets filled in later. + // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. + // If either is empty, wait for the next time around. + if(!regionName.empty()) + { + if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) + { + // We have changed parcels. Initiate a parcel channel lookup. + if (update) + { + mCurrentParcelLocalID = parcelLocalID; + mCurrentRegionName = regionName; + } + return true; + } + } + } + return false; +} + +bool LLVivoxVoiceClient::parcelVoiceInfoReceived(state requesting_state) +{ + // pop back to the state we were in when the parcel changed and we managed to + // do the request. + if(getState() == stateRetrievingParcelVoiceInfo) + { + setState(requesting_state); + return true; + } + else + { + // we've dropped out of stateRetrievingParcelVoiceInfo + // before we received the cap result, due to a terminate + // or transition to a non-voice channel. Don't switch channels. + return false; + } +} + + +bool LLVivoxVoiceClient::requestParcelVoiceInfo() +{ + LLViewerRegion * region = gAgent.getRegion(); + if (region == NULL || !region->capabilitiesReceived()) + { + // we don't have the cap yet, so return false so the caller can try again later. + + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not yet available, deferring" << LL_ENDL; + return false; + } + + // grab the cap. + std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); + if (url.empty()) + { + // Region dosn't have the cap. Stop probing. + LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest capability not available in this region" << LL_ENDL; + setState(stateDisableCleanup); + return false; + } + else + { + // if we've already retrieved the cap from the region, go ahead and make the request, + // and return true so we can go into the state that waits for the response. + checkParcelChanged(true); + LLSD data; + LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; + + LLHTTPClient::post( + url, + data, + new LLVivoxVoiceClientCapResponder(getState())); + return true; + } +} + +void LLVivoxVoiceClient::switchChannel( + std::string uri, + bool spatial, + bool no_reconnect, + bool is_p2p, + std::string hash) +{ + bool needsSwitch = false; + + LL_DEBUGS("Voice") + << "called in state " << state2string(getState()) + << " with uri \"" << uri << "\"" + << (spatial?", spatial is true":", spatial is false") + << LL_ENDL; + + switch(getState()) + { + case stateJoinSessionFailed: + case stateJoinSessionFailedWaiting: + case stateNoChannel: + case stateRetrievingParcelVoiceInfo: + // Always switch to the new URI from these states. + needsSwitch = true; + break; + + default: + if(mSessionTerminateRequested) + { + // If a terminate has been requested, we need to compare against where the URI we're already headed to. + if(mNextAudioSession) + { + if(mNextAudioSession->mSIPURI != uri) + needsSwitch = true; + } + else + { + // mNextAudioSession is null -- this probably means we're on our way back to spatial. + if(!uri.empty()) + { + // We do want to process a switch in this case. + needsSwitch = true; + } + } + } + else + { + // Otherwise, compare against the URI we're in now. + if(mAudioSession) + { + if(mAudioSession->mSIPURI != uri) + { + needsSwitch = true; + } + } + else + { + if(!uri.empty()) + { + // mAudioSession is null -- it's not clear what case would cause this. + // For now, log it as a warning and see if it ever crops up. + LL_WARNS("Voice") << "No current audio session." << LL_ENDL; + } + } + } + break; + } + + if(needsSwitch) + { + if(uri.empty()) + { + // Leave any channel we may be in + LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + + sessionState *oldSession = mNextAudioSession; + mNextAudioSession = NULL; + + // The old session may now need to be deleted. + reapSession(oldSession); + + notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); + } + else + { + LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; + + mNextAudioSession = addSession(uri); + mNextAudioSession->mHash = hash; + mNextAudioSession->mIsSpatial = spatial; + mNextAudioSession->mReconnect = !no_reconnect; + mNextAudioSession->mIsP2P = is_p2p; + } + + if(getState() >= stateRetrievingParcelVoiceInfo) + { + // If we're already in a channel, or if we're joining one, terminate + // so we can rejoin with the new session data. + sessionTerminate(); + } + } +} + +void LLVivoxVoiceClient::joinSession(sessionState *session) +{ + mNextAudioSession = session; + + if(getState() <= stateNoChannel) + { + // We're already set up to join a channel, just needed to fill in the session handle + } + else + { + // State machine will come around and rejoin if uri/handle is not empty. + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::setNonSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + switchChannel(uri, false, false, false, credentials); +} + +void LLVivoxVoiceClient::setSpatialChannel( + const std::string &uri, + const std::string &credentials) +{ + mSpatialSessionURI = uri; + mSpatialSessionCredentials = credentials; + mAreaVoiceDisabled = mSpatialSessionURI.empty(); + + LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; + + if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) + { + // User is in a non-spatial chat or joining a non-spatial chat. Don't switch channels. + LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; + } + else + { + switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); + } +} + +void LLVivoxVoiceClient::callUser(const LLUUID &uuid) +{ + std::string userURI = sipURIFromID(uuid); + + switchChannel(userURI, false, true, true); +} + +LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid) +{ + // Figure out if a session with the user already exists + sessionState *session = findSession(uuid); + if(!session) + { + // No session with user, need to start one. + std::string uri = sipURIFromID(uuid); + session = addSession(uri); + + llassert(session); + if (!session) return NULL; + + session->mIsSpatial = false; + session->mReconnect = false; + session->mIsP2P = true; + session->mCallerID = uuid; + } + + if(session->mHandle.empty()) + { + // Session isn't active -- start it up. + sessionCreateSendMessage(session, false, true); + } + else + { + // Session is already active -- start up text. + sessionTextConnectSendMessage(session); + } + + return session; +} + +BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) +{ + bool result = false; + + // Attempt to locate the indicated session + sessionState *session = startUserIMSession(participant_id); + if(session) + { + // found the session, attempt to send the message + session->mTextMsgQueue.push(message); + + // Try to send queued messages (will do nothing if the session is not open yet) + sendQueuedTextMessages(session); + + // The message is queued, so we succeed. + result = true; + } + else + { + LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; + } + + return result; +} + +void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session) +{ + if(session->mTextStreamState == 1) + { + if(!session->mTextMsgQueue.empty()) + { + std::ostringstream stream; + + while(!session->mTextMsgQueue.empty()) + { + std::string message = session->mTextMsgQueue.front(); + session->mTextMsgQueue.pop(); + stream + << "" + << "" << session->mHandle << "" + << "text/HTML" + << "" << message << "" + << "" + << "\n\n\n"; + } + writeString(stream.str()); + } + } + else + { + // Session isn't connected yet, defer until later. + } +} + +void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) +{ + // Figure out if a session with the user exists + sessionState *session = findSession(uuid); + if(session) + { + // found the session + if(!session->mHandle.empty()) + { + sessionTextDisconnectSendMessage(session); + } + } + else + { + LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; + } +} +bool LLVivoxVoiceClient::isValidChannel(std::string &sessionHandle) +{ + return(findSession(sessionHandle) != NULL); + +} +bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle) +{ + // this is only ever used to answer incoming p2p call invites. + + sessionState *session = findSession(sessionHandle); + if(session) + { + session->mIsSpatial = false; + session->mReconnect = false; + session->mIsP2P = true; + + joinSession(session); + return true; + } + + return false; +} + +BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id) +{ + bool result = false; + buddyListEntry *buddy = findBuddy(id); + if(buddy) + { + result = buddy->mOnlineSLim; + LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL; + } + + if(!result) + { + // This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM. + sessionState *session = findSession(id); + if(session && !session->mHandle.empty()) + { + if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle)) + { + LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL; + // we have a p2p text session open with this user, so by definition they're online. + result = true; + } + } + } + + return result; +} + +bool LLVivoxVoiceClient::isVoiceWorking() const +{ + //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) + // Condition with joining spatial num was added to take into account possible problems with connection to voice + // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. + return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); +} + +// Returns true if the indicated participant in the current audio session is really an SL avatar. +// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. +BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) +{ + BOOL result = TRUE; + sessionState *session = findSession(id); + + if(session != NULL) + { + // this is a p2p session with the indicated caller, or the session with the specified UUID. + if(session->mSynthesizedCallerID) + result = FALSE; + } + else + { + // Didn't find a matching session -- check the current audio session for a matching participant + if(mAudioSession != NULL) + { + participantState *participant = findParticipantByID(id); + if(participant != NULL) + { + result = participant->isAvatar(); + } + } + } + + return result; +} + +// Returns true if calling back the session URI after the session has closed is possible. +// Currently this will be false only for PSTN P2P calls. +BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) +{ + BOOL result = TRUE; + sessionState *session = findSession(session_id); + + if(session != NULL) + { + result = session->isCallBackPossible(); + } + + return result; +} + +// Returns true if the session can accepte text IM's. +// Currently this will be false only for PSTN P2P calls. +BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) +{ + bool result = TRUE; + sessionState *session = findSession(session_id); + + if(session != NULL) + { + result = session->isTextIMPossible(); + } + + return result; +} + + +void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle) +{ + sessionState *session = findSession(sessionHandle); + if(session) + { + sessionMediaDisconnectSendMessage(session); + } +} + +void LLVivoxVoiceClient::leaveNonSpatialChannel() +{ + LL_DEBUGS("Voice") + << "called in state " << state2string(getState()) + << LL_ENDL; + + // Make sure we don't rejoin the current session. + sessionState *oldNextSession = mNextAudioSession; + mNextAudioSession = NULL; + + // Most likely this will still be the current session at this point, but check it anyway. + reapSession(oldNextSession); + + verifySessionState(); + + sessionTerminate(); +} + +std::string LLVivoxVoiceClient::getCurrentChannel() +{ + std::string result; + + if((getState() == stateRunning) && !mSessionTerminateRequested) + { + result = getAudioSessionURI(); + } + + return result; +} + +bool LLVivoxVoiceClient::inProximalChannel() +{ + bool result = false; + + if((getState() == stateRunning) && !mSessionTerminateRequested) + { + result = inSpatialChannel(); + } + + return result; +} + +std::string LLVivoxVoiceClient::sipURIFromID(const LLUUID &id) +{ + std::string result; + result = "sip:"; + result += nameFromID(id); + result += "@"; + result += mVoiceSIPURIHostName; + + return result; +} + +std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) +{ + std::string result; + if(avatar) + { + result = "sip:"; + result += nameFromID(avatar->getID()); + result += "@"; + result += mVoiceSIPURIHostName; + } + + return result; +} + +std::string LLVivoxVoiceClient::nameFromAvatar(LLVOAvatar *avatar) +{ + std::string result; + if(avatar) + { + result = nameFromID(avatar->getID()); + } + return result; +} + +std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid) +{ + std::string result; + + if (uuid.isNull()) { + //VIVOX, the uuid emtpy look for the mURIString and return that instead. + //result.assign(uuid.mURIStringName); + LLStringUtil::replaceChar(result, '_', ' '); + return result; + } + // Prepending this apparently prevents conflicts with reserved names inside the vivox code. + result = "x"; + + // Base64 encode and replace the pieces of base64 that are less compatible + // with e-mail local-parts. + // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" + result += LLBase64::encode(uuid.mData, UUID_BYTES); + LLStringUtil::replaceChar(result, '+', '-'); + LLStringUtil::replaceChar(result, '/', '_'); + + // If you need to transform a GUID to this form on the Mac OS X command line, this will do so: + // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') + + // The reverse transform can be done with: + // echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p + + return result; +} + +bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) +{ + bool result = false; + + // SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com" + // If it is, convert to a bare name before doing the transform. + std::string name = nameFromsipURI(inName); + + // Doesn't look like a SIP URI, assume it's an actual name. + if(name.empty()) + name = inName; + + // This will only work if the name is of the proper form. + // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: + // "xFnPP04IpREWNkuw1cOXlhw==" + + if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) + { + // The name appears to have the right form. + + // Reverse the transforms done by nameFromID + std::string temp = name; + LLStringUtil::replaceChar(temp, '-', '+'); + LLStringUtil::replaceChar(temp, '_', '/'); + + U8 rawuuid[UUID_BYTES + 1]; + int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); + if(len == UUID_BYTES) + { + // The decode succeeded. Stuff the bits into the result's UUID + memcpy(uuid.mData, rawuuid, UUID_BYTES); + result = true; + } + } + + if(!result) + { + // VIVOX: not a standard account name, just copy the URI name mURIString field + // and hope for the best. bpj + uuid.setNull(); // VIVOX, set the uuid field to nulls + } + + return result; +} + +std::string LLVivoxVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) +{ + return avatar->getFullname(); +} + +std::string LLVivoxVoiceClient::sipURIFromName(std::string &name) +{ + std::string result; + result = "sip:"; + result += name; + result += "@"; + result += mVoiceSIPURIHostName; + +// LLStringUtil::toLower(result); + + return result; +} + +std::string LLVivoxVoiceClient::nameFromsipURI(const std::string &uri) +{ + std::string result; + + std::string::size_type sipOffset, atOffset; + sipOffset = uri.find("sip:"); + atOffset = uri.find("@"); + if((sipOffset != std::string::npos) && (atOffset != std::string::npos)) + { + result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); + } + + return result; +} + +bool LLVivoxVoiceClient::inSpatialChannel(void) +{ + bool result = false; + + if(mAudioSession) + result = mAudioSession->mIsSpatial; + + return result; +} + +std::string LLVivoxVoiceClient::getAudioSessionURI() +{ + std::string result; + + if(mAudioSession) + result = mAudioSession->mSIPURI; + + return result; +} + +std::string LLVivoxVoiceClient::getAudioSessionHandle() +{ + std::string result; + + if(mAudioSession) + result = mAudioSession->mHandle; + + return result; +} + + +///////////////////////////// +// Sending updates of current state + +void LLVivoxVoiceClient::enforceTether(void) +{ + LLVector3d tethered = mCameraRequestedPosition; + + // constrain 'tethered' to within 50m of mAvatarPosition. + { + F32 max_dist = 50.0f; + LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; + F32 camera_distance = (F32)camera_offset.magVec(); + if(camera_distance > max_dist) + { + tethered = mAvatarPosition + + (max_dist / camera_distance) * camera_offset; + } + } + + if(dist_vec_squared(mCameraPosition, tethered) > 0.01) + { + mCameraPosition = tethered; + mSpatialCoordsDirty = true; + } +} + +void LLVivoxVoiceClient::updatePosition(void) +{ + + LLViewerRegion *region = gAgent.getRegion(); + if(region && isAgentAvatarValid()) + { + LLMatrix3 rot; + LLVector3d pos; + + // TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... + // They're currently always set to zero. + + // Send the current camera position to the voice code + rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (), LLViewerCamera::getInstance()->getUpAxis()); + pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); + + LLVivoxVoiceClient::getInstance()->setCameraPosition( + pos, // position + LLVector3::zero, // velocity + rot); // rotation matrix + + // Send the current avatar position to the voice code + rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3(); + pos = gAgentAvatarp->getPositionGlobal(); + + // TODO: Can we get the head offset from outside the LLVOAvatar? + // pos += LLVector3d(mHeadOffset); + pos += LLVector3d(0.f, 0.f, 1.f); + + LLVivoxVoiceClient::getInstance()->setAvatarPosition( + pos, // position + LLVector3::zero, // velocity + rot); // rotation matrix + } +} + +void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ + mCameraRequestedPosition = position; + + if(mCameraVelocity != velocity) + { + mCameraVelocity = velocity; + mSpatialCoordsDirty = true; + } + + if(mCameraRot != rot) + { + mCameraRot = rot; + mSpatialCoordsDirty = true; + } +} + +void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ + if(dist_vec_squared(mAvatarPosition, position) > 0.01) + { + mAvatarPosition = position; + mSpatialCoordsDirty = true; + } + + if(mAvatarVelocity != velocity) + { + mAvatarVelocity = velocity; + mSpatialCoordsDirty = true; + } + + if(mAvatarRot != rot) + { + mAvatarRot = rot; + mSpatialCoordsDirty = true; + } +} + +bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) +{ + bool result = false; + + if(region) + { + name = region->getName(); + } + + if(!name.empty()) + result = true; + + return result; +} + +void LLVivoxVoiceClient::leaveChannel(void) +{ + if(getState() == stateRunning) + { + LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; + mChannelName.clear(); + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::setMuteMic(bool muted) +{ + if(mMuteMic != muted) + { + mMuteMic = muted; + mMuteMicDirty = true; + } +} + +void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) +{ + if (enabled != mVoiceEnabled) + { + // TODO: Refactor this so we don't call into LLVoiceChannel, but simply + // use the status observer + mVoiceEnabled = enabled; + LLVoiceClientStatusObserver::EStatusType status; + + + if (enabled) + { + LLVoiceChannel::getCurrentVoiceChannel()->activate(); + status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; + } + else + { + // Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. + LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); + status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; + } + notifyStatusObservers(status); + } +} + +bool LLVivoxVoiceClient::voiceEnabled() +{ + static const LLCachedControl enable_voice_chat("EnableVoiceChat",true); + static const LLCachedControl cmdline_disable_voice("CmdLineDisableVoice",false); + return enable_voice_chat && !cmdline_disable_voice; +} + +void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled) +{ + mLipSyncEnabled = enabled; +} + +BOOL LLVivoxVoiceClient::lipSyncEnabled() +{ + + if ( mVoiceEnabled && stateDisabled != getState() ) + { + return mLipSyncEnabled; + } + else + { + return FALSE; + } +} + + +void LLVivoxVoiceClient::setEarLocation(S32 loc) +{ + if(mEarLocation != loc) + { + LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; + + mEarLocation = loc; + mSpatialCoordsDirty = true; + } +} + +void LLVivoxVoiceClient::setVoiceVolume(F32 volume) +{ + int scaled_volume = scale_speaker_volume(volume); + + if(scaled_volume != mSpeakerVolume) + { + int min_volume = scale_speaker_volume(0); + if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) + { + mSpeakerMuteDirty = true; + } + + mSpeakerVolume = scaled_volume; + mSpeakerVolumeDirty = true; + } +} + +void LLVivoxVoiceClient::setMicGain(F32 volume) +{ + int scaled_volume = scale_mic_volume(volume); + + if(scaled_volume != mMicVolume) + { + mMicVolume = scaled_volume; + mMicVolumeDirty = true; + } +} + +///////////////////////////// +// Accessors for data related to nearby speakers +BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id) +{ + BOOL result = FALSE; + participantState *participant = findParticipantByID(id); + if(participant) + { + // I'm not sure what the semantics of this should be. + // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. + result = TRUE; + } + + return result; +} + +std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id) +{ + std::string result; + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mDisplayName; + } + + return result; +} + + + +BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) + { + participant->mIsSpeaking = FALSE; + } + result = participant->mIsSpeaking; + } + + return result; +} + +BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mIsModeratorMuted; + } + + return result; +} + +F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id) +{ + F32 result = 0; + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mPower; + } + + return result; +} + + + +BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + // I'm not sure what the semantics of this should be. + // Does "using PTT" mean they're configured with a push-to-talk button? + // For now, we know there's no PTT mechanism in place, so nobody is using it. + } + + return result; +} + +BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id) +{ + BOOL result = FALSE; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mOnMuteList; + } + + return result; +} + +// External accessors. +F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id) +{ + // Minimum volume will be returned for users with voice disabled + F32 result = LLVoiceClient::VOLUME_MIN; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mVolume; + + // Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging. + // LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL; + } + + return result; +} + +void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume) +{ + if(mAudioSession) + { + participantState *participant = findParticipantByID(id); + if (participant && !participant->mIsSelf) + { + if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT)) + { + // Store this volume setting for future sessions if it has been + // changed from the default + LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); + } + else + { + // Remove stored volume setting if it is returned to the default + LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); + } + + participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX); + participant->mVolumeDirty = true; + mAudioSession->mVolumeDirty = true; + } + } +} + +std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id) +{ + std::string result; + + participantState *participant = findParticipantByID(id); + if(participant) + { + result = participant->mGroupID; + } + + return result; +} + +BOOL LLVivoxVoiceClient::getAreaVoiceDisabled() +{ + return mAreaVoiceDisabled; +} + +void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; + + if(!mMainSessionGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << deltaFramesPerControlFrame << "" + << "" << "" << "" + << "false" + << "" << seconds << "" + << "\n\n\n"; + + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::recordingLoopSave(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Flush" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::recordingStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::filePlaybackStart(const std::string& filename) +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Start" + << "" << filename << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::filePlaybackStop() +{ +// LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; + + if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) + { + std::ostringstream stream; + stream + << "" + << "" << mMainSessionGroupHandle << "" + << "Stop" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::filePlaybackSetPaused(bool paused) +{ + // TODO: Implement once Vivox gives me a sample +} + +void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed) +{ + // TODO: Implement once Vivox gives me a sample +} + +LLVivoxVoiceClient::sessionState::sessionState() : + mErrorStatusCode(0), + mMediaStreamState(streamStateUnknown), + mTextStreamState(streamStateUnknown), + mCreateInProgress(false), + mMediaConnectInProgress(false), + mVoiceInvitePending(false), + mTextInvitePending(false), + mSynthesizedCallerID(false), + mIsChannel(false), + mIsSpatial(false), + mIsP2P(false), + mIncoming(false), + mVoiceEnabled(false), + mReconnect(false), + mVolumeDirty(false), + mMuteDirty(false), + mParticipantsChanged(false) +{ +} + +LLVivoxVoiceClient::sessionState::~sessionState() +{ + removeAllParticipants(); +} + +bool LLVivoxVoiceClient::sessionState::isCallBackPossible() +{ + // This may change to be explicitly specified by vivox in the future... + // Currently, only PSTN P2P calls cannot be returned. + // Conveniently, this is also the only case where we synthesize a caller UUID. + return !mSynthesizedCallerID; +} + +bool LLVivoxVoiceClient::sessionState::isTextIMPossible() +{ + // This may change to be explicitly specified by vivox in the future... + return !mSynthesizedCallerID; +} + + +LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsBegin(void) +{ + return mSessions.begin(); +} + +LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void) +{ + return mSessions.end(); +} + + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::string &handle) +{ + sessionState *result = NULL; + sessionMap::iterator iter = mSessionsByHandle.find(handle); + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) +{ + sessionState *result = NULL; + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + if(session->mCreateInProgress && (session->mSIPURI == uri)) + { + result = session; + break; + } + } + + return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &participant_id) +{ + sessionState *result = NULL; + + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) + { + result = session; + break; + } + } + + return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle) +{ + sessionState *result = NULL; + + if(handle.empty()) + { + // No handle supplied. + // Check whether there's already a session with this URI + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *s = *iter; + if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) + { + // TODO: I need to think about this logic... it's possible that this case should raise an internal error. + result = s; + break; + } + } + } + else // (!handle.empty()) + { + // Check for an existing session with this handle + sessionMap::iterator iter = mSessionsByHandle.find(handle); + + if(iter != mSessionsByHandle.end()) + { + result = iter->second; + } + } + + if(!result) + { + // No existing session found. + + LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; + result = new sessionState(); + result->mSIPURI = uri; + result->mHandle = handle; + + if (LLVoiceClient::instance().getVoiceEffectEnabled()) + { + result->mVoiceFontID = LLVoiceClient::instance().getVoiceEffectDefault(); + } + + mSessions.insert(result); + + if(!result->mHandle.empty()) + { + mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); + } + } + else + { + // Found an existing session + + if(uri != result->mSIPURI) + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; + setSessionURI(result, uri); + } + + if(handle != result->mHandle) + { + if(handle.empty()) + { + // There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. + LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; + } + else + { + // TODO: Should this be an internal error? + LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; + setSessionHandle(result, handle); + } + } + + LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; + } + + verifySessionState(); + + return result; +} + +void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) +{ + // Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. + + if(!session->mHandle.empty()) + { + // Remove session from the map if it should have been there. + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if(iter != mSessionsByHandle.end()) + { + if(iter->second != session) + { + LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; + } + + mSessionsByHandle.erase(iter); + } + else + { + LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; + } + } + + session->mHandle = handle; + + if(!handle.empty()) + { + mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session)); + } + + verifySessionState(); +} + +void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string &uri) +{ + // There used to be a map of session URIs to sessions, which made this complex.... + session->mSIPURI = uri; + + verifySessionState(); +} + +void LLVivoxVoiceClient::deleteSession(sessionState *session) +{ + // Remove the session from the handle map + if(!session->mHandle.empty()) + { + sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); + if(iter != mSessionsByHandle.end()) + { + if(iter->second != session) + { + LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; + } + mSessionsByHandle.erase(iter); + } + } + + // Remove the session from the URI map + mSessions.erase(session); + + // At this point, the session should be unhooked from all lists and all state should be consistent. + verifySessionState(); + + // If this is the current audio session, clean up the pointer which will soon be dangling. + if(mAudioSession == session) + { + mAudioSession = NULL; + mAudioSessionChanged = true; + } + + // ditto for the next audio session + if(mNextAudioSession == session) + { + mNextAudioSession = NULL; + } + + // delete the session + delete session; +} + +void LLVivoxVoiceClient::deleteAllSessions() +{ + LL_DEBUGS("Voice") << "called" << LL_ENDL; + + while(!mSessions.empty()) + { + deleteSession(*(sessionsBegin())); + } + + if(!mSessionsByHandle.empty()) + { + LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; + } +} + +void LLVivoxVoiceClient::verifySessionState(void) +{ + // This is mostly intended for debugging problems with session state management. + LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; + + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + + LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; + + if(!session->mHandle.empty()) + { + // every session with a non-empty handle needs to be in the handle map + sessionMap::iterator i2 = mSessionsByHandle.find(session->mHandle); + if(i2 == mSessionsByHandle.end()) + { + LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; + } + else + { + if(i2->second != session) + { + LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; + } + } + } + } + + // check that every entry in the handle map points to a valid session in the session set + for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) + { + sessionState *session = iter->second; + sessionIterator i2 = mSessions.find(session); + if(i2 == mSessions.end()) + { + LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; + } + else + { + if(session->mHandle != (*i2)->mHandle) + { + LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; + } + } + } +} + +LLVivoxVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : + mURI(uri) +{ + mOnlineSL = false; + mOnlineSLim = false; + mCanSeeMeOnline = true; + mHasBlockListEntry = false; + mHasAutoAcceptListEntry = false; + mNameResolved = false; + mInVivoxBuddies = false; + mInSLFriends = false; + mNeedsNameUpdate = false; +} + +void LLVivoxVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) +{ + buddyListEntry *buddy = addBuddy(uri, displayName); + buddy->mInVivoxBuddies = true; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri) +{ + std::string empty; + buddyListEntry *buddy = addBuddy(uri, empty); + if(buddy->mDisplayName.empty()) + { + buddy->mNameResolved = false; + } + return buddy; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri, const std::string &displayName) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter = mBuddyListMap.find(uri); + + if(iter != mBuddyListMap.end()) + { + // Found a matching buddy already in the map. + LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; + result = iter->second; + } + + if(!result) + { + // participant isn't already in one list or the other. + LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; + result = new buddyListEntry(uri); + result->mDisplayName = displayName; + + if(IDFromName(uri, result->mUUID)) + { + // Extracted UUID from name successfully. + } + else + { + LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; + } + + mBuddyListMap.insert(buddyListMap::value_type(result->mURI, result)); + } + + return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const std::string &uri) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter = mBuddyListMap.find(uri); + if(iter != mBuddyListMap.end()) + { + result = iter->second; + } + + return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const LLUUID &id) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter; + + for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) + { + if(iter->second->mUUID == id) + { + result = iter->second; + break; + } + } + + return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddyByDisplayName(const std::string &name) +{ + buddyListEntry *result = NULL; + buddyListMap::iterator iter; + + for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) + { + if(iter->second->mDisplayName == name) + { + result = iter->second; + break; + } + } + + return result; +} + +void LLVivoxVoiceClient::deleteBuddy(const std::string &uri) +{ + buddyListMap::iterator iter = mBuddyListMap.find(uri); + if(iter != mBuddyListMap.end()) + { + LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL; + buddyListEntry *buddy = iter->second; + mBuddyListMap.erase(iter); + delete buddy; + } + else + { + LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL; + } + +} + +void LLVivoxVoiceClient::deleteAllBuddies(void) +{ + while(!mBuddyListMap.empty()) + { + deleteBuddy(mBuddyListMap.begin()->first); + } + + // Don't want to correlate with friends list when we've emptied the buddy list. + mBuddyListMapPopulated = false; + + // Don't want to correlate with friends list when we've reset the block rules. + mBlockRulesListReceived = false; + mAutoAcceptRulesListReceived = false; +} + +void LLVivoxVoiceClient::deleteAllBlockRules(void) +{ + // Clear the block list entry flags from all local buddy list entries + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + { + buddy_it->second->mHasBlockListEntry = false; + } +} + +void LLVivoxVoiceClient::deleteAllAutoAcceptRules(void) +{ + // Clear the auto-accept list entry flags from all local buddy list entries + buddyListMap::iterator buddy_it; + for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) + { + buddy_it->second->mHasAutoAcceptListEntry = false; + } +} + +void LLVivoxVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly) +{ + buddyListEntry *buddy = NULL; + + // blockMask is the SIP URI of a friends list entry + buddyListMap::iterator iter = mBuddyListMap.find(blockMask); + if(iter != mBuddyListMap.end()) + { + LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL; + buddy = iter->second; + } + + if(buddy == NULL) + { + LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL; + buddy = addBuddy(blockMask); + } + + if(buddy != NULL) + { + buddy->mHasBlockListEntry = true; + } +} + +void LLVivoxVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy) +{ + buddyListEntry *buddy = NULL; + + // blockMask is the SIP URI of a friends list entry + buddyListMap::iterator iter = mBuddyListMap.find(autoAcceptMask); + if(iter != mBuddyListMap.end()) + { + LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL; + buddy = iter->second; + } + + if(buddy == NULL) + { + LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL; + buddy = addBuddy(autoAcceptMask); + } + + if(buddy != NULL) + { + buddy->mHasAutoAcceptListEntry = true; + } +} + +void LLVivoxVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) +{ + // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. + mBlockRulesListReceived = true; +} + +void LLVivoxVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString) +{ + // Block list entries were updated via addBlockRule() during parsing. Just flag that we're done. + mAutoAcceptRulesListReceived = true; +} + +void LLVivoxVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +{ + mParticipantObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyParticipantObservers() +{ + for (observer_set_t::iterator it = mParticipantObservers.begin(); + it != mParticipantObservers.end(); + ) + { + LLVoiceClientParticipantObserver* observer = *it; + observer->onParticipantsChanged(); + // In case onParticipantsChanged() deleted an entry. + it = mParticipantObservers.upper_bound(observer); + } +} + +void LLVivoxVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) +{ + mStatusObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) +{ + if(mAudioSession) + { + if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) + { + switch(mAudioSession->mErrorStatusCode) + { + case 20713: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; break; + case 20714: status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; break; + case 20715: + //invalid channel, we may be using a set of poorly cached + //info + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + case 1009: + //invalid username and password + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + break; + } + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + } + else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + switch(mAudioSession->mErrorStatusCode) + { + case 404: // NOT_FOUND + case 480: // TEMPORARILY_UNAVAILABLE + case 408: // REQUEST_TIMEOUT + // call failed because other user was not available + // treat this as an error case + status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + + // Reset the error code to make sure it won't be reused later by accident. + mAudioSession->mErrorStatusCode = 0; + break; + } + } + } + + LL_DEBUGS("Voice") + << " " << LLVoiceClientStatusObserver::status2string(status) + << ", session URI " << getAudioSessionURI() + << (inSpatialChannel()?", proximal is true":", proximal is false") + << LL_ENDL; + + for (status_observer_set_t::iterator it = mStatusObservers.begin(); + it != mStatusObservers.end(); + ) + { + LLVoiceClientStatusObserver* observer = *it; + observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); + // In case onError() deleted an entry. + it = mStatusObservers.upper_bound(observer); + } + + // skipped to avoid speak button blinking + if ( status != LLVoiceClientStatusObserver::STATUS_JOINING + && status != LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) + { + bool voice_status = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking(); + + gAgent.setVoiceConnected(voice_status); + + /*if (voice_status) + { + LLFirstUse::speak(true); + }*/ + } +} + +void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer) +{ + mFriendObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLFriendObserver* observer) +{ + mFriendObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyFriendObservers() +{ + for (friend_observer_set_t::iterator it = mFriendObservers.begin(); + it != mFriendObservers.end(); + ) + { + LLFriendObserver* observer = *it; + it++; + // The only friend-related thing we notify on is online/offline transitions. + observer->changed(LLFriendObserver::ONLINE); + } +} + +void LLVivoxVoiceClient::lookupName(const LLUUID &id) +{ + LLAvatarNameCache::get(id, + boost::bind(&LLVivoxVoiceClient::onAvatarNameCache, + this, _1, _2)); +} + +void LLVivoxVoiceClient::onAvatarNameCache(const LLUUID& agent_id, + const LLAvatarName& av_name) +{ + // For Vivox, we use the legacy name because I'm uncertain whether or + // not their service can tolerate switching to Username or Display Name + std::string legacy_name = av_name.getLegacyName(); + avatarNameResolved(agent_id, legacy_name); +} + +void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +{ + // If the avatar whose name just resolved is on our friends list, resync the friends list. + if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL) + { + mFriendsListDirty = true; + } + // Iterate over all sessions. + for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) + { + sessionState *session = *iter; + // Check for this user as a participant in this session + participantState *participant = session->findParticipantByID(id); + if(participant) + { + // Found -- fill in the name + participant->mAccountName = name; + // and post a "participants updated" message to listeners later. + session->mParticipantsChanged = true; + } + + // Check whether this is a p2p session whose caller name just resolved + if(session->mCallerID == id) + { + // this session's "caller ID" just resolved. Fill in the name. + session->mName = name; + if(session->mTextInvitePending) + { + session->mTextInvitePending = false; + + // We don't need to call LLIMMgr::getInstance()->addP2PSession() here. The first incoming message will create the panel. + } + if(session->mVoiceInvitePending) + { + session->mVoiceInvitePending = false; + + LLIMMgr::getInstance()->inviteToSession( + session->mIMSessionID, + session->mName, + session->mCallerID, + session->mName, + IM_SESSION_P2P_INVITE, + LLIMMgr::INVITATION_TYPE_VOICE, + session->mHandle, + session->mSIPURI); + } + + } + } +} + +bool LLVivoxVoiceClient::setVoiceEffect(const LLUUID& id) +{ + if (!mAudioSession) + { + return false; + } + + if (!id.isNull()) + { + if (mVoiceFontMap.empty()) + { + LL_DEBUGS("Voice") << "Voice fonts not available." << LL_ENDL; + return false; + } + else if (mVoiceFontMap.find(id) == mVoiceFontMap.end()) + { + LL_DEBUGS("Voice") << "Invalid voice font " << id << LL_ENDL; + return false; + } + } + + // *TODO: Check for expired fonts? + mAudioSession->mVoiceFontID = id; + + // *TODO: Separate voice font defaults for spatial chat and IM? + gSavedPerAccountSettings.setString("VoiceEffectDefault", id.asString()); + + sessionSetVoiceFontSendMessage(mAudioSession); + notifyVoiceFontObservers(); + + return true; +} + +const LLUUID LLVivoxVoiceClient::getVoiceEffect() +{ + return mAudioSession ? mAudioSession->mVoiceFontID : LLUUID::null; +} + +LLSD LLVivoxVoiceClient::getVoiceEffectProperties(const LLUUID& id) +{ + LLSD sd; + + voice_font_map_t::iterator iter = mVoiceFontMap.find(id); + if (iter != mVoiceFontMap.end()) + { + sd["template_only"] = false; + } + else + { + // Voice effect is not in the voice font map, see if there is a template + iter = mVoiceFontTemplateMap.find(id); + if (iter == mVoiceFontTemplateMap.end()) + { + LL_WARNS("Voice") << "Voice effect " << id << "not found." << LL_ENDL; + return sd; + } + sd["template_only"] = true; + } + + voiceFontEntry *font = iter->second; + sd["name"] = font->mName; + sd["expiry_date"] = font->mExpirationDate; + sd["is_new"] = font->mIsNew; + + return sd; +} + +LLVivoxVoiceClient::voiceFontEntry::voiceFontEntry(LLUUID& id) : + mID(id), + mFontIndex(0), + mFontType(VOICE_FONT_TYPE_NONE), + mFontStatus(VOICE_FONT_STATUS_NONE), + mIsNew(false) +{ + mExpiryTimer.stop(); + mExpiryWarningTimer.stop(); +} + +LLVivoxVoiceClient::voiceFontEntry::~voiceFontEntry() +{ +} + +void LLVivoxVoiceClient::refreshVoiceEffectLists(bool clear_lists) +{ + if (clear_lists) + { + mVoiceFontsReceived = false; + deleteAllVoiceFonts(); + deleteVoiceFontTemplates(); + } + + accountGetSessionFontsSendMessage(); + accountGetTemplateFontsSendMessage(); +} + +const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectList() const +{ + return mVoiceFontList; +} + +const voice_effect_list_t& LLVivoxVoiceClient::getVoiceEffectTemplateList() const +{ + return mVoiceFontTemplateList; +} + +void LLVivoxVoiceClient::addVoiceFont(const S32 font_index, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font) +{ + // Vivox SessionFontIDs are not guaranteed to remain the same between + // sessions or grids so use a UUID for the name. + + // If received name is not a UUID, fudge one by hashing the name and type. + LLUUID font_id; + if (LLUUID::validate(name)) + { + font_id = LLUUID(name); + } + else + { + font_id.generate(STRINGIZE(font_type << ":" << name)); + } + + voiceFontEntry *font = NULL; + + voice_font_map_t& font_map = template_font ? mVoiceFontTemplateMap : mVoiceFontMap; + voice_effect_list_t& font_list = template_font ? mVoiceFontTemplateList : mVoiceFontList; + + // Check whether we've seen this font before. + voice_font_map_t::iterator iter = font_map.find(font_id); + bool new_font = (iter == font_map.end()); + + // Override the has_expired flag if we have passed the expiration_date as a double check. + if (expiration_date.secondsSinceEpoch() < (LLDate::now().secondsSinceEpoch() + VOICE_FONT_EXPIRY_INTERVAL)) + { + has_expired = true; + } + + if (has_expired) + { + LL_DEBUGS("Voice") << "Expired " << (template_font ? "Template " : "") + << expiration_date.asString() << " " << font_id + << " (" << font_index << ") " << name << LL_ENDL; + + // Remove existing session fonts that have expired since we last saw them. + if (!new_font && !template_font) + { + deleteVoiceFont(font_id); + } + return; + } + + if (new_font) + { + // If it is a new font create a new entry. + font = new voiceFontEntry(font_id); + } + else + { + // Not a new font, update the existing entry + font = iter->second; + } + + if (font) + { + font->mFontIndex = font_index; + // Use the description for the human readable name if available, as the + // "name" may be a UUID. + font->mName = description.empty() ? name : description; + font->mFontType = font_type; + font->mFontStatus = font_status; + + // If the font is new or the expiration date has changed the expiry timers need updating. + if (!template_font && (new_font || font->mExpirationDate != expiration_date)) + { + font->mExpirationDate = expiration_date; + + // Set the expiry timer to trigger a notification when the voice font can no longer be used. + font->mExpiryTimer.start(); + font->mExpiryTimer.setExpiryAt(expiration_date.secondsSinceEpoch() - VOICE_FONT_EXPIRY_INTERVAL); + + // Set the warning timer to some interval before actual expiry. + S32 warning_time = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); + if (warning_time != 0) + { + font->mExpiryWarningTimer.start(); + F64 expiry_time = (expiration_date.secondsSinceEpoch() - (F64)warning_time); + font->mExpiryWarningTimer.setExpiryAt(expiry_time - VOICE_FONT_EXPIRY_INTERVAL); + } + else + { + // Disable the warning timer. + font->mExpiryWarningTimer.stop(); + } + + // Only flag new session fonts after the first time we have fetched the list. + if (mVoiceFontsReceived) + { + font->mIsNew = true; + mVoiceFontsNew = true; + } + } + + LL_DEBUGS("Voice") << (template_font ? "Template " : "") + << font->mExpirationDate.asString() << " " << font->mID + << " (" << font->mFontIndex << ") " << name << LL_ENDL; + + if (new_font) + { + font_map.insert(voice_font_map_t::value_type(font->mID, font)); + font_list.insert(voice_effect_list_t::value_type(font->mName, font->mID)); + } + + mVoiceFontListDirty = true; + + // Debugging stuff + + if (font_type < VOICE_FONT_TYPE_NONE || font_type >= VOICE_FONT_TYPE_UNKNOWN) + { + LL_DEBUGS("Voice") << "Unknown voice font type: " << font_type << LL_ENDL; + } + if (font_status < VOICE_FONT_STATUS_NONE || font_status >= VOICE_FONT_STATUS_UNKNOWN) + { + LL_DEBUGS("Voice") << "Unknown voice font status: " << font_status << LL_ENDL; + } + } +} + +void LLVivoxVoiceClient::expireVoiceFonts() +{ + // *TODO: If we are selling voice fonts in packs, there are probably + // going to be a number of fonts with the same expiration time, so would + // be more efficient to just keep a list of expiration times rather + // than checking each font individually. + + bool have_expired = false; + bool will_expire = false; + bool expired_in_use = false; + + LLUUID current_effect = LLVoiceClient::instance().getVoiceEffectDefault(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + voiceFontEntry* voice_font = iter->second; + LLFrameTimer& expiry_timer = voice_font->mExpiryTimer; + LLFrameTimer& warning_timer = voice_font->mExpiryWarningTimer; + + // Check for expired voice fonts + if (expiry_timer.getStarted() && expiry_timer.hasExpired()) + { + // Check whether it is the active voice font + if (voice_font->mID == current_effect) + { + // Reset to no voice effect. + setVoiceEffect(LLUUID::null); + expired_in_use = true; + } + + LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " has expired." << LL_ENDL; + deleteVoiceFont(voice_font->mID); + have_expired = true; + } + + // Check for voice fonts that will expire in less that the warning time + if (warning_timer.getStarted() && warning_timer.hasExpired()) + { + LL_DEBUGS("Voice") << "Voice Font " << voice_font->mName << " will expire soon." << LL_ENDL; + will_expire = true; + warning_timer.stop(); + } + } + + LLSD args; + args["URL"] = LLTrans::getString("voice_morphing_url"); + + // Give a notification if any voice fonts have expired. + if (have_expired) + { + if (expired_in_use) + { + LLNotificationsUtil::add("VoiceEffectsExpiredInUse", args); + } + else + { + LLNotificationsUtil::add("VoiceEffectsExpired", args); + } + + // Refresh voice font lists in the UI. + notifyVoiceFontObservers(); + } + + // Give a warning notification if any voice fonts are due to expire. + if (will_expire) + { + S32 seconds = gSavedSettings.getS32("VoiceEffectExpiryWarningTime"); + args["INTERVAL"] = llformat("%d", seconds / SEC_PER_DAY); + + LLNotificationsUtil::add("VoiceEffectsWillExpire", args); + } +} + +void LLVivoxVoiceClient::deleteVoiceFont(const LLUUID& id) +{ + // Remove the entry from the voice font list. + voice_effect_list_t::iterator list_iter = mVoiceFontList.begin(); + while (list_iter != mVoiceFontList.end()) + { + if (list_iter->second == id) + { + LL_DEBUGS("Voice") << "Removing " << id << " from the voice font list." << LL_ENDL; + mVoiceFontList.erase(list_iter++); + mVoiceFontListDirty = true; + } + else + { + ++list_iter; + } + } + + // Find the entry in the voice font map and erase its data. + voice_font_map_t::iterator map_iter = mVoiceFontMap.find(id); + if (map_iter != mVoiceFontMap.end()) + { + delete map_iter->second; + } + + // Remove the entry from the voice font map. + mVoiceFontMap.erase(map_iter); +} + +void LLVivoxVoiceClient::deleteAllVoiceFonts() +{ + mVoiceFontList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontMap.begin(); iter != mVoiceFontMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontMap.clear(); +} + +void LLVivoxVoiceClient::deleteVoiceFontTemplates() +{ + mVoiceFontTemplateList.clear(); + + voice_font_map_t::iterator iter; + for (iter = mVoiceFontTemplateMap.begin(); iter != mVoiceFontTemplateMap.end(); ++iter) + { + delete iter->second; + } + mVoiceFontTemplateMap.clear(); +} + +S32 LLVivoxVoiceClient::getVoiceFontIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontMap.find(id); + if (it != mVoiceFontMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +S32 LLVivoxVoiceClient::getVoiceFontTemplateIndex(const LLUUID& id) const +{ + S32 result = 0; + if (!id.isNull()) + { + voice_font_map_t::const_iterator it = mVoiceFontTemplateMap.find(id); + if (it != mVoiceFontTemplateMap.end()) + { + result = it->second->mFontIndex; + } + else + { + LL_DEBUGS("Voice") << "Selected voice font template " << id << " is not available." << LL_ENDL; + } + } + return result; +} + +void LLVivoxVoiceClient::accountGetSessionFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Requesting voice font list." << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::accountGetTemplateFontsSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Requesting voice font template list." << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::sessionSetVoiceFontSendMessage(sessionState *session) +{ + S32 font_index = getVoiceFontIndex(session->mVoiceFontID); + LL_DEBUGS("Voice") << "Requesting voice font: " << session->mVoiceFontID << " (" << font_index << "), session handle: " << session->mHandle << LL_ENDL; + + std::ostringstream stream; + + stream + << "" + << "" << session->mHandle << "" + << "" << font_index << "" + << "\n\n\n"; + + writeString(stream.str()); +} + +void LLVivoxVoiceClient::accountGetSessionFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + if(getState() == stateVoiceFontsWait) + { + setState(stateVoiceFontsReceived); + } + + notifyVoiceFontObservers(); + mVoiceFontsReceived = true; +} + +void LLVivoxVoiceClient::accountGetTemplateFontsResponse(int statusCode, const std::string &statusString) +{ + // Voice font list entries were updated via addVoiceFont() during parsing. + notifyVoiceFontObservers(); +} +void LLVivoxVoiceClient::addObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceEffectObserver* observer) +{ + mVoiceFontObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyVoiceFontObservers() +{ + LL_DEBUGS("Voice") << "Notifying voice effect observers. Lists changed: " << mVoiceFontListDirty << LL_ENDL; + + for (voice_font_observer_set_t::iterator it = mVoiceFontObservers.begin(); + it != mVoiceFontObservers.end(); + ) + { + LLVoiceEffectObserver* observer = *it; + observer->onVoiceEffectChanged(mVoiceFontListDirty); + // In case onVoiceEffectChanged() deleted an entry. + it = mVoiceFontObservers.upper_bound(observer); + } + mVoiceFontListDirty = false; + + // If new Voice Fonts have been added notify the user. + if (mVoiceFontsNew) + { + if(mVoiceFontsReceived) + { + LLNotificationsUtil::add("VoiceEffectsNew"); + } + mVoiceFontsNew = false; + } +} + +void LLVivoxVoiceClient::enablePreviewBuffer(bool enable) +{ + mCaptureBufferMode = enable; + if(mCaptureBufferMode && getState() >= stateNoChannel) + { + LL_DEBUGS("Voice") << "no channel" << LL_ENDL; + sessionTerminate(); + } +} + +void LLVivoxVoiceClient::recordPreviewBuffer() +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, cannot start recording." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + mCaptureBufferRecording = true; +} + +void LLVivoxVoiceClient::playPreviewBuffer(const LLUUID& effect_id) +{ + if (!mCaptureBufferMode) + { + LL_DEBUGS("Voice") << "Not in voice effect preview mode, no buffer to play." << LL_ENDL; + mCaptureBufferRecording = false; + return; + } + + if (!mCaptureBufferRecorded) + { + // Can't play until we have something recorded! + mCaptureBufferPlaying = false; + return; + } + + mPreviewVoiceFont = effect_id; + mCaptureBufferPlaying = true; +} + +void LLVivoxVoiceClient::stopPreviewBuffer() +{ + mCaptureBufferRecording = false; + mCaptureBufferPlaying = false; +} + +bool LLVivoxVoiceClient::isPreviewRecording() +{ + return (mCaptureBufferMode && mCaptureBufferRecording); +} + +bool LLVivoxVoiceClient::isPreviewPlaying() +{ + return (mCaptureBufferMode && mCaptureBufferPlaying); +} + +void LLVivoxVoiceClient::captureBufferRecordStartSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio capture to buffer." << LL_ENDL; + + // Start capture + stream + << "" + << "" + << "\n\n\n"; + + // Unmute the mic + stream << "" + << "" << mConnectorHandle << "" + << "false" + << "\n\n\n"; + + // Dirty the mute mic state so that it will get reset when we finishing previewing + mMuteMicDirty = true; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferRecordStopSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio capture to buffer." << LL_ENDL; + + // Mute the mic. Mic mute state was dirtied at recording start, so will be reset when finished previewing. + stream << "" + << "" << mConnectorHandle << "" + << "true" + << "\n\n\n"; + + // Stop capture + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferPlayStartSendMessage(const LLUUID& voice_font_id) +{ + if(!mAccountHandle.empty()) + { + // Track how may play requests are sent, so we know how many stop events to + // expect before play actually stops. + ++mPlayRequestCount; + + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Starting audio buffer playback." << LL_ENDL; + + S32 font_index = getVoiceFontTemplateIndex(voice_font_id); + LL_DEBUGS("Voice") << "With voice font: " << voice_font_id << " (" << font_index << ")" << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" << font_index << "" + << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +void LLVivoxVoiceClient::captureBufferPlayStopSendMessage() +{ + if(!mAccountHandle.empty()) + { + std::ostringstream stream; + + LL_DEBUGS("Voice") << "Stopping audio buffer playback." << LL_ENDL; + + stream + << "" + << "" << mAccountHandle << "" + << "" + << "\n\n\n"; + + writeString(stream.str()); + } +} + +LLVivoxProtocolParser::LLVivoxProtocolParser() +{ + parser = XML_ParserCreate(NULL); + + reset(); +} + +void LLVivoxProtocolParser::reset() +{ + responseDepth = 0; + ignoringTags = false; + accumulateText = false; + energy = 0.f; + hasText = false; + hasAudio = false; + hasVideo = false; + terminated = false; + ignoreDepth = 0; + isChannel = false; + incoming = false; + enabled = false; + isEvent = false; + isLocallyMuted = false; + isModeratorMuted = false; + isSpeaking = false; + participantType = 0; + squelchDebugOutput = false; + returnCode = -1; + state = 0; + statusCode = 0; + volume = 0; + textBuffer.clear(); + alias.clear(); + numberOfAliases = 0; + applicationString.clear(); +} + +//virtual +LLVivoxProtocolParser::~LLVivoxProtocolParser() +{ + if (parser) + XML_ParserFree(parser); +} + +static LLFastTimer::DeclareTimer FTM_VIVOX_PROCESS("Vivox Process"); + +// virtual +LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump) +{ + LLFastTimer t(FTM_VIVOX_PROCESS); + LLBufferStream istr(channels, buffer.get()); + std::ostringstream ostr; + while (istr.good()) + { + char buf[1024]; + istr.read(buf, sizeof(buf)); + mInput.append(buf, istr.gcount()); + } + + // Look for input delimiter(s) in the input buffer. If one is found, send the message to the xml parser. + int start = 0; + int delim; + while((delim = mInput.find("\n\n\n", start)) != std::string::npos) + { + + // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) + reset(); + + XML_ParserReset(parser, NULL); + XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); + XML_SetCharacterDataHandler(parser, ExpatCharHandler); + XML_SetUserData(parser, this); + XML_Parse(parser, mInput.data() + start, delim - start, false); + + // If this message isn't set to be squelched, output the raw XML received. + if(!squelchDebugOutput) + { + LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; + } + + start = delim + 3; + } + + if(start != 0) + mInput = mInput.substr(start); + + LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; + + if(!LLVivoxVoiceClient::getInstance()->mConnected) + { + // If voice has been disabled, we just want to close the socket. This does so. + LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; + return STATUS_STOP; + } + + return STATUS_OK; +} + +void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) +{ + if (data) + { + LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; + object->StartTag(el, attr); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el) +{ + if (data) + { + LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; + object->EndTag(el); + } +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) +{ + if (data) + { + LLVivoxProtocolParser *object = (LLVivoxProtocolParser*)data; + object->CharData(s, len); + } +} + +// -------------------------------------------------------------------------------- + + +void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) +{ + // Reset the text accumulator. We shouldn't have strings that are interrupted by new tags + textBuffer.clear(); + // only accumulate text if we're not ignoring tags. + accumulateText = !ignoringTags; + + if (responseDepth == 0) + { + isEvent = !stricmp("Event", tag); + + if (!stricmp("Response", tag) || isEvent) + { + // Grab the attributes + while (*attr) + { + const char *key = *attr++; + const char *value = *attr++; + + if (!stricmp("requestId", key)) + { + requestId = value; + } + else if (!stricmp("action", key)) + { + actionString = value; + } + else if (!stricmp("type", key)) + { + eventTypeString = value; + } + } + } + LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + } + else + { + if (ignoringTags) + { + LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + else + { + LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")" << LL_ENDL; + + // Ignore the InputXml stuff so we don't get confused + if (!stricmp("InputXml", tag)) + { + ignoringTags = true; + ignoreDepth = responseDepth; + accumulateText = false; + + LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; + } + else if (!stricmp("CaptureDevices", tag)) + { + LLVivoxVoiceClient::getInstance()->clearCaptureDevices(); + } + else if (!stricmp("RenderDevices", tag)) + { + LLVivoxVoiceClient::getInstance()->clearRenderDevices(); + } + else if (!stricmp("CaptureDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("RenderDevice", tag)) + { + deviceString.clear(); + } + else if (!stricmp("Buddies", tag)) + { + LLVivoxVoiceClient::getInstance()->deleteAllBuddies(); + } + else if (!stricmp("BlockRules", tag)) + { + LLVivoxVoiceClient::getInstance()->deleteAllBlockRules(); + } + else if (!stricmp("AutoAcceptRules", tag)) + { + LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules(); + } + else if (!stricmp("SessionFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("TemplateFont", tag)) + { + id = 0; + nameString.clear(); + descriptionString.clear(); + expirationDate = LLDate(); + hasExpired = false; + fontType = 0; + fontStatus = 0; + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType.clear(); + } + } + } + responseDepth++; +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::EndTag(const char *tag) +{ + const std::string& string = textBuffer; + + responseDepth--; + + if (ignoringTags) + { + if (ignoreDepth == responseDepth) + { + LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL; + ignoringTags = false; + } + else + { + LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + } + } + + if (!ignoringTags) + { + LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; + + // Closing a tag. Finalize the text we've accumulated and reset + if (!stricmp("ReturnCode", tag)) + returnCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("SessionHandle", tag)) + sessionHandle = string; + else if (!stricmp("SessionGroupHandle", tag)) + sessionGroupHandle = string; + else if (!stricmp("StatusCode", tag)) + statusCode = strtol(string.c_str(), NULL, 10); + else if (!stricmp("StatusString", tag)) + statusString = string; + else if (!stricmp("ParticipantURI", tag)) + uriString = string; + else if (!stricmp("Volume", tag)) + volume = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Energy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("IsModeratorMuted", tag)) + isModeratorMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("IsSpeaking", tag)) + isSpeaking = !stricmp(string.c_str(), "true"); + else if (!stricmp("Alias", tag)) + alias = string; + else if (!stricmp("NumberOfAliases", tag)) + numberOfAliases = strtol(string.c_str(), NULL, 10); + else if (!stricmp("Application", tag)) + applicationString = string; + else if (!stricmp("ConnectorHandle", tag)) + connectorHandle = string; + else if (!stricmp("VersionID", tag)) + versionID = string; + else if (!stricmp("AccountHandle", tag)) + accountHandle = string; + else if (!stricmp("State", tag)) + state = strtol(string.c_str(), NULL, 10); + else if (!stricmp("URI", tag)) + uriString = string; + else if (!stricmp("IsChannel", tag)) + isChannel = !stricmp(string.c_str(), "true"); + else if (!stricmp("Incoming", tag)) + incoming = !stricmp(string.c_str(), "true"); + else if (!stricmp("Enabled", tag)) + enabled = !stricmp(string.c_str(), "true"); + else if (!stricmp("Name", tag)) + nameString = string; + else if (!stricmp("AudioMedia", tag)) + audioMediaString = string; + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("DisplayName", tag)) + displayNameString = string; + else if (!stricmp("Device", tag)) + deviceString = string; + else if (!stricmp("AccountName", tag)) + nameString = string; + else if (!stricmp("ParticipantType", tag)) + participantType = strtol(string.c_str(), NULL, 10); + else if (!stricmp("IsLocallyMuted", tag)) + isLocallyMuted = !stricmp(string.c_str(), "true"); + else if (!stricmp("MicEnergy", tag)) + energy = (F32)strtod(string.c_str(), NULL); + else if (!stricmp("ChannelName", tag)) + nameString = string; + else if (!stricmp("ChannelURI", tag)) + uriString = string; + else if (!stricmp("BuddyURI", tag)) + uriString = string; + else if (!stricmp("Presence", tag)) + statusString = string; + else if (!stricmp("CaptureDevice", tag)) + { + LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString); + } + else if (!stricmp("RenderDevice", tag)) + { + LLVivoxVoiceClient::getInstance()->addRenderDevice(deviceString); + } + else if (!stricmp("Buddy", tag)) + { + LLVivoxVoiceClient::getInstance()->processBuddyListEntry(uriString, displayNameString); + } + else if (!stricmp("BlockRule", tag)) + { + LLVivoxVoiceClient::getInstance()->addBlockRule(blockMask, presenceOnly); + } + else if (!stricmp("BlockMask", tag)) + blockMask = string; + else if (!stricmp("PresenceOnly", tag)) + presenceOnly = string; + else if (!stricmp("AutoAcceptRule", tag)) + { + LLVivoxVoiceClient::getInstance()->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy); + } + else if (!stricmp("AutoAcceptMask", tag)) + autoAcceptMask = string; + else if (!stricmp("AutoAddAsBuddy", tag)) + autoAddAsBuddy = string; + else if (!stricmp("MessageHeader", tag)) + messageHeader = string; + else if (!stricmp("MessageBody", tag)) + messageBody = string; + else if (!stricmp("NotificationType", tag)) + notificationType = string; + else if (!stricmp("HasText", tag)) + hasText = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasAudio", tag)) + hasAudio = !stricmp(string.c_str(), "true"); + else if (!stricmp("HasVideo", tag)) + hasVideo = !stricmp(string.c_str(), "true"); + else if (!stricmp("Terminated", tag)) + terminated = !stricmp(string.c_str(), "true"); + else if (!stricmp("SubscriptionHandle", tag)) + subscriptionHandle = string; + else if (!stricmp("SubscriptionType", tag)) + subscriptionType = string; + else if (!stricmp("SessionFont", tag)) + { + LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, false); + } + else if (!stricmp("TemplateFont", tag)) + { + LLVivoxVoiceClient::getInstance()->addVoiceFont(id, nameString, descriptionString, expirationDate, hasExpired, fontType, fontStatus, true); + } + else if (!stricmp("ID", tag)) + { + id = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Description", tag)) + { + descriptionString = string; + } + else if (!stricmp("ExpirationDate", tag)) + { + expirationDate = expiryTimeStampToLLDate(string); + } + else if (!stricmp("Expired", tag)) + { + hasExpired = !stricmp(string.c_str(), "1"); + } + else if (!stricmp("Type", tag)) + { + fontType = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("Status", tag)) + { + fontStatus = strtol(string.c_str(), NULL, 10); + } + else if (!stricmp("MediaCompletionType", tag)) + { + mediaCompletionType = string;; + } + + textBuffer.clear(); + accumulateText= false; + + if (responseDepth == 0) + { + // We finished all of the XML, process the data + processResponse(tag); + } + } +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::CharData(const char *buffer, int length) +{ + /* + This method is called for anything that isn't a tag, which can be text you + want that lies between tags, and a lot of stuff you don't want like file formatting + (tabs, spaces, CR/LF, etc). + + Only copy text if we are in accumulate mode... + */ + if (accumulateText) + textBuffer.append(buffer, length); +} + +// -------------------------------------------------------------------------------- + +LLDate LLVivoxProtocolParser::expiryTimeStampToLLDate(const std::string& vivox_ts) +{ + // *HACK: Vivox reports the time incorrectly. LLDate also only parses a + // subset of valid ISO 8601 dates (only handles Z, not offsets). + // So just use the date portion and fix the time here. + std::string time_stamp = vivox_ts.substr(0, 10); + time_stamp += VOICE_FONT_EXPIRY_TIME; + + LL_DEBUGS("VivoxProtocolParser") << "Vivox timestamp " << vivox_ts << " modified to: " << time_stamp << LL_ENDL; + + return LLDate(time_stamp); +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::processResponse(std::string tag) +{ + LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; + + // SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success. This is a change vs. previous SDKs. + // According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", + // so I believe this will give correct behavior. + + if(returnCode == 0) + statusCode = 0; + + if (isEvent) + { + const char *eventTypeCstr = eventTypeString.c_str(); + if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) + { + LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); + } + else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-1408789@bhr.vivox.com + true + false + + + */ + LLVivoxVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) + { + LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) + { + LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); + } + else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + 200 + OK + 2 + false + + */ + LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); + } + else if (!stricmp(eventTypeCstr, "MediaCompletionEvent")) + { + /* + + + AuxBufferAudioCapture + + */ + LLVivoxVoiceClient::getInstance()->mediaCompletionEvent(sessionGroupHandle, mediaCompletionType); + } + else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==1 + true + 1 + true + + */ + LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); + } + else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com + xI5auBZ60SJWIk606-1JGRQ== + + 0 + + */ + LLVivoxVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); + } + else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==4 + sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com + xtx7YNV-3SGiG7rA1fo5Ndw== + + */ + LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); + } + else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com + false + true + 44 + 0.0879437 + + */ + + // These happen so often that logging them is pretty useless. + squelchDebugOutput = true; + + LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); + } + else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) + { + // These are really spammy in tuning mode + squelchDebugOutput = true; + + LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); + } + else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) + { + LLVivoxVoiceClient::getInstance()->buddyPresenceEvent(uriString, alias, statusString, applicationString); + } + else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent")) + { + // The buddy list was updated during parsing. + // Need to recheck against the friends list. + LLVivoxVoiceClient::getInstance()->buddyListChanged(); + } + else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw== + sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com + Monroe Tester + + 0 + Set + + */ + // TODO: Question: Do we need to process this at all? + } + else if (!stricmp(eventTypeCstr, "MessageEvent")) + { + LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); + } + else if (!stricmp(eventTypeCstr, "SessionNotificationEvent")) + { + LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); + } + else if (!stricmp(eventTypeCstr, "SubscriptionEvent")) + { + LLVivoxVoiceClient::getInstance()->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType); + } + else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + c1_m1000xFnPP04IpREWNkuw1cOXlhw==0 + sip:confctl-9@bhd.vivox.com + 0 + 50 + 1 + 0 + 000 + 0 + + */ + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + + else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent")) + { + /* + + c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0 + + */ + // We don't need to process this, but we also shouldn't warn on it, since that confuses people. + } + else + { + LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; + } + } + else + { + const char *actionCstr = actionString.c_str(); + if (!stricmp(actionCstr, "Connector.Create.1")) + { + LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); + } + else if (!stricmp(actionCstr, "Account.Login.1")) + { + LLVivoxVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); + } + else if (!stricmp(actionCstr, "Session.Create.1")) + { + LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) + { + LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle); + } + else if (!stricmp(actionCstr, "Session.Connect.1")) + { + LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.Logout.1")) + { + LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) + { + LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.ListBlockRules.1")) + { + LLVivoxVoiceClient::getInstance()->accountListBlockRulesResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1")) + { + LLVivoxVoiceClient::getInstance()->accountListAutoAcceptRulesResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) + { + // We don't need to process these, but they're so spammy we don't want to log them. + squelchDebugOutput = true; + } + else if (!stricmp(actionCstr, "Account.GetSessionFonts.1")) + { + LLVivoxVoiceClient::getInstance()->accountGetSessionFontsResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Account.GetTemplateFonts.1")) + { + LLVivoxVoiceClient::getInstance()->accountGetTemplateFontsResponse(statusCode, statusString); + } + /* + else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) + { + LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); + } + else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) + { + + } + else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) + { + + } + else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) + { + + } + else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) + { + + } + else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) + { + + } + */ + } +} + diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h new file mode 100644 index 000000000..a270c2315 --- /dev/null +++ b/indra/newview/llvoicevivox.h @@ -0,0 +1,1016 @@ +/** + * @file llvoicevivox.h + * @brief Declaration of LLDiamondwareVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef LL_VOICE_VIVOX_H +#define LL_VOICE_VIVOX_H + + + +#include "llvoiceclient.h" + +class LLAvatarName; + + +class LLVivoxVoiceClient : public LLSingleton, + virtual public LLVoiceModuleInterface, + virtual public LLVoiceEffectInterface +{ + LOG_CLASS(LLVivoxVoiceClient); +public: + LLVivoxVoiceClient(); + virtual ~LLVivoxVoiceClient(); + + + /// @name LLVoiceModuleInterface virtual implementations + /// @see LLVoiceModuleInterface + //@{ + virtual void init(LLPumpIO *pump); // Call this once at application startup (creates connector) + virtual void terminate(); // Call this to clean up during shutdown + + virtual const LLVoiceVersionInfo& getVersion(); + + virtual void updateSettings(); // call after loading settings and whenever they change + + // Returns true if vivox has successfully logged in and is not in error state + virtual bool isVoiceWorking() const; + + ///////////////////// + /// @name Tuning + //@{ + virtual void tuningStart(); + virtual void tuningStop(); + virtual bool inTuningMode(); + + virtual void tuningSetMicVolume(float volume); + virtual void tuningSetSpeakerVolume(float volume); + virtual float tuningGetEnergy(void); + //@} + + ///////////////////// + /// @name Devices + //@{ + // This returns true when it's safe to bring up the "device settings" dialog in the prefs. + // i.e. when the daemon is running and connected, and the device lists are populated. + virtual bool deviceSettingsAvailable(); + + // Requery the vivox daemon for the current list of input/output devices. + // If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed + // (use this if you want to know when it's done). + // If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. + virtual void refreshDeviceLists(bool clearCurrentList = true); + + virtual void setCaptureDevice(const std::string& name); + virtual void setRenderDevice(const std::string& name); + + virtual LLVoiceDeviceList& getCaptureDevices(); + virtual LLVoiceDeviceList& getRenderDevices(); + //@} + + virtual void getParticipantList(std::set &participants); + virtual bool isParticipant(const LLUUID& speaker_id); + + // Send a text message to the specified user, initiating the session if necessary. + virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); + + // close any existing text IM session with the specified user + virtual void endUserIMSession(const LLUUID &uuid); + + // Returns true if calling back the session URI after the session has closed is possible. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); + + // Returns true if the session can accept text IM's. + // Currently this will be false only for PSTN P2P calls. + // NOTE: this will return true if the session can't be found. + virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); + + + //////////////////////////// + /// @name Channel stuff + //@{ + // returns true if the user is currently in a proximal (local spatial) channel. + // Note that gestures should only fire if this returns true. + virtual bool inProximalChannel(); + + virtual void setNonSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual void setSpatialChannel(const std::string &uri, + const std::string &credentials); + + virtual void leaveNonSpatialChannel(); + + virtual void leaveChannel(void); + + // Returns the URI of the current channel, or an empty string if not currently in a channel. + // NOTE that it will return an empty string if it's in the process of joining a channel. + virtual std::string getCurrentChannel(); + //@} + + + ////////////////////////// + /// @name invitations + //@{ + // start a voice channel with the specified user + virtual void callUser(const LLUUID &uuid); + virtual bool isValidChannel(std::string &channelHandle); + virtual bool answerInvite(std::string &channelHandle); + virtual void declineInvite(std::string &channelHandle); + //@} + + ///////////////////////// + /// @name Volume/gain + //@{ + virtual void setVoiceVolume(F32 volume); + virtual void setMicGain(F32 volume); + //@} + + ///////////////////////// + /// @name enable disable voice and features + //@{ + virtual bool voiceEnabled(); + virtual void setVoiceEnabled(bool enabled); + virtual BOOL lipSyncEnabled(); + virtual void setLipSyncEnabled(BOOL enabled); + virtual void setMuteMic(bool muted); // Set the mute state of the local mic. + //@} + + ////////////////////////// + /// @name nearby speaker accessors + //@{ + virtual BOOL getVoiceEnabled(const LLUUID& id); // true if we've received data for this avatar + virtual std::string getDisplayName(const LLUUID& id); + virtual BOOL isOnlineSIP(const LLUUID &id); + virtual BOOL isParticipantAvatar(const LLUUID &id); + virtual BOOL getIsSpeaking(const LLUUID& id); + virtual BOOL getIsModeratorMuted(const LLUUID& id); + virtual F32 getCurrentPower(const LLUUID& id); // "power" is related to "amplitude" in a defined way. I'm just not sure what the formula is... + virtual BOOL getOnMuteList(const LLUUID& id); + virtual F32 getUserVolume(const LLUUID& id); + virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal) + //@} + + // authorize the user + virtual void userAuthorized(const std::string& user_id, + const LLUUID &agentID); + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceClientStatusObserver* observer); + virtual void removeObserver(LLVoiceClientStatusObserver* observer); + virtual void addObserver(LLFriendObserver* observer); + virtual void removeObserver(LLFriendObserver* observer); + virtual void addObserver(LLVoiceClientParticipantObserver* observer); + virtual void removeObserver(LLVoiceClientParticipantObserver* observer); + //@} + + virtual std::string sipURIFromID(const LLUUID &id); + //@} + + /// @name LLVoiceEffectInterface virtual implementations + /// @see LLVoiceEffectInterface + //@{ + + ////////////////////////// + /// @name Accessors + //@{ + virtual bool setVoiceEffect(const LLUUID& id); + virtual const LLUUID getVoiceEffect(); + virtual LLSD getVoiceEffectProperties(const LLUUID& id); + + virtual void refreshVoiceEffectLists(bool clear_lists); + virtual const voice_effect_list_t& getVoiceEffectList() const; + virtual const voice_effect_list_t& getVoiceEffectTemplateList() const; + //@} + + ////////////////////////////// + /// @name Status notification + //@{ + virtual void addObserver(LLVoiceEffectObserver* observer); + virtual void removeObserver(LLVoiceEffectObserver* observer); + //@} + + ////////////////////////////// + /// @name Effect preview buffer + //@{ + virtual void enablePreviewBuffer(bool enable); + virtual void recordPreviewBuffer(); + virtual void playPreviewBuffer(const LLUUID& effect_id = LLUUID::null); + virtual void stopPreviewBuffer(); + + virtual bool isPreviewRecording(); + virtual bool isPreviewPlaying(); + //@} + + //@} + + +protected: + ////////////////////// + // Vivox Specific definitions + + friend class LLVivoxVoiceAccountProvisionResponder; + friend class LLVivoxVoiceClientMuteListObserver; + friend class LLVivoxVoiceClientFriendsObserver; + + enum streamState + { + streamStateUnknown = 0, + streamStateIdle = 1, + streamStateConnected = 2, + streamStateRinging = 3, + }; + struct participantState + { + public: + participantState(const std::string &uri); + + bool updateMuteState(); // true if mute state has changed + bool isAvatar(); + + std::string mURI; + LLUUID mAvatarID; + std::string mAccountName; + std::string mDisplayName; + LLFrameTimer mSpeakingTimeout; + F32 mLastSpokeTimestamp; + F32 mPower; + F32 mVolume; + std::string mGroupID; + int mUserVolume; + bool mPTT; + bool mIsSpeaking; + bool mIsModeratorMuted; + bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted) + bool mVolumeSet; // true if incoming volume messages should not change the volume + bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) + bool mAvatarIDValid; + bool mIsSelf; + }; + + typedef std::map participantMap; + typedef std::map participantUUIDMap; + + struct sessionState + { + public: + sessionState(); + ~sessionState(); + + participantState *addParticipant(const std::string &uri); + // Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. + // Take care not to use the pointer again after that. + void removeParticipant(participantState *participant); + void removeAllParticipants(); + + participantState *findParticipant(const std::string &uri); + participantState *findParticipantByID(const LLUUID& id); + + bool isCallBackPossible(); + bool isTextIMPossible(); + + std::string mHandle; + std::string mGroupHandle; + std::string mSIPURI; + std::string mAlias; + std::string mName; + std::string mAlternateSIPURI; + std::string mHash; // Channel password + std::string mErrorStatusString; + std::queue mTextMsgQueue; + + LLUUID mIMSessionID; + LLUUID mCallerID; + int mErrorStatusCode; + int mMediaStreamState; + int mTextStreamState; + bool mCreateInProgress; // True if a Session.Create has been sent for this session and no response has been received yet. + bool mMediaConnectInProgress; // True if a Session.MediaConnect has been sent for this session and no response has been received yet. + bool mVoiceInvitePending; // True if a voice invite is pending for this session (usually waiting on a name lookup) + bool mTextInvitePending; // True if a text invite is pending for this session (usually waiting on a name lookup) + bool mSynthesizedCallerID; // True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. + bool mIsChannel; // True for both group and spatial channels (false for p2p, PSTN) + bool mIsSpatial; // True for spatial channels + bool mIsP2P; + bool mIncoming; + bool mVoiceEnabled; + bool mReconnect; // Whether we should try to reconnect to this session if it's dropped + + // Set to true when the volume/mute state of someone in the participant list changes. + // The code will have to walk the list to find the changed participant(s). + bool mVolumeDirty; + bool mMuteDirty; + + bool mParticipantsChanged; + participantMap mParticipantsByURI; + participantUUIDMap mParticipantsByUUID; + + LLUUID mVoiceFontID; + }; + + // internal state for a simple state machine. This is used to deal with the asynchronous nature of some of the messages. + // Note: if you change this list, please make corresponding changes to LLVivoxVoiceClient::state2string(). + enum state + { + stateDisableCleanup, + stateDisabled, // Voice is turned off. + stateStart, // Class is initialized, socket is created + stateDaemonLaunched, // Daemon has been launched + stateConnecting, // connect() call has been issued + stateConnected, // connection to the daemon has been made, send some initial setup commands. + stateIdle, // socket is connected, ready for messaging + stateMicTuningStart, + stateMicTuningRunning, + stateMicTuningStop, + stateCaptureBufferPaused, + stateCaptureBufferRecStart, + stateCaptureBufferRecording, + stateCaptureBufferPlayStart, + stateCaptureBufferPlaying, + stateConnectorStart, // connector needs to be started + stateConnectorStarting, // waiting for connector handle + stateConnectorStarted, // connector handle received + stateLoginRetry, // need to retry login (failed due to changing password) + stateLoginRetryWait, // waiting for retry timer + stateNeedsLogin, // send login request + stateLoggingIn, // waiting for account handle + stateLoggedIn, // account handle received + stateVoiceFontsWait, // Awaiting the list of voice fonts + stateVoiceFontsReceived, // List of voice fonts received + stateCreatingSessionGroup, // Creating the main session group + stateNoChannel, // Need to join a channel + stateRetrievingParcelVoiceInfo, // waiting for parcel voice info request to return with spatial credentials + stateJoiningSession, // waiting for session handle + stateSessionJoined, // session handle received + stateRunning, // in session, steady state + stateLeavingSession, // waiting for terminate session response + stateSessionTerminated, // waiting for terminate session response + + stateLoggingOut, // waiting for logout response + stateLoggedOut, // logout response received + stateConnectorStopping, // waiting for connector stop + stateConnectorStopped, // connector stop received + + // We go to this state if the login fails because the account needs to be provisioned. + + // error states. No way to recover from these yet. + stateConnectorFailed, + stateConnectorFailedWaiting, + stateLoginFailed, + stateLoginFailedWaiting, + stateJoinSessionFailed, + stateJoinSessionFailedWaiting, + + stateJail // Go here when all else has failed. Nothing will be retried, we're done. + }; + + typedef std::map sessionMap; + + + + /////////////////////////////////////////////////////// + // Private Member Functions + ////////////////////////////////////////////////////// + + ////////////////////////////// + /// @name TVC/Server management and communication + //@{ + // Call this if the connection to the daemon terminates unexpectedly. It will attempt to reset everything and relaunch. + void daemonDied(); + + // Call this if we're just giving up on voice (can't provision an account, etc.). It will clean up and go away. + void giveUp(); + + // write to the tvc + bool writeString(const std::string &str); + + void connectorCreate(); + void connectorShutdown(); + void closeSocket(void); + + void requestVoiceAccountProvision(S32 retries = 3); + void login( + const std::string& account_name, + const std::string& password, + const std::string& voice_sip_uri_hostname, + const std::string& voice_account_server_uri); + void loginSendMessage(); + void logout(); + void logoutSendMessage(); + + + //@} + + //------------------------------------ + // tuning + + void tuningRenderStartSendMessage(const std::string& name, bool loop); + void tuningRenderStopSendMessage(); + + void tuningCaptureStartSendMessage(int duration); + void tuningCaptureStopSendMessage(); + + //---------------------------------- + // devices + void clearCaptureDevices(); + void addCaptureDevice(const std::string& name); + void clearRenderDevices(); + void addRenderDevice(const std::string& name); + void buildSetAudioDevices(std::ostringstream &stream); + + void getCaptureDevicesSendMessage(); + void getRenderDevicesSendMessage(); + + // local audio updates + void buildLocalAudioUpdates(std::ostringstream &stream); + + + ///////////////////////////// + // Response/Event handlers + void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); + void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); + void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); + void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); + void logoutResponse(int statusCode, std::string &statusString); + void connectorShutdownResponse(int statusCode, std::string &statusString); + + void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); + void mediaCompletionEvent(std::string &sessionGroupHandle, std::string &mediaCompletionType); + void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); + void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); + void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); + void sessionGroupAddedEvent(std::string &sessionGroupHandle); + void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); + void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); + void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); + void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); + void auxAudioPropertiesEvent(F32 energy); + void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); + void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); + void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); + void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); + + void buddyListChanged(); + void muteListChanged(); + void updateFriends(U32 mask); + + ///////////////////////////// + // Sending updates of current state + void updatePosition(void); + void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); + bool channelFromRegion(LLViewerRegion *region, std::string &name); + + void setEarLocation(S32 loc); + + + ///////////////////////////// + // Accessors for data related to nearby speakers + + // MBW -- XXX -- Not sure how to get this data out of the TVC + BOOL getUsingPTT(const LLUUID& id); + std::string getGroupID(const LLUUID& id); // group ID if the user is in group chat (empty string if not applicable) + + ///////////////////////////// + BOOL getAreaVoiceDisabled(); // returns true if the area the avatar is in is speech-disabled. + // Use this to determine whether to show a "no speech" icon in the menu bar. + + + ///////////////////////////// + // Recording controls + void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); + void recordingLoopSave(const std::string& filename); + void recordingStop(); + + // Playback controls + void filePlaybackStart(const std::string& filename); + void filePlaybackStop(); + void filePlaybackSetPaused(bool paused); + void filePlaybackSetMode(bool vox = false, float speed = 1.0f); + + participantState *findParticipantByID(const LLUUID& id); + + + //////////////////////////////////////// + // voice sessions. + typedef std::set sessionSet; + + typedef sessionSet::iterator sessionIterator; + sessionIterator sessionsBegin(void); + sessionIterator sessionsEnd(void); + + sessionState *findSession(const std::string &handle); + sessionState *findSessionBeingCreatedByURI(const std::string &uri); + sessionState *findSession(const LLUUID &participant_id); + sessionState *findSessionByCreateID(const std::string &create_id); + + sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); + void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); + void setSessionURI(sessionState *session, const std::string &uri); + void deleteSession(sessionState *session); + void deleteAllSessions(void); + + void verifySessionState(void); + + void joinedAudioSession(sessionState *session); + void leftAudioSession(sessionState *session); + + // This is called in several places where the session _may_ need to be deleted. + // It contains logic for whether to delete the session or keep it around. + void reapSession(sessionState *session); + + // Returns true if the session seems to indicate we've moved to a region on a different voice server + bool sessionNeedsRelog(sessionState *session); + + + ////////////////////////////////////// + // buddy list stuff, needed for SLIM later + struct buddyListEntry + { + buddyListEntry(const std::string &uri); + std::string mURI; + std::string mDisplayName; + LLUUID mUUID; + bool mOnlineSL; + bool mOnlineSLim; + bool mCanSeeMeOnline; + bool mHasBlockListEntry; + bool mHasAutoAcceptListEntry; + bool mNameResolved; + bool mInSLFriends; + bool mInVivoxBuddies; + bool mNeedsNameUpdate; + }; + + typedef std::map buddyListMap; + + // This should be called when parsing a buddy list entry sent by SLVoice. + void processBuddyListEntry(const std::string &uri, const std::string &displayName); + + buddyListEntry *addBuddy(const std::string &uri); + buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); + buddyListEntry *findBuddy(const std::string &uri); + buddyListEntry *findBuddy(const LLUUID &id); + buddyListEntry *findBuddyByDisplayName(const std::string &name); + void deleteBuddy(const std::string &uri); + void deleteAllBuddies(void); + + void deleteAllBlockRules(void); + void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); + void deleteAllAutoAcceptRules(void); + void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); + void accountListBlockRulesResponse(int statusCode, const std::string &statusString); + void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString); + + ///////////////////////////// + // session control messages + + void accountListBlockRulesSendMessage(); + void accountListAutoAcceptRulesSendMessage(); + + void sessionGroupCreateSendMessage(); + void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); + void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); + void sessionMediaConnectSendMessage(sessionState *session); // just joins the audio session + void sessionTextConnectSendMessage(sessionState *session); // just joins the text session + void sessionTerminateSendMessage(sessionState *session); + void sessionGroupTerminateSendMessage(sessionState *session); + void sessionMediaDisconnectSendMessage(sessionState *session); + void sessionTextDisconnectSendMessage(sessionState *session); + + + + // Pokes the state machine to leave the audio session next time around. + void sessionTerminate(); + + // Pokes the state machine to shut down the connector and restart it. + void requestRelog(); + + // Does the actual work to get out of the audio session + void leaveAudioSession(); + + // notifies the voice client that we've received parcel voice info + bool parcelVoiceInfoReceived(state requesting_state); + + friend class LLVivoxVoiceClientCapResponder; + + + void lookupName(const LLUUID &id); + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); + void avatarNameResolved(const LLUUID &id, const std::string &name); + + ///////////////////////////// + // Voice fonts + + void addVoiceFont(const S32 id, + const std::string &name, + const std::string &description, + const LLDate &expiration_date, + bool has_expired, + const S32 font_type, + const S32 font_status, + const bool template_font = false); + void accountGetSessionFontsResponse(int statusCode, const std::string &statusString); + void accountGetTemplateFontsResponse(int statusCode, const std::string &statusString); + +private: + LLVoiceVersionInfo mVoiceVersion; + + /// Clean up objects created during a voice session. + void cleanUp(); + + state mState; + bool mSessionTerminateRequested; + bool mRelogRequested; + // Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). + // The larger it is the greater is possibility there is a problem with connection to voice server. + // Introduced while fixing EXT-4313. + int mSpatialJoiningNum; + + void setState(state inState); + state getState(void) { return mState; }; + std::string state2string(state inState); + + void stateMachine(); + static void idle(void *user_data); + + LLHost mDaemonHost; + LLSocket::ptr_t mSocket; + bool mConnected; + + + LLPumpIO *mPump; + friend class LLVivoxProtocolParser; + + std::string mAccountName; + std::string mAccountPassword; + std::string mAccountDisplayName; + + bool mTuningMode; + float mTuningEnergy; + std::string mTuningAudioFile; + int mTuningMicVolume; + bool mTuningMicVolumeDirty; + int mTuningSpeakerVolume; + bool mTuningSpeakerVolumeDirty; + state mTuningExitState; // state to return to when we leave tuning mode. + + std::string mSpatialSessionURI; + std::string mSpatialSessionCredentials; + + std::string mMainSessionGroupHandle; // handle of the "main" session group. + + std::string mChannelName; // Name of the channel to be looked up + bool mAreaVoiceDisabled; + sessionState *mAudioSession; // Session state for the current audio session + bool mAudioSessionChanged; // set to true when the above pointer gets changed, so observers can be notified. + + sessionState *mNextAudioSession; // Session state for the audio session we're trying to join + +// std::string mSessionURI; // URI of the session we're in. +// std::string mSessionHandle; // returned by ? + + S32 mCurrentParcelLocalID; // Used to detect parcel boundary crossings + std::string mCurrentRegionName; // Used to detect parcel boundary crossings + + std::string mConnectorHandle; // returned by "Create Connector" message + std::string mAccountHandle; // returned by login message + int mNumberOfAliases; + U32 mCommandCookie; + + std::string mVoiceAccountServerURI; + std::string mVoiceSIPURIHostName; + + int mLoginRetryCount; + + sessionMap mSessionsByHandle; // Active sessions, indexed by session handle. Sessions which are being initiated may not be in this map. + sessionSet mSessions; // All sessions, not indexed. This is the canonical session list. + + bool mBuddyListMapPopulated; + bool mBlockRulesListReceived; + bool mAutoAcceptRulesListReceived; + buddyListMap mBuddyListMap; + + LLVoiceDeviceList mCaptureDevices; + LLVoiceDeviceList mRenderDevices; + + std::string mCaptureDevice; + std::string mRenderDevice; + bool mCaptureDeviceDirty; + bool mRenderDeviceDirty; + + + bool checkParcelChanged(bool update = false); + // This should be called when the code detects we have changed parcels. + // It initiates the call to the server that gets the parcel channel. + bool requestParcelVoiceInfo(); + + void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); + void joinSession(sessionState *session); + + std::string nameFromAvatar(LLVOAvatar *avatar); + std::string nameFromID(const LLUUID &id); + bool IDFromName(const std::string name, LLUUID &uuid); + std::string displayNameFromAvatar(LLVOAvatar *avatar); + std::string sipURIFromAvatar(LLVOAvatar *avatar); + std::string sipURIFromName(std::string &name); + + // Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. + std::string nameFromsipURI(const std::string &uri); + + bool inSpatialChannel(void); + std::string getAudioSessionURI(); + std::string getAudioSessionHandle(); + + void sendPositionalUpdate(void); + + void buildSetCaptureDevice(std::ostringstream &stream); + void buildSetRenderDevice(std::ostringstream &stream); + + void clearAllLists(); + void checkFriend(const LLUUID& id); + void sendFriendsListUpdates(); + + // start a text IM session with the specified user + // This will be asynchronous, the session may be established at a future time. + sessionState* startUserIMSession(const LLUUID& uuid); + void sendQueuedTextMessages(sessionState *session); + + void enforceTether(void); + + bool mSpatialCoordsDirty; + + LLVector3d mCameraPosition; + LLVector3d mCameraRequestedPosition; + LLVector3 mCameraVelocity; + LLMatrix3 mCameraRot; + + LLVector3d mAvatarPosition; + LLVector3 mAvatarVelocity; + LLMatrix3 mAvatarRot; + + bool mMuteMic; + bool mMuteMicDirty; + + // Set to true when the friends list is known to have changed. + bool mFriendsListDirty; + + enum + { + earLocCamera = 0, // ear at camera + earLocAvatar, // ear at avatar + earLocMixed // ear at avatar location/camera direction + }; + + S32 mEarLocation; + + bool mSpeakerVolumeDirty; + bool mSpeakerMuteDirty; + int mSpeakerVolume; + + int mMicVolume; + bool mMicVolumeDirty; + + bool mVoiceEnabled; + bool mWriteInProgress; + std::string mWriteString; + size_t mWriteOffset; + + LLTimer mUpdateTimer; + + BOOL mLipSyncEnabled; + + typedef std::set observer_set_t; + observer_set_t mParticipantObservers; + + void notifyParticipantObservers(); + + typedef std::set status_observer_set_t; + status_observer_set_t mStatusObservers; + + void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); + + typedef std::set friend_observer_set_t; + friend_observer_set_t mFriendObservers; + void notifyFriendObservers(); + + // Voice Fonts + + void expireVoiceFonts(); + void deleteVoiceFont(const LLUUID& id); + void deleteAllVoiceFonts(); + void deleteVoiceFontTemplates(); + + S32 getVoiceFontIndex(const LLUUID& id) const; + S32 getVoiceFontTemplateIndex(const LLUUID& id) const; + + void accountGetSessionFontsSendMessage(); + void accountGetTemplateFontsSendMessage(); + void sessionSetVoiceFontSendMessage(sessionState *session); + + void notifyVoiceFontObservers(); + + typedef enum e_voice_font_type + { + VOICE_FONT_TYPE_NONE = 0, + VOICE_FONT_TYPE_ROOT = 1, + VOICE_FONT_TYPE_USER = 2, + VOICE_FONT_TYPE_UNKNOWN + } EVoiceFontType; + + typedef enum e_voice_font_status + { + VOICE_FONT_STATUS_NONE = 0, + VOICE_FONT_STATUS_FREE = 1, + VOICE_FONT_STATUS_NOT_FREE = 2, + VOICE_FONT_STATUS_UNKNOWN + } EVoiceFontStatus; + + struct voiceFontEntry + { + voiceFontEntry(LLUUID& id); + ~voiceFontEntry(); + + LLUUID mID; + S32 mFontIndex; + std::string mName; + LLDate mExpirationDate; + S32 mFontType; + S32 mFontStatus; + bool mIsNew; + + LLFrameTimer mExpiryTimer; + LLFrameTimer mExpiryWarningTimer; + }; + + bool mVoiceFontsReceived; + bool mVoiceFontsNew; + bool mVoiceFontListDirty; + voice_effect_list_t mVoiceFontList; + voice_effect_list_t mVoiceFontTemplateList; + + typedef std::map voice_font_map_t; + voice_font_map_t mVoiceFontMap; + voice_font_map_t mVoiceFontTemplateMap; + + typedef std::set voice_font_observer_set_t; + voice_font_observer_set_t mVoiceFontObservers; + + LLFrameTimer mVoiceFontExpiryTimer; + + + // Audio capture buffer + + void captureBufferRecordStartSendMessage(); + void captureBufferRecordStopSendMessage(); + void captureBufferPlayStartSendMessage(const LLUUID& voice_font_id = LLUUID::null); + void captureBufferPlayStopSendMessage(); + + bool mCaptureBufferMode; // Disconnected from voice channels while using the capture buffer. + bool mCaptureBufferRecording; // A voice sample is being captured. + bool mCaptureBufferRecorded; // A voice sample is captured in the buffer ready to play. + bool mCaptureBufferPlaying; // A voice sample is being played. + + LLTimer mCaptureTimer; + LLUUID mPreviewVoiceFont; + LLUUID mPreviewVoiceFontLast; + S32 mPlayRequestCount; +}; + +/** + * @class LLVivoxProtocolParser + * @brief This class helps construct new LLIOPipe specializations + * @see LLIOPipe + * + * THOROUGH_DESCRIPTION + */ +class LLVivoxProtocolParser : public LLIOPipe +{ + LOG_CLASS(LLVivoxProtocolParser); +public: + LLVivoxProtocolParser(); + virtual ~LLVivoxProtocolParser(); + +protected: + /* @name LLIOPipe virtual implementations + */ + //@{ + /** + * @brief Process the data in buffer + */ + virtual EStatus process_impl( + const LLChannelDescriptors& channels, + buffer_ptr_t& buffer, + bool& eos, + LLSD& context, + LLPumpIO* pump); + //@} + + std::string mInput; + + // Expat control members + XML_Parser parser; + int responseDepth; + bool ignoringTags; + bool isEvent; + int ignoreDepth; + + // Members for processing responses. The values are transient and only valid within a call to processResponse(). + bool squelchDebugOutput; + int returnCode; + int statusCode; + std::string statusString; + std::string requestId; + std::string actionString; + std::string connectorHandle; + std::string versionID; + std::string accountHandle; + std::string sessionHandle; + std::string sessionGroupHandle; + std::string alias; + std::string applicationString; + + // Members for processing events. The values are transient and only valid within a call to processResponse(). + std::string eventTypeString; + int state; + std::string uriString; + bool isChannel; + bool incoming; + bool enabled; + std::string nameString; + std::string audioMediaString; + std::string deviceString; + std::string displayNameString; + int participantType; + bool isLocallyMuted; + bool isModeratorMuted; + bool isSpeaking; + int volume; + F32 energy; + std::string messageHeader; + std::string messageBody; + std::string notificationType; + bool hasText; + bool hasAudio; + bool hasVideo; + bool terminated; + std::string blockMask; + std::string presenceOnly; + std::string autoAcceptMask; + std::string autoAddAsBuddy; + int numberOfAliases; + std::string subscriptionHandle; + std::string subscriptionType; + S32 id; + std::string descriptionString; + LLDate expirationDate; + bool hasExpired; + S32 fontType; + S32 fontStatus; + std::string mediaCompletionType; + + // Members for processing text between tags + std::string textBuffer; + bool accumulateText; + + void reset(); + + void processResponse(std::string tag); + + static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); + static void XMLCALL ExpatEndTag(void *data, const char *el); + static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); + + void StartTag(const char *tag, const char **attr); + void EndTag(const char *tag); + void CharData(const char *buffer, int length); + LLDate expiryTimeStampToLLDate(const std::string& vivox_ts); +}; + + +#endif //LL_VIVOX_VOICE_CLIENT_H diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 4f65135a4..806cff3b8 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -1238,7 +1238,7 @@ void LLWorldMapView::drawAgents() // Show Individual agents (or little stacks where real agents are) // Here's how we'd choose the color if info.mID were available but it's not being sent: - // LLColor4 color = (agent_count == 1 && is_agent_friend(info.mID)) ? friend_color : avatar_color; + // LLColor4 color = (agent_count == 1 && LLAvatarActions::isFriend(info.mID)) ? friend_color : avatar_color; // Reduce the stack size as you zoom out - always display at lease one agent where there is one or more S32 agent_count = (S32)(((it->getCount()-1) * agents_scale + (it->getCount()-1) * 0.1f)+.1f) + 1; drawImageStack(it->getGlobalPosition(), sAvatarSmallImage, agent_count, 3.f, avatar_color); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 88f1ad044..2d42aff94 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -5999,7 +5999,7 @@ void LLPipeline::setRenderDebugFeatureControl(U32 bit, bool value) } else { - gPipeline.mRenderDebugFeatureMask &= !bit; + gPipeline.mRenderDebugFeatureMask &= ~bit; } } diff --git a/indra/newview/randgauss.h b/indra/newview/randgauss.h deleted file mode 100644 index bc51d1567..000000000 --- a/indra/newview/randgauss.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @file randgauss.h - * @brief randgauss class definition - * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * - * Second Life Viewer Source Code - * The source code in this file ("Source Code") is provided by Linden Lab - * to you under the terms of the GNU General Public License, version 2.0 - * ("GPL"), unless you have obtained a separate licensing agreement - * ("Other License"), formally executed by you and Linden Lab. Terms of - * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 - * - * There are special exceptions to the terms and conditions of the GPL as - * it is applied to this Source Code. View the full text of the exception - * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception - * - * By copying, modifying or distributing this software, you acknowledge - * that you have read and understood your obligations described above, - * and agree to abide by those obligations. - * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. - * $/LicenseInfo$ - */ - -const F32 table[][2] = { -{.00f, .5f}, -{.01f, .504f}, -{.02f, .508f}, -{.03f, .512f}, -{.06f, .5239f}, -{.08f, .5319f}, -{.11f, .5438f}, -{.13f, .5517f}, -{.16f, .5636f}, -{.18f, .5714f}, -{.21f, .5832f}, -{.23f, .5910f}, -{.26f, .6026f}, -{.28f, .6103f}, -{.31f, .6217f}, -{.34f, .6331f}, -{.36f, .6406f}, -{.39f, .6517f}, -{.42f, .6628f}, -{.44f, .6700f}, -{.47f, .6808f}, -{.50f, .6915f}, -{.53f, .7019f}, -{.56f, .7123f}, -{.59f, .7224f}, -{.62f, .7324f}, -{.65f, .7422f}, -{.68f, .7517f}, -{.71f, .7611f}, -{.74f, .7703f}, -{.78f, .7823f}, -{.81f, .7910f}, -{.85f, .8023f}, -{.88f, .8106f}, -{.92f, .8212f}, -{.96f, .8315f}, -{1.0f, .8413f}, -{1.04f, .8508f}, -{1.09f, .8621f}, -{1.13f, .8708f}, -{1.18f, .8810f}, -{1.23f, .8907f}, -{1.29f, .9015f}, -{1.35f, .9115f}, -{1.41f, .9207f}, -{1.48f, .9306f}, -{1.56f, .9406f}, -{1.65f, .9505f}, -{1.76f, .9608f}, -{1.89f, .9706f}, -{2.06f, .9803f}, -{2.33f, .9901f}, -{99.0f, 1.0f}}; - -inline F32 randGauss(F32 mean, F32 stdev) -{ - S32 i = 0; - F32 u = rand() / (F32) RAND_MAX; - F32 n; - - if (u >= 0.5) - { - while (u > table[i][1]) - i++; - - n = table[i-1][0]; - - } else { - u = 1 - u; - while (u > table[i][1]) - i++; - n = 1 - table[i-1][0]; - } - //printf("u: %f, n: %f, i: %d\n", u, n, i); //debug - - return (mean + stdev * n); -} - diff --git a/indra/newview/res-sdl/working.BMP b/indra/newview/res-sdl/working.BMP index 26dec59af..851dbd5b9 100644 Binary files a/indra/newview/res-sdl/working.BMP and b/indra/newview/res-sdl/working.BMP differ diff --git a/indra/newview/rlvdefines.h b/indra/newview/rlvdefines.h index b7857fd41..e80b7e4cc 100644 --- a/indra/newview/rlvdefines.h +++ b/indra/newview/rlvdefines.h @@ -151,6 +151,8 @@ enum ERlvBehaviour { RLV_BHVR_SENDIMTO, // "sendimto" RLV_BHVR_RECVIM, // "recvim" RLV_BHVR_RECVIMFROM, // "recvimfrom" + RLV_BHVR_STARTIM, // "startim" + RLV_BHVR_STARTIMTO, // "startimto" RLV_BHVR_PERMISSIVE, // "permissive" RLV_BHVR_NOTIFY, // "notify" RLV_BHVR_SHOWINV, // "showinv" diff --git a/indra/newview/rlvextensions.cpp b/indra/newview/rlvextensions.cpp index 8489de7ed..aa07dfcd2 100644 --- a/indra/newview/rlvextensions.cpp +++ b/indra/newview/rlvextensions.cpp @@ -495,10 +495,6 @@ bool RlvExtGetSet::findDebugSetting(std::string& strSetting, S16& flags) { LLStringUtil::toLower(strSetting); // Convenience for non-RLV calls - // HACK-RLVa: bad code but it's just a temporary measure to provide a smooth changeover from the old to the new rebranded settings - if ( (strSetting.length() >= 14) && (0 == strSetting.find("restrainedlife")) ) - strSetting = "restrainedlove" + strSetting.substr(14); - std::string strTemp; for (std::map::const_iterator itSetting = m_DbgAllowed.begin(); itSetting != m_DbgAllowed.end(); ++itSetting) { diff --git a/indra/newview/rlvfloaterbehaviour.cpp b/indra/newview/rlvfloaterbehaviour.cpp index 64807840c..b8c15214d 100644 --- a/indra/newview/rlvfloaterbehaviour.cpp +++ b/indra/newview/rlvfloaterbehaviour.cpp @@ -58,6 +58,7 @@ bool rlvGetShowException(ERlvBehaviour eBhvr) case RLV_BHVR_RECVEMOTE: case RLV_BHVR_SENDIM: case RLV_BHVR_RECVIM: + case RLV_BHVR_STARTIM: case RLV_BHVR_TPLURE: case RLV_BHVR_ACCEPTTP: return true; diff --git a/indra/newview/rlvhandler.cpp b/indra/newview/rlvhandler.cpp index 15518ad0d..b9a720981 100644 --- a/indra/newview/rlvhandler.cpp +++ b/indra/newview/rlvhandler.cpp @@ -18,9 +18,9 @@ #include "llagent.h" #include "llappearancemgr.h" #include "llappviewer.h" +#include "llgroupactions.h" #include "llhudtext.h" #include "llstartup.h" -#include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" @@ -195,7 +195,7 @@ void RlvHandler::removeCommandHandler(RlvCommandHandler* pCmdHandler) m_CommandHandlers.remove(pCmdHandler); } -// Checked: 2009-10-26 (RLVa-1.1.0a) | Modified: RLVa-1.1.0a +// Checked: 2010-04-07 (RLVa-1.2.0d) | Modified: RLVa-1.1.0a void RlvHandler::clearCommandHandlers() { std::list::const_iterator itHandler = m_CommandHandlers.begin(); @@ -730,7 +730,6 @@ size_t utf8str_strlen(const std::string& utf8) return length; } -// TODO-RLV: works, but more testing won't hurt std::string utf8str_chtruncate(const std::string& utf8, size_t length) { if (0 == length) @@ -1113,7 +1112,7 @@ void RlvHandler::clearState() #define VERIFY_OPTION(x) { if (!(x)) { eRet = RLV_RET_FAILED_OPTION; break; } } #define VERIFY_OPTION_REF(x) { if (!(x)) { eRet = RLV_RET_FAILED_OPTION; break; } fRefCount = true; } -// Checked: 2010-03-03 (RLVa-1.1.3b) | Modified: RLVa-1.2.0a +// Checked: 2010-03-03 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) { // NOTE: - at this point the command has already been: @@ -1154,8 +1153,8 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) VERIFY_OPTION_REF(strOption.empty()); } break; - case RLV_BHVR_ADDOUTFIT: // @addoutfit[:]=n|y - Checked: 2010-08-29 (RLVa-1.1.3b) | Modified: RLVa-1.2.1c - case RLV_BHVR_REMOUTFIT: // @remoutfit[:]=n|y - Checked: 2010-08-29 (RLVa-1.1.3b) | Modified: RLVa-1.2.1c + case RLV_BHVR_ADDOUTFIT: // @addoutfit[:]=n|y - Checked: 2010-08-29 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c + case RLV_BHVR_REMOUTFIT: // @remoutfit[:]=n|y - Checked: 2010-08-29 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c { // If there's an option it should specify a wearable type name (reference count on no option *and* a valid option) RlvCommandOptionGeneric rlvCmdOption(rlvCmd.getOption()); @@ -1244,7 +1243,7 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) VERIFY_OPTION_REF(strOption.empty()); } break; - case RLV_BHVR_NOTIFY: // @notify:=add|rem - Checked: 2010-03-03 (RLVa-1.1.3a) | Modified: RLVa-1.2.0a + case RLV_BHVR_NOTIFY: // @notify:=add|rem - Checked: 2010-03-03 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a { // There should be an option that we can successfully parse (if there's an empty option the command is invalid) S32 nChannel; std::string strFilter; @@ -1277,10 +1276,10 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) case RLV_BHVR_SHOWLOC: // @showloc=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_SHOWNAMES: // @shownames=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_EMOTE: // @emote=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) - case RLV_BHVR_SENDCHAT: // @sendchat=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_CHATWHISPER: // @chatwhisper=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_CHATNORMAL: // @chatnormal=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_CHATSHOUT: // @chatshout=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h + case RLV_BHVR_SENDCHAT: // @sendchat=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) + case RLV_BHVR_CHATWHISPER: // @chatwhisper=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) + case RLV_BHVR_CHATNORMAL: // @chatnormal=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) + case RLV_BHVR_CHATSHOUT: // @chatshout=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) case RLV_BHVR_PERMISSIVE: // @permissive=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_SHOWINV: // @showinv=n|y - Checked: 2010-03-01 (RLVa-1.2.0a) case RLV_BHVR_SHOWMINIMAP: // @showminimap=n|y - Checked: 2010-02-28 (RLVa-1.2.0a) @@ -1291,12 +1290,12 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) case RLV_BHVR_STANDTP: // @standtp=n|y - Checked: 2010-08-29 (RLVa-1.2.1c) case RLV_BHVR_TPLM: // @tplm=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_TPLOC: // @tploc=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_VIEWNOTE: // @viewnote=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_VIEWSCRIPT: // @viewscript=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_VIEWTEXTURE: // @viewtexture=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h + case RLV_BHVR_VIEWNOTE: // @viewnote=n|y - Checked: 2010-03-27 (RLVa-1.2.0b) + case RLV_BHVR_VIEWSCRIPT: // @viewscript=n|y - Checked: 2010-03-27 (RLVa-1.2.0b) + case RLV_BHVR_VIEWTEXTURE: // @viewtexture=n|y - Checked: 2010-03-27 (RLVa-1.2.0b) case RLV_BHVR_ACCEPTPERMISSION: // @acceptpermission=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h #ifdef RLV_EXTENSION_CMD_ALLOWIDLE - case RLV_BHVR_ALLOWIDLE: // @allowidle=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h + case RLV_BHVR_ALLOWIDLE: // @allowidle=n|y - Checked: 2010-05-03 (RLVa-1.2.0g) | Modified: RLVa-1.1.0h #endif // RLV_EXTENSION_CMD_ALLOWIDLE case RLV_BHVR_REZ: // @rez=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_FARTOUCH: // @fartouch=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h @@ -1318,10 +1317,11 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) VERIFY_OPTION_REF(strOption.empty()); break; // The following block is only valid if there's no option (= restriction) or if it specifies a valid UUID (= behaviour exception) - case RLV_BHVR_RECVCHAT: // @recvchat[:]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h - case RLV_BHVR_RECVEMOTE: // @recvemote[:]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h + case RLV_BHVR_RECVCHAT: // @recvchat[:]=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) + case RLV_BHVR_RECVEMOTE: // @recvemote[:]=n|y - Checked: 2010-03-26 (RLVa-1.2.0b) case RLV_BHVR_SENDIM: // @sendim[:]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_RECVIM: // @recvim[:]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h + case RLV_BHVR_STARTIM: // @startim[:]=n|y - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h case RLV_BHVR_TPLURE: // @tplure[:]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_ACCEPTTP: // @accepttp[:]=n|y - Checked: 2009-12-05 (RLVa-1.1.0h) | Modified: RLVa-1.1.0h case RLV_BHVR_TOUCHATTACH: // @touchattach[:=n|y - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l @@ -1350,6 +1350,7 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) case RLV_BHVR_RECVEMOTEFROM: // @recvemotefrom:=n|y - Checked: 2010-11-30 (RLVa-1.3.0c) | Added: RLVa-1.3.0c case RLV_BHVR_SENDIMTO: // @sendimto:=n|y - Checked: 2010-11-30 (RLVa-1.3.0c) | Added: RLVa-1.3.0c case RLV_BHVR_RECVIMFROM: // @recvimfrom:=n|y - Checked: 2010-11-30 (RLVa-1.3.0c) | Added: RLVa-1.3.0c + case RLV_BHVR_STARTIMTO: // @startimto:=n|y - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h case RLV_BHVR_EDITOBJ: // @editobj:=n|y - Checked: 2010-11-29 (RLVa-1.3.0c) | Added: RLVa-1.3.0c case RLV_BHVR_TOUCHTHIS: // @touchthis:=n|y - Checked: 2010-01-01 (RLVa-1.1.0l) | Added: RLVa-1.1.0l { @@ -1399,7 +1400,7 @@ ERlvCmdRet RlvHandler::processAddRemCommand(const RlvCommand& rlvCmd) return eRet; } -// Checked: 2010-03-03 (RLVa-1.1.3b) | Modified: RLVa-1.2.0a +// Checked: 2010-03-03 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a ERlvCmdRet RlvHandler::onAddRemAttach(const RlvCommand& rlvCmd, bool& fRefCount) { RLV_ASSERT( (RLV_TYPE_ADD == rlvCmd.getParamType()) || (RLV_TYPE_REMOVE == rlvCmd.getParamType()) ); @@ -1434,7 +1435,7 @@ ERlvCmdRet RlvHandler::onAddRemAttach(const RlvCommand& rlvCmd, bool& fRefCount) return RLV_RET_SUCCESS; } -// Checked: 2010-02-28 (RLVa-1.1.3b) | Modified: RLVa-1.2.0a +// Checked: 2010-02-28 (RLVa-1.2.0a) | Modified: RLVa-1.2.0a ERlvCmdRet RlvHandler::onAddRemDetach(const RlvCommand& rlvCmd, bool& fRefCount) { RLV_ASSERT( (RLV_TYPE_ADD == rlvCmd.getParamType()) || (RLV_TYPE_REMOVE == rlvCmd.getParamType()) ); @@ -1608,7 +1609,10 @@ ERlvCmdRet RlvHandler::processForceCommand(const RlvCommand& rlvCmd) const { F32 nValue = (rlvCmdOption.m_nPelvisToFoot - gAgentAvatarp->getPelvisToFoot()) * rlvCmdOption.m_nPelvisToFootDeltaMult; nValue += rlvCmdOption.m_nPelvisToFootOffset; - gSavedSettings.setF32(RLV_SETTING_AVATAROFFSET_Z, llclamp(nValue, -1.0f, 1.0f)); + if (!gAgentAvatarp->isUsingServerBakes()) + gSavedSettings.setF32(RLV_SETTING_AVATAROFFSET_Z, llclamp(nValue, -1.0f, 1.0f)); + else + eRet = RLV_RET_FAILED_DISABLED; } } break; @@ -1665,7 +1669,7 @@ ERlvCmdRet RlvHandler::processForceCommand(const RlvCommand& rlvCmd) const return eRet; } -// Checked: 2010-08-29 (RLVa-1.1.3b) | Modified: RLVa-1.2.1c +// Checked: 2010-08-29 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c ERlvCmdRet RlvHandler::onForceRemAttach(const RlvCommand& rlvCmd) const { RLV_ASSERT(RLV_TYPE_FORCE == rlvCmd.getParamType()); @@ -1700,7 +1704,7 @@ ERlvCmdRet RlvHandler::onForceRemAttach(const RlvCommand& rlvCmd) const return RLV_RET_FAILED_OPTION; } -// Checked: 2010-08-29 (RLVa-1.1.3b) | Modified: RLVa-1.2.1c +// Checked: 2010-08-29 (RLVa-1.2.1c) | Modified: RLVa-1.2.1c ERlvCmdRet RlvHandler::onForceRemOutfit(const RlvCommand& rlvCmd) const { RlvCommandOptionGeneric rlvCmdOption(rlvCmd.getOption()); @@ -1739,14 +1743,7 @@ ERlvCmdRet RlvHandler::onForceGroup(const RlvCommand& rlvCmd) const if (fValid) { m_idAgentGroup = idGroup; - - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_ActivateGroup); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast(_PREHASH_GroupID, idGroup); - gAgent.sendReliableMessage(); + LLGroupActions::activate(idGroup); } return (fValid) ? RLV_RET_SUCCESS : RLV_RET_FAILED_OPTION; @@ -1831,7 +1828,7 @@ void RlvHandler::onForceWearCallback(const uuid_vec_t& idItems, ERlvBehaviour eB // Command handlers (RLV_TYPE_REPLY) // -// Checked: 2009-11-26 (RLVa-1.1.0f) | Modified: RLVa-1.1.0f +// Checked: 2010-04-07 (RLVa-1.2.0d) | Modified: RLVa-1.1.0f ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const { RLV_ASSERT(RLV_TYPE_REPLY == rlvCmd.getParamType()); @@ -1844,12 +1841,12 @@ ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const ERlvCmdRet eRet = RLV_RET_SUCCESS; std::string strReply; switch (rlvCmd.getBehaviourType()) { - case RLV_BHVR_VERSION: // @version= - Checked: 2010-03-27 (RLVa-1.2.0b) - case RLV_BHVR_VERSIONNEW: // @versionnew= - Checked: 2010-03-27 (RLVa-1.2.0b) | Added: RLVa-1.2.0b + case RLV_BHVR_VERSION: // @version= - Checked: 2010-03-27 (RLVa-1.4.0a) + case RLV_BHVR_VERSIONNEW: // @versionnew= - Checked: 2010-03-27 (RLVa-1.4.0a) | Added: RLVa-1.2.0b // NOTE: RLV will respond even if there's an option strReply = RlvStrings::getVersion(RLV_BHVR_VERSION == rlvCmd.getBehaviourType()); break; - case RLV_BHVR_VERSIONNUM: // @versionnum= - Checked: 2009-11-26 (RLVa-1.1.0f) | Added: RLVa-1.0.4b + case RLV_BHVR_VERSIONNUM: // @versionnum= - Checked: 2010-03-27 (RLVa-1.4.0a) | Added: RLVa-1.0.4b // NOTE: RLV will respond even if there's an option strReply = RlvStrings::getVersionNum(); break; @@ -1886,7 +1883,7 @@ ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const case RLV_BHVR_GETINV: // @getinv[:]= eRet = onGetInv(rlvCmd, strReply); break; - case RLV_BHVR_GETINVWORN: // @getinvworn[:path]= + case RLV_BHVR_GETINVWORN: // @getinvworn[:]= eRet = onGetInvWorn(rlvCmd, strReply); break; case RLV_BHVR_GETGROUP: // @getgroup= - Checked: 2011-03-28 (RLVa-1.4.1a) | Added: RLVa-1.3.0f @@ -1915,7 +1912,7 @@ ERlvCmdRet RlvHandler::processReplyCommand(const RlvCommand& rlvCmd) const } break; #endif // RLV_EXTENSION_CMD_GETCOMMAND - case RLV_BHVR_GETSTATUS: // @getstatus[: