diff --git a/autobuild.xml b/autobuild.xml index e0f82a45eb..25d95a6849 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -482,9 +482,9 @@ archive hash - 2653c3627fd8687ff9e003425fd14834 + 439d92ec73f0500ba1671faad2bd8090 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90199/821852/dullahan-1.12.3.202111032211_91.1.21_g9dd45fe_chromium-91.0.4472.114-darwin64-565428.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104637/916643/dullahan-1.12.4.202209142017_91.1.21_g9dd45fe_chromium-91.0.4472.114-darwin64-575005.tar.bz2 name darwin64 @@ -494,9 +494,9 @@ archive hash - b4003772562a5dd40bc112eec7cba5f5 + 2a7c01da15de77bc1fd1863327174d5e url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90201/821871/dullahan-1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows-565428.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104638/916654/dullahan-1.12.4.202209142021_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows-575005.tar.bz2 name windows @@ -506,16 +506,16 @@ archive hash - d9030d7a7390b3bda7de2adcc27e535a + d06bee9b2517fbb09ba1a65e6d675361 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/90200/821876/dullahan-1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows64-565428.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/104639/916659/dullahan-1.12.4.202209142021_91.1.21_g9dd45fe_chromium-91.0.4472.114-windows64-575005.tar.bz2 name windows64 version - 1.12.3.202111032221_91.1.21_g9dd45fe_chromium-91.0.4472.114 + 1.12.4.202209142021_91.1.21_g9dd45fe_chromium-91.0.4472.114 expat @@ -2393,9 +2393,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 6ce3cbaed968a69fb7a2cca80220874d + b583668b28fde0490e6953f10e93e4ab url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80380/758537/slvoice-4.10.0000.32327.5fc3fe7c.558436-darwin64-558436.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/98681/871545/slvoice-4.10.0000.32327.5fc3fe7c.571099-darwin64-571099.tar.bz2 name darwin64 @@ -2417,9 +2417,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 2eb38c5eff4d0f18fbb89d0c30c4f0a4 + 6e0ed41653955afe8eeb8945776cf07b url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80382/758550/slvoice-4.10.0000.32327.5fc3fe7c.558436-windows-558436.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/98683/871560/slvoice-4.10.0000.32327.5fc3fe7c.571099-windows-571099.tar.bz2 name windows @@ -2429,16 +2429,16 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 9ee8f3cbc5369c598a998c61961ed16d + c39735851fd05c194d0be09b8f9e8cb7 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/80381/758551/slvoice-4.10.0000.32327.5fc3fe7c.558436-windows64-558436.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/98682/871552/slvoice-4.10.0000.32327.5fc3fe7c.571099-windows64-571099.tar.bz2 name windows64 version - 4.10.0000.32327.5fc3fe7c.558436 + 4.10.0000.32327.5fc3fe7c.571099 threejs @@ -2888,7 +2888,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors package_description canonical_repo - https://bitbucket.org/lindenlab/viewer + https://github.com/secondlife/viewer copyright Copyright (c) 2020, Linden Research, Inc. description diff --git a/doc/contributions.txt b/doc/contributions.txt index 640ffb037e..9368781c9a 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -223,6 +223,7 @@ Ansariel Hiller MAINT-8723 SL-10385 SL-10891 + SL-10675 SL-13364 SL-13858 SL-13697 @@ -235,6 +236,7 @@ Ansariel Hiller SL-15226 SL-15227 SL-15398 + SL-18432 Aralara Rajal Arare Chantilly CHUIBUG-191 @@ -400,6 +402,7 @@ Cinder Roxley STORM-2127 STORM-2144 SL-3404 + SL-17634 Clara Young Coaldust Numbers VWR-1095 @@ -820,6 +823,7 @@ Jonathan Yap Kadah Coba STORM-1060 STORM-1843 + SL-10675 Jondan Lundquist Joosten Briebers MAINT-7074 @@ -1120,6 +1124,7 @@ Nicky Dasmijn SL-14541 SL-16438 SL-17218 + SL-17585 Nicky Perian OPEN-1 STORM-1087 diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp index 2d6d2a10d2..f0df3e1474 100644 --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -927,6 +927,9 @@ BOOL LLAvatarAppearance::loadAvatar() return FALSE; } + // initialize mJointAliasMap + getJointAliases(); + // avatar_lad.xml : if( !loadSkeletonNode() ) { @@ -1047,7 +1050,6 @@ BOOL LLAvatarAppearance::loadAvatar() return FALSE; } } - return TRUE; } diff --git a/indra/llappearance/llpolymesh.cpp b/indra/llappearance/llpolymesh.cpp index 0a7a8d27bb..3892e4ce43 100644 --- a/indra/llappearance/llpolymesh.cpp +++ b/indra/llappearance/llpolymesh.cpp @@ -612,14 +612,16 @@ BOOL LLPolyMeshSharedData::loadMesh( const std::string& fileName ) // we reached the end of the morphs break; } - LLPolyMorphData* morph_data = new LLPolyMorphData(std::string(morphName)); + std::string morph_name(morphName); + LLPolyMorphData* morph_data = new LLPolyMorphData(morph_name); BOOL result = morph_data->loadBinary(fp, this); if (!result) { - delete morph_data; - continue; + LL_WARNS() << "Failure loading " << morph_name << " from " << fileName << LL_ENDL; + delete morph_data; + continue; } mMorphData.insert(morph_data); diff --git a/indra/llappearance/llpolymorph.cpp b/indra/llappearance/llpolymorph.cpp index 16b5f1e204..84dd6156a9 100644 --- a/indra/llappearance/llpolymorph.cpp +++ b/indra/llappearance/llpolymorph.cpp @@ -156,7 +156,9 @@ BOOL LLPolyMorphData::loadBinary(LLFILE *fp, LLPolyMeshSharedData *mesh) if (mVertexIndices[v] > 10000) { - LL_ERRS() << "Bad morph index: " << mVertexIndices[v] << LL_ENDL; + // Bad install? These are usually .llm files from 'character' fodler + LL_WARNS() << "Bad morph index " << v << ": " << mVertexIndices[v] << LL_ENDL; + return FALSE; } diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp old mode 100644 new mode 100755 index ff0aa6e76e..38a6b41afe --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -35,6 +35,8 @@ #include "llendianswizzle.h" #include "llassetstorage.h" #include "llrefcount.h" +#include "threadpool.h" +#include "workqueue.h" #include "llvorbisencode.h" @@ -45,15 +47,13 @@ extern LLAudioEngine *gAudiop; -LLAudioDecodeMgr *gAudioDecodeMgrp = NULL; - static const S32 WAV_HEADER_SIZE = 44; ////////////////////////////////////////////////////////////////////////////// -class LLVorbisDecodeState : public LLRefCount +class LLVorbisDecodeState : public LLThreadSafeRefCount { public: class WriteResponder : public LLLFSThread::Responder @@ -532,146 +532,254 @@ void LLVorbisDecodeState::flushBadFile() class LLAudioDecodeMgr::Impl { - friend class LLAudioDecodeMgr; -public: - Impl() {}; - ~Impl() {}; + friend class LLAudioDecodeMgr; + Impl(); + public: - void processQueue(const F32 num_secs = 0.005); + void processQueue(); -protected: - std::deque mDecodeQueue; - LLPointer mCurrentDecodep; + void startMoreDecodes(); + void enqueueFinishAudio(const LLUUID &decode_id, LLPointer& decode_state); + void checkDecodesFinished(); + + protected: + std::deque mDecodeQueue; + std::map> mDecodes; }; - -void LLAudioDecodeMgr::Impl::processQueue(const F32 num_secs) +LLAudioDecodeMgr::Impl::Impl() { - LLUUID uuid; +} - LLTimer decode_timer; +// Returns the in-progress decode_state, which may be an empty LLPointer if +// there was an error and there is no more work to be done. +LLPointer beginDecodingAndWritingAudio(const LLUUID &decode_id); - BOOL done = FALSE; - while (!done) - { - if (mCurrentDecodep) - { - BOOL res; +// Return true if finished +bool tryFinishAudio(const LLUUID &decode_id, LLPointer decode_state); - // Decode in a loop until we're done or have run out of time. - while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs)) - { - // decodeSection does all of the work above - } +void LLAudioDecodeMgr::Impl::processQueue() +{ + // First, check if any audio from in-progress decodes are ready to play. If + // so, mark them ready for playback (or errored, in case of error). + checkDecodesFinished(); - if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid()) - { - // We had an error when decoding, abort. - LL_WARNS("AudioEngine") << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << LL_ENDL; - mCurrentDecodep->flushBadFile(); + // Second, start as many decodes from the queue as permitted + startMoreDecodes(); +} - if (gAudiop) - { - LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - adp->setHasValidData(false); - adp->setHasCompletedDecode(true); - } +void LLAudioDecodeMgr::Impl::startMoreDecodes() +{ + llassert_always(gAudiop); - mCurrentDecodep = NULL; - done = TRUE; - } + LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); + // *NOTE: main_queue->postTo casts this refcounted smart pointer to a weak + // pointer + LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); + const LL::ThreadPool::ptr_t general_thread_pool = LL::ThreadPool::getInstance("General"); + llassert_always(main_queue); + llassert_always(general_queue); + llassert_always(general_thread_pool); + // Set max decodes to double the thread count of the general work queue. + // This ensures the general work queue is full, but prevents theoretical + // buildup of buffers in memory due to disk writes once the + // LLVorbisDecodeState leaves the worker thread (see + // LLLFSThread::sLocal->write). This is probably as fast as we can get it + // without modifying/removing LLVorbisDecodeState, at which point we should + // consider decoding the audio during the asset download process. + // -Cosmic,2022-05-11 + const size_t max_decodes = general_thread_pool->getWidth() * 2; - if (!res) - { - // We've used up out time slice, bail... - done = TRUE; - } - else if (mCurrentDecodep) - { - if (gAudiop && mCurrentDecodep->finishDecode()) - { - // We finished! - LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID()); - if (!adp) - { - LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << mCurrentDecodep->getUUID() << LL_ENDL; - } - else if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone()) - { - adp->setHasCompletedDecode(true); - adp->setHasDecodedData(true); - adp->setHasValidData(true); + while (!mDecodeQueue.empty() && mDecodes.size() < max_decodes) + { + const LLUUID decode_id = mDecodeQueue.front(); + mDecodeQueue.pop_front(); - // At this point, we could see if anyone needs this sound immediately, but - // I'm not sure that there's a reason to - we need to poll all of the playing - // sounds anyway. - //LL_INFOS("AudioEngine") << "Finished the vorbis decode, now what?" << LL_ENDL; - } - else - { - adp->setHasCompletedDecode(true); - LL_INFOS("AudioEngine") << "Vorbis decode failed for " << mCurrentDecodep->getUUID() << LL_ENDL; - } - mCurrentDecodep = NULL; - } - done = TRUE; // done for now - } - } + // Don't decode the same file twice + if (mDecodes.find(decode_id) != mDecodes.end()) + { + continue; + } + if (gAudiop->hasDecodedFile(decode_id)) + { + continue; + } - if (!done) - { - if (mDecodeQueue.empty()) - { - // Nothing else on the queue. - done = TRUE; - } - else - { - LLUUID uuid; - uuid = mDecodeQueue.front(); - mDecodeQueue.pop_front(); - if (!gAudiop || gAudiop->hasDecodedFile(uuid)) - { - // This file has already been decoded, don't decode it again. - continue; - } + // Kick off a decode + mDecodes[decode_id] = LLPointer(NULL); + try + { + main_queue->postTo( + general_queue, + [decode_id]() // Work done on general queue + { + LLPointer decode_state = beginDecodingAndWritingAudio(decode_id); - LL_DEBUGS() << "Decoding " << uuid << " from audio queue!" << LL_ENDL; + if (!decode_state) + { + // Audio decode has errored + return decode_state; + } - std::string uuid_str; - std::string d_path; + // Disk write of decoded audio is now in progress off-thread + return decode_state; + }, + [decode_id, this](LLPointer decode_state) // Callback to main thread + mutable { + if (!gAudiop) + { + // There is no LLAudioEngine anymore. This might happen if + // an audio decode is enqueued just before shutdown. + return; + } - LLTimer timer; - timer.reset(); + // At this point, we can be certain that the pointer to "this" + // is valid because the lifetime of "this" is dependent upon + // the lifetime of gAudiop. - uuid.toString(uuid_str); - d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; + enqueueFinishAudio(decode_id, decode_state); + }); + } + catch (const LLThreadSafeQueueInterrupt&) + { + // Shutdown + // Consider making processQueue() do a cleanup instead + // of starting more decodes + LL_WARNS() << "Tried to start decoding on shutdown" << LL_ENDL; + } + } +} - mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path); - if (!mCurrentDecodep->initDecode()) - { - mCurrentDecodep = NULL; - } - } - } - } +LLPointer beginDecodingAndWritingAudio(const LLUUID &decode_id) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; + + LL_DEBUGS() << "Decoding " << decode_id << " from audio queue!" << LL_ENDL; + + std::string d_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, decode_id.asString()) + ".dsf"; + LLPointer decode_state = new LLVorbisDecodeState(decode_id, d_path); + + if (!decode_state->initDecode()) + { + return NULL; + } + + // Decode in a loop until we're done + while (!decode_state->decodeSection()) + { + // decodeSection does all of the work above + } + + if (!decode_state->isDone()) + { + // Decode stopped early, or something bad happened to the file + // during decoding. + LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data or decode has been canceled, aborting decode" << LL_ENDL; + decode_state->flushBadFile(); + return NULL; + } + + if (!decode_state->isValid()) + { + // We had an error when decoding, abort. + LL_WARNS("AudioEngine") << decode_id << " has invalid vorbis data, aborting decode" << LL_ENDL; + decode_state->flushBadFile(); + return NULL; + } + + // Kick off the writing of the decoded audio to the disk cache. + // The receiving thread can then cheaply call finishDecode() again to check + // if writing has finished. Someone has to hold on to the refcounted + // decode_state to prevent it from getting destroyed during write. + decode_state->finishDecode(); + + return decode_state; +} + +void LLAudioDecodeMgr::Impl::enqueueFinishAudio(const LLUUID &decode_id, LLPointer& decode_state) +{ + // Assumed fast + if (tryFinishAudio(decode_id, decode_state)) + { + // Done early! + auto decode_iter = mDecodes.find(decode_id); + llassert(decode_iter != mDecodes.end()); + mDecodes.erase(decode_iter); + return; + } + + // Not done yet... enqueue it + mDecodes[decode_id] = decode_state; +} + +void LLAudioDecodeMgr::Impl::checkDecodesFinished() +{ + auto decode_iter = mDecodes.begin(); + while (decode_iter != mDecodes.end()) + { + const LLUUID& decode_id = decode_iter->first; + const LLPointer& decode_state = decode_iter->second; + if (tryFinishAudio(decode_id, decode_state)) + { + decode_iter = mDecodes.erase(decode_iter); + } + else + { + ++decode_iter; + } + } +} + +bool tryFinishAudio(const LLUUID &decode_id, LLPointer decode_state) +{ + // decode_state is a file write in progress unless finished is true + bool finished = decode_state && decode_state->finishDecode(); + if (!finished) + { + return false; + } + + llassert_always(gAudiop); + + LLAudioData *adp = gAudiop->getAudioData(decode_id); + if (!adp) + { + LL_WARNS("AudioEngine") << "Missing LLAudioData for decode of " << decode_id << LL_ENDL; + return true; + } + + bool valid = decode_state && decode_state->isValid(); + // Mark current decode finished regardless of success or failure + adp->setHasCompletedDecode(true); + // Flip flags for decoded data + adp->setHasDecodeFailed(!valid); + adp->setHasDecodedData(valid); + // When finished decoding, there will also be a decoded wav file cached on + // disk with the .dsf extension + if (valid) + { + adp->setHasWAVLoadFailed(false); + } + + return true; } ////////////////////////////////////////////////////////////////////////////// LLAudioDecodeMgr::LLAudioDecodeMgr() { - mImpl = new Impl; + mImpl = new Impl(); } LLAudioDecodeMgr::~LLAudioDecodeMgr() { - delete mImpl; + delete mImpl; + mImpl = nullptr; } -void LLAudioDecodeMgr::processQueue(const F32 num_secs) +void LLAudioDecodeMgr::processQueue() { - mImpl->processQueue(num_secs); + mImpl->processQueue(); } BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) @@ -687,7 +795,7 @@ BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid) { // Just put it on the decode queue. LL_DEBUGS("AudioEngine") << "addDecodeRequest for " << uuid << " has local asset file already" << LL_ENDL; - mImpl->mDecodeQueue.push_back(uuid); + mImpl->mDecodeQueue.push_back(uuid); return TRUE; } diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h index ceaff3f2d8..4c17b46156 100644 --- a/indra/llaudio/llaudiodecodemgr.h +++ b/indra/llaudio/llaudiodecodemgr.h @@ -32,24 +32,23 @@ #include "llassettype.h" #include "llframetimer.h" +#include "llsingleton.h" +template class LLPointer; class LLVorbisDecodeState; -class LLAudioDecodeMgr +class LLAudioDecodeMgr : public LLSingleton { + LLSINGLETON(LLAudioDecodeMgr); + ~LLAudioDecodeMgr(); public: - LLAudioDecodeMgr(); - ~LLAudioDecodeMgr(); - - void processQueue(const F32 num_secs = 0.005); + void processQueue(); BOOL addDecodeRequest(const LLUUID &uuid); void addAudioRequest(const LLUUID &uuid); protected: class Impl; - Impl* mImpl; + Impl* mImpl; }; -extern LLAudioDecodeMgr *gAudioDecodeMgrp; - #endif diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index e0ebbb76bd..008e1827c5 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -83,18 +83,10 @@ void LLAudioEngine::setDefaults() mLastStatus = 0; - mNumChannels = 0; mEnableWind = false; - S32 i; - for (i = 0; i < MAX_CHANNELS; i++) - { - mChannels[i] = NULL; - } - for (i = 0; i < MAX_BUFFERS; i++) - { - mBuffers[i] = NULL; - } + mChannels.fill(nullptr); + mBuffers.fill(nullptr); mMasterGain = 1.f; // Setting mInternalGain to an out of range value fixes the issue reported in STORM-830. @@ -111,18 +103,14 @@ void LLAudioEngine::setDefaults() } -bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine::init(void* userdata, const std::string &app_title) { setDefaults(); - mNumChannels = num_channels; mUserData = userdata; allocateListener(); - // Initialize the decode manager - gAudioDecodeMgrp = new LLAudioDecodeMgr; - LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << LL_ENDL; return true; @@ -131,10 +119,6 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata, const std::stri void LLAudioEngine::shutdown() { - // Clean up decode manager - delete gAudioDecodeMgrp; - gAudioDecodeMgrp = NULL; - // Clean up wind source cleanupWind(); @@ -156,14 +140,14 @@ void LLAudioEngine::shutdown() // Clean up channels S32 i; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { delete mChannels[i]; mChannels[i] = NULL; } // Clean up buffers - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { delete mBuffers[i]; mBuffers[i] = NULL; @@ -229,7 +213,7 @@ std::string LLAudioEngine::getInternetStreamURL() void LLAudioEngine::updateChannels() { S32 i; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (mChannels[i]) { @@ -240,20 +224,14 @@ void LLAudioEngine::updateChannels() } } -static const F32 default_max_decode_time = .002f; // 2 ms -void LLAudioEngine::idle(F32 max_decode_time) +void LLAudioEngine::idle() { - if (max_decode_time <= 0.f) - { - max_decode_time = default_max_decode_time; - } - // "Update" all of our audio sources, clean up dead ones. // Primarily does position updating, cleanup of unused audio sources. // Also does regeneration of the current priority of each audio source. S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -276,7 +254,7 @@ void LLAudioEngine::idle(F32 max_decode_time) { // The source is done playing, clean it up. delete sourcep; - mAllSources.erase(iter++); + iter = mAllSources.erase(iter); continue; } @@ -473,7 +451,7 @@ void LLAudioEngine::idle(F32 max_decode_time) commitDeferredChanges(); // Flush unused buffers that are stale enough - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -489,7 +467,7 @@ void LLAudioEngine::idle(F32 max_decode_time) // Clear all of the looped flags for the channels - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (mChannels[i]) { @@ -498,7 +476,7 @@ void LLAudioEngine::idle(F32 max_decode_time) } // Decode audio files - gAudioDecodeMgrp->processQueue(max_decode_time); + LLAudioDecodeMgr::getInstance()->processQueue(); // Call this every frame, just in case we somehow // missed picking it up in all the places that can add @@ -532,7 +510,7 @@ bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uu { if (audio_uuid.notNull()) { - gAudioDecodeMgrp->addDecodeRequest(audio_uuid); + LLAudioDecodeMgr::getInstance()->addDecodeRequest(audio_uuid); } } else @@ -561,7 +539,7 @@ void LLAudioEngine::enableWind(bool enable) LLAudioBuffer * LLAudioEngine::getFreeBuffer() { S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (!mBuffers[i]) { @@ -574,7 +552,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() // Grab the oldest unused buffer F32 max_age = -1.f; S32 buffer_id = -1; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i]) { @@ -605,7 +583,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) { S32 i; - for (i = 0; i < mNumChannels; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -633,7 +611,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) F32 min_priority = 10000.f; LLAudioChannel *min_channelp = NULL; - for (i = 0; i < mNumChannels; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { LLAudioChannel *channelp = mChannels[i]; LLAudioSource *sourcep = channelp->getSource(); @@ -660,7 +638,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp) { S32 i; - for (i = 0; i < MAX_BUFFERS; i++) + for (i = 0; i < LL_MAX_AUDIO_BUFFERS; i++) { if (mBuffers[i] == bufferp) { @@ -678,7 +656,7 @@ bool LLAudioEngine::preloadSound(const LLUUID &uuid) getAudioData(uuid); // We don't care about the return value, this is just to make sure // that we have an entry, which will mean that the audio engine knows about this - if (gAudioDecodeMgrp->addDecodeRequest(uuid)) + if (LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid)) { // This means that we do have a local copy, and we're working on decoding it. return true; @@ -827,7 +805,8 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i addAudioSource(asp); if (pos_global.isExactlyZero()) { - asp->setAmbient(true); + // For sound preview and UI + asp->setForcedPriority(true); } else { @@ -953,6 +932,7 @@ LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id) LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) { + LL_PROFILE_ZONE_SCOPED_CATEGORY_MEDIA; data_map::iterator iter; iter = mAllData.find(audio_uuid); if (iter == mAllData.end()) @@ -1039,7 +1019,7 @@ void LLAudioEngine::startNextTransfer() // Check all channels for currently playing sounds. F32 max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1067,7 +1047,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1078,7 +1058,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.isNull()) { max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1103,7 +1083,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1115,7 +1095,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.isNull()) { max_pri = -1.f; - for (i = 0; i < MAX_CHANNELS; i++) + for (i = 0; i < LL_MAX_AUDIO_CHANNELS; i++) { if (!mChannels[i]) { @@ -1143,7 +1123,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1171,7 +1151,7 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getCurrentData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1179,7 +1159,7 @@ void LLAudioEngine::startNextTransfer() } adp = asp->getQueuedData(); - if (adp && !adp->hasLocalData() && adp->hasValidData()) + if (adp && !adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1194,7 +1174,7 @@ void LLAudioEngine::startNextTransfer() continue; } - if (!adp->hasLocalData() && adp->hasValidData()) + if (!adp->hasLocalData() && !adp->hasDecodeFailed()) { asset_id = adp->getID(); max_pri = asp->getPriority(); @@ -1235,7 +1215,7 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v LLAudioData *adp = gAudiop->getAudioData(uuid); if (adp) { // Make sure everything is cleared - adp->setHasValidData(false); + adp->setHasDecodeFailed(true); adp->setHasLocalData(false); adp->setHasDecodedData(false); adp->setHasCompletedDecode(true); @@ -1252,9 +1232,9 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v else { // LL_INFOS() << "Got asset callback with good audio data for " << uuid << ", making decode request" << LL_ENDL; - adp->setHasValidData(true); + adp->setHasDecodeFailed(false); adp->setHasLocalData(true); - gAudioDecodeMgrp->addDecodeRequest(uuid); + LLAudioDecodeMgr::getInstance()->addDecodeRequest(uuid); } } gAudiop->mCurrentTransfer = LLUUID::null; @@ -1273,7 +1253,7 @@ LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 mPriority(0.f), mGain(gain), mSourceMuted(false), - mAmbient(false), + mForcedPriority(false), mLoop(false), mSyncMaster(false), mSyncSlave(false), @@ -1324,11 +1304,15 @@ void LLAudioSource::update() { // Hack - try and load the sound. Will do this as a callback // on decode later. - if (adp->load() && adp->getBuffer()) + if (adp->getBuffer()) { play(adp->getID()); } - else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done + else if (adp->hasDecodedData() && !adp->hasWAVLoadFailed()) + { + adp->load(); + } + else if (adp->hasCompletedDecode() && adp->hasDecodeFailed()) // Only mark corrupted after decode is done { LL_WARNS() << "Marking LLAudioSource corrupted for " << adp->getID() << LL_ENDL; mCorrupted = true ; @@ -1339,7 +1323,7 @@ void LLAudioSource::update() void LLAudioSource::updatePriority() { - if (isAmbient()) + if (isForcedPriority()) { mPriority = 1.f; } @@ -1624,12 +1608,12 @@ bool LLAudioSource::hasPendingPreloads() const { LLAudioData *adp = iter->second; // note: a bad UUID will forever be !hasDecodedData() - // but also !hasValidData(), hence the check for hasValidData() + // but also hasDecodeFailed(), hence the check for hasDecodeFailed() if (!adp) { continue; } - if (!adp->hasDecodedData() && adp->hasValidData()) + if (!adp->hasDecodedData() && !adp->hasDecodeFailed()) { // This source is still waiting for a preload return true; @@ -1786,7 +1770,8 @@ LLAudioData::LLAudioData(const LLUUID &uuid) : mHasLocalData(false), mHasDecodedData(false), mHasCompletedDecode(false), - mHasValidData(true) + mHasDecodeFailed(false), + mHasWAVLoadFailed(false) { if (uuid.isNull()) { @@ -1821,12 +1806,14 @@ bool LLAudioData::load() { // We already have this sound in a buffer, don't do anything. LL_INFOS() << "Already have a buffer for this sound, don't bother loading!" << LL_ENDL; + mHasWAVLoadFailed = false; return true; } if (!gAudiop) { LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; + mHasWAVLoadFailed = true; return false; } @@ -1835,6 +1822,8 @@ bool LLAudioData::load() { // No free buffers, abort. LL_INFOS() << "Not able to allocate a new audio buffer, aborting." << LL_ENDL; + // *TODO: Mark this failure differently so the audio engine could retry loading this buffer in the future + mHasWAVLoadFailed = true; return true; } @@ -1843,7 +1832,8 @@ bool LLAudioData::load() mID.toString(uuid_str); wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; - if (!mBufferp->loadWAV(wav_path)) + mHasWAVLoadFailed = !mBufferp->loadWAV(wav_path); + if (mHasWAVLoadFailed) { // Hrm. Right now, let's unset the buffer, since it's empty. gAudiop->cleanupBuffer(mBufferp); diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h old mode 100644 new mode 100755 index b5fd4c27a1..0fe8b3d756 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -47,8 +47,8 @@ const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; const F32 DEFAULT_MIN_DISTANCE = 2.0f; -#define MAX_CHANNELS 30 -#define MAX_BUFFERS 40 // Some extra for preloading, maybe? +#define LL_MAX_AUDIO_CHANNELS 30 +#define LL_MAX_AUDIO_BUFFERS 40 // Some extra for preloading, maybe? class LLAudioSource; class LLAudioData; @@ -88,7 +88,7 @@ public: virtual ~LLAudioEngine(); // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *userdata, const std::string &app_title); + virtual bool init(void *userdata, const std::string &app_title); virtual std::string getDriverName(bool verbose) = 0; virtual void shutdown(); @@ -96,7 +96,7 @@ public: //virtual void processQueue(const LLUUID &sound_guid); virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at); virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0; - virtual void idle(F32 max_decode_time = 0.f); + virtual void idle(); virtual void updateChannels(); // @@ -209,7 +209,6 @@ protected: S32 mLastStatus; - S32 mNumChannels; bool mEnableWind; LLUUID mCurrentTransfer; // Audio file currently being transferred by the system @@ -224,11 +223,11 @@ protected: source_map mAllSources; data_map mAllData; - LLAudioChannel *mChannels[MAX_CHANNELS]; + std::array mChannels; // Buffers needs to change into a different data structure, as the number of buffers // that we have active should be limited by RAM usage, not count. - LLAudioBuffer *mBuffers[MAX_BUFFERS]; + std::array mBuffers; F32 mMasterGain; F32 mInternalGain; // Actual gain set; either mMasterGain or 0 when mMuted is true. @@ -266,8 +265,8 @@ public: void addAudioData(LLAudioData *adp, bool set_current = TRUE); - void setAmbient(const bool ambient) { mAmbient = ambient; } - bool isAmbient() const { return mAmbient; } + void setForcedPriority(const bool ambient) { mForcedPriority = ambient; } + bool isForcedPriority() const { return mForcedPriority; } void setLoop(const bool loop) { mLoop = loop; } bool isLoop() const { return mLoop; } @@ -326,7 +325,7 @@ protected: F32 mPriority; F32 mGain; bool mSourceMuted; - bool mAmbient; + bool mForcedPriority; // ignore mute, set high priority, researved for sound preview and UI bool mLoop; bool mSyncMaster; bool mSyncSlave; @@ -360,32 +359,36 @@ protected: class LLAudioData { -public: - LLAudioData(const LLUUID &uuid); - bool load(); + public: + LLAudioData(const LLUUID &uuid); + bool load(); - LLUUID getID() const { return mID; } - LLAudioBuffer *getBuffer() const { return mBufferp; } + LLUUID getID() const { return mID; } + LLAudioBuffer *getBuffer() const { return mBufferp; } - bool hasLocalData() const { return mHasLocalData; } - bool hasDecodedData() const { return mHasDecodedData; } - bool hasCompletedDecode() const { return mHasCompletedDecode; } - bool hasValidData() const { return mHasValidData; } + bool hasLocalData() const { return mHasLocalData; } + bool hasDecodedData() const { return mHasDecodedData; } + bool hasCompletedDecode() const { return mHasCompletedDecode; } + bool hasDecodeFailed() const { return mHasDecodeFailed; } + bool hasWAVLoadFailed() const { return mHasWAVLoadFailed; } - void setHasLocalData(const bool hld) { mHasLocalData = hld; } - void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } - void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } - void setHasValidData(const bool hvd) { mHasValidData = hvd; } + void setHasLocalData(const bool hld) { mHasLocalData = hld; } + void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } + void setHasCompletedDecode(const bool hcd) { mHasCompletedDecode = hcd; } + void setHasDecodeFailed(const bool hdf) { mHasDecodeFailed = hdf; } + void setHasWAVLoadFailed(const bool hwlf) { mHasWAVLoadFailed = hwlf; } - friend class LLAudioEngine; // Severe laziness, bad. + friend class LLAudioEngine; // Severe laziness, bad. -protected: - LLUUID mID; - LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. - bool mHasLocalData; // Set true if the sound asset file is available locally - bool mHasDecodedData; // Set true if the sound file has been decoded - bool mHasCompletedDecode; // Set true when the sound is decoded - bool mHasValidData; // Set false if decoding failed, meaning the sound asset is bad + protected: + LLUUID mID; + LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. + bool mHasLocalData; // Set true if the encoded sound asset file is available locally + bool mHasDecodedData; // Set true if the decoded sound file is available on disk + bool mHasCompletedDecode; // Set true when the sound is decoded + bool mHasDecodeFailed; // Set true if decoding failed, meaning the sound asset is bad + bool mHasWAVLoadFailed; // Set true if loading the decoded WAV file failed, meaning the sound asset should be decoded instead if + // possible }; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index b0c87b0208..ba743020b5 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -74,7 +74,7 @@ static inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) return true; } -bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title) { U32 version; FMOD_RESULT result; @@ -86,7 +86,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons return false; //will call LLAudioEngine_FMODSTUDIO::allocateListener, which needs a valid mSystem pointer. - LLAudioEngine::init(num_channels, userdata, app_title); + LLAudioEngine::init(userdata, app_title); result = mSystem->getVersion(&version); Check_FMOD_Error(result, "FMOD::System::getVersion"); @@ -98,7 +98,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons } // In this case, all sounds, PLUS wind and stream will be software. - result = mSystem->setSoftwareChannels(num_channels + 2); + result = mSystem->setSoftwareChannels(LL_MAX_AUDIO_CHANNELS + 2); Check_FMOD_Error(result, "FMOD::System::setSoftwareChannels"); FMOD_ADVANCEDSETTINGS settings; @@ -127,7 +127,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { LL_DEBUGS("AppInit") << "Trying PulseAudio audio output..." << LL_ENDL; if (mSystem->setOutput(FMOD_OUTPUTTYPE_PULSEAUDIO) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, const_cast(app_title.c_str()))) == FMOD_OK) + (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, const_cast(app_title.c_str()))) == FMOD_OK) { LL_DEBUGS("AppInit") << "PulseAudio output initialized OKAY" << LL_ENDL; audio_ok = true; @@ -149,7 +149,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; if (mSystem->setOutput(FMOD_OUTPUTTYPE_ALSA) == FMOD_OK && - (result = mSystem->init(num_channels + 2, fmod_flags, 0)) == FMOD_OK) + (result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0)) == FMOD_OK) { LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; audio_ok = true; @@ -190,7 +190,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons // initialize the FMOD engine // number of channel in this case looks to be identiacal to number of max simultaneously // playing objects and we can set practically any number - result = mSystem->init(num_channels + 2, fmod_flags, 0); + result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0); if (Check_FMOD_Error(result, "Error initializing FMOD Studio with default settins, retrying with other format")) { result = mSystem->setSoftwareFormat(44100, FMOD_SPEAKERMODE_STEREO, 0/*- ignore*/); @@ -198,7 +198,7 @@ bool LLAudioEngine_FMODSTUDIO::init(const S32 num_channels, void* userdata, cons { return false; } - result = mSystem->init(num_channels + 2, fmod_flags, 0); + result = mSystem->init(LL_MAX_AUDIO_CHANNELS + 2, fmod_flags, 0); } if (Check_FMOD_Error(result, "Error initializing FMOD Studio")) { @@ -519,9 +519,9 @@ void LLAudioChannelFMODSTUDIO::update3DPosition() return; } - if (mCurrentSourcep->isAmbient()) + if (mCurrentSourcep->isForcedPriority()) { - // Ambient sound, don't need to do any positional updates. + // Prioritized UI and preview sounds don't need to do any positional updates. set3DMode(false); } else diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h index f2361df1b6..d3d6d69685 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.h +++ b/indra/llaudio/llaudioengine_fmodstudio.h @@ -51,7 +51,7 @@ public: virtual ~LLAudioEngine_FMODSTUDIO(); // initialization/startup/shutdown - virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title); + virtual bool init(void *user_data, const std::string &app_title); virtual std::string getDriverName(bool verbose); virtual void allocateListener(); diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 3bdd0302ee..0a79614424 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -52,10 +52,10 @@ LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() } // virtual -bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata, const std::string &app_title) +bool LLAudioEngine_OpenAL::init(void* userdata, const std::string &app_title) { mWindGen = NULL; - LLAudioEngine::init(num_channels, userdata, app_title); + LLAudioEngine::init(userdata, app_title); if(!alutInit(NULL, NULL)) { @@ -297,7 +297,7 @@ void LLAudioChannelOpenAL::update3DPosition() { return; } - if (mCurrentSourcep->isAmbient()) + if (mCurrentSourcep->isForcedPriority()) { alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0); alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0); diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h index 366f9259e3..a3cab97cd2 100644 --- a/indra/llaudio/llaudioengine_openal.h +++ b/indra/llaudio/llaudioengine_openal.h @@ -40,7 +40,7 @@ class LLAudioEngine_OpenAL : public LLAudioEngine LLAudioEngine_OpenAL(); virtual ~LLAudioEngine_OpenAL(); - virtual bool init(const S32 num_channels, void *user_data, const std::string &app_title); + virtual bool init(void *user_data, const std::string &app_title); virtual std::string getDriverName(bool verbose); virtual void allocateListener(); diff --git a/indra/llaudio/llstreamingaudio_fmodstudio.cpp b/indra/llaudio/llstreamingaudio_fmodstudio.cpp index 1ad29a3f59..85577992a6 100644 --- a/indra/llaudio/llstreamingaudio_fmodstudio.cpp +++ b/indra/llaudio/llstreamingaudio_fmodstudio.cpp @@ -70,7 +70,11 @@ mRetryCount(0) // Must be larger than the usual Second Life frame stutter time. const U32 buffer_seconds = 10; //sec const U32 estimated_bitrate = 128; //kbit/sec - mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + FMOD_RESULT result = mSystem->setStreamBufferSize(estimated_bitrate * buffer_seconds * 128/*bytes/kbit*/, FMOD_TIMEUNIT_RAWBYTES); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL; + } // Here's where we set the size of the network buffer and some buffering // parameters. In this case we want a network buffer of 16k, we want it @@ -134,7 +138,7 @@ void LLStreamingAudio_FMODSTUDIO::killDeadStreams() { LL_INFOS("FMOD") << "Closed dead stream" << LL_ENDL; delete streamp; - mDeadStreams.erase(iter++); + iter = mDeadStreams.erase(iter); } else { @@ -404,7 +408,11 @@ FMOD::Channel *LLAudioStreamManagerFMODSTUDIO::startStream() if (mStreamChannel) return mStreamChannel; //Already have a channel for this stream. - mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel); + FMOD_RESULT result = mSystem->playSound(mInternetStream, NULL, true, &mStreamChannel); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL; + } return mStreamChannel; } @@ -445,16 +453,29 @@ bool LLAudioStreamManagerFMODSTUDIO::stopStream() FMOD_OPENSTATE LLAudioStreamManagerFMODSTUDIO::getOpenState(unsigned int* percentbuffered, bool* starving, bool* diskbusy) { FMOD_OPENSTATE state; - mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + FMOD_RESULT result = mInternetStream->getOpenState(&state, percentbuffered, starving, diskbusy); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << FMOD_ErrorString(result) << LL_ENDL; + } return state; } void LLStreamingAudio_FMODSTUDIO::setBufferSizes(U32 streambuffertime, U32 decodebuffertime) { - mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES); + FMOD_RESULT result = mSystem->setStreamBufferSize(streambuffertime / 1000 * 128 * 128, FMOD_TIMEUNIT_RAWBYTES); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << "setStreamBufferSize error: " << FMOD_ErrorString(result) << LL_ENDL; + return; + } FMOD_ADVANCEDSETTINGS settings; memset(&settings, 0, sizeof(settings)); settings.cbSize = sizeof(settings); settings.defaultDecodeBufferSize = decodebuffertime;//ms - mSystem->setAdvancedSettings(&settings); + result = mSystem->setAdvancedSettings(&settings); + if (result != FMOD_OK) + { + LL_WARNS("FMOD") << "setAdvancedSettings error: " << FMOD_ErrorString(result) << LL_ENDL; + } } diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index e906d81ce1..c38614b0b4 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -44,6 +44,14 @@ using namespace std; #define INCHES_TO_METERS 0.02540005f +/// The .bvh does not have a formal spec, and different readers interpret things in their own way. +/// In OUR usage, frame 0 is used in optimization and is not considered to be part of the animation. +const S32 NUMBER_OF_IGNORED_FRAMES_AT_START = 1; +/// In our usage, the last frame is used only to indicate what the penultimate frame should be interpolated towards. +/// I.e., the animation only plays up to the start of the last frame. There is no hold or exptrapolation past that point.. +/// Thus there are two frame of the total that do not contribute to the total running time of the animation. +const S32 NUMBER_OF_UNPLAYED_FRAMES = NUMBER_OF_IGNORED_FRAMES_AT_START + 1; + const F32 POSITION_KEYFRAME_THRESHOLD_SQUARED = 0.03f * 0.03f; const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f; @@ -865,7 +873,10 @@ ELoadStatus LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 & return E_ST_NO_FRAME_TIME; } - mDuration = (F32)mNumFrames * mFrameTime; + // If the user only supplies one animation frame (after the ignored reference frame 0), hold for mFrameTime. + // If the user supples exactly one total frame, it isn't clear if that is a pose or reference frame, and the + // behavior is not defined. In this case, retain historical undefined behavior. + mDuration = llmax((F32)(mNumFrames - NUMBER_OF_UNPLAYED_FRAMES), 1.0f) * mFrameTime; if (!mLoop) { mLoopOutPoint = mDuration; @@ -1355,12 +1366,13 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) LLQuaternion::Order order = bvhStringToOrder( joint->mOrder ); S32 outcount = 0; - S32 frame = 1; + S32 frame = 0; for ( ki = joint->mKeys.begin(); ki != joint->mKeys.end(); ++ki ) { - if ((frame == 1) && joint->mRelativeRotationKey) + + if ((frame == 0) && joint->mRelativeRotationKey) { first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order); @@ -1373,7 +1385,7 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) continue; } - time = (F32)frame * mFrameTime; + time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts. if (mergeParent) { @@ -1433,12 +1445,12 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) LLVector3 relPos = joint->mRelativePosition; LLVector3 relKey; - frame = 1; + frame = 0; for ( ki = joint->mKeys.begin(); ki != joint->mKeys.end(); ++ki ) { - if ((frame == 1) && joint->mRelativePositionKey) + if ((frame == 0) && joint->mRelativePositionKey) { relKey.setVec(ki->mPos); } @@ -1449,7 +1461,7 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) continue; } - time = (F32)frame * mFrameTime; + time = llmax((F32)(frame - NUMBER_OF_IGNORED_FRAMES_AT_START), 0.0f) * mFrameTime; // Time elapsed before this frame starts. LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot; LLVector3 outPos = inPos * frameRot * offsetRot; diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index a25ff16786..ebf7454a61 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -1222,8 +1222,11 @@ void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8 //----------------------------------------------------------------------------- // deserialize() +// +// allow_invalid_joints should be true when handling existing content, to avoid breakage. +// During upload, we should be more restrictive and reject such animations. //----------------------------------------------------------------------------- -BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) +BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints) { BOOL old_version = FALSE; mJointMotionList = new LLKeyframeMotion::JointMotionList; @@ -1344,6 +1347,15 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) return FALSE; } + //SL-17206 hack to alter Female_land loop setting, while current behavior won't be changed serverside + LLUUID const female_land_anim("ca1baf4d-0a18-5a1f-0330-e4bd1e71f09e"); + LLUUID const formal_female_land_anim("6a9a173b-61fa-3ad5-01fa-a851cfc5f66a"); + if (female_land_anim == asset_id || formal_female_land_anim == asset_id) + { + LL_WARNS() << "Animation(" << asset_id << ") won't be looped." << LL_ENDL; + mJointMotionList->mLoop = FALSE; + } + //------------------------------------------------------------------------- // get easeIn and easeOut //------------------------------------------------------------------------- @@ -1443,6 +1455,7 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) if (joint) { S32 joint_num = joint->getJointNum(); + joint_name = joint->getName(); // canonical name in case this is an alias. // LL_INFOS() << " joint: " << joint_name << LL_ENDL; if ((joint_num >= (S32)LL_CHARACTER_MAX_ANIMATED_JOINTS) || (joint_num < 0)) { @@ -1457,7 +1470,10 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id) { LL_WARNS() << "invalid joint name: " << joint_name << " for animation " << asset_id << LL_ENDL; - //return FALSE; + if (!allow_invalid_joints) + { + return FALSE; + } } joint_motion->mJointName = joint_name; @@ -2096,8 +2112,9 @@ U32 LLKeyframeMotion::getFileSize() //----------------------------------------------------------------------------- // dumpToFile() //----------------------------------------------------------------------------- -void LLKeyframeMotion::dumpToFile(const std::string& name) +bool LLKeyframeMotion::dumpToFile(const std::string& name) { + bool succ = false; if (isLoaded()) { std::string outfile_base; @@ -2114,10 +2131,24 @@ void LLKeyframeMotion::dumpToFile(const std::string& name) const LLUUID& id = getID(); outfile_base = id.asString(); } - std::string outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base + ".anim"); + + if (gDirUtilp->getExtension(outfile_base).empty()) + { + outfile_base += ".anim"; + } + std::string outfilename; + if (gDirUtilp->getDirName(outfile_base).empty()) + { + outfilename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,outfile_base); + } + else + { + outfilename = outfile_base; + } if (LLFile::isfile(outfilename)) { - return; + LL_WARNS() << outfilename << " already exists, write failed" << LL_ENDL; + return false; } S32 file_size = getFileSize(); @@ -2131,11 +2162,13 @@ void LLKeyframeMotion::dumpToFile(const std::string& name) outfile.open(outfilename, LL_APR_WPB); if (outfile.getFileHandle()) { - outfile.write(buffer, file_size); + S32 wrote_bytes = outfile.write(buffer, file_size); + succ = (wrote_bytes == file_size); } } delete [] buffer; } + return succ; } //----------------------------------------------------------------------------- diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h index 9a927ede9a..96746f57c9 100644 --- a/indra/llcharacter/llkeyframemotion.h +++ b/indra/llcharacter/llkeyframemotion.h @@ -156,9 +156,9 @@ public: public: U32 getFileSize(); BOOL serialize(LLDataPacker& dp) const; - BOOL deserialize(LLDataPacker& dp, const LLUUID& asset_id); + BOOL deserialize(LLDataPacker& dp, const LLUUID& asset_id, bool allow_invalid_joints = true); BOOL isLoaded() { return mJointMotionList != NULL; } - void dumpToFile(const std::string& name); + bool dumpToFile(const std::string& name); // setters for modifying a keyframe animation @@ -432,6 +432,9 @@ protected: F32 mLastUpdateTime; F32 mLastLoopedTime; AssetStatus mAssetStatus; + +public: + void setCharacter(LLCharacter* character) { mCharacter = character; } }; class LLKeyframeDataCache diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 59aa731af2..df65828d02 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -266,6 +266,11 @@ set(llcommon_HEADER_FILES workqueue.h StackWalker.h ) + +if (DARWIN) + list(APPEND llcommon_HEADER_FILES llsys_objc.h) + list(APPEND llcommon_SOURCE_FILES llsys_objc.mm) +endif (DARWIN) set_source_files_properties(${llcommon_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) @@ -313,12 +318,6 @@ target_link_libraries( ${TRACY_LIBRARY} ) -if (DARWIN) - include(CMakeFindFrameworks) - find_library(CARBON_LIBRARY Carbon) - target_link_libraries(llcommon ${CARBON_LIBRARY}) -endif (DARWIN) - add_dependencies(llcommon stage_third_party_libs) if (LL_TESTS) diff --git a/indra/llcommon/llalignedarray.h b/indra/llcommon/llalignedarray.h index b68e9e0f82..da9d98c16c 100644 --- a/indra/llcommon/llalignedarray.h +++ b/indra/llcommon/llalignedarray.h @@ -116,14 +116,20 @@ void LLAlignedArray::resize(U32 size) template T& LLAlignedArray::operator[](int idx) { - llassert(idx < mElementCount); + if(idx >= mElementCount || idx < 0) + { + LL_ERRS() << "Out of bounds LLAlignedArray, requested: " << (S32)idx << " size: " << mElementCount << LL_ENDL; + } return mArray[idx]; } template const T& LLAlignedArray::operator[](int idx) const { - llassert(idx < mElementCount); + if (idx >= mElementCount || idx < 0) + { + LL_ERRS() << "Out of bounds LLAlignedArray, requested: " << (S32)idx << " size: " << mElementCount << LL_ENDL; + } return mArray[idx]; } diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index c2d353b0fc..14bfb98629 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -35,6 +35,7 @@ // STL headers // std headers #include +#include // external library headers #include #include @@ -214,6 +215,22 @@ std::string LLCoros::logname() return data.mName.empty()? data.getKey() : data.mName; } +void LLCoros::saveException(const std::string& name, std::exception_ptr exc) +{ + mExceptionQueue.emplace(name, exc); +} + +void LLCoros::rethrow() +{ + if (! mExceptionQueue.empty()) + { + ExceptionData front = mExceptionQueue.front(); + mExceptionQueue.pop(); + LL_WARNS("LLCoros") << "Rethrowing exception from coroutine " << front.name << LL_ENDL; + std::rethrow_exception(front.exception); + } +} + void LLCoros::setStackSize(S32 stacksize) { LL_DEBUGS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; @@ -302,11 +319,11 @@ U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop, } } -void LLCoros::winlevel(const std::string& name, const callable_t& callable) +void LLCoros::sehHandle(const std::string& name, const LLCoros::callable_t& callable) { __try { - toplevelTryWrapper(name, callable); + LLCoros::toplevelTryWrapper(name, callable); } __except (cpp_exception_filter(GetExceptionCode(), GetExceptionInformation(), name)) { @@ -321,7 +338,6 @@ void LLCoros::winlevel(const std::string& name, const callable_t& callable) throw std::exception(integer_string); } } - #endif void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& callable) @@ -350,11 +366,19 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call } catch (...) { +#if LL_WINDOWS // Any OTHER kind of uncaught exception will cause the viewer to - // crash, hopefully informatively. + // crash, SEH handling should catch it and report to bugsplat. LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name)); // to not modify callstack throw; +#else + // Stash any OTHER kind of uncaught exception in the rethrow() queue + // to be rethrown by the main fiber. + LL_WARNS("LLCoros") << "Capturing uncaught exception in coroutine " + << name << LL_ENDL; + LLCoros::instance().saveException(name, std::current_exception()); +#endif } } @@ -364,8 +388,9 @@ void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& call void LLCoros::toplevel(std::string name, callable_t callable) { #if LL_WINDOWS - // Can not use __try in functions that require unwinding, so use one more wrapper - winlevel(name, callable); + // Because SEH can's have unwinding, need to call a wrapper + // 'try' is inside SEH handling to not catch LLContinue + sehHandle(name, callable); #else toplevelTryWrapper(name, callable); #endif diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index a94cfca19f..dbff921f16 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -38,6 +38,8 @@ #include "llinstancetracker.h" #include #include +#include +#include // e.g. #include LLCOROS_MUTEX_HEADER #define LLCOROS_MUTEX_HEADER @@ -156,6 +158,19 @@ public: * LLCoros::launch()). */ static std::string getName(); + + /** + * rethrow() is called by the thread's main fiber to propagate an + * exception from any coroutine into the main fiber, where it can engage + * the normal unhandled-exception machinery, up to and including crash + * reporting. + * + * LLCoros maintains a queue of otherwise-uncaught exceptions from + * terminated coroutines. Each call to rethrow() pops the first of those + * and rethrows it. When the queue is empty (normal case), rethrow() is a + * no-op. + */ + void rethrow(); /** * This variation returns a name suitable for log messages: the explicit @@ -292,13 +307,27 @@ public: private: std::string generateDistinctName(const std::string& prefix) const; -#if LL_WINDOWS - void winlevel(const std::string& name, const callable_t& callable); -#endif void toplevelTryWrapper(const std::string& name, const callable_t& callable); - void toplevel(std::string name, callable_t callable); +#if LL_WINDOWS + void sehHandle(const std::string& name, const callable_t& callable); // calls toplevelTryWrapper +#endif + void toplevel(std::string name, callable_t callable); // calls sehHandle or toplevelTryWrapper struct CoroData; static CoroData& get_CoroData(const std::string& caller); + void saveException(const std::string& name, std::exception_ptr exc); + + struct ExceptionData + { + ExceptionData(const std::string& nm, std::exception_ptr exc): + name(nm), + exception(exc) + {} + // name of coroutine that originally threw this exception + std::string name; + // the thrown exception + std::exception_ptr exception; + }; + std::queue mExceptionQueue; S32 mStackSize; diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 849867586a..d6ae1284d3 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -212,11 +212,9 @@ U64 LLMemory::getCurrentRSS() mach_msg_type_number_t basicInfoCount = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) { -// residentSize = basicInfo.resident_size; - // Although this method is defined to return the "resident set size," - // in fact what callers want from it is the total virtual memory - // consumed by the application. - residentSize = basicInfo.virtual_size; + residentSize = basicInfo.resident_size; + // 64-bit macos apps allocate 32 GB or more at startup, and this is reflected in virtual_size. + // basicInfo.virtual_size is not what we want. } else { diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 818df07bb2..4a1a81f083 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -118,7 +118,11 @@ namespace eMONTIOR_MWAIT=33, eCPLDebugStore=34, eThermalMonitor2=35, - eAltivec=36 + eAltivec=36, + eSSE3S_Features = 37, + eSSE4_1_Features = 38, + eSSE4_2_Features = 39, + eSSE4a_Features = 40, }; const char* cpu_feature_names[] = @@ -161,7 +165,11 @@ namespace "CPL Qualified Debug Store", "Thermal Monitor 2", - "Altivec" + "Altivec", + "SSE3S Instructions", + "SSE4.1 Instructions", + "SSE4.2 Instructions", + "SSE4a Instructions", }; std::string intel_CPUFamilyName(int composed_family) @@ -250,6 +258,31 @@ public: return hasExtension(cpu_feature_names[eSSE2_Ext]); } + bool hasSSE3() const + { + return hasExtension(cpu_feature_names[eSSE3_Features]); + } + + bool hasSSE3S() const + { + return hasExtension(cpu_feature_names[eSSE3S_Features]); + } + + bool hasSSE41() const + { + return hasExtension(cpu_feature_names[eSSE4_1_Features]); + } + + bool hasSSE42() const + { + return hasExtension(cpu_feature_names[eSSE4_2_Features]); + } + + bool hasSSE4a() const + { + return hasExtension(cpu_feature_names[eSSE4a_Features]); + } + bool hasAltivec() const { return hasExtension("Altivec"); @@ -473,6 +506,12 @@ private: *((int*)(cpu_vendor+4)) = cpu_info[3]; *((int*)(cpu_vendor+8)) = cpu_info[2]; setInfo(eVendor, cpu_vendor); + std::string cmp_vendor(cpu_vendor); + bool is_amd = false; + if (cmp_vendor == "AuthenticAMD") + { + is_amd = true; + } // Get the information associated with each valid Id for(unsigned int i=0; i<=ids; ++i) @@ -504,6 +543,7 @@ private: if(cpu_info[2] & 0x8) { + // intel specific SSE3 suplements setExtension(cpu_feature_names[eMONTIOR_MWAIT]); } @@ -516,7 +556,22 @@ private: { setExtension(cpu_feature_names[eThermalMonitor2]); } - + + if (cpu_info[2] & 0x200) + { + setExtension(cpu_feature_names[eSSE3S_Features]); + } + + if (cpu_info[2] & 0x80000) + { + setExtension(cpu_feature_names[eSSE4_1_Features]); + } + + if (cpu_info[2] & 0x100000) + { + setExtension(cpu_feature_names[eSSE4_2_Features]); + } + unsigned int feature_info = (unsigned int) cpu_info[3]; for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) { @@ -543,8 +598,17 @@ private: __cpuid(cpu_info, i); // Interpret CPU brand string and cache information. - if (i == 0x80000002) - memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info)); + if (i == 0x80000001) + { + if (is_amd) + { + setExtension(cpu_feature_names[eSSE4a_Features]); + } + } + else if (i == 0x80000002) + { + memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info)); + } else if (i == 0x80000003) memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info)); else if (i == 0x80000004) @@ -690,6 +754,41 @@ private: uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits"); S32 *ext_feature_infos = (S32*)(&ext_feature_info); setConfig(eExtFeatureBits, ext_feature_infos[0]); + + + char cpu_features[1024]; + len = sizeof(cpu_features); + memset(cpu_features, 0, len); + sysctlbyname("machdep.cpu.features", (void*)cpu_features, &len, NULL, 0); + + std::string cpu_features_str(cpu_features); + cpu_features_str = " " + cpu_features_str + " "; + + if (cpu_features_str.find(" SSE3 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE3_Features]); + } + + if (cpu_features_str.find(" SSSE3 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE3S_Features]); + } + + if (cpu_features_str.find(" SSE4.1 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE4_1_Features]); + } + + if (cpu_features_str.find(" SSE4.2 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE4_2_Features]); + } + + if (cpu_features_str.find(" SSE4A ") != std::string::npos) + { + // Not supposed to happen? + setExtension(cpu_feature_names[eSSE4a_Features]); + } } }; @@ -800,6 +899,31 @@ private: { setExtension(cpu_feature_names[eSSE2_Ext]); } + + if (flags.find(" pni ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE3_Features]); + } + + if (flags.find(" ssse3 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE3S_Features]); + } + + if (flags.find(" sse4_1 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE4_1_Features]); + } + + if (flags.find(" sse4_2 ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE4_2_Features]); + } + + if (flags.find(" sse4a ") != std::string::npos) + { + setExtension(cpu_feature_names[eSSE4a_Features]); + } # endif // LL_X86 } @@ -860,6 +984,11 @@ LLProcessorInfo::~LLProcessorInfo() {} F64MegahertzImplicit LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); } bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); } bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); } +bool LLProcessorInfo::hasSSE3() const { return mImpl->hasSSE3(); } +bool LLProcessorInfo::hasSSE3S() const { return mImpl->hasSSE3S(); } +bool LLProcessorInfo::hasSSE41() const { return mImpl->hasSSE41(); } +bool LLProcessorInfo::hasSSE42() const { return mImpl->hasSSE42(); } +bool LLProcessorInfo::hasSSE4a() const { return mImpl->hasSSE4a(); } bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); } std::string LLProcessorInfo::getCPUFamilyName() const { return mImpl->getCPUFamilyName(); } std::string LLProcessorInfo::getCPUBrandName() const { return mImpl->getCPUBrandName(); } diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h index b77eb22c3a..1a473ddc97 100644 --- a/indra/llcommon/llprocessor.h +++ b/indra/llcommon/llprocessor.h @@ -54,6 +54,11 @@ public: F64MegahertzImplicit getCPUFrequency() const; bool hasSSE() const; bool hasSSE2() const; + bool hasSSE3() const; + bool hasSSE3S() const; + bool hasSSE41() const; + bool hasSSE42() const; + bool hasSSE4a() const; bool hasAltivec() const; std::string getCPUFamilyName() const; std::string getCPUBrandName() const; diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp index 5cbd346411..6852b5536a 100644 --- a/indra/llcommon/llrefcount.cpp +++ b/indra/llcommon/llrefcount.cpp @@ -30,7 +30,7 @@ #include "llerror.h" // maximum reference count before sounding memory leak alarm -const S32 gMaxRefCount = 65536; +const S32 gMaxRefCount = S32_MAX; LLRefCount::LLRefCount(const LLRefCount& other) : mRef(0) diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 9b6bb3826c..a8b5c7b3a8 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -64,6 +64,7 @@ using namespace llsd; # include // GetPerformanceInfo() et al. # include #elif LL_DARWIN +# include "llsys_objc.h" # include # include # include @@ -74,12 +75,6 @@ using namespace llsd; # include # include # include - -// disable warnings about Gestalt calls being deprecated -// until Apple get's on the ball and provides an alternative -// -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" - #elif LL_LINUX # include # include @@ -278,12 +273,9 @@ LLOSInfo::LLOSInfo() : { const char * DARWIN_PRODUCT_NAME = "Mac OS X"; - SInt32 major_version, minor_version, bugfix_version; - OSErr r1 = Gestalt(gestaltSystemVersionMajor, &major_version); - OSErr r2 = Gestalt(gestaltSystemVersionMinor, &minor_version); - OSErr r3 = Gestalt(gestaltSystemVersionBugFix, &bugfix_version); + S32 major_version, minor_version, bugfix_version = 0; - if((r1 == noErr) && (r2 == noErr) && (r3 == noErr)) + if (LLGetDarwinOSInfo(major_version, minor_version, bugfix_version)) { mMajorVer = major_version; mMinorVer = minor_version; @@ -597,6 +589,11 @@ LLCPUInfo::LLCPUInfo() // proc.WriteInfoTextFile("procInfo.txt"); mHasSSE = proc.hasSSE(); mHasSSE2 = proc.hasSSE2(); + mHasSSE3 = proc.hasSSE3(); + mHasSSE3S = proc.hasSSE3S(); + mHasSSE41 = proc.hasSSE41(); + mHasSSE42 = proc.hasSSE42(); + mHasSSE4a = proc.hasSSE4a(); mHasAltivec = proc.hasAltivec(); mCPUMHz = (F64)proc.getCPUFrequency(); mFamily = proc.getCPUFamilyName(); @@ -609,6 +606,35 @@ LLCPUInfo::LLCPUInfo() } mCPUString = out.str(); LLStringUtil::trim(mCPUString); + + if (mHasSSE) + { + mSSEVersions.append("1"); + } + if (mHasSSE2) + { + mSSEVersions.append("2"); + } + if (mHasSSE3) + { + mSSEVersions.append("3"); + } + if (mHasSSE3S) + { + mSSEVersions.append("3S"); + } + if (mHasSSE41) + { + mSSEVersions.append("4.1"); + } + if (mHasSSE42) + { + mSSEVersions.append("4.2"); + } + if (mHasSSE4a) + { + mSSEVersions.append("4a"); + } } bool LLCPUInfo::hasAltivec() const @@ -626,6 +652,31 @@ bool LLCPUInfo::hasSSE2() const return mHasSSE2; } +bool LLCPUInfo::hasSSE3() const +{ + return mHasSSE3; +} + +bool LLCPUInfo::hasSSE3S() const +{ + return mHasSSE3S; +} + +bool LLCPUInfo::hasSSE41() const +{ + return mHasSSE41; +} + +bool LLCPUInfo::hasSSE42() const +{ + return mHasSSE42; +} + +bool LLCPUInfo::hasSSE4a() const +{ + return mHasSSE4a; +} + F64 LLCPUInfo::getMHz() const { return mCPUMHz; @@ -636,6 +687,11 @@ std::string LLCPUInfo::getCPUString() const return mCPUString; } +const LLSD& LLCPUInfo::getSSEVersions() const +{ + return mSSEVersions; +} + void LLCPUInfo::stream(std::ostream& s) const { // gather machine information. @@ -645,6 +701,11 @@ void LLCPUInfo::stream(std::ostream& s) const // CPU's attributes regardless of platform s << "->mHasSSE: " << (U32)mHasSSE << std::endl; s << "->mHasSSE2: " << (U32)mHasSSE2 << std::endl; + s << "->mHasSSE3: " << (U32)mHasSSE3 << std::endl; + s << "->mHasSSE3S: " << (U32)mHasSSE3S << std::endl; + s << "->mHasSSE41: " << (U32)mHasSSE41 << std::endl; + s << "->mHasSSE42: " << (U32)mHasSSE42 << std::endl; + s << "->mHasSSE4a: " << (U32)mHasSSE4a << std::endl; s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl; s << "->mCPUMHz: " << mCPUMHz << std::endl; s << "->mCPUString: " << mCPUString << std::endl; @@ -1341,10 +1402,3 @@ BOOL gzip_file(const std::string& srcfile, const std::string& dstfile) if (dst != NULL) gzclose(dst); return retval; } - -#if LL_DARWIN -// disable warnings about Gestalt calls being deprecated -// until Apple get's on the ball and provides an alternative -// -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index cb92cb0ac6..5ffbf5a732 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -80,10 +80,16 @@ public: void stream(std::ostream& s) const; std::string getCPUString() const; + const LLSD& getSSEVersions() const; bool hasAltivec() const; bool hasSSE() const; bool hasSSE2() const; + bool hasSSE3() const; + bool hasSSE3S() const; + bool hasSSE41() const; + bool hasSSE42() const; + bool hasSSE4a() const; F64 getMHz() const; // Family is "AMD Duron" or "Intel Pentium Pro" @@ -92,10 +98,16 @@ public: private: bool mHasSSE; bool mHasSSE2; + bool mHasSSE3; + bool mHasSSE3S; + bool mHasSSE41; + bool mHasSSE42; + bool mHasSSE4a; bool mHasAltivec; F64 mCPUMHz; std::string mFamily; std::string mCPUString; + LLSD mSSEVersions; }; //============================================================================= diff --git a/indra/llcommon/llsys_objc.h b/indra/llcommon/llsys_objc.h new file mode 100644 index 0000000000..35599a574b --- /dev/null +++ b/indra/llcommon/llsys_objc.h @@ -0,0 +1,33 @@ +/** + * @file llsys_objc.h + * @brief Header file for llsys_objc.mm + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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_LLSYS_OBJC_H +#define LL_LLSYS_OBJC_H + +bool LLGetDarwinOSInfo(int &major, int &minor, int &patch); + + +#endif // LL_LLSYS_OBJC_H diff --git a/indra/llcommon/llsys_objc.mm b/indra/llcommon/llsys_objc.mm new file mode 100644 index 0000000000..cdb1e320d5 --- /dev/null +++ b/indra/llcommon/llsys_objc.mm @@ -0,0 +1,64 @@ +/** + * @file llsys_objc.mm + * @brief obj-c implementation of the system information functions + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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$ + */ + +#import "llsys_objc.h" +#import + +static int intAtStringIndex(NSArray *array, int index) +{ + return [(NSString *)[array objectAtIndex:index] integerValue]; +} + +bool LLGetDarwinOSInfo(int &major, int &minor, int &patch) +{ + if (NSAppKitVersionNumber > NSAppKitVersionNumber10_8) + { + NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; + major = osVersion.majorVersion; + minor = osVersion.minorVersion; + patch = osVersion.patchVersion; + } + else + { + NSString* versionString = [[NSDictionary dictionaryWithContentsOfFile: + @"/System/Library/CoreServices/SystemVersion.plist"] objectForKey:@"ProductVersion"]; + NSArray* versions = [versionString componentsSeparatedByString:@"."]; + NSUInteger count = [versions count]; + if (count > 0) + { + major = intAtStringIndex(versions, 0); + if (count > 1) + { + minor = intAtStringIndex(versions, 1); + if (count > 2) + { + patch = intAtStringIndex(versions, 2); + } + } + } + } + return true; +} diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp index ba914035e2..d5adf11264 100644 --- a/indra/llcommon/threadpool.cpp +++ b/indra/llcommon/threadpool.cpp @@ -22,6 +22,7 @@ #include "stringize.h" LL::ThreadPool::ThreadPool(const std::string& name, size_t threads, size_t capacity): + super(name), mQueue(name, capacity), mName("ThreadPool:" + name), mThreadCount(threads) diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index b79c9b9090..f8eec3b457 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -22,8 +22,10 @@ namespace LL { - class ThreadPool + class ThreadPool: public LLInstanceTracker { + private: + using super = LLInstanceTracker; public: /** * Pass ThreadPool a string name. This can be used to look up the diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index 96574a18b9..70fd65bd0c 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -403,7 +403,7 @@ namespace LL [result = std::forward(callable)(), callback = std::move(callback)] () - { callback(std::move(result)); }; + mutable { callback(std::move(result)); }; } }; @@ -449,7 +449,7 @@ namespace LL callable = std::move(callable), callback = std::move(callback)] () - { + mutable { // Use postMaybe() below in case this originating WorkQueue // has been closed or destroyed. Remember, the outer lambda is // now running on a thread servicing the target WorkQueue, and @@ -513,7 +513,7 @@ namespace LL // We dare to bind a reference to Promise because it's // specifically designed for cross-thread communication. [&promise, callable = std::move(callable)]() - { + mutable { try { // call the caller's callable and trigger promise with result @@ -542,7 +542,7 @@ namespace LL time, // &promise is designed for cross-thread access [&promise, callable = std::move(callable)]() - { + mutable { try { callable(); diff --git a/indra/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index ee43a599f7..6de99dfbff 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -131,28 +131,44 @@ void LLDiskCache::purge() LL_INFOS() << "Purging cache to a maximum of " << mMaxSizeBytes << " bytes" << LL_ENDL; + std::vector file_removed; + if (mEnableCacheDebugInfo) + { + file_removed.reserve(file_info.size()); + } uintmax_t file_size_total = 0; for (file_info_t& entry : file_info) { file_size_total += entry.second.first; - std::string action = ""; - if (file_size_total > mMaxSizeBytes) + bool should_remove = file_size_total > mMaxSizeBytes; + if (mEnableCacheDebugInfo) + { + file_removed.push_back(should_remove); + } + if (should_remove) { - action = "DELETE:"; boost::filesystem::remove(entry.second.second, ec); if (ec.failed()) { LL_WARNS() << "Failed to delete cache file " << entry.second.second << ": " << ec.message() << LL_ENDL; } } - else - { - action = " KEEP:"; - } + } - if (mEnableCacheDebugInfo) + if (mEnableCacheDebugInfo) + { + auto end_time = std::chrono::high_resolution_clock::now(); + auto execute_time = std::chrono::duration_cast(end_time - start_time).count(); + + // Log afterward so it doesn't affect the time measurement + // Logging thousands of file results can take hundreds of milliseconds + for (size_t i = 0; i < file_info.size(); ++i) { + const file_info_t& entry = file_info[i]; + const bool removed = file_removed[i]; + const std::string action = removed ? "DELETE:" : "KEEP:"; + // have to do this because of LL_INFO/LL_END weirdness std::ostringstream line; @@ -163,12 +179,7 @@ void LLDiskCache::purge() line << " (" << file_size_total << "/" << mMaxSizeBytes << ")"; LL_INFOS() << line.str() << LL_ENDL; } - } - if (mEnableCacheDebugInfo) - { - auto end_time = std::chrono::high_resolution_clock::now(); - auto execute_time = std::chrono::duration_cast(end_time - start_time).count(); LL_INFOS() << "Total dir size after purge is " << dirFileSize(mCacheDir) << LL_ENDL; LL_INFOS() << "Cache purge took " << execute_time << " ms to execute for " << file_info.size() << " files" << LL_ENDL; } @@ -354,6 +365,38 @@ void LLDiskCache::clearCache() } } +void LLDiskCache::removeOldVFSFiles() +{ + //VFS files won't be created, so consider removing this code later + static const char CACHE_FORMAT[] = "inv.llsd"; + static const char DB_FORMAT[] = "db2.x"; + + boost::system::error_code ec; +#if LL_WINDOWS + std::wstring cache_path(utf8str_to_utf16str(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""))); +#else + std::string cache_path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "")); +#endif + if (boost::filesystem::is_directory(cache_path, ec) && !ec.failed()) + { + for (auto& entry : boost::make_iterator_range(boost::filesystem::directory_iterator(cache_path, ec), {})) + { + if (boost::filesystem::is_regular_file(entry, ec) && !ec.failed()) + { + if ((entry.path().string().find(CACHE_FORMAT) != std::string::npos) || + (entry.path().string().find(DB_FORMAT) != std::string::npos)) + { + boost::filesystem::remove(entry, ec); + if (ec.failed()) + { + LL_WARNS() << "Failed to delete cache file " << entry << ": " << ec.message() << LL_ENDL; + } + } + } + } + } +} + uintmax_t LLDiskCache::dirFileSize(const std::string dir) { uintmax_t total_file_size = 0; diff --git a/indra/llfilesystem/lldiskcache.h b/indra/llfilesystem/lldiskcache.h index 1cbd2c58aa..b60e74f8c9 100644 --- a/indra/llfilesystem/lldiskcache.h +++ b/indra/llfilesystem/lldiskcache.h @@ -140,6 +140,8 @@ class LLDiskCache : */ const std::string getCacheInfo(); + void removeOldVFSFiles(); + private: /** * Utility function to gather the total size the files in a given diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index e25dae8a90..134e783053 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -234,6 +234,8 @@ void LLParcel::init(const LLUUID &owner_id, setRegionAllowEnvironmentOverride(FALSE); setParcelEnvironmentVersion(INVALID_PARCEL_ENVIRONMENT_VERSION); + + setObscureMOAP(false); } void LLParcel::overrideOwner(const LLUUID& owner_id, BOOL is_group_owned) @@ -540,6 +542,7 @@ void LLParcel::packMessage(LLSD& msg) msg["see_avs"] = (LLSD::Boolean) getSeeAVs(); msg["group_av_sounds"] = (LLSD::Boolean) getAllowGroupAVSounds(); msg["any_av_sounds"] = (LLSD::Boolean) getAllowAnyAVSounds(); + msg["obscure_moap"] = (LLSD::Boolean) getObscureMOAP(); } diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index 5d08c1f4c6..f5ee1241ab 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -306,6 +306,7 @@ public: void setRestrictPushObject(BOOL b) { setParcelFlag(PF_RESTRICT_PUSHOBJECT, b); } void setAllowGroupAVSounds(BOOL b) { mAllowGroupAVSounds = b; } void setAllowAnyAVSounds(BOOL b) { mAllowAnyAVSounds = b; } + void setObscureMOAP(bool b) { mObscureMOAP = b; } void setDrawDistance(F32 dist) { mDrawDistance = dist; } void setSalePrice(S32 price) { mSalePrice = price; } @@ -517,6 +518,8 @@ public: BOOL getAllowGroupAVSounds() const { return mAllowGroupAVSounds; } BOOL getAllowAnyAVSounds() const { return mAllowAnyAVSounds; } + + bool getObscureMOAP() const { return mObscureMOAP; } F32 getDrawDistance() const { return mDrawDistance; } S32 getSalePrice() const { return mSalePrice; } @@ -670,6 +673,7 @@ protected: BOOL mRegionAllowEnvironmentOverride; BOOL mAllowGroupAVSounds; BOOL mAllowAnyAVSounds; + bool mObscureMOAP; S32 mCurrentEnvironmentVersion; bool mIsDefaultDayCycle; diff --git a/indra/llmath/CMakeLists.txt b/indra/llmath/CMakeLists.txt index 552e820127..4617309606 100644 --- a/indra/llmath/CMakeLists.txt +++ b/indra/llmath/CMakeLists.txt @@ -4,12 +4,14 @@ project(llmath) include(00-Common) include(LLCommon) +include(LLMeshOptimizer) include(bugsplat) include(Boost) include_directories( ${LLCOMMON_INCLUDE_DIRS} ${LLCOMMON_SYSTEM_INCLUDE_DIRS} + ${LLMESHOPTIMIZER_INCLUDE_DIRS} ) set(llmath_SOURCE_FILES @@ -109,6 +111,7 @@ add_library (llmath ${llmath_SOURCE_FILES}) target_link_libraries(llmath ${LLCOMMON_LIBRARIES} + ${LLMESHOPTIMIZER_LIBRARIES} ) # Add tests diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h index a9a54a8113..318ee65cc0 100644 --- a/indra/llmath/lloctree.h +++ b/indra/llmath/lloctree.h @@ -48,52 +48,59 @@ extern float gOctreeMinSize; #define LL_OCTREE_MAX_CAPACITY 128 #endif*/ -template class LLOctreeNode; +// T is the type of the element referenced by the octree node. +// T_PTR determines how pointers to elements are stored internally. +// LLOctreeNode> assumes ownership of inserted elements and +// deletes elements removed from the tree. +// LLOctreeNode doesn't take ownership of inserted elements, so the API +// user is responsible for managing the storage lifecycle of elements added to +// the tree. +template class LLOctreeNode; -template +template class LLOctreeListener: public LLTreeListener { public: typedef LLTreeListener BaseType; - typedef LLOctreeNode oct_node; + typedef LLOctreeNode oct_node; virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0; virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0; }; -template +template class LLOctreeTraveler { public: - virtual void traverse(const LLOctreeNode* node); - virtual void visit(const LLOctreeNode* branch) = 0; + virtual void traverse(const LLOctreeNode* node); + virtual void visit(const LLOctreeNode* branch) = 0; }; -template -class LLOctreeTravelerDepthFirst : public LLOctreeTraveler +template +class LLOctreeTravelerDepthFirst : public LLOctreeTraveler { public: - virtual void traverse(const LLOctreeNode* node) override; + virtual void traverse(const LLOctreeNode* node) override; }; -template +template class alignas(16) LLOctreeNode : public LLTreeNode { LL_ALIGN_NEW public: - typedef LLOctreeTraveler oct_traveler; - typedef LLTreeTraveler tree_traveler; - typedef std::vector< LLPointer > element_list; // note: don't remove the whitespace between "> >" - typedef LLPointer* element_iter; - typedef const LLPointer* const_element_iter; + typedef LLOctreeTraveler oct_traveler; + typedef LLTreeTraveler tree_traveler; + typedef std::vector element_list; + typedef typename element_list::iterator element_iter; + typedef typename element_list::const_iterator const_element_iter; typedef typename std::vector*>::iterator tree_listener_iter; - typedef LLOctreeNode** child_list; - typedef LLOctreeNode** child_iter; + typedef LLOctreeNode** child_list; + typedef LLOctreeNode** child_iter; - typedef LLTreeNode BaseType; - typedef LLOctreeNode oct_node; - typedef LLOctreeListener oct_listener; + typedef LLTreeNode BaseType; + typedef LLOctreeNode oct_node; + typedef LLOctreeListener oct_listener; enum { @@ -108,9 +115,6 @@ public: mOctant(octant) { llassert(size[0] >= gOctreeMinSize*0.5f); - //always keep a NULL terminated list to avoid out of bounds exceptions in debug builds - mData.push_back(NULL); - mDataEnd = &mData[0]; mCenter = center; mSize = size; @@ -121,8 +125,6 @@ public: mOctant = ((oct_node*) mParent)->getOctant(mCenter); } - mElementCount = 0; - clearChildren(); } @@ -130,15 +132,14 @@ public: { BaseType::destroyListeners(); - for (U32 i = 0; i < mElementCount; ++i) + const U32 element_count = getElementCount(); + for (U32 i = 0; i < element_count; ++i) { mData[i]->setBinIndex(-1); mData[i] = NULL; } mData.clear(); - mData.push_back(NULL); - mDataEnd = &mData[0]; for (U32 i = 0; i < getChildCount(); i++) { @@ -238,14 +239,12 @@ public: void accept(oct_traveler* visitor) { visitor->visit(this); } virtual bool isLeaf() const { return mChildCount == 0; } - U32 getElementCount() const { return mElementCount; } - bool isEmpty() const { return mElementCount == 0; } - element_list& getData() { return mData; } - const element_list& getData() const { return mData; } - element_iter getDataBegin() { return &mData[0]; } - element_iter getDataEnd() { return mDataEnd; } - const_element_iter getDataBegin() const { return &mData[0]; } - const_element_iter getDataEnd() const { return mDataEnd; } + U32 getElementCount() const { return (U32)mData.size(); } + bool isEmpty() const { return mData.empty(); } + element_iter getDataBegin() { return mData.begin(); } + element_iter getDataEnd() { return mData.end(); } + const_element_iter getDataBegin() const { return mData.cbegin(); } + const_element_iter getDataEnd() const { return mData.cend(); } U32 getChildCount() const { return mChildCount; } oct_node* getChild(U32 index) { return mChild[index]; } @@ -263,7 +262,7 @@ public: U8 idx = mChildMap[i]; if (idx != NO_CHILD_NODES) { - LLOctreeNode* child = mChild[idx]; + oct_node* child = mChild[idx]; if (child->getOctant() != i) { @@ -281,7 +280,7 @@ public: oct_node* getNodeAt(const LLVector4a& pos, const F32& rad) { - LLOctreeNode* node = this; + oct_node* node = this; if (node->isInside(pos, rad)) { @@ -303,7 +302,7 @@ public: } else if (!node->contains(rad) && node->getParent()) { //if we got here, data does not exist in this node - return ((LLOctreeNode*) node->getParent())->getNodeAt(pos, rad); + return ((oct_node*) node->getParent())->getNodeAt(pos, rad); } return node; @@ -318,7 +317,7 @@ public: OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << LL_ENDL; return false; } - LLOctreeNode* parent = getOctParent(); + oct_node* parent = getOctParent(); //is it here? if (isInside(data->getPositionGroup())) @@ -326,11 +325,8 @@ public: if ((((getElementCount() < gOctreeMaxCapacity || getSize()[0] <= gOctreeMinSize) && contains(data->getBinRadius())) || (data->getBinRadius() > getSize()[0] && parent && parent->getElementCount() >= gOctreeMaxCapacity))) { //it belongs here - mData.push_back(NULL); - mData[mElementCount] = data; - mElementCount++; - mDataEnd = &mData[mElementCount]; - data->setBinIndex(mElementCount-1); + mData.push_back(data); + data->setBinIndex(getElementCount() - 1); BaseType::insert(data); return true; } @@ -354,7 +350,7 @@ public: size.mul(0.5f); //push center in direction of data - LLOctreeNode::pushCenter(center, size, data); + oct_node::pushCenter(center, size, data); // handle case where floating point number gets too small LLVector4a val; @@ -366,11 +362,8 @@ public: if( lt == 0x7 ) { - mData.push_back(NULL); - mData[mElementCount] = data; - mElementCount++; - mDataEnd = &mData[mElementCount]; - data->setBinIndex(mElementCount-1); + mData.push_back(data); + data->setBinIndex(getElementCount() - 1); BaseType::insert(data); return true; } @@ -396,7 +389,7 @@ public: llassert(size[0] >= gOctreeMinSize*0.5f); //make the new kid - child = new LLOctreeNode(center, size, this); + child = new oct_node(center, size, this); addChild(child); child->insert(data); @@ -429,28 +422,25 @@ public: } void _remove(T* data, S32 i) - { //precondition -- mElementCount > 0, idx is in range [0, mElementCount) + { //precondition -- getElementCount() > 0, idx is in range [0, getElementCount()) - mElementCount--; data->setBinIndex(-1); - if (mElementCount > 0) + const U32 new_element_count = getElementCount() - 1; + if (new_element_count > 0) { - if (mElementCount != i) + if (new_element_count != i) { - mData[i] = mData[mElementCount]; //might unref data, do not access data after this point + mData[i] = mData[new_element_count]; //might unref data, do not access data after this point mData[i]->setBinIndex(i); } - mData[mElementCount] = NULL; + mData[new_element_count] = NULL; mData.pop_back(); - mDataEnd = &mData[mElementCount]; } else { mData.clear(); - mData.push_back(NULL); - mDataEnd = &mData[0]; } this->notifyRemoval(data); @@ -463,7 +453,7 @@ public: S32 i = data->getBinIndex(); - if (i >= 0 && i < mElementCount) + if (i >= 0 && i < getElementCount()) { if (mData[i] == data) { //found it @@ -506,7 +496,8 @@ public: void removeByAddress(T* data) { - for (U32 i = 0; i < mElementCount; ++i) + const U32 element_count = getElementCount(); + for (U32 i = 0; i < element_count; ++i) { if (mData[i] == data) { //we have data @@ -518,7 +509,7 @@ public: for (U32 i = 0; i < getChildCount(); i++) { //we don't contain data, so pass this guy down - LLOctreeNode* child = (LLOctreeNode*) getChild(i); + oct_node* child = (oct_node*) getChild(i); child->removeByAddress(data); } } @@ -672,22 +663,20 @@ protected: oct_node* mParent; U8 mOctant; - LLOctreeNode* mChild[8]; + oct_node* mChild[8]; U8 mChildMap[8]; U32 mChildCount; element_list mData; - element_iter mDataEnd; - U32 mElementCount; }; //just like a regular node, except it might expand on insert and compress on balance -template -class LLOctreeRoot : public LLOctreeNode +template +class LLOctreeRoot : public LLOctreeNode { public: - typedef LLOctreeNode BaseType; - typedef LLOctreeNode oct_node; + typedef LLOctreeNode BaseType; + typedef LLOctreeNode oct_node; LLOctreeRoot(const LLVector4a& center, const LLVector4a& size, @@ -768,7 +757,7 @@ public: oct_node* node = this->getNodeAt(data); if (node == this) { - LLOctreeNode::insert(data); + oct_node::insert(data); } else if (node->isInside(data->getPositionGroup())) { @@ -788,13 +777,13 @@ public: LLVector4a center, size; center = this->getCenter(); size = this->getSize(); - LLOctreeNode::pushCenter(center, size, data); + oct_node::pushCenter(center, size, data); this->setCenter(center); size.mul(2.f); this->setSize(size); this->updateMinMax(); } - LLOctreeNode::insert(data); + oct_node::insert(data); } else { @@ -806,7 +795,7 @@ public: //expand this node LLVector4a newcenter(center); - LLOctreeNode::pushCenter(newcenter, size, data); + oct_node::pushCenter(newcenter, size, data); this->setCenter(newcenter); LLVector4a size2 = size; size2.mul(2.f); @@ -816,11 +805,11 @@ public: llassert(size[0] >= gOctreeMinSize); //copy our children to a new branch - LLOctreeNode* newnode = new LLOctreeNode(center, size, this); + oct_node* newnode = new oct_node(center, size, this); for (U32 i = 0; i < this->getChildCount(); i++) { - LLOctreeNode* child = this->getChild(i); + oct_node* child = this->getChild(i); newnode->addChild(child); } @@ -846,8 +835,8 @@ public: //======================== // LLOctreeTraveler //======================== -template -void LLOctreeTraveler::traverse(const LLOctreeNode* node) +template +void LLOctreeTraveler::traverse(const LLOctreeNode* node) { node->accept(this); for (U32 i = 0; i < node->getChildCount(); i++) @@ -856,8 +845,8 @@ void LLOctreeTraveler::traverse(const LLOctreeNode* node) } } -template -void LLOctreeTravelerDepthFirst::traverse(const LLOctreeNode* node) +template +void LLOctreeTravelerDepthFirst::traverse(const LLOctreeNode* node) { for (U32 i = 0; i < node->getChildCount(); i++) { diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 5099920f32..f43d07ce5e 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -49,6 +49,7 @@ #include "llsdserialize.h" #include "llvector4a.h" #include "llmatrix4a.h" +#include "llmeshoptimizer.h" #include "lltimer.h" #define DEBUG_SILHOUETTE_BINORMALS 0 @@ -370,7 +371,7 @@ BOOL LLTriangleRayIntersect(const LLVector3& vert0, const LLVector3& vert1, cons } } -class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst +class LLVolumeOctreeRebound : public LLOctreeTravelerDepthFirst { public: const LLVolumeFace* mFace; @@ -380,7 +381,7 @@ public: mFace = face; } - virtual void visit(const LLOctreeNode* branch) + virtual void visit(const LLOctreeNode* branch) { //this is a depth first traversal, so it's safe to assum all children have complete //bounding data LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME @@ -398,8 +399,7 @@ public: min = *(tri->mV[0]); max = *(tri->mV[0]); - for (LLOctreeNode::const_element_iter iter = - branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) + for (LLOctreeNode::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) { //for each triangle in node //stretch by triangles in node @@ -683,7 +683,7 @@ LLProfile::Face* LLProfile::addHole(const LLProfileParams& params, BOOL flat, F3 Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat); - static LLAlignedArray pt; + static thread_local LLAlignedArray pt; pt.resize(mTotal) ; for (S32 i=mTotalOut;i 0) + { + // Invalid number of triangle indices + LL_WARNS() << "Incomplete triangle discarded from face! Indices count " << num_indices << " was not divisible by 3. face index: " << i << " Total: " << face_count << LL_ENDL; + num_indices -= indices_to_discard; + } face.resizeIndices(num_indices); if (num_indices > 2 && !face.mIndices) @@ -2441,8 +2444,7 @@ bool LLVolume::unpackVolumeFaces(std::istream& is, S32 size) } U16* indices = (U16*) &(idx[0]); - U32 count = idx.size()/2; - for (U32 j = 0; j < count; ++j) + for (U32 j = 0; j < num_indices; ++j) { face.mIndices[j] = indices[j]; } @@ -3834,8 +3836,8 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, #if DEBUG_SILHOUETTE_EDGE_MAP //for each triangle - U32 count = face.mNumIndices; - for (U32 j = 0; j < count/3; j++) { + U32 tri_count = face.mNumIndices / 3; + for (U32 j = 0; j < tri_count; j++) { //get vertices S32 v1 = face.mIndices[j*3+0]; S32 v2 = face.mIndices[j*3+1]; @@ -3853,7 +3855,7 @@ void LLVolume::generateSilhouetteVertices(std::vector &vertices, continue; } - if (nIndex >= (S32) count/3) { + if (nIndex >= (S32)tri_count) { continue; } //get neighbor vertices @@ -4145,13 +4147,13 @@ S32 LLVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& en } else { - if (!face.mOctree) + if (!face.getOctree()) { face.createOctree(); } LLOctreeTriangleRayIntersect intersect(start, dir, &face, &closest_t, intersection, tex_coord, normal, tangent_out); - intersect.traverse(face.mOctree); + intersect.traverse(face.getOctree()); if (intersect.mHitFace) { hit_face = i; @@ -4706,6 +4708,7 @@ LLVolumeFace::LLVolumeFace() : #endif mWeightsScrubbed(FALSE), mOctree(NULL), + mOctreeTriangles(NULL), mOptimized(FALSE) { mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); @@ -4735,8 +4738,9 @@ LLVolumeFace::LLVolumeFace(const LLVolumeFace& src) mJointIndices(NULL), #endif mWeightsScrubbed(FALSE), - mOctree(NULL) -{ + mOctree(NULL), + mOctreeTriangles(NULL) +{ mExtents = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*3); mCenter = mExtents+2; *this = src; @@ -4876,8 +4880,7 @@ void LLVolumeFace::freeData() mJustWeights = NULL; #endif - delete mOctree; - mOctree = NULL; + destroyOctree(); } BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build) @@ -4885,8 +4888,7 @@ BOOL LLVolumeFace::create(LLVolume* volume, BOOL partial_build) LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME //tree for this face is no longer valid - delete mOctree; - mOctree = NULL; + destroyOctree(); LL_CHECK_MEMORY BOOL ret = FALSE ; @@ -4952,6 +4954,50 @@ bool LLVolumeFace::VertexMapData::ComparePosition::operator()(const LLVector3& a return a.mV[2] < b.mV[2]; } +void LLVolumeFace::remap() +{ + // Generate a remap buffer + std::vector remap(mNumVertices); + S32 remap_vertices_count = LLMeshOptimizer::generateRemapMultiU16(&remap[0], + mIndices, + mNumIndices, + mPositions, + mNormals, + mTexCoords, + mNumVertices); + + // Allocate new buffers + S32 size = ((mNumIndices * sizeof(U16)) + 0xF) & ~0xF; + U16* remap_indices = (U16*)ll_aligned_malloc_16(size); + + S32 tc_bytes_size = ((remap_vertices_count * sizeof(LLVector2)) + 0xF) & ~0xF; + LLVector4a* remap_positions = (LLVector4a*)ll_aligned_malloc<64>(sizeof(LLVector4a) * 2 * remap_vertices_count + tc_bytes_size); + LLVector4a* remap_normals = remap_positions + remap_vertices_count; + LLVector2* remap_tex_coords = (LLVector2*)(remap_normals + remap_vertices_count); + + // Fill the buffers + LLMeshOptimizer::remapIndexBufferU16(remap_indices, mIndices, mNumIndices, &remap[0]); + LLMeshOptimizer::remapPositionsBuffer(remap_positions, mPositions, mNumVertices, &remap[0]); + LLMeshOptimizer::remapNormalsBuffer(remap_normals, mNormals, mNumVertices, &remap[0]); + LLMeshOptimizer::remapUVBuffer(remap_tex_coords, mTexCoords, mNumVertices, &remap[0]); + + // Free unused buffers + ll_aligned_free_16(mIndices); + ll_aligned_free<64>(mPositions); + + // Tangets are now invalid + ll_aligned_free_16(mTangents); + mTangents = NULL; + + // Assign new values + mIndices = remap_indices; + mPositions = remap_positions; + mNormals = remap_normals; + mTexCoords = remap_tex_coords; + mNumVertices = remap_vertices_count; + mNumAllocatedVertices = remap_vertices_count; +} + void LLVolumeFace::optimize(F32 angle_cutoff) { LLVolumeFace new_face; @@ -5555,21 +5601,27 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe { LL_PROFILE_ZONE_SCOPED_CATEGORY_VOLUME - if (mOctree) + if (getOctree()) { return; } - mOctree = new LLOctreeRoot(center, size, NULL); - new LLVolumeOctreeListener(mOctree); + llassert(mNumIndices % 3 == 0); - for (U32 i = 0; i < mNumIndices; i+= 3) + mOctree = new LLOctreeRoot(center, size, NULL); + new LLVolumeOctreeListener(mOctree); + const U32 num_triangles = mNumIndices / 3; + // Initialize all the triangles we need + mOctreeTriangles = new LLVolumeTriangle[num_triangles]; + + for (U32 triangle_index = 0; triangle_index < num_triangles; ++triangle_index) { //for each triangle - LLPointer tri = new LLVolumeTriangle(); + const U32 index = triangle_index * 3; + LLVolumeTriangle* tri = &mOctreeTriangles[triangle_index]; - const LLVector4a& v0 = mPositions[mIndices[i]]; - const LLVector4a& v1 = mPositions[mIndices[i+1]]; - const LLVector4a& v2 = mPositions[mIndices[i+2]]; + const LLVector4a& v0 = mPositions[mIndices[index]]; + const LLVector4a& v1 = mPositions[mIndices[index + 1]]; + const LLVector4a& v2 = mPositions[mIndices[index + 2]]; //store pointers to vertex data tri->mV[0] = &v0; @@ -5577,9 +5629,9 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe tri->mV[2] = &v2; //store indices - tri->mIndex[0] = mIndices[i]; - tri->mIndex[1] = mIndices[i+1]; - tri->mIndex[2] = mIndices[i+2]; + tri->mIndex[0] = mIndices[index]; + tri->mIndex[1] = mIndices[index + 1]; + tri->mIndex[2] = mIndices[index + 2]; //get minimum point LLVector4a min = v0; @@ -5622,6 +5674,19 @@ void LLVolumeFace::createOctree(F32 scaler, const LLVector4a& center, const LLVe } } +void LLVolumeFace::destroyOctree() +{ + delete mOctree; + mOctree = NULL; + delete[] mOctreeTriangles; + mOctreeTriangles = NULL; +} + +const LLOctreeNode* LLVolumeFace::getOctree() const +{ + return mOctree; +} + void LLVolumeFace::swapData(LLVolumeFace& rhs) { @@ -6493,6 +6558,7 @@ void LLVolumeFace::allocateJointIndices(S32 num_verts) void LLVolumeFace::resizeIndices(S32 num_indices) { ll_aligned_free_16(mIndices); + llassert(num_indices % 3 == 0); if (num_indices) { @@ -6629,13 +6695,19 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) else { // Get s value for tex-coord. - if (!flat) + S32 index = mBeginS + s; + if (index >= profile.size()) + { + // edge? + ss = flat ? 1.f - begin_stex : 1.f; + } + else if (!flat) { - ss = profile[mBeginS + s][2]; + ss = profile[index][2]; } else { - ss = profile[mBeginS + s][2] - begin_stex; + ss = profile[index][2] - begin_stex; } } @@ -6821,7 +6893,7 @@ BOOL LLVolumeFace::createSide(LLVolume* volume, BOOL partial_build) LLVector4a* norm = mNormals; - static LLAlignedArray triangle_normals; + static thread_local LLAlignedArray triangle_normals; try { triangle_normals.resize(count); diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index c0b224b1ff..3ccaed47f1 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -35,7 +35,8 @@ class LLVolumeParams; class LLProfile; class LLPath; -template class LLOctreeNode; +template class LLPointer; +template class LLOctreeNode; class LLVolumeFace; class LLVolume; @@ -902,10 +903,17 @@ public: typedef std::map, VertexMapData::ComparePosition > PointMap; }; + // Eliminates non unique triangles, takes positions, + // normals and texture coordinates into account. + void remap(); + void optimize(F32 angle_cutoff = 2.f); bool cacheOptimize(); void createOctree(F32 scaler = 0.25f, const LLVector4a& center = LLVector4a(0,0,0), const LLVector4a& size = LLVector4a(0.5f,0.5f,0.5f)); + void destroyOctree(); + // Get a reference to the octree, which may be null + const LLOctreeNode* getOctree() const; enum { @@ -973,13 +981,14 @@ public: // Which joints are rigged to, and the bounding box of any rigged // vertices per joint. LLJointRiggingInfoTab mJointRiggingInfoTab; - - LLOctreeNode* mOctree; //whether or not face has been cache optimized BOOL mOptimized; private: + LLOctreeNode* mOctree; + LLVolumeTriangle* mOctreeTriangles; + BOOL createUnCutCubeCap(LLVolume* volume, BOOL partial_build = FALSE); BOOL createCap(LLVolume* volume, BOOL partial_build = FALSE); BOOL createSide(LLVolume* volume, BOOL partial_build = FALSE); diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp index fb232d5f6c..6894d04d3c 100644 --- a/indra/llmath/llvolumeoctree.cpp +++ b/indra/llmath/llvolumeoctree.cpp @@ -75,7 +75,7 @@ BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, c } -LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode* node) +LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode* node) { node->addListener(this); } @@ -85,13 +85,12 @@ LLVolumeOctreeListener::~LLVolumeOctreeListener() } -void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode* parent, - LLOctreeNode* child) +void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode* parent, + LLOctreeNode* child) { new LLVolumeOctreeListener(child); } - LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, const LLVolumeFace* face, F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) @@ -108,7 +107,7 @@ LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& sta mEnd.setAdd(mStart, mDir); } -void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode* node) +void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode* node) { LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0); @@ -122,9 +121,9 @@ void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode } } -void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode* node) +void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode* node) { - for (LLOctreeNode::const_element_iter iter = + for (typename LLOctreeNode::const_element_iter iter = node->getDataBegin(); iter != node->getDataEnd(); ++iter) { const LLVolumeTriangle* tri = *iter; @@ -219,7 +218,7 @@ const F32& LLVolumeTriangle::getBinRadius() const //TEST CODE -void LLVolumeOctreeValidate::visit(const LLOctreeNode* branch) +void LLVolumeOctreeValidate::visit(const LLOctreeNode* branch) { LLVolumeOctreeListener* node = (LLVolumeOctreeListener*) branch->getListener(0); @@ -256,7 +255,7 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode* branch) } //children fit, check data - for (LLOctreeNode::const_element_iter iter = branch->getDataBegin(); + for (typename LLOctreeNode::const_element_iter iter = branch->getDataBegin(); iter != branch->getDataEnd(); ++iter) { const LLVolumeTriangle* tri = *iter; @@ -273,4 +272,3 @@ void LLVolumeOctreeValidate::visit(const LLOctreeNode* branch) } } - diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h index b2bc440368..d65bca5e52 100644 --- a/indra/llmath/llvolumeoctree.h +++ b/indra/llmath/llvolumeoctree.h @@ -77,11 +77,11 @@ public: }; -class alignas(16) LLVolumeOctreeListener : public LLOctreeListener +class alignas(16) LLVolumeOctreeListener : public LLOctreeListener { LL_ALIGN_NEW public: - LLVolumeOctreeListener(LLOctreeNode* node); + LLVolumeOctreeListener(LLOctreeNode* node); ~LLVolumeOctreeListener(); LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs) @@ -96,11 +96,9 @@ public: } //LISTENER FUNCTIONS - virtual void handleChildAddition(const LLOctreeNode* parent, - LLOctreeNode* child); + virtual void handleChildAddition(const LLOctreeNode* parent, LLOctreeNode* child); virtual void handleStateChange(const LLTreeNode* node) { } - virtual void handleChildRemoval(const LLOctreeNode* parent, - const LLOctreeNode* child) { } + virtual void handleChildRemoval(const LLOctreeNode* parent, const LLOctreeNode* child) { } virtual void handleInsertion(const LLTreeNode* node, LLVolumeTriangle* tri) { } virtual void handleRemoval(const LLTreeNode* node, LLVolumeTriangle* tri) { } virtual void handleDestruction(const LLTreeNode* node) { } @@ -111,7 +109,7 @@ public: LL_ALIGN_16(LLVector4a mExtents[2]); // extents (min, max) of this node and all its children }; -class LLOctreeTriangleRayIntersect : public LLOctreeTraveler +class LLOctreeTriangleRayIntersect : public LLOctreeTraveler { public: const LLVolumeFace* mFace; @@ -129,14 +127,14 @@ public: const LLVolumeFace* face, F32* closest_t, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent); - void traverse(const LLOctreeNode* node); + void traverse(const LLOctreeNode* node); - virtual void visit(const LLOctreeNode* node); + virtual void visit(const LLOctreeNode* node); }; -class LLVolumeOctreeValidate : public LLOctreeTraveler +class LLVolumeOctreeValidate : public LLOctreeTraveler { - virtual void visit(const LLOctreeNode* branch); + virtual void visit(const LLOctreeNode* branch); }; #endif diff --git a/indra/llmeshoptimizer/llmeshoptimizer.cpp b/indra/llmeshoptimizer/llmeshoptimizer.cpp index a879389c5a..c178348968 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.cpp +++ b/indra/llmeshoptimizer/llmeshoptimizer.cpp @@ -28,6 +28,9 @@ #include "meshoptimizer.h" +#include "llmath.h" +#include "v2math.h" + LLMeshOptimizer::LLMeshOptimizer() { // Todo: Looks like for memory management, we can add allocator and deallocator callbacks @@ -40,24 +43,218 @@ LLMeshOptimizer::~LLMeshOptimizer() } //static -void LLMeshOptimizer::generateShadowIndexBuffer(U16 *destination, - const U16 *indices, +void LLMeshOptimizer::generateShadowIndexBufferU32(U32 *destination, + const U32 *indices, U64 index_count, - const LLVector4a *vertex_positions, - U64 vertex_count, - U64 vertex_positions_stride + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count ) { - meshopt_generateShadowIndexBuffer(destination, + meshopt_Stream streams[3]; + + S32 index = 0; + if (vertex_positions) + { + streams[index].data = (const float*)vertex_positions; + // Despite being LLVector4a, only x, y and z are in use + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (normals) + { + streams[index].data = (const float*)normals; + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (text_coords) + { + streams[index].data = (const float*)text_coords; + streams[index].size = sizeof(F32) * 2; + streams[index].stride = sizeof(F32) * 2; + index++; + } + + if (index == 0) + { + // invalid + return; + } + + meshopt_generateShadowIndexBufferMulti(destination, indices, index_count, - (const float*)vertex_positions, // verify that it is correct to convert to float vertex_count, - sizeof(LLVector4a), - vertex_positions_stride + streams, + index ); } +//static +void LLMeshOptimizer::generateShadowIndexBufferU16(U16 *destination, + const U16 *indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count +) +{ + meshopt_Stream streams[3]; + + S32 index = 0; + if (vertex_positions) + { + streams[index].data = (const float*)vertex_positions; + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (normals) + { + streams[index].data = (const float*)normals; + streams[index].size = sizeof(F32) * 3; + streams[index].stride = sizeof(F32) * 4; + index++; + } + if (text_coords) + { + streams[index].data = (const float*)text_coords; + streams[index].size = sizeof(F32) * 2; + streams[index].stride = sizeof(F32) * 2; + index++; + } + + if (index == 0) + { + // invalid + return; + } + + meshopt_generateShadowIndexBufferMulti(destination, + indices, + index_count, + vertex_count, + streams, + index); +} + +void LLMeshOptimizer::optimizeVertexCacheU32(U32 * destination, const U32 * indices, U64 index_count, U64 vertex_count) +{ + meshopt_optimizeVertexCache(destination, indices, index_count, vertex_count); +} + +void LLMeshOptimizer::optimizeVertexCacheU16(U16 * destination, const U16 * indices, U64 index_count, U64 vertex_count) +{ + meshopt_optimizeVertexCache(destination, indices, index_count, vertex_count); +} + +size_t LLMeshOptimizer::generateRemapMultiU32( + unsigned int* remap, + const U32 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count) +{ + meshopt_Stream streams[] = { + {(const float*)vertex_positions, sizeof(F32) * 3, sizeof(F32) * 4}, + {(const float*)normals, sizeof(F32) * 3, sizeof(F32) * 4}, + {(const float*)text_coords, sizeof(F32) * 2, sizeof(F32) * 2}, + }; + + // Remap can function without indices, + // but providing indices helps with removing unused vertices + U64 indeces_cmp = indices ? index_count : vertex_count; + + // meshopt_generateVertexRemapMulti will throw an assert if (indices[i] >= vertex_count) + return meshopt_generateVertexRemapMulti(&remap[0], indices, indeces_cmp, vertex_count, streams, sizeof(streams) / sizeof(streams[0])); +} + +size_t LLMeshOptimizer::generateRemapMultiU16( + unsigned int* remap, + const U16 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count) +{ + S32 out_of_range_count = 0; + U32* indices_u32 = NULL; + if (indices) + { + indices_u32 = (U32*)ll_aligned_malloc_32(index_count * sizeof(U32)); + for (U64 i = 0; i < index_count; i++) + { + if (indices[i] < vertex_count) + { + indices_u32[i] = (U32)indices[i]; + } + else + { + out_of_range_count++; + indices_u32[i] = 0; + } + } + } + + if (out_of_range_count) + { + LL_WARNS() << out_of_range_count << " indices are out of range." << LL_ENDL; + } + + size_t unique = generateRemapMultiU32(remap, indices_u32, index_count, vertex_positions, normals, text_coords, vertex_count); + + ll_aligned_free_32(indices_u32); + + return unique; +} + +void LLMeshOptimizer::remapIndexBufferU32(U32 * destination_indices, + const U32 * indices, + U64 index_count, + const unsigned int* remap) +{ + meshopt_remapIndexBuffer(destination_indices, indices, index_count, remap); +} + +void LLMeshOptimizer::remapIndexBufferU16(U16 * destination_indices, + const U16 * indices, + U64 index_count, + const unsigned int* remap) +{ + meshopt_remapIndexBuffer(destination_indices, indices, index_count, remap); +} + +void LLMeshOptimizer::remapPositionsBuffer(LLVector4a * destination_vertices, + const LLVector4a * vertex_positions, + U64 vertex_count, + const unsigned int* remap) +{ + meshopt_remapVertexBuffer((float*)destination_vertices, (const float*)vertex_positions, vertex_count, sizeof(LLVector4a), remap); +} + +void LLMeshOptimizer::remapNormalsBuffer(LLVector4a * destination_normalss, + const LLVector4a * normals, + U64 mormals_count, + const unsigned int* remap) +{ + meshopt_remapVertexBuffer((float*)destination_normalss, (const float*)normals, mormals_count, sizeof(LLVector4a), remap); +} + +void LLMeshOptimizer::remapUVBuffer(LLVector2 * destination_uvs, + const LLVector2 * uv_positions, + U64 uv_count, + const unsigned int* remap) +{ + meshopt_remapVertexBuffer((float*)destination_uvs, (const float*)uv_positions, uv_count, sizeof(LLVector2), remap); +} + //static U64 LLMeshOptimizer::simplifyU32(U32 *destination, const U32 *indices, diff --git a/indra/llmeshoptimizer/llmeshoptimizer.h b/indra/llmeshoptimizer/llmeshoptimizer.h index e8dd16dae9..ea965d6b47 100644 --- a/indra/llmeshoptimizer/llmeshoptimizer.h +++ b/indra/llmeshoptimizer/llmeshoptimizer.h @@ -28,7 +28,8 @@ #include "linden_common.h" -#include "llmath.h" +class LLVector4a; +class LLVector2; class LLMeshOptimizer { @@ -36,13 +37,85 @@ public: LLMeshOptimizer(); ~LLMeshOptimizer(); - static void generateShadowIndexBuffer( + static void generateShadowIndexBufferU32( + U32 *destination, + const U32 *indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static void generateShadowIndexBufferU16( U16 *destination, const U16 *indices, U64 index_count, - const LLVector4a *vertex_positions, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static void optimizeVertexCacheU32( + U32 *destination, + const U32 *indices, + U64 index_count, + U64 vertex_count); + + static void optimizeVertexCacheU16( + U16 *destination, + const U16 *indices, + U64 index_count, + U64 vertex_count); + + // Remap functions + // Welds indentical vertexes together. + // Removes unused vertices if indices were provided. + + static size_t generateRemapMultiU32( + unsigned int* remap, + const U32 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static size_t generateRemapMultiU16( + unsigned int* remap, + const U16 * indices, + U64 index_count, + const LLVector4a * vertex_positions, + const LLVector4a * normals, + const LLVector2 * text_coords, + U64 vertex_count); + + static void remapIndexBufferU32(U32 * destination_indices, + const U32 * indices, + U64 index_count, + const unsigned int* remap); + + static void remapIndexBufferU16(U16 * destination_indices, + const U16 * indices, + U64 index_count, + const unsigned int* remap); + + + static void remapPositionsBuffer(LLVector4a * destination_vertices, + const LLVector4a * vertex_positions, U64 vertex_count, - U64 vertex_positions_stride); + const unsigned int* remap); + + static void remapNormalsBuffer(LLVector4a * destination_normalss, + const LLVector4a * normals, + U64 mormals_count, + const unsigned int* remap); + + static void remapUVBuffer(LLVector2 * destination_uvs, + const LLVector2 * uv_positions, + U64 uv_count, + const unsigned int* remap); + + // Simplification // returns amount of indices in destiantion // sloppy engages a variant of a mechanizm that does not respect topology as much diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp index c67f59bc0c..846549b368 100644 --- a/indra/llmessage/llavatarnamecache.cpp +++ b/indra/llmessage/llavatarnamecache.cpp @@ -196,6 +196,10 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vectorhandleAvNameCacheSuccess(results, httpResults); } } + catch (const LLCoros::Stop&) + { + LL_DEBUGS("AvNameCache") << "Received a shutdown exception" << LL_ENDL; + } catch (...) { LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::getName() diff --git a/indra/llmessage/llcoproceduremanager.h b/indra/llmessage/llcoproceduremanager.h index 2d460826ff..c5bc37dd0e 100644 --- a/indra/llmessage/llcoproceduremanager.h +++ b/indra/llmessage/llcoproceduremanager.h @@ -32,7 +32,6 @@ #include "llcoros.h" #include "llcorehttputil.h" #include "lluuid.h" -#include class LLCoprocedurePool; @@ -84,7 +83,7 @@ public: private: - typedef boost::shared_ptr poolPtr_t; + typedef std::shared_ptr poolPtr_t; typedef std::map poolMap_t; poolMap_t mPoolMap; diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp index 96c1297e0d..b6adc102d2 100644 --- a/indra/llmessage/lldatapacker.cpp +++ b/indra/llmessage/lldatapacker.cpp @@ -127,13 +127,7 @@ BOOL LLDataPacker::unpackFixed(F32 &value, const char *name, total_bits++; } - S32 min_val; U32 max_val; - if (is_signed) - { - min_val = 1 << int_bits; - min_val *= -1; - } max_val = 1 << int_bits; F32 fixed_val; diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp index 53aa35c0f9..6664eb02dc 100644 --- a/indra/llmessage/llpartdata.cpp +++ b/indra/llmessage/llpartdata.cpp @@ -311,8 +311,9 @@ BOOL LLPartSysData::unpack(LLDataPacker &dp) std::ostream& operator<<(std::ostream& s, const LLPartSysData &data) { s << "Flags: " << std::hex << data.mFlags; - s << " Pattern: " << std::hex << (U32) data.mPattern << "\n"; - s << "Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n"; + s << "Pattern: " << std::hex << (U32) data.mPattern << "\n"; + s << "Source Age: [" << data.mStartAge << ", " << data.mMaxAge << "]\n"; + s << "Particle Age: " << data.mPartData.mMaxAge << "\n"; s << "Angle: [" << data.mInnerAngle << ", " << data.mOuterAngle << "]\n"; s << "Burst Rate: " << data.mBurstRate << "\n"; s << "Burst Radius: " << data.mBurstRadius << "\n"; diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp index 7605da4d3f..935af2aa5a 100644 --- a/indra/llmessage/llthrottle.cpp +++ b/indra/llmessage/llthrottle.cpp @@ -374,7 +374,6 @@ BOOL LLThrottleGroup::dynamicAdjust() } mDynamicAdjustTime = mt_sec; - S32 total = 0; // Update historical information for (i = 0; i < TC_EOF; i++) { @@ -391,7 +390,6 @@ BOOL LLThrottleGroup::dynamicAdjust() } mBitsSentThisPeriod[i] = 0; - total += ll_round(mBitsSentHistory[i]); } // Look for busy channels diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 219b1855d2..35dcbe3836 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1386,6 +1386,7 @@ char const* const _PREHASH_RegionAllowAccessBlock = LLMessageStringTable::getIns char const* const _PREHASH_RegionAllowAccessOverride = LLMessageStringTable::getInstance()->getString("RegionAllowAccessOverride"); char const* const _PREHASH_ParcelEnvironmentBlock = LLMessageStringTable::getInstance()->getString("ParcelEnvironmentBlock"); char const* const _PREHASH_ParcelEnvironmentVersion = LLMessageStringTable::getInstance()->getString("ParcelEnvironmentVersion"); +char const* const _PREHASH_ParcelExtendedFlags = LLMessageStringTable::getInstance()->getString("ParcelExtendedFlags"); char const* const _PREHASH_RegionAllowEnvironmentOverride = LLMessageStringTable::getInstance()->getString("RegionAllowEnvironmentOverride"); char const* const _PREHASH_UCoord = LLMessageStringTable::getInstance()->getString("UCoord"); char const* const _PREHASH_VCoord = LLMessageStringTable::getInstance()->getString("VCoord"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 8f6ee5a327..3015f438b5 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1386,6 +1386,7 @@ extern char const* const _PREHASH_RegionAllowAccessBlock; extern char const* const _PREHASH_RegionAllowAccessOverride; extern char const* const _PREHASH_ParcelEnvironmentBlock; extern char const* const _PREHASH_ParcelEnvironmentVersion; +extern char const* const _PREHASH_ParcelExtendedFlags; extern char const* const _PREHASH_RegionAllowEnvironmentOverride; extern char const* const _PREHASH_UCoord; extern char const* const _PREHASH_VCoord; diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index 6f88232c1d..3e72710366 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -1549,6 +1549,7 @@ void LLPluginClassMedia::seek(float time) LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "seek"); message.setValueReal("time", time); + mCurrentTime = time; // assume that it worked and we will receive an update later sendMessage(message); } diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp index eef22156bc..1fbbad06d4 100644 --- a/indra/llplugin/llpluginprocessparent.cpp +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -999,7 +999,7 @@ void LLPluginProcessParent::poll(F64 timeout) while (itClean != sInstances.end()) { if ((*itClean).second->isDone()) - sInstances.erase(itClean++); + itClean = sInstances.erase(itClean); else ++itClean; } diff --git a/indra/llprimitive/lldaeloader.cpp b/indra/llprimitive/lldaeloader.cpp index e89690438e..68654486a4 100644 --- a/indra/llprimitive/lldaeloader.cpp +++ b/indra/llprimitive/lldaeloader.cpp @@ -2577,7 +2577,7 @@ bool LLDAELoader::loadModelsFromDomMesh(domMesh* mesh, std::vector& mo if (!mNoOptimize) { - ret->optimizeVolumeFaces(); + ret->remapVolumeFaces(); } volume_faces = remainder.size(); diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp index b1d371a399..805af55299 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -107,6 +107,14 @@ void LLModel::offsetMesh( const LLVector3& pivotPoint ) } } +void LLModel::remapVolumeFaces() +{ + for (U32 i = 0; i < getNumVolumeFaces(); ++i) + { + mVolumeFaces[i].remap(); + } +} + void LLModel::optimizeVolumeFaces() { for (U32 i = 0; i < getNumVolumeFaces(); ++i) @@ -371,6 +379,8 @@ void LLModel::setVolumeFaceData( U32 num_verts, U32 num_indices) { + llassert(num_indices % 3 == 0); + LLVolumeFace& face = mVolumeFaces[f]; face.resizeVertices(num_verts); @@ -835,7 +845,7 @@ LLSD LLModel::writeModel( { LLVector3 pos(face.mPositions[j].getF32ptr()); - weight_list& weights = high->getJointInfluences(pos); + weight_list& weights = model[idx]->getJointInfluences(pos); S32 count = 0; for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter) @@ -881,8 +891,6 @@ LLSD LLModel::writeModel( LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BOOL as_slm) { - U32 bytes = 0; - std::string::size_type cur_offset = 0; LLSD header; @@ -904,7 +912,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO header["skin"]["offset"] = (LLSD::Integer) cur_offset; header["skin"]["size"] = (LLSD::Integer) size; cur_offset += size; - bytes += size; } } @@ -920,7 +927,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO header["physics_convex"]["offset"] = (LLSD::Integer) cur_offset; header["physics_convex"]["size"] = (LLSD::Integer) size; cur_offset += size; - bytes += size; } } @@ -942,7 +948,6 @@ LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite, BO header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset; header[model_names[i]]["size"] = (LLSD::Integer) size; cur_offset += size; - bytes += size; } } @@ -1545,6 +1550,25 @@ void LLMeshSkinInfo::updateHash() mHash = digest[0]; } +U32 LLMeshSkinInfo::sizeBytes() const +{ + U32 res = sizeof(LLUUID); // mMeshID + + res += sizeof(std::vector) + sizeof(std::string) * mJointNames.size(); + for (U32 i = 0; i < mJointNames.size(); ++i) + { + res += mJointNames[i].size(); // actual size, not capacity + } + + res += sizeof(std::vector) + sizeof(S32) * mJointNums.size(); + res += sizeof(std::vector) + 16 * sizeof(float) * mInvBindMatrix.size(); + res += sizeof(std::vector) + 16 * sizeof(float) * mAlternateBindMatrix.size(); + res += 16 * sizeof(float); //mBindShapeMatrix + res += sizeof(float) + 3 * sizeof(bool); + + return res; +} + LLModel::Decomposition::Decomposition(LLSD& data) { fromLLSD(data); @@ -1651,6 +1675,30 @@ void LLModel::Decomposition::fromLLSD(LLSD& decomp) } } +U32 LLModel::Decomposition::sizeBytes() const +{ + U32 res = sizeof(LLUUID); // mMeshID + + res += sizeof(LLModel::convex_hull_decomposition) + sizeof(std::vector) * mHull.size(); + for (U32 i = 0; i < mHull.size(); ++i) + { + res += mHull[i].size() * sizeof(LLVector3); + } + + res += sizeof(LLModel::hull) + sizeof(LLVector3) * mBaseHull.size(); + + res += sizeof(std::vector) + sizeof(std::vector) * mMesh.size(); + for (U32 i = 0; i < mMesh.size(); ++i) + { + res += mMesh[i].sizeBytes(); + } + + res += sizeof(std::vector) * 2; + res += mBaseHullMesh.sizeBytes() + mPhysicsShapeMesh.sizeBytes(); + + return res; +} + bool LLModel::Decomposition::hasHullList() const { return !mHull.empty() ; diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h index 3881b1338c..a6ab96ab18 100644 --- a/indra/llprimitive/llmodel.h +++ b/indra/llprimitive/llmodel.h @@ -50,6 +50,7 @@ public: void fromLLSD(LLSD& data); LLSD asLLSD(bool include_joints, bool lock_scale_if_joint_position) const; void updateHash(); + U32 sizeBytes() const; LLUUID mMeshID; std::vector mJointNames; @@ -112,6 +113,14 @@ public: { return mPositions.empty(); } + + U32 sizeBytes() const + { + U32 res = sizeof(std::vector) * 2; + res += sizeof(LLVector3) * mPositions.size(); + res += sizeof(LLVector3) * mNormals.size(); + return res; + } }; class Decomposition @@ -122,6 +131,7 @@ public: void fromLLSD(LLSD& data); LLSD asLLSD() const; bool hasHullList() const; + U32 sizeBytes() const; void merge(const Decomposition* rhs); @@ -184,6 +194,7 @@ public: void sortVolumeFacesByMaterialName(); void normalizeVolumeFaces(); void trimVolumeFacesToSize(U32 new_count = LL_SCULPT_MESH_MAX_FACES, LLVolume::face_list_t* remainder = NULL); + void remapVolumeFaces(); void optimizeVolumeFaces(); void offsetMesh( const LLVector3& pivotPoint ); void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index 2b7ce155f6..193cfa64b8 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -743,6 +743,14 @@ bool LLGLManager::initGL() LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_multitexture" << LL_ENDL; return false; } + + if (!mHasFramebufferObject) + { + mHasRequirements = FALSE; + + LL_WARNS("RenderInit") << "GL Drivers do not support GL_ARB_framebuffer_object" << LL_ENDL; + return false; + } stop_glerror(); @@ -758,12 +766,6 @@ bool LLGLManager::initGL() //HACK always disable texture multisample, use FXAA instead mHasTextureMultisample = FALSE; -#if LL_WINDOWS - if (mIsIntel && mGLVersion <= 3.f) - { //never try to use framebuffer objects on older intel drivers (crashy) - mHasFramebufferObject = FALSE; - } -#endif if (mHasFramebufferObject) { diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 9bd3a0a6b0..9dc140b5b9 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -1248,7 +1248,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt if (pixels != nullptr) { use_scratch = true; - scratch = new U32[width * height]; + scratch = new(std::nothrow) U32[width * height]; + if (!scratch) + { + LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) + << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + } U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) @@ -1268,7 +1273,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt if (pixels != nullptr) { use_scratch = true; - scratch = new U32[width * height]; + scratch = new(std::nothrow) U32[width * height]; + if (!scratch) + { + LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) + << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + } U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) @@ -1291,7 +1301,12 @@ void LLImageGL::setManualImage(U32 target, S32 miplevel, S32 intformat, S32 widt if (pixels != nullptr) { use_scratch = true; - scratch = new U32[width * height]; + scratch = new(std::nothrow) U32[width * height]; + if (!scratch) + { + LL_ERRS() << "Failed to allocate " << (U32)(width * height * sizeof(U32)) + << " bytes for a manual image W" << width << " H" << height << LL_ENDL; + } U32 pixel_count = (U32)(width * height); for (U32 i = 0; i < pixel_count; i++) @@ -2432,7 +2447,7 @@ void LLImageGLThread::run() // We must perform setup on this thread before actually servicing our // WorkQueue, likewise cleanup afterwards. mWindow->makeContextCurrent(mContext); - gGL.init(); + gGL.init(false); ThreadPool::run(); gGL.shutdown(); mWindow->destroySharedContext(mContext); diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index a03a27cf94..72cca1f2a2 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -869,7 +869,7 @@ LLRender::~LLRender() shutdown(); } -void LLRender::init() +void LLRender::init(bool needs_vertex_buffer) { #if LL_WINDOWS if (gGLManager.mHasDebugOutput && gDebugGL) @@ -897,15 +897,27 @@ void LLRender::init() #endif } + if (needs_vertex_buffer) + { + initVertexBuffer(); + } +} - llassert_always(mBuffer.isNull()) ; - stop_glerror(); - mBuffer = new LLVertexBuffer(immediate_mask, 0); - mBuffer->allocateBuffer(4096, 0, TRUE); - mBuffer->getVertexStrider(mVerticesp); - mBuffer->getTexCoord0Strider(mTexcoordsp); - mBuffer->getColorStrider(mColorsp); - stop_glerror(); +void LLRender::initVertexBuffer() +{ + llassert_always(mBuffer.isNull()); + stop_glerror(); + mBuffer = new LLVertexBuffer(immediate_mask, 0); + mBuffer->allocateBuffer(4096, 0, TRUE); + mBuffer->getVertexStrider(mVerticesp); + mBuffer->getTexCoord0Strider(mTexcoordsp); + mBuffer->getColorStrider(mColorsp); + stop_glerror(); +} + +void LLRender::resetVertexBuffer() +{ + mBuffer = NULL; } void LLRender::shutdown() @@ -923,7 +935,7 @@ void LLRender::shutdown() delete mLightState[i]; } mLightState.clear(); - mBuffer = NULL ; + resetVertexBuffer(); } void LLRender::refreshState(void) @@ -1625,25 +1637,34 @@ void LLRender::flush() mCount = 0; - if (mBuffer->useVBOs() && !mBuffer->isLocked()) - { //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata) - mBuffer->getVertexStrider(mVerticesp, 0, count); - mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count); - mBuffer->getColorStrider(mColorsp, 0, count); - } - - mBuffer->flush(); - mBuffer->setBuffer(immediate_mask); + if (mBuffer) + { + if (mBuffer->useVBOs() && !mBuffer->isLocked()) + { //hack to only flush the part of the buffer that was updated (relies on stream draw using buffersubdata) + mBuffer->getVertexStrider(mVerticesp, 0, count); + mBuffer->getTexCoord0Strider(mTexcoordsp, 0, count); + mBuffer->getColorStrider(mColorsp, 0, count); + } + + mBuffer->flush(); + mBuffer->setBuffer(immediate_mask); + + if (mMode == LLRender::QUADS && sGLCoreProfile) + { + mBuffer->drawArrays(LLRender::TRIANGLES, 0, count); + mQuadCycle = 1; + } + else + { + mBuffer->drawArrays(mMode, 0, count); + } + } + else + { + // mBuffer is present in main thread and not present in an image thread + LL_ERRS() << "A flush call from outside main rendering thread" << LL_ENDL; + } - if (mMode == LLRender::QUADS && sGLCoreProfile) - { - mBuffer->drawArrays(LLRender::TRIANGLES, 0, count); - mQuadCycle = 1; - } - else - { - mBuffer->drawArrays(mMode, 0, count); - } mVerticesp[0] = mVerticesp[count]; mTexcoordsp[0] = mTexcoordsp[count]; diff --git a/indra/llrender/llrender.h b/indra/llrender/llrender.h index e2489876e4..9c36c230fb 100644 --- a/indra/llrender/llrender.h +++ b/indra/llrender/llrender.h @@ -354,7 +354,9 @@ public: LLRender(); ~LLRender(); - void init() ; + void init(bool needs_vertex_buffer); + void initVertexBuffer(); + void resetVertexBuffer(); void shutdown(); // Refreshes renderer state to the cached values diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index 0408010513..fffc15efc3 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -133,7 +133,7 @@ bool LLRenderTarget::allocate(U32 resx, U32 resy, U32 color_fmt, bool depth, boo mUsage = usage; mUseDepth = depth; - if ((sUseFBO || use_fbo) && gGLManager.mHasFramebufferObject) + if (sUseFBO || use_fbo) { if (depth) { diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index c100c182dd..c64f46f38a 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -665,7 +665,7 @@ GLhandleARB LLShaderMgr::loadShaderFile(const std::string& filename, S32 & shade if (file == NULL) { - LL_SHADER_LOADING_WARNS() << "GLSL Shader file not found: " << open_file_name << LL_ENDL; + LL_WARNS("ShaderLoading") << "GLSL Shader file not found: " << open_file_name << LL_ENDL; return 0; } diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index 954b3d5fa8..16e4b7e5f6 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -1061,7 +1061,7 @@ void LLVertexBuffer::releaseIndices() bool LLVertexBuffer::createGLBuffer(U32 size) { - if (mGLBuffer) + if (mGLBuffer || mMappedData) { destroyGLBuffer(); } diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 3c52b30570..3833f7c0e6 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -266,7 +266,6 @@ public: bool getTangentStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getTangentStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getColorStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); - bool getTextureIndexStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getEmissiveStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getWeightStrider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); bool getWeight4Strider(LLStrider& strider, S32 index=0, S32 count = -1, bool map_range = false); diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 0e59fdf519..8028f397f3 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -102,6 +102,7 @@ LLButton::Params::Params() scale_image("scale_image", true), hover_glow_amount("hover_glow_amount"), commit_on_return("commit_on_return", true), + commit_on_capture_lost("commit_on_capture_lost", false), display_pressed_state("display_pressed_state", true), use_draw_context_alpha("use_draw_context_alpha", true), badge("badge"), @@ -165,6 +166,7 @@ LLButton::LLButton(const LLButton::Params& p) mBottomVPad(p.pad_bottom), mHoverGlowStrength(p.hover_glow_amount), mCommitOnReturn(p.commit_on_return), + mCommitOnCaptureLost(p.commit_on_capture_lost), mFadeWhenDisabled(FALSE), mForcePressedState(false), mDisplayPressedState(p.display_pressed_state), @@ -475,6 +477,10 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) { + // reset timers before focus change, to not cause + // additional commits if mCommitOnCaptureLost. + resetMouseDownTimer(); + // Always release the mouse gFocusMgr.setMouseCapture( NULL ); @@ -489,8 +495,6 @@ BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask) // Regardless of where mouseup occurs, handle callback if(mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); - resetMouseDownTimer(); - // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked. // If mouseup in the widget, it's been clicked if (pointInView(x, y)) @@ -1195,6 +1199,18 @@ void LLButton::setImageOverlay(const LLUUID& image_id, LLFontGL::HAlign alignmen void LLButton::onMouseCaptureLost() { + if (mCommitOnCaptureLost + && mMouseDownTimer.getStarted()) + { + if (mMouseUpSignal) (*mMouseUpSignal)(this, LLSD()); + + if (mIsToggle) + { + toggleState(); + } + + LLUICtrl::onCommit(); + } resetMouseDownTimer(); } diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 572d36996c..ccd31e90c0 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -124,6 +124,7 @@ public: Optional is_toggle, scale_image, commit_on_return, + commit_on_capture_lost, display_pressed_state; Optional hover_glow_amount; @@ -374,6 +375,7 @@ protected: F32 mCurGlowStrength; bool mCommitOnReturn; + bool mCommitOnCaptureLost; bool mFadeWhenDisabled; bool mForcePressedState; bool mDisplayPressedState; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 03efd09689..d413fab270 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -259,6 +259,7 @@ LLFloater::LLFloater(const LLSD& key, const LLFloater::Params& p) mMinHeight(p.min_height), mHeaderHeight(p.header_height), mLegacyHeaderHeight(p.legacy_header_height), + mDefaultRectForGroup(true), mMinimized(FALSE), mForeground(FALSE), mFirstLook(TRUE), @@ -761,17 +762,13 @@ void LLFloater::closeFloater(bool app_quitting) for(handle_set_iter_t dependent_it = mDependents.begin(); dependent_it != mDependents.end(); ) { - LLFloater* floaterp = dependent_it->get(); - if (floaterp) - { - ++dependent_it; - floaterp->closeFloater(app_quitting); - } - else - { - mDependents.erase(dependent_it++); - } + dependent_it = mDependents.erase(dependent_it); + if (floaterp) + { + floaterp->mDependeeHandle = LLHandle(); + floaterp->closeFloater(app_quitting); + } } cleanupHandles(); @@ -906,7 +903,10 @@ bool LLFloater::applyRectControl() if (last_in_group && last_in_group != this) { // other floaters in our group, position ourselves relative to them and don't save the rect - mRectControl.clear(); + if (mDefaultRectForGroup) + { + mRectControl.clear(); + } mPositioning = LLFloaterEnums::POSITIONING_CASCADE_GROUP; } else @@ -1439,7 +1439,7 @@ void LLFloater::cleanupHandles() LLFloater* floaterp = dependent_it->get(); if (!floaterp) { - mDependents.erase(dependent_it++); + dependent_it = mDependents.erase(dependent_it); } else { @@ -3481,8 +3481,15 @@ void LLFloater::stackWith(LLFloater& other) } next_rect.translate(floater_offset, -floater_offset); - next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight()); - + const LLRect& rect = getControlGroup()->getRect(mRectControl); + if (rect.notEmpty() && !mDefaultRectForGroup && mResizable) + { + next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight())); + } + else + { + next_rect.setLeftTopAndSize(next_rect.mLeft, next_rect.mTop, getRect().getWidth(), getRect().getHeight()); + } setShape(next_rect); if (!other.getHost()) diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 306760b7fb..668cd208a9 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -454,6 +454,7 @@ public: protected: bool mSaveRect; + bool mDefaultRectForGroup; std::string mRectControl; std::string mPosXControl; std::string mPosYControl; diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 82b01e705d..e01aba402e 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -35,6 +35,7 @@ #include "llui.h" #include "lluictrlfactory.h" #include "lluiimage.h" +#include "llwindow.h" static LLDefaultChildRegistry::Register r("icon"); @@ -42,6 +43,7 @@ LLIconCtrl::Params::Params() : image("image_name"), color("color"), use_draw_context_alpha("use_draw_context_alpha", true), + interactable("interactable", false), scale_image("scale_image"), min_width("min_width", 0), min_height("min_height", 0) @@ -52,6 +54,7 @@ LLIconCtrl::LLIconCtrl(const LLIconCtrl::Params& p) mColor(p.color()), mImagep(p.image), mUseDrawContextAlpha(p.use_draw_context_alpha), + mInteractable(p.interactable), mPriority(0), mMinWidth(p.min_width), mMinHeight(p.min_height), @@ -81,6 +84,16 @@ void LLIconCtrl::draw() LLUICtrl::draw(); } +BOOL LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) +{ + if (mInteractable && getEnabled()) + { + getWindow()->setCursor(UI_CURSOR_HAND); + return TRUE; + } + return LLUICtrl::handleHover(x, y, mask); +} + // virtual // value might be a string or a UUID void LLIconCtrl::setValue(const LLSD& value) diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index dd83e78fd3..9c3b517bca 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -48,7 +48,8 @@ public: { Optional image; Optional color; - Optional use_draw_context_alpha; + Optional use_draw_context_alpha, + interactable; Optional min_width, min_height; Ignored scale_image; @@ -67,6 +68,9 @@ public: // llview overrides virtual void draw(); + // llview overrides + virtual BOOL handleHover(S32 x, S32 y, MASK mask); + // lluictrl overrides virtual void setValue(const LLSD& value ); @@ -88,6 +92,7 @@ protected: // If set to true (default), use the draw context transparency. // If false, will use transparency returned by getCurrentTransparency(). See STORM-698. bool mUseDrawContextAlpha; + bool mInteractable; private: LLUIColor mColor; diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index aac28e04b9..77938edf27 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -395,7 +395,6 @@ void LLLayoutStack::updateLayout() space_to_distribute += panelp ? ll_round((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0; S32 remaining_space = space_to_distribute; - F32 fraction_distributed = 0.f; if (space_to_distribute > 0 && total_visible_fraction > 0.f) { // give space proportionally to visible auto resize panels BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) @@ -404,7 +403,6 @@ void LLLayoutStack::updateLayout() { F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); S32 delta = ll_round((F32)space_to_distribute * fraction_to_distribute); - fraction_distributed += fraction_to_distribute; panelp->mTargetDim += delta; remaining_space -= delta; } diff --git a/indra/llui/llmenubutton.cpp b/indra/llui/llmenubutton.cpp index 303afcda15..583704418b 100644 --- a/indra/llui/llmenubutton.cpp +++ b/indra/llui/llmenubutton.cpp @@ -40,6 +40,7 @@ void LLMenuButton::MenuPositions::declareValues() declare("topleft", MP_TOP_LEFT); declare("topright", MP_TOP_RIGHT); declare("bottomleft", MP_BOTTOM_LEFT); + declare("bottomright", MP_BOTTOM_RIGHT); } LLMenuButton::Params::Params() @@ -212,6 +213,13 @@ void LLMenuButton::updateMenuOrigin() mY = rect.mBottom; break; } + case MP_BOTTOM_RIGHT: + { + const LLRect& menu_rect = menu->getRect(); + mX = rect.mRight - menu_rect.getWidth(); + mY = rect.mBottom; + break; + } } } diff --git a/indra/llui/llmenubutton.h b/indra/llui/llmenubutton.h index 67ec1983b3..e42f8f53bd 100644 --- a/indra/llui/llmenubutton.h +++ b/indra/llui/llmenubutton.h @@ -41,7 +41,8 @@ public: { MP_TOP_LEFT, MP_TOP_RIGHT, - MP_BOTTOM_LEFT + MP_BOTTOM_LEFT, + MP_BOTTOM_RIGHT } EMenuPosition; struct MenuPositions diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 8f00d1274e..4264028338 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3960,8 +3960,8 @@ void LLTearOffMenu::draw() { // animate towards target height reshape(getRect().getWidth(), llceil(lerp((F32)getRect().getHeight(), (F32)mTargetHeight, LLSmoothInterpolation::getInterpolant(0.05f)))); - mMenu->needsArrange(); } + mMenu->needsArrange(); LLFloater::draw(); } diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index 5cfa8ea973..3e5978eb59 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -100,7 +100,10 @@ void LLModalDialog::onOpen(const LLSD& key) if (!sModalStack.empty()) { LLModalDialog* front = sModalStack.front(); - front->setVisible(FALSE); + if (front != this) + { + front->setVisible(FALSE); + } } // This is a modal dialog. It sucks up all mouse and keyboard operations. @@ -108,7 +111,14 @@ void LLModalDialog::onOpen(const LLSD& key) LLUI::getInstance()->addPopup(this); setFocus(TRUE); - sModalStack.push_front( this ); + std::list::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this); + if (iter != sModalStack.end()) + { + // if already present, we want to move it to front. + sModalStack.erase(iter); + } + + sModalStack.push_front(this); } } diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index 209796565c..cf57b1fe76 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -69,16 +69,22 @@ void LLProgressBar::draw() static LLTimer timer; F32 alpha = getDrawContext().mAlpha; - LLColor4 image_bar_color = mColorBackground.get(); - image_bar_color.setAlpha(alpha); - mImageBar->draw(getLocalRect(), image_bar_color); + if (mImageBar) // optional according to parameters + { + LLColor4 image_bar_color = mColorBackground.get(); + image_bar_color.setAlpha(alpha); + mImageBar->draw(getLocalRect(), image_bar_color); + } - alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32())); - LLColor4 bar_color = mColorBar.get(); - bar_color.mV[VALPHA] *= alpha; // modulate alpha - LLRect progress_rect = getLocalRect(); - progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f)); - mImageFill->draw(progress_rect, bar_color); + if (mImageFill) + { + alpha *= 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32())); + LLColor4 bar_color = mColorBar.get(); + bar_color.mV[VALPHA] *= alpha; // modulate alpha + LLRect progress_rect = getLocalRect(); + progress_rect.mRight = ll_round(getRect().getWidth() * (mPercentDone / 100.f)); + mImageFill->draw(progress_rect, bar_color); + } } void LLProgressBar::setValue(const LLSD& value) diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 11b0eb9f80..65c7b420ce 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -1388,6 +1388,84 @@ BOOL LLScrollListCtrl::selectItemByPrefix(const LLWString& target, BOOL case_sen return found; } +U32 LLScrollListCtrl::searchItems(const std::string& substring, bool case_sensitive, bool focus) +{ + return searchItems(utf8str_to_wstring(substring), case_sensitive, focus); +} + +U32 LLScrollListCtrl::searchItems(const LLWString& substring, bool case_sensitive, bool focus) +{ + U32 found = 0; + + LLWString substring_trimmed(substring); + S32 len = substring_trimmed.size(); + + if (0 == len) + { + // at the moment search for empty element is not supported + return 0; + } + else + { + deselectAllItems(TRUE); + if (!case_sensitive) + { + // do comparisons in lower case + LLWStringUtil::toLower(substring_trimmed); + } + + for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++) + { + LLScrollListItem* item = *iter; + // Only select enabled items with matching names + if (!item->getEnabled()) + { + continue; + } + LLScrollListCell* cellp = item->getColumn(getSearchColumn()); + if (!cellp) + { + continue; + } + LLWString item_label = utf8str_to_wstring(cellp->getValue().asString()); + if (!case_sensitive) + { + LLWStringUtil::toLower(item_label); + } + // remove extraneous whitespace from searchable label + LLWStringUtil::trim(item_label); + + size_t found_iter = item_label.find(substring_trimmed); + + if (found_iter != std::string::npos) + { + // find offset of matching text + cellp->highlightText(found_iter, substring_trimmed.size()); + selectItem(item, -1, FALSE); + + found++; + + if (!mAllowMultipleSelection) + { + break; + } + } + } + } + + if (focus && found != 0) + { + mNeedsScroll = true; + } + + if (mCommitOnSelectionChange) + { + commitIfChanged(); + } + + return found; +} + const std::string LLScrollListCtrl::getSelectedItemLabel(S32 column) const { LLScrollListItem* item; @@ -1912,6 +1990,7 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) registrar.add("Url.SendIM", boost::bind(&LLScrollListCtrl::sendIM, id)); registrar.add("Url.AddFriend", boost::bind(&LLScrollListCtrl::addFriend, id)); registrar.add("Url.RemoveFriend", boost::bind(&LLScrollListCtrl::removeFriend, id)); + registrar.add("Url.ReportAbuse", boost::bind(&LLScrollListCtrl::reportAbuse, id, is_group)); registrar.add("Url.Execute", boost::bind(&LLScrollListCtrl::showNameDetails, id, is_group)); registrar.add("Url.CopyLabel", boost::bind(&LLScrollListCtrl::copyNameToClipboard, id, is_group)); registrar.add("Url.CopyUrl", boost::bind(&LLScrollListCtrl::copySLURLToClipboard, id, is_group)); @@ -1975,6 +2054,15 @@ void LLScrollListCtrl::removeFriend(std::string id) LLUrlAction::removeFriend(slurl); } +void LLScrollListCtrl::reportAbuse(std::string id, bool is_group) +{ + if (!is_group) + { + std::string slurl = "secondlife:///app/agent/" + id + "/about"; + LLUrlAction::reportAbuse(slurl); + } +} + void LLScrollListCtrl::showNameDetails(std::string id, bool is_group) { // open the resident's details or the group details diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index 08134bbfc8..77d10fdec7 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -267,6 +267,14 @@ public: const std::string getSelectedItemLabel(S32 column = 0) const; LLSD getSelectedValue(); + // If multi select is on, select all element that include substring, + // otherwise select first match only. + // If focus is true will scroll to selection. + // Returns number of results. + // Note: at the moment search happens in one go and is expensive + U32 searchItems(const std::string& substring, bool case_sensitive = false, bool focus = true); + U32 searchItems(const LLWString& substring, bool case_sensitive = false, bool focus = true); + // DEPRECATED: Use LLSD versions of setCommentText() and getSelectedValue(). // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which // has an associated, unique UUID, and only one of which can be selected at a time. @@ -325,6 +333,7 @@ public: // support right-click context menus for avatar/group lists enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP }; void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; } + ContextMenuType getContextMenuType() { return mContextMenuType; } // Overridden from LLView /*virtual*/ void draw(); @@ -460,6 +469,7 @@ private: static void sendIM(std::string id); static void addFriend(std::string id); static void removeFriend(std::string id); + static void reportAbuse(std::string id, bool is_group); static void showNameDetails(std::string id, bool is_group); static void copyNameToClipboard(std::string id, bool is_group); static void copySLURLToClipboard(std::string id, bool is_group); diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index ee78b82429..ef7c8ec012 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -103,6 +103,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) up_button_params.rect = LLRect(btn_left, getRect().getHeight(), btn_right, getRect().getHeight() - spinctrl_btn_height); up_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); up_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onUpBtn, this, _2)); + up_button_params.commit_on_capture_lost = true; mUpBtn = LLUICtrlFactory::create(up_button_params); addChild(mUpBtn); @@ -111,6 +112,7 @@ LLSpinCtrl::LLSpinCtrl(const LLSpinCtrl::Params& p) down_button_params.rect = LLRect(btn_left, getRect().getHeight() - spinctrl_btn_height, btn_right, getRect().getHeight() - 2 * spinctrl_btn_height); down_button_params.click_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); down_button_params.mouse_held_callback.function(boost::bind(&LLSpinCtrl::onDownBtn, this, _2)); + down_button_params.commit_on_capture_lost = true; mDownBtn = LLUICtrlFactory::create(down_button_params); addChild(mDownBtn); diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 459fdcf2ae..0aa7a2d217 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -402,9 +402,13 @@ void LLTabContainer::draw() S32 cur_scroll_pos = getScrollPos(); if (cur_scroll_pos > 0) { - S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); - if (!mIsVertical) + if (mIsVertical) { + target_pixel_scroll = cur_scroll_pos * (BTN_HEIGHT + tabcntrv_pad); + } + else + { + S32 available_width_with_arrows = getRect().getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + tabcntr_arrow_btn_size + tabcntr_arrow_btn_size + 1); for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter) { if (cur_scroll_pos == 0) @@ -1189,13 +1193,15 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) sendChildToFront(mNextArrowBtn); sendChildToFront(mJumpPrevArrowBtn); sendChildToFront(mJumpNextArrowBtn); - + + updateMaxScrollPos(); + if( select ) { selectLastTab(); + mScrollPos = mMaxScrollPos; } - updateMaxScrollPos(); } void LLTabContainer::addPlaceholder(LLPanel* child, const std::string& label) @@ -2079,9 +2085,9 @@ void LLTabContainer::updateMaxScrollPos() if( tab_total_height > available_height ) { static LLUICachedControl tabcntrv_arrow_btn_size ("UITabCntrvArrowBtnSize", 0); - S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad); + S32 available_height_with_arrows = getRect().getHeight() - 2*(tabcntrv_arrow_btn_size + 3*tabcntrv_pad) - mNextArrowBtn->getRect().mBottom; S32 additional_needed = tab_total_height - available_height_with_arrows; - setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT) ) ); + setMaxScrollPos((S32) ceil(additional_needed / float(BTN_HEIGHT + tabcntrv_pad) ) ); no_scroll = FALSE; } } diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 0dc99fdde6..7e4aaa53bf 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -163,6 +163,7 @@ LLTextBase::Params::Params() font_shadow("font_shadow"), wrap("wrap"), trusted_content("trusted_content", true), + always_show_icons("always_show_icons", false), use_ellipses("use_ellipses", false), parse_urls("parse_urls", false), force_urls_external("force_urls_external", false), @@ -212,6 +213,7 @@ LLTextBase::LLTextBase(const LLTextBase::Params &p) mClip(p.clip), mClipPartial(p.clip_partial && !p.allow_scroll), mTrustedContent(p.trusted_content), + mAlwaysShowIcons(p.always_show_icons), mTrackEnd( p.track_end ), mScrollIndex(-1), mSelectionStart( 0 ), @@ -448,8 +450,48 @@ void LLTextBase::drawSelectionBackground() ++rect_it) { LLRect selection_rect = *rect_it; - selection_rect = *rect_it; - selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); + if (mScroller) + { + // If scroller is On content_display_rect has correct rect and safe to use as is + // Note: we might need to account for border + selection_rect.translate(mVisibleTextRect.mLeft - content_display_rect.mLeft, mVisibleTextRect.mBottom - content_display_rect.mBottom); + } + else + { + // If scroller is Off content_display_rect will have rect from document, adjusted to text width, heigh and position + // and we have to acount for offset depending on position + S32 v_delta = 0; + S32 h_delta = 0; + switch (mVAlign) + { + case LLFontGL::TOP: + v_delta = mVisibleTextRect.mTop - content_display_rect.mTop - mVPad; + break; + case LLFontGL::VCENTER: + v_delta = (llmax(mVisibleTextRect.getHeight() - content_display_rect.mTop, -content_display_rect.mBottom) + (mVisibleTextRect.mBottom - content_display_rect.mBottom)) / 2; + break; + case LLFontGL::BOTTOM: + v_delta = mVisibleTextRect.mBottom - content_display_rect.mBottom; + break; + default: + break; + } + switch (mHAlign) + { + case LLFontGL::LEFT: + h_delta = mVisibleTextRect.mLeft - content_display_rect.mLeft + mHPad; + break; + case LLFontGL::HCENTER: + h_delta = (llmax(mVisibleTextRect.getWidth() - content_display_rect.mLeft, -content_display_rect.mRight) + (mVisibleTextRect.mRight - content_display_rect.mRight)) / 2; + break; + case LLFontGL::RIGHT: + h_delta = mVisibleTextRect.mRight - content_display_rect.mRight; + break; + default: + break; + } + selection_rect.translate(h_delta, v_delta); + } gl_rect_2d(selection_rect, selection_color); } } @@ -2007,6 +2049,7 @@ void LLTextBase::createUrlContextMenu(S32 x, S32 y, const std::string &in_url) registrar.add("Url.ShowProfile", boost::bind(&LLUrlAction::showProfile, url)); registrar.add("Url.AddFriend", boost::bind(&LLUrlAction::addFriend, url)); registrar.add("Url.RemoveFriend", boost::bind(&LLUrlAction::removeFriend, url)); + registrar.add("Url.ReportAbuse", boost::bind(&LLUrlAction::reportAbuse, url)); registrar.add("Url.SendIM", boost::bind(&LLUrlAction::sendIM, url)); registrar.add("Url.ShowOnMap", boost::bind(&LLUrlAction::showLocationOnMap, url)); registrar.add("Url.CopyLabel", boost::bind(&LLUrlAction::copyLabelToClipboard, url)); @@ -2116,7 +2159,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para LLUrlMatch match; std::string text = new_text; while ( LLUrlRegistry::instance().findUrl(text, match, - boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted())) + boost::bind(&LLTextBase::replaceUrl, this, _1, _2, _3),isContentTrusted() || mAlwaysShowIcons)) { start = match.getStart(); end = match.getEnd()+1; @@ -2141,7 +2184,7 @@ void LLTextBase::appendTextImpl(const std::string &new_text, const LLStyle::Para } // add icon before url if need - LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted()); + LLTextUtil::processUrlMatch(&match, this, isContentTrusted() || match.isTrusted() || mAlwaysShowIcons); if ((isContentTrusted() || match.isTrusted()) && !match.getIcon().empty() ) { setLastSegmentToolTip(LLTrans::getString("TooltipSLIcon")); diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index a4e83b42b4..25f8fa1c2b 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -321,7 +321,8 @@ public: parse_highlights, clip, clip_partial, - trusted_content; + trusted_content, + always_show_icons; Optional v_pad, h_pad; @@ -369,6 +370,8 @@ public: virtual void onFocusReceived(); virtual void onFocusLost(); + void setParseHTML(bool parse_html) { mParseHTML = parse_html; } + // LLSpellCheckMenuHandler overrides /*virtual*/ bool getSpellCheck() const; @@ -702,6 +705,8 @@ protected: bool mAutoIndent; S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes bool mSkipTripleClick; + bool mAlwaysShowIcons; + bool mSkipLinkUnderline; // support widgets diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 26702b2412..1a10d2fd1e 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -196,6 +196,7 @@ public: const LLUUID& getSourceID() const { return mSourceID; } const LLTextSegmentPtr getPreviousSegment() const; + const LLTextSegmentPtr getLastSegment() const; void getSelectedSegments(segment_vec_t& segments) const; void setShowContextMenu(bool show) { mShowContextMenu = show; } diff --git a/indra/llui/lltextutil.cpp b/indra/llui/lltextutil.cpp index 538508b856..78049319bc 100644 --- a/indra/llui/lltextutil.cpp +++ b/indra/llui/lltextutil.cpp @@ -76,22 +76,6 @@ void LLTextUtil::textboxSetGreyedVal(LLTextBox *txtbox, const LLStyle::Params& n txtbox->appendText(text.substr(greyed_begin + greyed_len), false, normal_style); } -const std::string& LLTextUtil::formatPhoneNumber(const std::string& phone_str) -{ - static const std::string PHONE_SEPARATOR = LLUI::getInstance()->mSettingGroups["config"]->getString("AvalinePhoneSeparator"); - static const S32 PHONE_PART_LEN = 2; - - static std::string formatted_phone_str; - formatted_phone_str = phone_str; - S32 separator_pos = (S32)(formatted_phone_str.size()) - PHONE_PART_LEN; - for (; separator_pos >= PHONE_PART_LEN; separator_pos -= PHONE_PART_LEN) - { - formatted_phone_str.insert(separator_pos, PHONE_SEPARATOR); - } - - return formatted_phone_str; -} - bool LLTextUtil::processUrlMatch(LLUrlMatch* match,LLTextBase* text_base, bool is_content_trusted) { if (match == 0 || text_base == 0) diff --git a/indra/llui/lltextutil.h b/indra/llui/lltextutil.h index a9c143e445..1adc3516f7 100644 --- a/indra/llui/lltextutil.h +++ b/indra/llui/lltextutil.h @@ -58,18 +58,6 @@ namespace LLTextUtil const std::string& text, const std::string& greyed); - /** - * Formats passed phone number to be more human readable. - * - * It just divides the number on parts by two digits from right to left. The first left part - * can have 2 or 3 digits, i.e. +44-33-33-44-55-66 or 12-34-56-78-90. Separator is set in - * application settings (AvalinePhoneSeparator) - * - * @param[in] phone_str string with original phone number - * @return reference to string with formatted phone number - */ - const std::string& formatPhoneNumber(const std::string& phone_str); - /** * Adds icon before url if need. * diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp index 84ea770a8d..8216046174 100644 --- a/indra/llui/llurlaction.cpp +++ b/indra/llui/llurlaction.cpp @@ -222,6 +222,15 @@ void LLUrlAction::removeFriend(std::string url) } } +void LLUrlAction::reportAbuse(std::string url) +{ + std::string id_str = getUserID(url); + if (LLUUID::validate(id_str)) + { + executeSLURL("secondlife:///app/agent/" + id_str + "/reportAbuse"); + } +} + void LLUrlAction::blockObject(std::string url) { std::string object_id = getObjectId(url); diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h index 2d2a8dfef1..c2c576254d 100644 --- a/indra/llui/llurlaction.h +++ b/indra/llui/llurlaction.h @@ -82,6 +82,7 @@ public: static void sendIM(std::string url); static void addFriend(std::string url); static void removeFriend(std::string url); + static void reportAbuse(std::string url); static void blockObject(std::string url); static void unblockObject(std::string url); diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index 70eb99c86c..55befaef51 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -197,4 +197,10 @@ if (SDL_FOUND) endif (SDL_FOUND) target_link_libraries (llwindow ${llwindow_LINK_LIBRARIES}) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(CARBON_LIBRARY Carbon) + target_link_libraries(llwindow ${CARBON_LIBRARY}) +endif (DARWIN) diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp index 5404ac50e5..e65cc7563e 100644 --- a/indra/llwindow/llkeyboard.cpp +++ b/indra/llwindow/llkeyboard.cpp @@ -148,6 +148,22 @@ void LLKeyboard::addKeyName(KEY key, const std::string& name) sNamesToKeys[nameuc] = key; } +void LLKeyboard::resetKeyDownAndHandle() +{ + MASK mask = currentMask(FALSE); + for (S32 i = 0; i < KEY_COUNT; i++) + { + if (mKeyLevel[i]) + { + mKeyDown[i] = FALSE; + mKeyLevel[i] = FALSE; + mKeyUp[i] = TRUE; + mCurTranslatedKey = (KEY)i; + mCallbacks->handleTranslatedKeyUp(i, mask); + } + } +} + // BUG this has to be called when an OS dialog is shown, otherwise modifier key state // is wrong because the keyup event is never received by the main window. JC void LLKeyboard::resetKeys() diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h index 36bd8bcbed..fb1ae10f50 100644 --- a/indra/llwindow/llkeyboard.h +++ b/indra/llwindow/llkeyboard.h @@ -58,7 +58,8 @@ public: LLKeyboard(); virtual ~LLKeyboard(); - void resetKeys(); + void resetKeyDownAndHandle(); + void resetKeys(); F32 getCurKeyElapsedTime() { return getKeyDown(mCurScanKey) ? getKeyElapsedTime( mCurScanKey ) : 0.f; } diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index fd20f2ad15..049226db65 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -495,14 +495,14 @@ attributedStringInfo getSegments(NSAttributedString *str) // e.g. OS Window for upload something or Input Window... // mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit) mModifiers = [theEvent modifierFlags]; + unichar ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; + bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers, ch); - bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers, [[theEvent characters] characterAtIndex:0]); - unichar ch; if (acceptsText && !mMarkedTextAllowed && !(mModifiers & (NSControlKeyMask | NSCommandKeyMask)) && // commands don't invoke InputWindow ![(LLAppDelegate*)[NSApp delegate] romanScript] && - (ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]) > ' ' && + ch > ' ' && ch != NSDeleteCharacter && (ch < 0xF700 || ch > 0xF8FF)) // 0xF700-0xF8FF: reserved for function keys on the keyboard(from NSEvent.h) { diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index f895c17643..5ec9b017cf 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -100,13 +100,13 @@ const unsigned short *copyFromPBoard() CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - + // extra retain on the NSCursor since we want it to live for the lifetime of the app. NSCursor *cursor = [[[NSCursor alloc] initWithImage: [[[NSImage alloc] initWithContentsOfFile: - [NSString stringWithFormat:@"%s", fullpath] + [NSString stringWithUTF8String:fullpath] ]autorelease] hotSpot:NSMakePoint(hotspotX, hotspotY) ]retain]; diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index bc4f07941b..c29131d60b 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -621,8 +621,6 @@ void LLWindowMacOSX::getMouseDeltas(float* delta) BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL enable_vsync) { - BOOL glNeedsInit = FALSE; - mFullscreen = fullscreen; if (mWindow == NULL) @@ -637,9 +635,6 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits mGLView = createOpenGLView(mWindow, mFSAASamples, enable_vsync); mContext = getCGLContextObj(mGLView); - // Since we just created the context, it needs to be set up. - glNeedsInit = TRUE; - gGLManager.mVRAM = getVramSize(mGLView); } @@ -1666,7 +1661,7 @@ void LLWindowMacOSX::hideCursor() void LLWindowMacOSX::showCursor() { - if(mCursorHidden) + if(mCursorHidden || !isCGCursorVisible()) { // LL_INFOS() << "showCursor: showing" << LL_ENDL; mCursorHidden = FALSE; @@ -1721,9 +1716,7 @@ void LLSplashScreenMacOSX::updateImpl(const std::string& mesg) { if(mWindow != NULL) { - CFStringRef string = NULL; - - string = CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8); + CFStringCreateWithCString(NULL, mesg.c_str(), kCFStringEncodingUTF8); } } diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 4c3aeb4695..c487877caf 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -874,21 +874,20 @@ void LLWindowWin32::close() // Restore gamma to the system values. restoreGamma(); - if (mhDC) - { - if (!ReleaseDC(mWindowHandle, mhDC)) - { - LL_WARNS("Window") << "Release of ghDC failed" << LL_ENDL; - } - mhDC = NULL; - } - LL_DEBUGS("Window") << "Destroying Window" << LL_ENDL; mWindowThread->post([=]() { if (IsWindow(mWindowHandle)) { + if (mhDC) + { + if (!ReleaseDC(mWindowHandle, mhDC)) + { + LL_WARNS("Window") << "Release of ghDC failed!" << LL_ENDL; + } + } + // Make sure we don't leave a blank toolbar button. ShowWindow(mWindowHandle, SW_HIDE); @@ -914,6 +913,7 @@ void LLWindowWin32::close() // Even though the above lambda might not yet have run, we've already // bound mWindowHandle into it by value, which should suffice for the // operations we're asking. That's the last time WE should touch it. + mhDC = NULL; mWindowHandle = NULL; mWindowThread->close(); } @@ -1506,12 +1506,10 @@ const S32 max_format = (S32)num_formats - 1; { wglDeleteContext (mhRC); // Release The Rendering Context mhRC = 0; // Zero The Rendering Context - } - ReleaseDC (mWindowHandle, mhDC); // Release The Device Context - mhDC = 0; // Zero The Device Context } + // will release and recreate mhDC, mWindowHandle recreateWindow(window_rect, dw_ex_style, dw_style); RECT rect; @@ -1661,7 +1659,8 @@ const S32 max_format = (S32)num_formats - 1; void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw_style) { - auto oldHandle = mWindowHandle; + auto oldWindowHandle = mWindowHandle; + auto oldDCHandle = mhDC; // zero out mWindowHandle and mhDC before destroying window so window // thread falls back to peekmessage @@ -1673,7 +1672,8 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw auto window_work = [this, self=mWindowThread, - oldHandle, + oldWindowHandle, + oldDCHandle, // bind CreateWindowEx() parameters by value instead of // back-referencing LLWindowWin32 members windowClassName=mWindowClassName, @@ -1689,11 +1689,20 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw self->mWindowHandle = 0; self->mhDC = 0; - // important to call DestroyWindow() from the window thread - if (oldHandle && !destroy_window_handler(oldHandle)) + if (oldWindowHandle) { - LL_WARNS("Window") << "Failed to properly close window before recreating it!" - << LL_ENDL; + if (oldDCHandle && !ReleaseDC(oldWindowHandle, oldDCHandle)) + { + LL_WARNS("Window") << "Failed to ReleaseDC" << LL_ENDL; + } + + // important to call DestroyWindow() from the window thread + if (!destroy_window_handler(oldWindowHandle)) + { + + LL_WARNS("Window") << "Failed to properly close window before recreating it!" + << LL_ENDL; + } } auto handle = CreateWindowEx(dw_ex_style, @@ -1731,7 +1740,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw }; // But how we pass window_work to the window thread depends on whether we // already have a window handle. - if (! oldHandle) + if (!oldWindowHandle) { // Pass window_work using the WorkQueue: without an existing window // handle, the window thread can't call GetMessage(). @@ -1744,7 +1753,7 @@ void LLWindowWin32::recreateWindow(RECT window_rect, DWORD dw_ex_style, DWORD dw // PostMessage(oldHandle) because oldHandle won't be destroyed until // the window thread has retrieved and executed window_work. LL_DEBUGS("Window") << "posting window_work to message queue" << LL_ENDL; - mWindowThread->Post(oldHandle, window_work); + mWindowThread->Post(oldWindowHandle, window_work); } auto future = promise.get_future(); diff --git a/indra/media_plugins/cef/media_plugin_cef.cpp b/indra/media_plugins/cef/media_plugin_cef.cpp index ea70e21414..43d3a32e64 100644 --- a/indra/media_plugins/cef/media_plugin_cef.cpp +++ b/indra/media_plugins/cef/media_plugin_cef.cpp @@ -34,6 +34,7 @@ #include "llplugininstance.h" #include "llpluginmessage.h" #include "llpluginmessageclasses.h" +#include "llstring.h" #include "volume_catcher.h" #include "media_plugin_base.h" @@ -55,7 +56,7 @@ private: bool init(); void onPageChangedCallback(const unsigned char* pixels, int x, int y, const int width, const int height); - void onCustomSchemeURLCallback(std::string url); + void onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect); void onConsoleMessageCallback(std::string message, std::string source, int line); void onStatusMessageCallback(std::string value); void onTitleChangeCallback(std::string title); @@ -299,11 +300,18 @@ void MediaPluginCEF::onOpenPopupCallback(std::string url, std::string target) //////////////////////////////////////////////////////////////////////////////// // -void MediaPluginCEF::onCustomSchemeURLCallback(std::string url) +void MediaPluginCEF::onCustomSchemeURLCallback(std::string url, bool user_gesture, bool is_redirect) { LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); - message.setValue("uri", url); - message.setValue("nav_type", "clicked"); // TODO: differentiate between click and navigate to + message.setValue("uri", url); + + // indicate if this interaction was from a user click (okay on a SLAPP) or + // via a navigation (e.g. a data URL - see SL-18151) (not okay on a SLAPP) + const std::string nav_type = user_gesture ? "clicked" : "navigated"; + + message.setValue("nav_type", nav_type); + message.setValueBoolean("is_redirect", is_redirect); + sendMessage(message); } @@ -592,7 +600,7 @@ void MediaPluginCEF::receiveMessage(const char* message_string) { // event callbacks from Dullahan mCEFLib->setOnPageChangedCallback(std::bind(&MediaPluginCEF::onPageChangedCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); - mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1)); + mCEFLib->setOnCustomSchemeURLCallback(std::bind(&MediaPluginCEF::onCustomSchemeURLCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); mCEFLib->setOnConsoleMessageCallback(std::bind(&MediaPluginCEF::onConsoleMessageCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); mCEFLib->setOnStatusMessageCallback(std::bind(&MediaPluginCEF::onStatusMessageCallback, this, std::placeholders::_1)); mCEFLib->setOnTitleChangeCallback(std::bind(&MediaPluginCEF::onTitleChangeCallback, this, std::placeholders::_1)); @@ -616,9 +624,9 @@ void MediaPluginCEF::receiveMessage(const char* message_string) // dir as the executable that loaded it (SLPlugin.exe). The code in // Dullahan that tried to figure out the location automatically uses // the location of the exe which isn't helpful so we tell it explicitly. - char cur_dir_str[MAX_PATH]; - GetCurrentDirectoryA(MAX_PATH, cur_dir_str); - settings.host_process_path = std::string(cur_dir_str); + std::vector buffer(MAX_PATH + 1); + GetCurrentDirectoryW(MAX_PATH, &buffer[0]); + settings.host_process_path = ll_convert_wide_to_string(&buffer[0]); #endif settings.accept_language_list = mHostLanguage; diff --git a/indra/media_plugins/libvlc/media_plugin_libvlc.cpp b/indra/media_plugins/libvlc/media_plugin_libvlc.cpp index 1afe25e9a1..89144922cc 100644 --- a/indra/media_plugins/libvlc/media_plugin_libvlc.cpp +++ b/indra/media_plugins/libvlc/media_plugin_libvlc.cpp @@ -73,6 +73,7 @@ private: static void display(void* data, void* id); /*virtual*/ void setDirty(int left, int top, int right, int bottom) /* override, but that is not supported in gcc 4.6 */; + void setDurationDirty(); static void eventCallbacks(const libvlc_event_t* event, void* ptr); @@ -93,8 +94,8 @@ private: bool mIsLooping; - float mCurTime; - float mDuration; + F64 mCurTime; + F64 mDuration; EStatus mVlcStatus; }; @@ -213,6 +214,19 @@ void MediaPluginLibVLC::setDirty(int left, int top, int right, int bottom) sendMessage(message); } +//////////////////////////////////////////////////////////////////////////////// +// *virtual* +void MediaPluginLibVLC::setDurationDirty() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueReal("current_time", mCurTime); + message.setValueReal("duration", mDuration); + message.setValueReal("current_rate", 1.0f); + + sendMessage(message); +} + //////////////////////////////////////////////////////////////////////////////// // void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) @@ -233,6 +247,7 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) parent->mDuration = (float)(libvlc_media_get_duration(parent->mLibVLCMedia)) / 1000.0f; parent->mVlcStatus = STATUS_PLAYING; parent->setVolumeVLC(); + parent->setDurationDirty(); break; case libvlc_MediaPlayerPaused: @@ -245,6 +260,8 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) case libvlc_MediaPlayerEndReached: parent->mVlcStatus = STATUS_DONE; + parent->mCurTime = parent->mDuration; + parent->setDurationDirty(); break; case libvlc_MediaPlayerEncounteredError: @@ -253,6 +270,11 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) case libvlc_MediaPlayerTimeChanged: parent->mCurTime = (float)libvlc_media_player_get_time(parent->mLibVLCMediaPlayer) / 1000.0f; + if (parent->mVlcStatus == STATUS_DONE && libvlc_media_player_is_playing(parent->mLibVLCMediaPlayer)) + { + parent->mVlcStatus = STATUS_PLAYING; + } + parent->setDurationDirty(); break; case libvlc_MediaPlayerPositionChanged: @@ -260,6 +282,7 @@ void MediaPluginLibVLC::eventCallbacks(const libvlc_event_t* event, void* ptr) case libvlc_MediaPlayerLengthChanged: parent->mDuration = (float)libvlc_media_get_duration(parent->mLibVLCMedia) / 1000.0f; + parent->setDurationDirty(); break; case libvlc_MediaPlayerTitleChanged: @@ -562,7 +585,24 @@ void MediaPluginLibVLC::receiveMessage(const char* message_string) mTextureWidth = texture_width; mTextureHeight = texture_height; + libvlc_time_t time = 1000.0 * mCurTime; + playMedia(); + + if (mLibVLCMediaPlayer) + { + libvlc_media_player_set_time(mLibVLCMediaPlayer, time); + time = libvlc_media_player_get_time(mLibVLCMediaPlayer); + if (time < 0) + { + // -1 if there is no media + mCurTime = 0; + } + else + { + mCurTime = (F64)time / 1000.0; + } + } }; }; @@ -594,6 +634,13 @@ void MediaPluginLibVLC::receiveMessage(const char* message_string) { if (mLibVLCMediaPlayer) { + if (mVlcStatus == STATUS_DONE && !libvlc_media_player_is_playing(mLibVLCMediaPlayer)) + { + // stop or vlc will ignore 'play', it will just + // make an MediaPlayerEndReached event even if + // seek was used + libvlc_media_player_stop(mLibVLCMediaPlayer); + } libvlc_media_player_play(mLibVLCMediaPlayer); } } @@ -606,15 +653,32 @@ void MediaPluginLibVLC::receiveMessage(const char* message_string) } else if (message_name == "seek") { - if (mDuration > 0) - { - F64 normalized_offset = message_in.getValueReal("time") / mDuration; - libvlc_media_player_set_position(mLibVLCMediaPlayer, normalized_offset); - } + if (mLibVLCMediaPlayer) + { + libvlc_time_t time = 1000.0 * message_in.getValueReal("time"); + libvlc_media_player_set_time(mLibVLCMediaPlayer, time); + time = libvlc_media_player_get_time(mLibVLCMediaPlayer); + if (time < 0) + { + // -1 if there is no media + mCurTime = 0; + } + else + { + mCurTime = (F64)time / 1000.0; + } + + if (!libvlc_media_player_is_playing(mLibVLCMediaPlayer)) + { + // if paused, won't trigger update, update now + setDurationDirty(); + } + } } else if (message_name == "set_loop") { - mIsLooping = true; + bool loop = message_in.getValueBoolean("loop"); + mIsLooping = loop; } else if (message_name == "set_volume") { diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c577d062f9..7d78ec9e3c 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -234,12 +234,14 @@ set(viewer_SOURCE_FILES llfloatercamera.cpp llfloatercamerapresets.cpp llfloaterchatvoicevolume.cpp + llfloaterclassified.cpp llfloatercolorpicker.cpp llfloaterconversationlog.cpp llfloaterconversationpreview.cpp llfloatercreatelandmark.cpp llfloaterdeleteprefpreset.cpp llfloaterdestinations.cpp + llfloaterdisplayname.cpp llfloatereditenvironmentbase.cpp llfloatereditextdaycycle.cpp llfloaterenvironmentadjust.cpp @@ -295,9 +297,11 @@ set(viewer_SOURCE_FILES llfloaterpay.cpp llfloaterperms.cpp llfloaterpostprocess.cpp + llfloaterprofile.cpp llfloaterpreference.cpp llfloaterpreferenceviewadvanced.cpp llfloaterpreviewtrash.cpp + llfloaterprofiletexture.cpp llfloaterproperties.cpp llfloaterregiondebugconsole.cpp llfloaterregioninfo.cpp @@ -330,7 +334,6 @@ set(viewer_SOURCE_FILES llfloatervoiceeffect.cpp llfloatervoicevolume.cpp llfloaterwebcontent.cpp - llfloaterwebprofile.cpp llfloaterwhitelistentry.cpp llfloaterwindowsize.cpp llfloaterworldmap.cpp @@ -476,7 +479,6 @@ set(viewer_SOURCE_FILES llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp - llpanelme.cpp llpanelnearbymedia.cpp llpanelobject.cpp llpanelobjectinventory.cpp @@ -486,8 +488,6 @@ set(viewer_SOURCE_FILES llpanelpeople.cpp llpanelpeoplemenus.cpp llpanelpermissions.cpp - llpanelpick.cpp - llpanelpicks.cpp llpanelplaceinfo.cpp llpanelplaceprofile.cpp llpanelplaces.cpp @@ -496,6 +496,8 @@ set(viewer_SOURCE_FILES llpanelpresetspulldown.cpp llpanelprimmediacontrols.cpp llpanelprofile.cpp + llpanelprofileclassifieds.cpp + llpanelprofilepicks.cpp llpanelsnapshot.cpp llpanelsnapshotinventory.cpp llpanelsnapshotlocal.cpp @@ -657,6 +659,7 @@ set(viewer_SOURCE_FILES llviewercontrol.cpp llviewercontrollistener.cpp llviewerdisplay.cpp + llviewerdisplayname.cpp llviewerfloaterreg.cpp llviewerfoldertype.cpp llviewergenericmessage.cpp @@ -870,12 +873,14 @@ set(viewer_HEADER_FILES llfloatercamerapresets.h llfloatercamera.h llfloaterchatvoicevolume.h + llfloaterclassified.h llfloatercolorpicker.h llfloaterconversationlog.h llfloaterconversationpreview.h llfloatercreatelandmark.h llfloaterdeleteprefpreset.h llfloaterdestinations.h + llfloaterdisplayname.h llfloatereditenvironmentbase.h llfloatereditextdaycycle.h llfloaterenvironmentadjust.h @@ -934,9 +939,11 @@ set(viewer_HEADER_FILES llfloaterpay.h llfloaterperms.h llfloaterpostprocess.h + llfloaterprofile.h llfloaterpreference.h llfloaterpreferenceviewadvanced.h llfloaterpreviewtrash.h + llfloaterprofiletexture.h llfloaterproperties.h llfloaterregiondebugconsole.h llfloaterregioninfo.h @@ -969,7 +976,6 @@ set(viewer_HEADER_FILES llfloatervoiceeffect.h llfloatervoicevolume.h llfloaterwebcontent.h - llfloaterwebprofile.h llfloaterwhitelistentry.h llfloaterwindowsize.h llfloaterworldmap.h @@ -1105,7 +1111,6 @@ set(viewer_HEADER_FILES llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h - llpanelme.h llpanelnearbymedia.h llpanelobject.h llpanelobjectinventory.h @@ -1115,8 +1120,6 @@ set(viewer_HEADER_FILES llpanelpeople.h llpanelpeoplemenus.h llpanelpermissions.h - llpanelpick.h - llpanelpicks.h llpanelplaceinfo.h llpanelplaceprofile.h llpanelplaces.h @@ -1125,6 +1128,8 @@ set(viewer_HEADER_FILES llpanelpresetspulldown.h llpanelprimmediacontrols.h llpanelprofile.h + llpanelprofileclassifieds.h + llpanelprofilepicks.h llpanelsnapshot.h llpanelteleporthistory.h llpaneltiptoast.h @@ -1287,6 +1292,7 @@ set(viewer_HEADER_FILES llviewercontrol.h llviewercontrollistener.h llviewerdisplay.h + llviewerdisplayname.h llviewerfloaterreg.h llviewerfoldertype.h llviewergenericmessage.h diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 28179fc1f5..4aa5a3a58e 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.2 +6.6.8 diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 3dfe3f6634..2644f5f449 100644 --- a/indra/newview/app_settings/commands.xml +++ b/indra/newview/app_settings/commands.xml @@ -167,10 +167,8 @@ icon="Command_Picks_Icon" label_ref="Command_Picks_Label" tooltip_ref="Command_Picks_Tooltip" - execute_function="Floater.ToggleOrBringToFront" - execute_parameters="picks" - is_running_function="Floater.IsOpen" - is_running_parameters="picks" + execute_function="Avatar.TogglePicks" + is_running_function="Avatar.IsPicksTabOpen" /> - diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 16d8cd9f06..6861153d43 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -544,17 +544,6 @@ Value 1 - AvalinePhoneSeparator - - Comment - Separator of phone parts to have Avaline numbers human readable in Voice Control Panel - Persist - 1 - Type - String - Value - - - AvatarAxisDeadZone0 Comment @@ -1371,7 +1360,7 @@ Type F32 Value - 20.0 + 40.0 DiskCacheDirName @@ -4758,7 +4747,7 @@ Type String Value - https://search.[GRID]/viewer/[CATEGORY]/?q=[QUERY]&r=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD] + https://search.[GRID]/?query_term=[QUERY]&search_type=[TYPE][COLLECTION]&maturity=[MATURITY]&lang=[LANGUAGE]&g=[GODLIKE]&sid=[SESSION_ID]&rid=[REGION_ID]&pid=[PARCEL_ID]&channel=[CHANNEL]&version=[VERSION]&major=[VERSION_MAJOR]&minor=[VERSION_MINOR]&patch=[VERSION_PATCH]&build=[VERSION_BUILD] GuidebookURL @@ -5806,6 +5795,17 @@ Value 0 + DiskCacheVersion + + Comment + Version number of disk cache + Persist + 1 + Type + S32 + Value + 0 + LocalFileSystemBrowsingEnabled Comment @@ -6808,6 +6808,9 @@ Type Boolean Value + 1 MiniMapScale @@ -6821,6 +6824,17 @@ Value 128.0 + MiniMapShowPropertyLines + + Comment + Whether or not to show parcel borders on the MiniMap. + Persist + 1 + Type + Boolean + Value + 1 + MouseSensitivity Comment @@ -7099,13 +7113,13 @@ NonvisibleObjectsInMemoryTime Comment - Number of frames non-visible objects stay in memory before being removed. 0 means never to remove. + Number of frames non-visible objects stay in memory before being removed. 0 means max. Persist 1 Type U32 Value - 300 + 64 NoPreload @@ -11022,7 +11036,7 @@ Type U32 Value - 1024 + 2048 SceneLoadLowMemoryBound @@ -15736,6 +15750,17 @@ Boolean Value 1 + + AllowSelectAvatar + + Comment + Allows user to select and move avatars, move is viewer sided, does not propagate to server, also supresses avatar position updates while avatars are selected + Persist + 0 + Type + Boolean + Value + 0 WebProfileFloaterRect diff --git a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl index 02d83925ea..b26194f278 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/materialF.glsl @@ -202,7 +202,7 @@ VARYING vec2 vary_texcoord2; uniform float env_intensity; uniform vec4 specular_color; // specular color RGB and specular exponent (glossiness) in alpha -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) +#ifdef HAS_ALPHA_MASK uniform float minimum_alpha; #endif @@ -227,11 +227,12 @@ void main() vec4 diffcol = texture2D(diffuseMap, vary_texcoord0.xy); diffcol.rgb *= vertex_color.rgb; -#if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_MASK) - - // Comparing floats cast from 8-bit values, produces acne right at the 8-bit transition points - float bias = 0.001953125; // 1/512, or half an 8-bit quantization - if (diffcol.a < minimum_alpha-bias) +#ifdef HAS_ALPHA_MASK +#if DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND + if (diffcol.a*vertex_color.a < minimum_alpha) +#else + if (diffcol.a < minimum_alpha) +#endif { discard; } diff --git a/indra/newview/character/avatar_skeleton.xml b/indra/newview/character/avatar_skeleton.xml index 2241a12545..6cfc0b0be2 100644 --- a/indra/newview/character/avatar_skeleton.xml +++ b/indra/newview/character/avatar_skeleton.xml @@ -2,15 +2,15 @@ - - + + - - + + @@ -23,58 +23,58 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - + + + @@ -86,29 +86,29 @@ - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + @@ -123,49 +123,49 @@ - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - - - - + + + + + + - - - - - + + + + + @@ -200,30 +200,30 @@ - - - - - - + + + + + + - - - - - - + + + + + + - - - - + + + + diff --git a/indra/newview/cube.dae b/indra/newview/cube.dae new file mode 100644 index 0000000000..085b2c7309 --- /dev/null +++ b/indra/newview/cube.dae @@ -0,0 +1,103 @@ + + + + + modified from https://gist.github.com/wtsnz/bfa11c40e04594b260255b5dc7956f26 + + 2018-10-25T16:29:03Z + 2022-02-18T00:00:00Z + + Y_UP + + + + + + + + + + + + + + + + 0 0 0 1 + + + 0.137255 0.403922 0.870588 1 + + + 0.5 0.5 0.5 1 + + + 16 + + + 0 0 0 1 + + + 1 + + + 1 + + + + + + + + + + + + + + -0.5 0.5 0.5 -0.5 -0.5 0.5 0.5 -0.5 0.5 0.5 0.5 0.5 -0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 -0.5 -0.5 0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 -0.5 0.5 -0.5 0.5 -0.5 -0.5 0.5 -0.5 0.5 0.5 -0.5 0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 0.5 0.5 -0.5 0.5 0.5 -0.5 -0.5 0.5 0.5 -0.5 0.5 0.5 0.5 0.5 0.5 -0.5 0.5 -0.5 -0.5 -0.5 -0.5 -0.5 -0.5 0.5 -0.5 + + + + + + + + + + + + + + + + 0 1 2 0 2 3 4 5 6 4 6 7 8 9 10 8 10 11 12 13 14 12 14 15 16 17 18 16 18 19 20 21 22 20 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 7513908cb4..60e26274cb 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -351,11 +351,29 @@ DeleteRegValue HKEY_CLASSES_ROOT "Applications\$VIEWER_EXE" "IsHostApp" DeleteRegValue HKEY_CLASSES_ROOT "Applications\$VIEWER_EXE" "NoStartPage" ClearErrors +INSTALL_FILES_START: + Call RemoveProgFilesOnInst # Remove existing files to prevent certain errors when running the new version of the viewer # This placeholder is replaced by the complete list of all the files in the installer, by viewer_manifest.py %%INSTALL_FILES%% +IfErrors 0 INSTALL_FILES_DONE + StrCmp $SKIP_DIALOGS "true" INSTALL_FILES_DONE + MessageBox MB_ABORTRETRYIGNORE $(ErrorSecondLifeInstallRetry) IDABORT INSTALL_FILES_CANCEL IDRETRY INSTALL_FILES_START + # MB_ABORTRETRYIGNORE does not accept IDIGNORE + Goto INSTALL_FILES_DONE + +INSTALL_FILES_CANCEL: + # We are quiting, cleanup. + # Silence warnings from RemoveProgFilesOnInst. + StrCpy $SKIP_DIALOGS "true" + Call RemoveProgFilesOnInst + MessageBox MB_OK $(ErrorSecondLifeInstallSupport) + Quit + +INSTALL_FILES_DONE: + # Pass the installer's language to the client to use as a default StrCpy $SHORTCUT_LANG_PARAM "--set InstallLanguage $(LanguageCode)" @@ -622,7 +640,9 @@ Function RemoveProgFilesOnInst Push $0 StrCpy $0 0 -PREINSTALLREMOVE: +ClearErrors + +PREINSTALL_REMOVE: # Remove old SecondLife.exe to invalidate any old shortcuts to it that may be in non-standard locations. See MAINT-3575 Delete "$INSTDIR\$INSTEXE" @@ -642,17 +662,17 @@ RMDir /r "$INSTDIR\llplugin" IntOp $0 $0 + 1 -IfErrors 0 PREINSTALLDONE - IntCmp $0 1 PREINSTALLREMOVE #try again once - StrCmp $SKIP_DIALOGS "true" PREINSTALLDONE - MessageBox MB_ABORTRETRYIGNORE $(CloseSecondLifeInstRM) IDABORT PREINSTALLFAIL IDRETRY PREINSTALLREMOVE +IfErrors 0 PREINSTALL_DONE + IntCmp $0 1 PREINSTALL_REMOVE #try again once + StrCmp $SKIP_DIALOGS "true" PREINSTALL_DONE + MessageBox MB_ABORTRETRYIGNORE $(CloseSecondLifeInstRM) IDABORT PREINSTALL_FAIL IDRETRY PREINSTALL_REMOVE # MB_ABORTRETRYIGNORE does not accept IDIGNORE - Goto PREINSTALLDONE + Goto PREINSTALL_DONE -PREINSTALLFAIL: +PREINSTALL_FAIL: Quit -PREINSTALLDONE: +PREINSTALL_DONE: # We are no longer including release notes with the viewer, so remove them. Delete "$SMPROGRAMS\$INSTSHORTCUT\SL Release Notes.lnk" diff --git a/indra/newview/installers/windows/lang_da.nsi b/indra/newview/installers/windows/lang_da.nsi index 648ddbfb85..73f23086be 100644 Binary files a/indra/newview/installers/windows/lang_da.nsi and b/indra/newview/installers/windows/lang_da.nsi differ diff --git a/indra/newview/installers/windows/lang_de.nsi b/indra/newview/installers/windows/lang_de.nsi index 188c30197a..7cc70e4c76 100755 --- a/indra/newview/installers/windows/lang_de.nsi +++ b/indra/newview/installers/windows/lang_de.nsi @@ -73,6 +73,10 @@ LangString CloseSecondLifeUnInstMB ${LANG_GERMAN} "Second Life kann nicht entfer ; CheckNetworkConnection LangString CheckNetworkConnectionDP ${LANG_GERMAN} "Prüfe Netzwerkverbindung..." +; error during installation +LangString ErrorSecondLifeInstallRetry ${LANG_GERMAN} "Second Life konnte nicht korrekt installiert werden, einige Dateien wurden eventuell nicht korrekt von der Installationroutine kopiert." +LangString ErrorSecondLifeInstallSupport ${LANG_GERMAN} "Bitte laden Sie den Viewer erneut von https://secondlife.com/support/downloads/ und versuchen Sie die Installation erneut. Sollte das Problem weiterhin bestehen, dann kontaktieren Sie unseren Support unter https://support.secondlife.com." + ; ask to remove user's data files LangString RemoveDataFilesMB ${LANG_GERMAN} "Möchten Sie alle anderen zu Second Life gehörigen Dateien ebenfalls ENTFERNEN?$\n$\nWir empfehlen, die Einstellungen und Cache-Dateien zu behalten, wenn Sie andere Versionen von Second Life installiert haben oder eine Deinstallation durchführen, um Second Life auf eine neuere Version zu aktualisieren." diff --git a/indra/newview/installers/windows/lang_en-us.nsi b/indra/newview/installers/windows/lang_en-us.nsi index 0639d51e10..2eaf97d023 100644 Binary files a/indra/newview/installers/windows/lang_en-us.nsi and b/indra/newview/installers/windows/lang_en-us.nsi differ diff --git a/indra/newview/installers/windows/lang_es.nsi b/indra/newview/installers/windows/lang_es.nsi index ee30651a38..364cc9f67e 100755 Binary files a/indra/newview/installers/windows/lang_es.nsi and b/indra/newview/installers/windows/lang_es.nsi differ diff --git a/indra/newview/installers/windows/lang_fr.nsi b/indra/newview/installers/windows/lang_fr.nsi index 7cd90ec314..2f34c0c87a 100755 Binary files a/indra/newview/installers/windows/lang_fr.nsi and b/indra/newview/installers/windows/lang_fr.nsi differ diff --git a/indra/newview/installers/windows/lang_it.nsi b/indra/newview/installers/windows/lang_it.nsi index 194062da9a..51214d3a9c 100755 Binary files a/indra/newview/installers/windows/lang_it.nsi and b/indra/newview/installers/windows/lang_it.nsi differ diff --git a/indra/newview/installers/windows/lang_ja.nsi b/indra/newview/installers/windows/lang_ja.nsi index a54005ba14..296703d1a3 100755 Binary files a/indra/newview/installers/windows/lang_ja.nsi and b/indra/newview/installers/windows/lang_ja.nsi differ diff --git a/indra/newview/installers/windows/lang_pl.nsi b/indra/newview/installers/windows/lang_pl.nsi index 355d806866..299645bbb7 100644 Binary files a/indra/newview/installers/windows/lang_pl.nsi and b/indra/newview/installers/windows/lang_pl.nsi differ diff --git a/indra/newview/installers/windows/lang_pt-br.nsi b/indra/newview/installers/windows/lang_pt-br.nsi index 97f5d2b44a..542c8654b5 100755 Binary files a/indra/newview/installers/windows/lang_pt-br.nsi and b/indra/newview/installers/windows/lang_pt-br.nsi differ diff --git a/indra/newview/installers/windows/lang_ru.nsi b/indra/newview/installers/windows/lang_ru.nsi index 65a9f4846d..4e53a4957d 100755 Binary files a/indra/newview/installers/windows/lang_ru.nsi and b/indra/newview/installers/windows/lang_ru.nsi differ diff --git a/indra/newview/installers/windows/lang_tr.nsi b/indra/newview/installers/windows/lang_tr.nsi index e71886cc66..bae5029ad1 100755 Binary files a/indra/newview/installers/windows/lang_tr.nsi and b/indra/newview/installers/windows/lang_tr.nsi differ diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsi index f5f0c6cbdf..7922d9df52 100755 Binary files a/indra/newview/installers/windows/lang_zh.nsi and b/indra/newview/installers/windows/lang_zh.nsi differ diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 7f6f6e5997..5250369813 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -96,6 +96,7 @@ #include "stringize.h" #include "boost/foreach.hpp" #include "llcorehttputil.h" +#include "lluiusage.h" using namespace LLAvatarAppearanceDefines; @@ -574,6 +575,8 @@ void LLAgent::ageChat() //----------------------------------------------------------------------------- void LLAgent::moveAt(S32 direction, bool reset) { + LLUIUsage::instance().logCommand("Agent.MoveAt"); + mMoveTimer.reset(); LLFirstUse::notMoving(false); @@ -716,6 +719,9 @@ void LLAgent::moveYaw(F32 mag, bool reset_view) U32 mask = AGENT_CONTROL_YAW_POS | AGENT_CONTROL_YAW_NEG; if ((getControlFlags() & mask) == mask) { + // Rotation into both directions should cancel out + // But keep sending controls to simulator, + // it's needed for script based controls gAgentCamera.setYawKey(0); } @@ -4023,6 +4029,7 @@ void LLAgent::startTeleportRequest() } if (hasPendingTeleportRequest()) { + LLUIUsage::instance().logCommand("Agent.StartTeleportRequest"); mTeleportCanceled.reset(); if (!isMaturityPreferenceSyncedWithServer()) { diff --git a/indra/newview/llagentpicksinfo.h b/indra/newview/llagentpicksinfo.h index f981e08ff7..21df036cb7 100644 --- a/indra/newview/llagentpicksinfo.h +++ b/indra/newview/llagentpicksinfo.h @@ -74,10 +74,10 @@ public: void decrementNumberOfPicks() { --mNumberOfPicks; } -private: - void onServerRespond(LLAvatarPicks* picks); +private: + /** * Sets number of Picks. */ diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 39c9fa1bca..909f32cd21 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -60,6 +60,7 @@ #include "llappviewer.h" #include "llcoros.h" #include "lleventcoro.h" +#include "lluiusage.h" #include "llavatarpropertiesprocessor.h" @@ -1426,6 +1427,9 @@ void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, bool replace, LLPointer cb) { + LL_DEBUGS("UIUsage") << "wearItemsOnAvatar" << LL_ENDL; + LLUIUsage::instance().logCommand("Avatar.WearItem"); + bool first = true; LLInventoryObject::const_object_list_t items_to_link; @@ -2761,6 +2765,7 @@ void LLAppearanceMgr::wearInventoryCategoryOnAvatar( LLInventoryCategory* catego LL_INFOS("Avatar") << self_av_string() << "wearInventoryCategoryOnAvatar '" << category->getName() << "'" << LL_ENDL; + LLUIUsage::instance().logCommand("Avatar.WearCategory"); if (gAgentCamera.cameraCustomizeAvatar()) { @@ -3968,6 +3973,8 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo { if (!isAgentAvatarValid()) return; + LLUIUsage::instance().logCommand("Avatar.CreateNewOutfit"); + LL_DEBUGS("Avatar") << "creating new outfit" << LL_ENDL; gAgentWearables.notifyLoadingStarted(); @@ -4006,6 +4013,9 @@ void LLAppearanceMgr::wearBaseOutfit() void LLAppearanceMgr::removeItemsFromAvatar(const uuid_vec_t& ids_to_remove) { + LL_DEBUGS("UIUsage") << "removeItemsFromAvatar" << LL_ENDL; + LLUIUsage::instance().logCommand("Avatar.RemoveItem"); + if (ids_to_remove.empty()) { LL_WARNS() << "called with empty list, nothing to do" << LL_ENDL; @@ -4485,6 +4495,8 @@ public: "Quick Appearance"); if ( gInventory.getCategory( folder_uuid ) != NULL ) { + // Assume this is coming from the predefined avatars web floater + LLUIUsage::instance().logCommand("Avatar.WearPredefinedAppearance"); LLAppearanceMgr::getInstance()->wearInventoryCategory(category, true, false); // *TODOw: This may not be necessary if initial outfit is chosen already -- josh diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 92a5fe5893..e98e18a96e 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -557,7 +557,7 @@ static void settings_to_globals() gDebugWindowProc = gSavedSettings.getBOOL("DebugWindowProc"); gShowObjectUpdates = gSavedSettings.getBOOL("ShowObjectUpdates"); - LLWorldMapView::sMapScale = gSavedSettings.getF32("MapScale"); + LLWorldMapView::setScaleSetting(gSavedSettings.getF32("MapScale")); #if LL_DARWIN gHiDPISupport = gSavedSettings.getBOOL("RenderHiDPI"); @@ -1450,6 +1450,8 @@ bool LLAppViewer::doFrame() LL_PROFILE_ZONE_NAMED_CATEGORY_APP( "df suspend" ) // give listeners a chance to run llcoro::suspend(); + // if one of our coroutines threw an uncaught exception, rethrow it now + LLCoros::instance().rethrow(); } if (!LLApp::isExiting()) @@ -2490,10 +2492,24 @@ bool LLAppViewer::initConfiguration() //Load settings files list std::string settings_file_list = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings_files.xml"); LLXMLNodePtr root; - BOOL success = LLXMLNode::parseFile(settings_file_list, root, NULL); + BOOL success = LLXMLNode::parseFile(settings_file_list, root, NULL); if (!success) { - LL_ERRS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL; + LL_WARNS() << "Cannot load default configuration file " << settings_file_list << LL_ENDL; + if (gDirUtilp->fileExists(settings_file_list)) + { + LL_ERRS() << "Cannot load default configuration file settings_files.xml. " + << "Please reinstall viewer from https://secondlife.com/support/downloads/ " + << "and contact https://support.secondlife.com if issue persists after reinstall." + << LL_ENDL; + } + else + { + LL_ERRS() << "Default configuration file settings_files.xml not found. " + << "Please reinstall viewer from https://secondlife.com/support/downloads/ " + << "and contact https://support.secondlife.com if issue persists after reinstall." + << LL_ENDL; + } } mSettingsLocationList = new SettingsFiles(); @@ -2716,19 +2732,14 @@ bool LLAppViewer::initConfiguration() if (clp.hasOption("graphicslevel")) { - // User explicitly requested --graphicslevel on the command line. We - // expect this switch has already set RenderQualityPerformance. Check - // that value for validity. - U32 graphicslevel = gSavedSettings.getU32("RenderQualityPerformance"); - if (LLFeatureManager::instance().isValidGraphicsLevel(graphicslevel)) - { - // graphicslevel is valid: save it and engage it later. Capture - // the requested value separately from the settings variable - // because, if this is the first run, LLViewerWindow's constructor - // will call LLFeatureManager::applyRecommendedSettings(), which - // overwrites this settings variable! - mForceGraphicsLevel = graphicslevel; - } + // User explicitly requested --graphicslevel on the command line. We + // expect this switch has already set RenderQualityPerformance. Check + // that value for validity later. + // Capture the requested value separately from the settings variable + // because, if this is the first run, LLViewerWindow's constructor + // will call LLFeatureManager::applyRecommendedSettings(), which + // overwrites this settings variable! + mForceGraphicsLevel = gSavedSettings.getU32("RenderQualityPerformance"); } LLFastTimerView::sAnalyzePerformance = gSavedSettings.getBOOL("AnalyzePerformance"); @@ -3086,7 +3097,7 @@ bool LLAppViewer::initWindow() // Initialize GL stuff // - if (mForceGraphicsLevel) + if (mForceGraphicsLevel && (LLFeatureManager::instance().isValidGraphicsLevel(*mForceGraphicsLevel))) { LLFeatureManager::getInstance()->setGraphicsLevel(*mForceGraphicsLevel, false); gSavedSettings.setU32("RenderQualityPerformance", *mForceGraphicsLevel); @@ -3296,9 +3307,18 @@ LLSD LLAppViewer::getViewerInfo() const info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : "Undefined"; if(LLVoiceClient::getInstance()->voiceEnabled()) { - LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); + const std::string build_version = version.mBuildVersion; std::ostringstream version_string; - version_string << version.serverType << " " << version.serverVersion << std::endl; + if (std::equal(build_version.begin(), build_version.begin() + version.serverVersion.size(), + version.serverVersion.begin())) + { // Normal case: Show type and build version. + version_string << version.serverType << " " << build_version << std::endl; + } + else + { // Mismatch: Show both versions. + version_string << version.serverVersion << "/" << build_version << std::endl; + } info["VOICE_VERSION"] = version_string.str(); } else @@ -3491,7 +3511,7 @@ void LLAppViewer::cleanupSavedSettings() } } - gSavedSettings.setF32("MapScale", LLWorldMapView::sMapScale ); + gSavedSettings.setF32("MapScale", LLWorldMapView::getScaleSetting()); // Some things are cached in LLAgent. if (gAgent.isInitialized()) @@ -4261,6 +4281,15 @@ U32 LLAppViewer::getTextureCacheVersion() return TEXTURE_CACHE_VERSION ; } +//static +U32 LLAppViewer::getDiskCacheVersion() +{ + // Viewer disk cache version intorduced in Simple Cache Viewer, change if the cache format changes. + const U32 DISK_CACHE_VERSION = 1; + + return DISK_CACHE_VERSION ; +} + //static U32 LLAppViewer::getObjectCacheVersion() { @@ -4281,21 +4310,30 @@ bool LLAppViewer::initCache() // initialize the new disk cache using saved settings const std::string cache_dir_name = gSavedSettings.getString("DiskCacheDirName"); + const U32 MB = 1024 * 1024; + const uintmax_t MIN_CACHE_SIZE = 256 * MB; + const uintmax_t MAX_CACHE_SIZE = 9984ll * MB; + const uintmax_t setting_cache_total_size = uintmax_t(gSavedSettings.getU32("CacheSize")) * MB; + const uintmax_t cache_total_size = llclamp(setting_cache_total_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); + const F64 disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); + const F64 texture_cache_percent = 100.0 - disk_cache_percent; + // note that the maximum size of this cache is defined as a percentage of the // total cache size - the 'CacheSize' pref - for all caches. - const unsigned int cache_total_size_mb = gSavedSettings.getU32("CacheSize"); - const double disk_cache_percent = gSavedSettings.getF32("DiskCachePercentOfTotal"); - const unsigned int disk_cache_mb = cache_total_size_mb * disk_cache_percent / 100; - const uintmax_t disk_cache_bytes = disk_cache_mb * 1024 * 1024; + const uintmax_t disk_cache_size = uintmax_t(cache_total_size * disk_cache_percent / 100); const bool enable_cache_debug_info = gSavedSettings.getBOOL("EnableDiskCacheDebugInfo"); bool texture_cache_mismatch = false; + bool remove_vfs_files = false; if (gSavedSettings.getS32("LocalCacheVersion") != LLAppViewer::getTextureCacheVersion()) { texture_cache_mismatch = true; if(!read_only) { gSavedSettings.setS32("LocalCacheVersion", LLAppViewer::getTextureCacheVersion()); + + //texture cache version was bumped up in Simple Cache Viewer, and at this point old vfs files are not needed + remove_vfs_files = true; } } @@ -4337,11 +4375,23 @@ bool LLAppViewer::initCache() } const std::string cache_dir = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, cache_dir_name); - LLDiskCache::initParamSingleton(cache_dir, disk_cache_bytes, enable_cache_debug_info); + LLDiskCache::initParamSingleton(cache_dir, disk_cache_size, enable_cache_debug_info); if (!read_only) { - if (mPurgeCache) + if (gSavedSettings.getS32("DiskCacheVersion") != LLAppViewer::getDiskCacheVersion()) + { + LLDiskCache::getInstance()->clearCache(); + remove_vfs_files = true; + gSavedSettings.setS32("DiskCacheVersion", LLAppViewer::getDiskCacheVersion()); + } + + if (remove_vfs_files) + { + LLDiskCache::getInstance()->removeOldVFSFiles(); + } + + if (mPurgeCache) { LLSplashScreen::update(LLTrans::getString("StartupClearingCache")); purgeCache(); @@ -4360,18 +4410,10 @@ bool LLAppViewer::initCache() LLSplashScreen::update(LLTrans::getString("StartupInitializingTextureCache")); // Init the texture cache - // Allocate 80% of the cache size for textures - const S32 MB = 1024 * 1024; - const S64 MIN_CACHE_SIZE = 256 * MB; - const S64 MAX_CACHE_SIZE = 9984ll * MB; + // Allocate the remaining percent which is not allocated to the disk cache + const S64 texture_cache_size = S64(cache_total_size * texture_cache_percent / 100); - S64 cache_size = (S64)(gSavedSettings.getU32("CacheSize")) * MB; - cache_size = llclamp(cache_size, MIN_CACHE_SIZE, MAX_CACHE_SIZE); - - S64 texture_cache_size = cache_size; - - S64 extra = LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); - texture_cache_size -= extra; + LLAppViewer::getTextureCache()->initCache(LL_PATH_CACHE, texture_cache_size, texture_cache_mismatch); LLVOCache::getInstance()->initCache(LL_PATH_CACHE, gSavedSettings.getU32("CacheNumberOfRegionsForObjects"), getObjectCacheVersion()); @@ -5061,8 +5103,7 @@ void LLAppViewer::idle() audio_update_wind(false); // this line actually commits the changes we've made to source positions, etc. - const F32 max_audio_decode_time = 0.002f; // 2 ms decode time - gAudiop->idle(max_audio_decode_time); + gAudiop->idle(); } } @@ -5423,7 +5464,7 @@ void LLAppViewer::disconnectViewer() gFloaterView->restoreAll(); } - if (LLSelectMgr::getInstance()) + if (LLSelectMgr::instanceExists()) { LLSelectMgr::getInstance()->deselectAll(); } diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 7ab21f35cd..f28a90c703 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -130,6 +130,7 @@ public: static U32 getTextureCacheVersion() ; static U32 getObjectCacheVersion() ; + static U32 getDiskCacheVersion() ; const std::string& getSerialNumber() { return mSerialNumber; } @@ -153,16 +154,16 @@ public: void removeMarkerFiles(); void removeDumpDir(); - // LLAppViewer testing helpers. - // *NOTE: These will potentially crash the viewer. Only for debugging. - virtual void forceErrorLLError(); - virtual void forceErrorBreakpoint(); - virtual void forceErrorBadMemoryAccess(); - virtual void forceErrorInfiniteLoop(); - virtual void forceErrorSoftwareException(); - virtual void forceErrorDriverCrash(); - virtual void forceErrorCoroutineCrash(); - virtual void forceErrorThreadCrash(); + // LLAppViewer testing helpers. + // *NOTE: These will potentially crash the viewer. Only for debugging. + virtual void forceErrorLLError(); + virtual void forceErrorBreakpoint(); + virtual void forceErrorBadMemoryAccess(); + virtual void forceErrorInfiniteLoop(); + virtual void forceErrorSoftwareException(); + virtual void forceErrorDriverCrash(); + virtual void forceErrorCoroutineCrash(); + virtual void forceErrorThreadCrash(); // The list is found in app_settings/settings_files.xml // but since they are used explicitly in code, diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 1797d2dd6e..3e450e6dec 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -48,6 +48,7 @@ #include "llfloatergroups.h" #include "llfloaterreg.h" #include "llfloaterpay.h" +#include "llfloaterprofile.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterwebcontent.h" #include "llfloaterworldmap.h" @@ -62,11 +63,14 @@ #include "llnotificationsutil.h" // for LLNotificationsUtil #include "llpaneloutfitedit.h" #include "llpanelprofile.h" +#include "llparcel.h" #include "llrecentpeople.h" #include "lltrans.h" #include "llviewercontrol.h" #include "llviewerobjectlist.h" #include "llviewermessage.h" // for handle_lure +#include "llviewernetwork.h" //LLGridManager +#include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "lltrans.h" #include "llcallingcard.h" @@ -74,6 +78,7 @@ #include "llsidepanelinventory.h" #include "llavatarname.h" #include "llagentui.h" +#include "lluiusage.h" // Flags for kick message const U32 KICK_FLAGS_DEFAULT = 0x0; @@ -81,6 +86,19 @@ const U32 KICK_FLAGS_FREEZE = 1 << 0; const U32 KICK_FLAGS_UNFREEZE = 1 << 1; +std::string getProfileURL(const std::string& agent_name, bool feed_only) +{ + std::string url = "[WEB_PROFILE_URL][AGENT_NAME][FEED_ONLY]"; + LLSD subs; + subs["WEB_PROFILE_URL"] = LLGridManager::getInstance()->getWebProfileURL(); + subs["AGENT_NAME"] = agent_name; + subs["FEED_ONLY"] = feed_only ? "/?feed_only=true" : ""; + url = LLWeb::expandURLSubstitutions(url, subs); + LLStringUtil::toLower(url); + return url; +} + + // static void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::string& name) { @@ -96,7 +114,7 @@ void LLAvatarActions::requestFriendshipDialog(const LLUUID& id, const std::strin payload["id"] = id; payload["name"] = name; - LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage); + LLNotificationsUtil::add("AddFriendWithMessage", args, payload, &callbackAddFriendWithMessage); // add friend to recent people list LLRecentPeople::instance().add(id); @@ -316,57 +334,144 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids, const LLUUID& float make_ui_sound("UISndStartIM"); } -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) -{ - std::string url = getProfileURL(av_name.getAccountName()); - - // 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) +void LLAvatarActions::showProfile(const LLUUID& avatar_id) { - if (id.notNull()) + if (avatar_id.notNull()) { - LLAvatarNameCache::get(id, boost::bind(&on_avatar_name_show_profile, _1, _2)); + LLFloaterReg::showInstance("profile", LLSD().with("id", avatar_id)); } } +// static +void LLAvatarActions::showPicks(const LLUUID& avatar_id) +{ + if (avatar_id.notNull()) + { + LLFloaterProfile* profilefloater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", avatar_id))); + if (profilefloater) + { + profilefloater->showPick(); + } + } +} + +// static +void LLAvatarActions::showPick(const LLUUID& avatar_id, const LLUUID& pick_id) +{ + if (avatar_id.notNull()) + { + LLFloaterProfile* profilefloater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", avatar_id))); + if (profilefloater) + { + profilefloater->showPick(pick_id); + } + } +} + +// static +void LLAvatarActions::createPick() +{ + LLFloaterProfile* profilefloater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", gAgent.getID()))); + LLViewerRegion* region = gAgent.getRegion(); + if (profilefloater && region) + { + LLPickData data; + data.pos_global = gAgent.getPositionGlobal(); + data.sim_name = region->getName(); + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel) + { + data.name = parcel->getName(); + data.desc = parcel->getDesc(); + data.snapshot_id = parcel->getSnapshotID(); + data.parcel_id = parcel->getID(); + } + else + { + data.name = region->getName(); + } + + profilefloater->createPick(data); + } +} + +// static +bool LLAvatarActions::isPickTabSelected(const LLUUID& avatar_id) +{ + if (avatar_id.notNull()) + { + LLFloaterProfile* profilefloater = LLFloaterReg::findTypedInstance("profile", LLSD().with("id", avatar_id)); + if (profilefloater) + { + return profilefloater->isPickTabSelected(); + } + } + return false; +} + +// static +void LLAvatarActions::showClassifieds(const LLUUID& avatar_id) +{ + if (avatar_id.notNull()) + { + LLFloaterProfile* profilefloater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", avatar_id))); + if (profilefloater) + { + profilefloater->showClassified(); + } + } +} + +// static +void LLAvatarActions::showClassified(const LLUUID& avatar_id, const LLUUID& classified_id, bool edit) +{ + if (avatar_id.notNull()) + { + LLFloaterProfile* profilefloater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", avatar_id))); + if (profilefloater) + { + profilefloater->showClassified(classified_id, edit); + } + } +} + +// static +void LLAvatarActions::createClassified() +{ + LLFloaterProfile* profilefloater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", gAgent.getID()))); + if (profilefloater) + { + profilefloater->createClassified(); + } +} + //static -bool LLAvatarActions::profileVisible(const LLUUID& id) +bool LLAvatarActions::profileVisible(const LLUUID& avatar_id) { LLSD sd; - sd["id"] = id; - LLFloater* browser = getProfileFloater(id); - return browser && browser->isShown(); + sd["id"] = avatar_id; + LLFloater* floater = getProfileFloater(avatar_id); + return floater && floater->isShown(); } //static -LLFloater* LLAvatarActions::getProfileFloater(const LLUUID& id) +LLFloater* LLAvatarActions::getProfileFloater(const LLUUID& avatar_id) { - LLFloaterWebContent *browser = dynamic_cast - (LLFloaterReg::findInstance(get_profile_floater_name(id), LLSD().with("id", id))); - return browser; + LLFloaterProfile* floater = LLFloaterReg::findTypedInstance("profile", LLSD().with("id", avatar_id)); + return floater; } //static -void LLAvatarActions::hideProfile(const LLUUID& id) +void LLAvatarActions::hideProfile(const LLUUID& avatar_id) { LLSD sd; - sd["id"] = id; - LLFloater* browser = getProfileFloater(id); - if (browser) + sd["id"] = avatar_id; + LLFloater* floater = getProfileFloater(avatar_id); + if (floater) { - browser->closeFloater(); + floater->closeFloater(); } } @@ -990,7 +1095,7 @@ bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NUL } // static -void LLAvatarActions::toggleBlock(const LLUUID& id) +bool LLAvatarActions::toggleBlock(const LLUUID& id) { LLAvatarName av_name; LLAvatarNameCache::get(id, &av_name); @@ -1000,10 +1105,12 @@ void LLAvatarActions::toggleBlock(const LLUUID& id) if (LLMuteList::getInstance()->isMuted(mute.mID, mute.mName)) { LLMuteList::getInstance()->remove(mute); + return false; } else { LLMuteList::getInstance()->add(mute); + return true; } } @@ -1312,6 +1419,8 @@ bool LLAvatarActions::handleUnfreeze(const LLSD& notification, const LLSD& respo 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); + LLUIUsage::instance().logCommand("Agent.SendFriendRequest"); + send_improved_im(target_id, target_name, message, diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 7c721076c8..86183cc119 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -38,6 +38,8 @@ class LLInventoryPanel; class LLFloater; class LLView; +std::string getProfileURL(const std::string& agent_name, bool feed_only = false); + /** * Friend-related actions (add, remove, offer teleport, etc) */ @@ -91,13 +93,20 @@ public: */ static void startConference(const uuid_vec_t& ids, const LLUUID& floater_id = LLUUID::null); - /** - * 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 profile. + */ + static void showProfile(const LLUUID& avatar_id); + static void showPicks(const LLUUID& avatar_id); + static void showPick(const LLUUID& avatar_id, const LLUUID& pick_id); + static void createPick(); + static void showClassifieds(const LLUUID& avatar_id); + static void showClassified(const LLUUID& avatar_id, const LLUUID& classified_id, bool edit = false); + static void createClassified(); + static void hideProfile(const LLUUID& avatar_id); + static bool profileVisible(const LLUUID& avatar_id); + static bool isPickTabSelected(const LLUUID& avatar_id); + static LLFloater* getProfileFloater(const LLUUID& avatar_id); /** * Show avatar on world map. @@ -126,9 +135,10 @@ public: static void shareWithAvatars(LLView * panel); /** - * Block/unblock the avatar. + * Block/unblock the avatar by id. + * Returns true if blocked, returns false if unblocked */ - static void toggleBlock(const LLUUID& id); + static bool toggleBlock(const LLUUID& id); /** * Mute/unmute avatar. diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index b0715a3afd..c0990d9d11 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -241,21 +241,6 @@ void LLAvatarList::setDirty(bool val /*= true*/, bool force_refresh /*= false*/) } } -void LLAvatarList::addAvalineItem(const LLUUID& item_id, const LLUUID& session_id, const std::string& item_name) -{ - LL_DEBUGS("Avaline") << "Adding avaline item into the list: " << item_name << "|" << item_id << ", session: " << session_id << LL_ENDL; - LLAvalineListItem* item = new LLAvalineListItem(/*hide_number=*/false); - item->setAvatarId(item_id, session_id, true, false); - item->setName(item_name); - item->showLastInteractionTime(mShowLastInteractionTime); - item->showSpeakingIndicator(mShowSpeakingIndicator); - item->setOnline(false); - - addItem(item, item_id); - mIDs.push_back(item_id); - sort(); -} - ////////////////////////////////////////////////////////////////////////// // PROTECTED SECTION ////////////////////////////////////////////////////////////////////////// @@ -296,18 +281,10 @@ void LLAvatarList::refresh() { // *NOTE: If you change the UI to show a different string, // be sure to change the filter code below. - if (LLRecentPeople::instance().isAvalineCaller(buddy_id)) - { - const LLSD& call_data = LLRecentPeople::instance().getData(buddy_id); - addAvalineItem(buddy_id, call_data["session_id"].asUUID(), call_data["call_number"].asString()); - } - else - { - std::string display_name = getAvatarName(av_name); - addNewItem(buddy_id, - display_name.empty() ? waiting_str : display_name, - LLAvatarTracker::instance().isBuddyOnline(buddy_id)); - } + std::string display_name = getAvatarName(av_name); + addNewItem(buddy_id, + display_name.empty() ? waiting_str : display_name, + LLAvatarTracker::instance().isBuddyOnline(buddy_id)); modified = true; nadded++; @@ -463,7 +440,7 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, BOOL is BOOL LLAvatarList::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); - if ( mContextMenu && !isAvalineItemSelected()) + if ( mContextMenu) { uuid_vec_t selected_uuids; getSelectedUUIDs(selected_uuids); @@ -523,21 +500,6 @@ BOOL LLAvatarList::handleHover(S32 x, S32 y, MASK mask) return handled; } -bool LLAvatarList::isAvalineItemSelected() -{ - std::vector selected_items; - getSelectedItems(selected_items); - std::vector::iterator it = selected_items.begin(); - - for(; it != selected_items.end(); ++it) - { - if (dynamic_cast(*it)) - return true; - } - - return false; -} - void LLAvatarList::setVisible(BOOL visible) { if ( visible == FALSE && mContextMenu ) @@ -626,63 +588,3 @@ bool LLAvatarItemAgentOnTopComparator::doCompare(const LLAvatarListItem* avatar_ } return LLAvatarItemNameComparator::doCompare(avatar_item1,avatar_item2); } - -/************************************************************************/ -/* class LLAvalineListItem */ -/************************************************************************/ -LLAvalineListItem::LLAvalineListItem(bool hide_number/* = true*/) : LLAvatarListItem(false) -, mIsHideNumber(hide_number) -{ - // should not use buildPanel from the base class to ensure LLAvalineListItem::postBuild is called. - buildFromFile( "panel_avatar_list_item.xml"); -} - -BOOL LLAvalineListItem::postBuild() -{ - BOOL rv = LLAvatarListItem::postBuild(); - - if (rv) - { - setOnline(true); - showLastInteractionTime(false); - setShowProfileBtn(false); - setShowInfoBtn(false); - mAvatarIcon->setValue("Avaline_Icon"); - mAvatarIcon->setToolTip(std::string("")); - } - return rv; -} - -// to work correctly this method should be called AFTER setAvatarId for avaline callers with hidden phone number -void LLAvalineListItem::setName(const std::string& name) -{ - if (mIsHideNumber) - { - static U32 order = 0; - typedef std::map avaline_callers_nums_t; - static avaline_callers_nums_t mAvalineCallersNums; - - llassert(getAvatarId() != LLUUID::null); - - const LLUUID &uuid = getAvatarId(); - - if (mAvalineCallersNums.find(uuid) == mAvalineCallersNums.end()) - { - mAvalineCallersNums[uuid] = ++order; - LL_DEBUGS("Avaline") << "Set name for new avaline caller: " << uuid << ", order: " << order << LL_ENDL; - } - LLStringUtil::format_map_t args; - args["[ORDER]"] = llformat("%u", mAvalineCallersNums[uuid]); - std::string hidden_name = LLTrans::getString("AvalineCaller", args); - - LL_DEBUGS("Avaline") << "Avaline caller: " << uuid << ", name: " << hidden_name << LL_ENDL; - LLAvatarListItem::setAvatarName(hidden_name); - LLAvatarListItem::setAvatarToolTip(hidden_name); - } - else - { - const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name); - LLAvatarListItem::setAvatarName(formatted_phone); - LLAvatarListItem::setAvatarToolTip(formatted_phone); - } -} diff --git a/indra/newview/llavatarlist.h b/indra/newview/llavatarlist.h index 1a672c279b..48b0e70454 100644 --- a/indra/newview/llavatarlist.h +++ b/indra/newview/llavatarlist.h @@ -98,7 +98,6 @@ public: virtual S32 notifyParent(const LLSD& info); - void addAvalineItem(const LLUUID& item_id, const LLUUID& session_id, const std::string& item_name); void handleDisplayNamesOptionChanged(); void setShowCompleteName(bool show) { mShowCompleteName = show;}; @@ -118,8 +117,6 @@ protected: private: - bool isAvalineItemSelected(); - bool mIgnoreOnlineStatus; bool mShowLastInteractionTime; bool mDirty; @@ -189,27 +186,4 @@ protected: virtual bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const; }; -/** - * Represents Avaline caller in Avatar list in Voice Control Panel and group chats. - */ -class LLAvalineListItem : public LLAvatarListItem -{ -public: - - /** - * Constructor - * - * @param hide_number - flag indicating if number should be hidden. - * In this case It will be shown as "Avaline Caller 1", "Avaline Caller 1", etc. - */ - LLAvalineListItem(bool hide_number = true); - - /*virtual*/ BOOL postBuild(); - - /*virtual*/ void setName(const std::string& name); - -private: - bool mIsHideNumber; -}; - #endif // LL_LLAVATARLIST_H diff --git a/indra/newview/llavatarpropertiesprocessor.cpp b/indra/newview/llavatarpropertiesprocessor.cpp index f41eb3daf4..dd0d06a8c8 100644 --- a/indra/newview/llavatarpropertiesprocessor.cpp +++ b/indra/newview/llavatarpropertiesprocessor.cpp @@ -36,6 +36,7 @@ #include "llstartup.h" // Linden library includes +#include "llavataractions.h" // for getProfileUrl #include "lldate.h" #include "lltrans.h" #include "llui.h" // LLUI::getLanguage() @@ -94,54 +95,98 @@ void LLAvatarPropertiesProcessor::removeObserver(const LLUUID& avatar_id, LLAvat } } - -void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string method) +void LLAvatarPropertiesProcessor::sendRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method) { + // this is the startup state when send_complete_agent_movement() message is sent. + // Before this messages won't work so don't bother trying + if (LLStartUp::getStartupState() <= STATE_AGENT_SEND) + { + return; + } + + if (avatar_id.isNull()) + { + return; + } + // Suppress duplicate requests while waiting for a response from the network if (isPendingRequest(avatar_id, type)) { // waiting for a response, don't re-request return; } - // indicate we're going to make a request - addPendingRequest(avatar_id, type); - std::vector strings; - strings.push_back( avatar_id.asString() ); - send_generic_message(method, strings); + std::string cap; + + switch (type) + { + case APT_PROPERTIES: + // indicate we're going to make a request + sendAvatarPropertiesRequestMessage(avatar_id); + // can use getRegionCapability("AgentProfile"), but it is heavy + // initAgentProfileCapRequest(avatar_id, cap); + break; + case APT_PICKS: + case APT_GROUPS: + case APT_NOTES: + if (cap.empty()) + { + // indicate we're going to make a request + sendGenericRequest(avatar_id, type, method); + } + else + { + initAgentProfileCapRequest(avatar_id, cap); + } + break; + default: + sendGenericRequest(avatar_id, type, method); + break; + } +} + +void LLAvatarPropertiesProcessor::sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method) +{ + // indicate we're going to make a request + addPendingRequest(avatar_id, type); + + std::vector strings; + strings.push_back(avatar_id.asString()); + send_generic_message(method, strings); +} + +void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequestMessage(const LLUUID& avatar_id) +{ + addPendingRequest(avatar_id, APT_PROPERTIES); + + LLMessageSystem *msg = gMessageSystem; + + msg->newMessageFast(_PREHASH_AvatarPropertiesRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->addUUIDFast(_PREHASH_AvatarID, avatar_id); + gAgent.sendReliableMessage(); +} + +void LLAvatarPropertiesProcessor::initAgentProfileCapRequest(const LLUUID& avatar_id, const std::string& cap_url) +{ + addPendingRequest(avatar_id, APT_PROPERTIES); + addPendingRequest(avatar_id, APT_PICKS); + addPendingRequest(avatar_id, APT_GROUPS); + addPendingRequest(avatar_id, APT_NOTES); + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(requestAvatarPropertiesCoro, cap_url, avatar_id)); } void LLAvatarPropertiesProcessor::sendAvatarPropertiesRequest(const LLUUID& avatar_id) { - // this is the startup state when send_complete_agent_movement() message is sent. - // Before this, the AvatarPropertiesRequest message - // won't work so don't bother trying - if (LLStartUp::getStartupState() <= STATE_AGENT_SEND) - { - return; - } - - if (isPendingRequest(avatar_id, APT_PROPERTIES)) - { - // waiting for a response, don't re-request - return; - } - // indicate we're going to make a request - addPendingRequest(avatar_id, APT_PROPERTIES); - - LLMessageSystem *msg = gMessageSystem; - - msg->newMessageFast(_PREHASH_AvatarPropertiesRequest); - msg->nextBlockFast( _PREHASH_AgentData); - msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() ); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->addUUIDFast( _PREHASH_AvatarID, avatar_id); - gAgent.sendReliableMessage(); + sendRequest(avatar_id, APT_PROPERTIES, "AvatarPropertiesRequest"); } void LLAvatarPropertiesProcessor::sendAvatarPicksRequest(const LLUUID& avatar_id) { - sendGenericRequest(avatar_id, APT_PICKS, "avatarpicksrequest"); + sendGenericRequest(avatar_id, APT_PICKS, "avatarpicksrequest"); } void LLAvatarPropertiesProcessor::sendAvatarNotesRequest(const LLUUID& avatar_id) @@ -174,7 +219,7 @@ void LLAvatarPropertiesProcessor::sendAvatarPropertiesUpdate(const LLAvatarData* return; } - LL_INFOS() << "Sending avatarinfo update" << LL_ENDL; + LL_WARNS() << "Sending avatarinfo update. This trims profile descriptions!!!" << LL_ENDL; // This value is required by sendAvatarPropertiesUpdate method. //A profile should never be mature. (From the original code) @@ -266,6 +311,113 @@ bool LLAvatarPropertiesProcessor::hasPaymentInfoOnFile(const LLAvatarData* avata return ((avatar_data->flags & AVATAR_TRANSACTED) || (avatar_data->flags & AVATAR_IDENTIFIED)); } +// static +void LLAvatarPropertiesProcessor::requestAvatarPropertiesCoro(std::string cap_url, LLUUID agent_id) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("requestAvatarPropertiesCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + httpOpts->setFollowRedirects(true); + + std::string finalUrl = cap_url + "/" + agent_id.asString(); + + LLSD result = httpAdapter->getAndSuspend(httpRequest, finalUrl, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status + || !result.has("id") + || agent_id != result["id"].asUUID()) + { + LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << agent_id << LL_ENDL; + LLAvatarPropertiesProcessor* self = getInstance(); + self->removePendingRequest(agent_id, APT_PROPERTIES); + self->removePendingRequest(agent_id, APT_PICKS); + self->removePendingRequest(agent_id, APT_GROUPS); + self->removePendingRequest(agent_id, APT_NOTES); + return; + } + + // Avatar Data + + LLAvatarData avatar_data; + std::string birth_date; + + avatar_data.agent_id = agent_id; + avatar_data.avatar_id = agent_id; + avatar_data.image_id = result["sl_image_id"].asUUID(); + avatar_data.fl_image_id = result["fl_image_id"].asUUID(); + avatar_data.partner_id = result["partner_id"].asUUID(); + avatar_data.about_text = result["sl_about_text"].asString(); + avatar_data.fl_about_text = result["fl_about_text"].asString(); + avatar_data.born_on = result["member_since"].asDate(); + avatar_data.profile_url = getProfileURL(agent_id.asString()); + + avatar_data.flags = 0; + avatar_data.caption_index = 0; + + LLAvatarPropertiesProcessor* self = getInstance(); + // Request processed, no longer pending + self->removePendingRequest(agent_id, APT_PROPERTIES); + self->notifyObservers(agent_id, &avatar_data, APT_PROPERTIES); + + // Picks + + LLSD picks_array = result["picks"]; + LLAvatarPicks avatar_picks; + avatar_picks.agent_id = agent_id; // Not in use? + avatar_picks.target_id = agent_id; + + for (LLSD::array_const_iterator it = picks_array.beginArray(); it != picks_array.endArray(); ++it) + { + const LLSD& pick_data = *it; + avatar_picks.picks_list.emplace_back(pick_data["id"].asUUID(), pick_data["name"].asString()); + } + + // Request processed, no longer pending + self->removePendingRequest(agent_id, APT_PICKS); + self->notifyObservers(agent_id, &avatar_picks, APT_PICKS); + + // Groups + + LLSD groups_array = result["groups"]; + LLAvatarGroups avatar_groups; + avatar_groups.agent_id = agent_id; // Not in use? + avatar_groups.avatar_id = agent_id; // target_id + + for (LLSD::array_const_iterator it = groups_array.beginArray(); it != groups_array.endArray(); ++it) + { + const LLSD& group_info = *it; + LLAvatarGroups::LLGroupData group_data; + group_data.group_powers = 0; // Not in use? + group_data.group_title = group_info["name"].asString(); // Missing data, not in use? + group_data.group_id = group_info["id"].asUUID(); + group_data.group_name = group_info["name"].asString(); + group_data.group_insignia_id = group_info["image_id"].asUUID(); + + avatar_groups.group_list.push_back(group_data); + } + + self->removePendingRequest(agent_id, APT_GROUPS); + self->notifyObservers(agent_id, &avatar_groups, APT_GROUPS); + + // Notes + LLAvatarNotes avatar_notes; + + avatar_notes.agent_id = agent_id; + avatar_notes.target_id = agent_id; + avatar_notes.notes = result["notes"].asString(); + + // Request processed, no longer pending + self->removePendingRequest(agent_id, APT_NOTES); + self->notifyObservers(agent_id, &avatar_notes, APT_NOTES); +} + void LLAvatarPropertiesProcessor::processAvatarPropertiesReply(LLMessageSystem* msg, void**) { LLAvatarData avatar_data; @@ -312,6 +464,21 @@ void LLAvatarPropertiesProcessor::processAvatarInterestsReply(LLMessageSystem* m That will suppress the warnings and be compatible with old server versions. WARNING: LLTemplateMessageReader::decodeData: Message from 216.82.37.237:13000 with no handler function received: AvatarInterestsReply */ + + LLInterestsData interests_data; + + msg->getUUIDFast( _PREHASH_AgentData, _PREHASH_AgentID, interests_data.agent_id ); + msg->getUUIDFast( _PREHASH_AgentData, _PREHASH_AvatarID, interests_data.avatar_id ); + msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_WantToMask, interests_data.want_to_mask ); + msg->getStringFast( _PREHASH_PropertiesData, _PREHASH_WantToText, interests_data.want_to_text ); + msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_SkillsMask, interests_data.skills_mask ); + msg->getStringFast( _PREHASH_PropertiesData, _PREHASH_SkillsText, interests_data.skills_text ); + msg->getString( _PREHASH_PropertiesData, _PREHASH_LanguagesText, interests_data.languages_text ); + + LLAvatarPropertiesProcessor* self = getInstance(); + // Request processed, no longer pending + self->removePendingRequest(interests_data.avatar_id, APT_INTERESTS_INFO); + self->notifyObservers(interests_data.avatar_id, &interests_data, APT_INTERESTS_INFO); } void LLAvatarPropertiesProcessor::processAvatarClassifiedsReply(LLMessageSystem* msg, void**) @@ -385,7 +552,7 @@ void LLAvatarPropertiesProcessor::processAvatarNotesReply(LLMessageSystem* msg, void LLAvatarPropertiesProcessor::processAvatarPicksReply(LLMessageSystem* msg, void**) { LLAvatarPicks avatar_picks; - msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, avatar_picks.target_id); + msg->getUUID(_PREHASH_AgentData, _PREHASH_AgentID, avatar_picks.agent_id); msg->getUUID(_PREHASH_AgentData, _PREHASH_TargetID, avatar_picks.target_id); S32 block_count = msg->getNumberOfBlocks(_PREHASH_Data); @@ -551,6 +718,29 @@ void LLAvatarPropertiesProcessor::sendClassifiedDelete(const LLUUID& classified_ gAgent.sendReliableMessage(); } +void LLAvatarPropertiesProcessor::sendInterestsInfoUpdate(const LLInterestsData* interests_data) +{ + if(!interests_data) + { + return; + } + + LLMessageSystem* msg = gMessageSystem; + + msg->newMessage(_PREHASH_AvatarInterestsUpdate); + msg->nextBlockFast( _PREHASH_AgentData); + msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() ); + msg->addUUIDFast( _PREHASH_SessionID, gAgent.getSessionID() ); + msg->nextBlockFast( _PREHASH_PropertiesData); + msg->addU32Fast( _PREHASH_WantToMask, interests_data->want_to_mask); + msg->addStringFast( _PREHASH_WantToText, interests_data->want_to_text); + msg->addU32Fast( _PREHASH_SkillsMask, interests_data->skills_mask); + msg->addStringFast( _PREHASH_SkillsText, interests_data->skills_text); + msg->addString( _PREHASH_LanguagesText, interests_data->languages_text); + + gAgent.sendReliableMessage(); +} + void LLAvatarPropertiesProcessor::sendPickInfoUpdate(const LLPickData* new_pick) { if (!new_pick) return; diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index b063048c26..f778634d25 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -56,10 +56,22 @@ enum EAvatarProcessorType APT_PICKS, APT_PICK_INFO, APT_TEXTURES, + APT_INTERESTS_INFO, APT_CLASSIFIEDS, APT_CLASSIFIED_INFO }; +struct LLInterestsData +{ + LLUUID agent_id; + LLUUID avatar_id; //target id + U32 want_to_mask; + std::string want_to_text; + U32 skills_mask; + std::string skills_text; + std::string languages_text; +}; + struct LLAvatarData { LLUUID agent_id; @@ -223,6 +235,8 @@ public: void sendClassifiedDelete(const LLUUID& classified_id); + void sendInterestsInfoUpdate(const LLInterestsData* interests_data); + // Returns translated, human readable string for account type, such // as "Resident" or "Linden Employee". Used for profiles, inspectors. static std::string accountType(const LLAvatarData* avatar_data); @@ -234,6 +248,8 @@ public: static bool hasPaymentInfoOnFile(const LLAvatarData* avatar_data); + static void requestAvatarPropertiesCoro(std::string cap_url, LLUUID agent_id); + static void processAvatarPropertiesReply(LLMessageSystem* msg, void**); static void processAvatarInterestsReply(LLMessageSystem* msg, void**); @@ -252,7 +268,10 @@ public: protected: - void sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string method); + void sendRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method); + void sendGenericRequest(const LLUUID& avatar_id, EAvatarProcessorType type, const std::string &method); + void sendAvatarPropertiesRequestMessage(const LLUUID& avatar_id); + void initAgentProfileCapRequest(const LLUUID& avatar_id, const std::string& cap_url); void notifyObservers(const LLUUID& id,void* data, EAvatarProcessorType type); diff --git a/indra/newview/llavatarrenderinfoaccountant.cpp b/indra/newview/llavatarrenderinfoaccountant.cpp index fe94cd27b6..275f17b02a 100644 --- a/indra/newview/llavatarrenderinfoaccountant.cpp +++ b/indra/newview/llavatarrenderinfoaccountant.cpp @@ -320,9 +320,16 @@ void LLAvatarRenderInfoAccountant::sendRenderInfoToRegion(LLViewerRegion * regio // make sure we won't re-report, coro will update timer with correct time later regionp->getRenderInfoReportTimer().resetWithExpiry(SECS_BETWEEN_REGION_REPORTS); - std::string coroname = - LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro", - boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro, url, regionp->getHandle())); + try + { + std::string coroname = + LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro", + boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoReportCoro, url, regionp->getHandle())); + } + catch (std::bad_alloc&) + { + LL_ERRS() << "LLCoros::launch() allocation failure" << LL_ENDL; + } } } @@ -343,10 +350,17 @@ void LLAvatarRenderInfoAccountant::getRenderInfoFromRegion(LLViewerRegion * regi // make sure we won't re-request, coro will update timer with correct time later regionp->getRenderInfoRequestTimer().resetWithExpiry(SECS_BETWEEN_REGION_REQUEST); - // First send a request to get the latest data - std::string coroname = - LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro", - boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro, url, regionp->getHandle())); + try + { + // First send a request to get the latest data + std::string coroname = + LLCoros::instance().launch("LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro", + boost::bind(&LLAvatarRenderInfoAccountant::avatarRenderInfoGetCoro, url, regionp->getHandle())); + } + catch (std::bad_alloc&) + { + LL_ERRS() << "LLCoros::launch() allocation failure" << LL_ENDL; + } } } diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 8d1e9a438e..1ad2157df0 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -53,6 +53,7 @@ #include "llviewerobjectlist.h" #include "llvoavatar.h" #include "llavataractions.h" +#include "lluiusage.h" ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs @@ -294,6 +295,8 @@ void LLAvatarTracker::copyBuddyList(buddy_map_t& buddies) const void LLAvatarTracker::terminateBuddy(const LLUUID& id) { LL_DEBUGS() << "LLAvatarTracker::terminateBuddy()" << LL_ENDL; + LLUIUsage::instance().logCommand("Agent.TerminateFriendship"); + LLRelationship* buddy = get_ptr_in_map(mBuddyInfo, id); if(!buddy) return; mBuddyInfo.erase(id); @@ -640,6 +643,7 @@ void LLAvatarTracker::processChange(LLMessageSystem* msg) if(mBuddyInfo.find(agent_related) != mBuddyInfo.end()) { (mBuddyInfo[agent_related])->setRightsTo(new_rights); + mChangedBuddyIDs.insert(agent_related); } } else diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index e400609a74..72f667a0b8 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -568,8 +568,6 @@ void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL // how to chat gWarningSettings.setBOOL("FirstOtherChatBeforeUser", FALSE); - LLUIUsage::instance().logCommand("Chat.Send"); // Pseudo-command - // Look for "/20 foo" channel chats. S32 channel = 0; LLWString out_text = stripChannelNumber(wtext, &channel); diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index bdd516e1de..7ff24f64ac 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -48,6 +48,7 @@ #include "llspeakers.h" //for LLIMSpeakerMgr #include "lltrans.h" #include "llfloaterreg.h" +#include "llfloaterreporter.h" #include "llfloatersidepanelcontainer.h" #include "llmutelist.h" #include "llstylemap.h" @@ -118,6 +119,7 @@ public: mSourceType(CHAT_SOURCE_UNKNOWN), mFrom(), mSessionID(), + mCreationTime(time_corrected()), mMinUserNameWidth(0), mUserNameFont(NULL), mUserNameTextBox(NULL), @@ -403,6 +405,48 @@ public: { LLAvatarActions::pay(getAvatarId()); } + else if (level == "report_abuse") + { + std::string time_string; + if (mTime > 0) // have frame time + { + time_t current_time = time_corrected(); + time_t message_time = current_time - LLFrameTimer::getElapsedSeconds() + mTime; + + time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] [" + + LLTrans::getString("TimeHour") + "]:[" + + LLTrans::getString("TimeMin") + "]"; + + LLSD substitution; + + substitution["datetime"] = (S32)message_time; + LLStringUtil::format(time_string, substitution); + } + else + { + // From history. This might be empty or not full. + // See LLChatLogParser::parse + time_string = getChild("time_box")->getValue().asString(); + + // Just add current date if not full. + // Should be fine since both times are supposed to be stl + if (!time_string.empty() && time_string.size() < 7) + { + time_string = "[" + LLTrans::getString("TimeMonth") + "]/[" + + LLTrans::getString("TimeDay") + "]/[" + + LLTrans::getString("TimeYear") + "] " + time_string; + + LLSD substitution; + // To avoid adding today's date to yesterday's timestamp, + // use creation time instead of current time + substitution["datetime"] = (S32)mCreationTime; + LLStringUtil::format(time_string, substitution); + } + } + LLFloaterReporter::showFromChat(mAvatarID, mFrom, time_string, mText); + } else if(level == "block_unblock") { LLAvatarActions::toggleMute(getAvatarId(), LLMute::flagVoiceChat); @@ -477,6 +521,10 @@ public: { return canModerate(userdata); } + else if (level == "report_abuse") + { + return gAgentID != mAvatarID; + } else if (level == "can_ban_member") { return canBanGroupMember(getAvatarId()); @@ -634,6 +682,12 @@ public: mSessionID = chat.mSessionID; mSourceType = chat.mSourceType; + // To be able to report a message, we need a copy of it's text + // and it's easier to store text directly than trying to get + // it from a lltextsegment or chat's mEditor + mText = chat.mText; + mTime = chat.mTime; + //*TODO overly defensive thing, source type should be maintained out there if((chat.mFromID.isNull() && chat.mFromName.empty()) || (chat.mFromName == SYSTEM_FROM && chat.mFromID.isNull())) { @@ -983,6 +1037,9 @@ protected: EChatSourceType mSourceType; std::string mFrom; LLUUID mSessionID; + std::string mText; + F64 mTime; // IM's frame time + time_t mCreationTime; // Views's time S32 mMinUserNameWidth; const LLFontGL* mUserNameFont; diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index 80d810d159..036ff17074 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -290,9 +290,14 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op ) pickerp->getCurG (), pickerp->getCurB (), subject->mColor.mV[VALPHA] ); // keep current alpha - subject->mColor = updatedColor; - subject->setControlValue(updatedColor.getValue()); - pickerp->setRevertOnCancel(TRUE); + + bool color_changed = subject->mColor != updatedColor; + if (color_changed) + { + subject->mColor = updatedColor; + subject->setControlValue(updatedColor.getValue()); + } + if (pick_op == COLOR_CANCEL && subject->mOnCancelCallback) { subject->mOnCancelCallback( subject, LLSD()); @@ -306,6 +311,13 @@ void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op ) // just commit change subject->onCommit (); } + + if (pick_op == COLOR_CANCEL || pick_op == COLOR_SELECT) + { + // both select and cancel close LLFloaterColorPicker + // but COLOR_CHANGE does not + subject->setFocus(TRUE); + } } } } diff --git a/indra/newview/llconversationloglist.cpp b/indra/newview/llconversationloglist.cpp index 86e23e7c83..97b16a5e93 100644 --- a/indra/newview/llconversationloglist.cpp +++ b/indra/newview/llconversationloglist.cpp @@ -391,7 +391,8 @@ bool LLConversationLogList::isActionEnabled(const LLSD& userdata) "can_invite_to_group" == command_name || "can_share" == command_name || "can_block" == command_name || - "can_pay" == command_name) + "can_pay" == command_name || + "report_abuse" == command_name) { return is_p2p; } diff --git a/indra/newview/llconversationmodel.cpp b/indra/newview/llconversationmodel.cpp index a685639427..9ec4fb085b 100644 --- a/indra/newview/llconversationmodel.cpp +++ b/indra/newview/llconversationmodel.cpp @@ -182,6 +182,7 @@ void LLConversationItem::buildParticipantMenuOptions(menuentry_vec_t& items, U32 items.push_back(std::string("map")); items.push_back(std::string("share")); items.push_back(std::string("pay")); + items.push_back(std::string("report_abuse")); items.push_back(std::string("block_unblock")); items.push_back(std::string("MuteText")); diff --git a/indra/newview/llconversationview.cpp b/indra/newview/llconversationview.cpp index 20fa6d490b..48c7df40df 100644 --- a/indra/newview/llconversationview.cpp +++ b/indra/newview/llconversationview.cpp @@ -272,9 +272,9 @@ BOOL LLConversationViewSession::postBuild() default: break; } - } - refresh(); + refresh(); // requires vmi + } return TRUE; } @@ -490,17 +490,20 @@ void LLConversationViewSession::refresh() { // Refresh the session view from its model data LLConversationItem* vmi = dynamic_cast(getViewModelItem()); - vmi->resetRefresh(); + if (vmi) + { + vmi->resetRefresh(); - if (mSessionTitle) - { - if (!highlightFriendTitle(vmi)) - { - LLStyle::Params title_style; - title_style.color = LLUIColorTable::instance().getColor("LabelTextColor"); - mSessionTitle->setText(vmi->getDisplayName(), title_style); - } - } + if (mSessionTitle) + { + if (!highlightFriendTitle(vmi)) + { + LLStyle::Params title_style; + title_style.color = LLUIColorTable::instance().getColor("LabelTextColor"); + mSessionTitle->setText(vmi->getDisplayName(), title_style); + } + } + } // Update all speaking indicators LLSpeakingIndicatorManager::updateSpeakingIndicators(); @@ -524,8 +527,11 @@ void LLConversationViewSession::refresh() } requestArrange(); - // Do the regular upstream refresh - LLFolderViewFolder::refresh(); + if (vmi) + { + // Do the regular upstream refresh + LLFolderViewFolder::refresh(); + } } void LLConversationViewSession::onCurrentVoiceSessionChanged(const LLUUID& session_id) @@ -627,8 +633,11 @@ BOOL LLConversationViewParticipant::postBuild() } updateChildren(); - LLFolderViewItem::postBuild(); - refresh(); + if (getViewModelItem()) + { + LLFolderViewItem::postBuild(); + refresh(); + } return TRUE; } @@ -712,10 +721,10 @@ void LLConversationViewParticipant::refresh() // *TODO: We should also do something with vmi->isModerator() to echo that state in the UI somewhat mSpeakingIndicator->setIsModeratorMuted(participant_model->isModeratorMuted()); + + // Do the regular upstream refresh + LLFolderViewItem::refresh(); } - - // Do the regular upstream refresh - LLFolderViewItem::refresh(); } void LLConversationViewParticipant::addToFolder(LLFolderViewFolder* folder) diff --git a/indra/newview/lldonotdisturbnotificationstorage.cpp b/indra/newview/lldonotdisturbnotificationstorage.cpp index 7d4961c598..4d9ef99319 100644 --- a/indra/newview/lldonotdisturbnotificationstorage.cpp +++ b/indra/newview/lldonotdisturbnotificationstorage.cpp @@ -80,9 +80,14 @@ LLDoNotDisturbNotificationStorage::~LLDoNotDisturbNotificationStorage() { } +void LLDoNotDisturbNotificationStorage::reset() +{ + setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml")); +} + void LLDoNotDisturbNotificationStorage::initialize() { - setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml")); + reset(); getCommunicationChannel()->connectFailedFilter(boost::bind(&LLDoNotDisturbNotificationStorage::onChannelChanged, this, _1)); } diff --git a/indra/newview/lldonotdisturbnotificationstorage.h b/indra/newview/lldonotdisturbnotificationstorage.h index c6f0bf1ab5..237d58b4de 100644 --- a/indra/newview/lldonotdisturbnotificationstorage.h +++ b/indra/newview/lldonotdisturbnotificationstorage.h @@ -61,6 +61,7 @@ public: void loadNotifications(); void updateNotifications(); void removeNotification(const char * name, const LLUUID& id); + void reset(); protected: diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 2372ea01a6..2a0f4c93ac 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -133,6 +133,7 @@ public: inline LLFace* getFace(const S32 i) const; inline S32 getNumFaces() const; face_list_t& getFaces() { return mFaces; } + const face_list_t& getFaces() const { return mFaces; } //void removeFace(const S32 i); // SJB: Avoid using this, it's slow LLFace* addFace(LLFacePool *poolp, LLViewerTexture *texturep); diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index e674707c01..6c1abb24c9 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -58,6 +58,7 @@ static BOOL deferred_render = FALSE; // minimum alpha before discarding a fragment static const F32 MINIMUM_ALPHA = 0.004f; // ~ 1/255 + // minimum alpha before discarding a fragment when rendering impostors static const F32 MINIMUM_IMPOSTOR_ALPHA = 0.1f; @@ -147,6 +148,10 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) (LLPipeline::sUnderWaterRender) ? &gDeferredAlphaWaterProgram : &gDeferredAlphaProgram; prepare_alpha_shader(simple_shader, false, true); //prime simple shader (loads shadow relevant uniforms) + for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i) + { + prepare_alpha_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], false, false); // note: bindDeferredShader will get called during render loop for materials + } // first pass, render rigged objects only and render to depth buffer forwardRender(true); @@ -215,9 +220,15 @@ void LLDrawPoolAlpha::render(S32 pass) { minimum_alpha = MINIMUM_IMPOSTOR_ALPHA; } + prepare_forward_shader(fullbright_shader, minimum_alpha); prepare_forward_shader(simple_shader, minimum_alpha); + for (int i = 0; i < LLMaterial::SHADER_COUNT; ++i) + { + prepare_forward_shader(LLPipeline::sUnderWaterRender ? &gDeferredMaterialWaterProgram[i] : &gDeferredMaterialProgram[i], minimum_alpha); + } + //first pass -- rigged only and drawn to depth buffer forwardRender(true); diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp index 63e7887d81..361a7666fa 100644 --- a/indra/newview/lldynamictexture.cpp +++ b/indra/newview/lldynamictexture.cpp @@ -125,7 +125,7 @@ void LLViewerDynamicTexture::preRender(BOOL clear_depth) llassert(mFullHeight <= static_cast(gPipeline.mPhysicsDisplay.getHeight())); } - if (gGLManager.mHasFramebufferObject && gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsAMD) + if (gPipeline.mPhysicsDisplay.isComplete() && !gGLManager.mIsAMD) { //using offscreen render target, just use the bottom left corner mOrigin.set(0, 0); } @@ -212,7 +212,7 @@ BOOL LLViewerDynamicTexture::updateAllInstances() return TRUE; } - bool use_fbo = gGLManager.mHasFramebufferObject && gPipeline.mBake.isComplete() && !gGLManager.mIsAMD; + bool use_fbo = gPipeline.mBake.isComplete() && !gGLManager.mIsAMD; if (use_fbo) { diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index b76dc6a961..1300cf3658 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -2641,7 +2641,7 @@ void LLEnvironment::setExperienceEnvironment(LLUUID experience_id, LLSD data, F3 if (!water.isUndefined()) { - environment->injectWaterSettings(sky, experience_id, LLSettingsBase::Seconds(transition_time)); + environment->injectWaterSettings(water, experience_id, LLSettingsBase::Seconds(transition_time)); } if (updateenvironment) diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index 1605e4133d..9bb3bac104 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -504,20 +504,6 @@ void LLFastTimerView::exportCharts(const std::string& base, const std::string& t is.close(); } - //get time domain - LLSD::Real cur_total_time = 0.0; - - for (U32 i = 0; i < cur_data.size(); ++i) - { - cur_total_time += cur_data[i]["Total"]["Time"].asReal(); - } - - LLSD::Real base_total_time = 0.0; - for (U32 i = 0; i < base_data.size(); ++i) - { - base_total_time += base_data[i]["Total"]["Time"].asReal(); - } - //allocate raw scratch space LLPointer scratch = new LLImageRaw(1024, 512, 3); diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index a02bb56489..70e8437190 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -471,7 +471,25 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, } // check if we are dragging an existing item from the favorites bar - if (item && mDragItemId == item->getUUID()) + bool existing_drop = false; + if (item && mDragItemId == item->getUUID()) + { + // There is a chance of mDragItemId being obsolete + // ex: can happen if something interrupts viewer, which + // results in viewer not geting a 'mouse up' signal + for (LLInventoryModel::item_array_t::iterator i = mItems.begin(); i != mItems.end(); ++i) + { + LLViewerInventoryItem* currItem = *i; + + if (currItem->getUUID() == mDragItemId) + { + existing_drop = true; + break; + } + } + } + + if (existing_drop) { *accept = ACCEPT_YES_SINGLE; @@ -500,6 +518,7 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, if (mItems.empty()) { setLandingTab(NULL); + mLastTab = NULL; } handleNewFavoriteDragAndDrop(item, favorites_id, x, y); } @@ -515,6 +534,12 @@ BOOL LLFavoritesBarCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, void LLFavoritesBarCtrl::handleExistingFavoriteDragAndDrop(S32 x, S32 y) { + if (mItems.empty()) + { + // Isn't supposed to be empty + return; + } + // Identify the button hovered and the side to drop LLFavoriteLandmarkButton* dest = dynamic_cast(mLandingTab); bool insert_before = true; @@ -787,6 +812,7 @@ void LLFavoritesBarCtrl::updateButtons(bool force_update) if(mItems.empty()) { mBarLabel->setVisible(TRUE); + mLastTab = NULL; } else { @@ -833,6 +859,10 @@ void LLFavoritesBarCtrl::updateButtons(bool force_update) dynamic_cast (*cur_it); if (button) { + if (mLastTab == button) + { + mLastTab = NULL; + } removeChild(button); delete button; } @@ -870,6 +900,17 @@ void LLFavoritesBarCtrl::updateButtons(bool force_update) mLastTab = last_new_button; } + if (!mLastTab && mItems.size() > 0) + { + // mMoreTextBox was removed, so LLFavoriteLandmarkButtons + // should be the only ones in the list + LLFavoriteLandmarkButton* button = dynamic_cast (childs->back()); + if (button) + { + mLastTab = button; + } + } + mFirstDropDownItem = j; // Chevron button if (mFirstDropDownItem < mItems.size()) diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index ffbb0bbee9..c075f7e8bd 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -114,6 +114,11 @@ BOOL LLFloater360Capture::postBuild() // by default each time vs restoring the last value mQualityRadioGroup->setSelectedIndex(0); + return true; +} + +void LLFloater360Capture::onOpen(const LLSD& key) +{ // Construct a URL pointing to the first page to load. Although // we do not use this page for anything (after some significant // design changes), we retain the code to load the start page @@ -154,8 +159,6 @@ BOOL LLFloater360Capture::postBuild() // We do an initial capture when the floater is opened, albeit at a 'preview' // quality level (really low resolution, but really fast) onCapture360ImagesBtn(); - - return true; } // called when the user choose a quality level using diff --git a/indra/newview/llfloater360capture.h b/indra/newview/llfloater360capture.h index 6da7ee074a..8f765c0b1b 100644 --- a/indra/newview/llfloater360capture.h +++ b/indra/newview/llfloater360capture.h @@ -47,6 +47,7 @@ class LLFloater360Capture: ~LLFloater360Capture(); BOOL postBuild() override; + void onOpen(const LLSD& key) override; void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) override; void changeInterestListMode(bool send_everything); diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp index ab95bc06b8..0186c4aebe 100644 --- a/indra/newview/llfloateravatarpicker.cpp +++ b/indra/newview/llfloateravatarpicker.cpp @@ -105,6 +105,7 @@ LLFloaterAvatarPicker::LLFloaterAvatarPicker(const LLSD& key) mNumResultsReturned(0), mNearMeListComplete(FALSE), mCloseOnSelect(FALSE), + mExcludeAgentFromSearchResults(FALSE), mContextConeOpacity (0.f), mContextConeInAlpha(0.f), mContextConeOutAlpha(0.f), @@ -295,7 +296,7 @@ void LLFloaterAvatarPicker::populateNearMe() for(U32 i=0; iserialize(dp); dp.reset(); LL_INFOS("BVH") << "Deserializing motionp" << LL_ENDL; - BOOL success = motionp && motionp->deserialize(dp, mMotionID); + BOOL success = motionp && motionp->deserialize(dp, mMotionID, false); LL_INFOS("BVH") << "Done" << LL_ENDL; delete []buffer; @@ -1047,7 +1047,12 @@ LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLViewerDynamicT mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion(), LLViewerObject::CO_FLAG_UI_AVATAR); mDummyAvatar->mSpecialRenderMode = 1; mDummyAvatar->startMotion(ANIM_AGENT_STAND, BASE_ANIM_TIME_OFFSET); - mDummyAvatar->hideSkirt(); + + // on idle overall apperance update will set skirt to visible, so either + // call early or account for mSpecialRenderMode in updateMeshVisibility + mDummyAvatar->updateOverallAppearance(); + mDummyAvatar->hideHair(); + mDummyAvatar->hideSkirt(); // stop extraneous animations mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE ); @@ -1135,6 +1140,7 @@ BOOL LLPreviewAnimation::render() { LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)face->getPool(); avatarp->dirtyMesh(); + gPipeline.enableLightsPreview(); avatarPoolp->renderAvatars(avatarp); // renders only one avatar } } diff --git a/indra/newview/llfloaterchatvoicevolume.cpp b/indra/newview/llfloaterchatvoicevolume.cpp index 45aea00a49..67c412dfa6 100644 --- a/indra/newview/llfloaterchatvoicevolume.cpp +++ b/indra/newview/llfloaterchatvoicevolume.cpp @@ -35,7 +35,7 @@ LLFloaterChatVoiceVolume::LLFloaterChatVoiceVolume(const LLSD& key) void LLFloaterChatVoiceVolume::onOpen(const LLSD& key) { LLInspect::onOpen(key); - LLUI::getInstance()->positionViewNearMouse(this); + LLInspect::repositionInspector(key); } LLFloaterChatVoiceVolume::~LLFloaterChatVoiceVolume() diff --git a/indra/newview/llfloaterclassified.cpp b/indra/newview/llfloaterclassified.cpp new file mode 100644 index 0000000000..3520b0f67a --- /dev/null +++ b/indra/newview/llfloaterclassified.cpp @@ -0,0 +1,71 @@ +/** + * @file llfloaterclassified.cpp + * @brief LLFloaterClassified for displaying classifieds. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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 "llfloaterclassified.h" + +LLFloaterClassified::LLFloaterClassified(const LLSD& key) + : LLFloater(key) +{ +} + +LLFloaterClassified::~LLFloaterClassified() +{ +} + +void LLFloaterClassified::onOpen(const LLSD& key) +{ + LLPanel* panel = findChild("main_panel", true); + if (panel) + { + panel->onOpen(key); + } + if (key.has("classified_name")) + { + setTitle(key["classified_name"].asString()); + } + LLFloater::onOpen(key); +} + +BOOL LLFloaterClassified::postBuild() +{ + return TRUE; +} + + +bool LLFloaterClassified::matchesKey(const LLSD& key) +{ + bool is_mkey_valid = mKey.has("classified_id"); + bool is_key_valid = key.has("classified_id"); + if (is_mkey_valid && is_key_valid) + { + return key["classified_id"].asUUID() == mKey["classified_id"].asUUID(); + } + return is_mkey_valid == is_key_valid; +} + +// eof diff --git a/indra/newview/llfloaterwebprofile.h b/indra/newview/llfloaterclassified.h similarity index 51% rename from indra/newview/llfloaterwebprofile.h rename to indra/newview/llfloaterclassified.h index 4c355e401b..2c95d82b2c 100644 --- a/indra/newview/llfloaterwebprofile.h +++ b/indra/newview/llfloaterclassified.h @@ -1,59 +1,45 @@ -/** - * @file llfloaterwebprofile.h - * @brief Avatar profile floater. +/** + * @file llfloaterclassified.h + * @brief LLFloaterClassified for displaying classifieds. * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * + * Copyright (C) 2022, 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_LLFLOATERWEBPROFILE_H -#define LL_LLFLOATERWEBPROFILE_H +#ifndef LL_LLFLOATERCLASSIFIED_H +#define LL_LLFLOATERCLASSIFIED_H -#include "llfloaterwebcontent.h" -#include "llviewermediaobserver.h" +#include "llfloater.h" -#include - -class LLMediaCtrl; - -/** - * Displays avatar profile web page. - */ -class LLFloaterWebProfile -: public LLFloaterWebContent +class LLFloaterClassified : public LLFloater { - LOG_CLASS(LLFloaterWebProfile); + LOG_CLASS(LLFloaterClassified); public: - typedef LLFloaterWebContent::Params Params; + LLFloaterClassified(const LLSD& key); + virtual ~LLFloaterClassified(); - LLFloaterWebProfile(const Params& key); + void onOpen(const LLSD& key) override; + BOOL postBuild() override; - /*virtual*/ void onOpen(const LLSD& key); - /*virtual*/ void handleReshape(const LLRect& new_rect, bool by_user = false); - - static LLFloater* create(const LLSD& key); - -private: - void applyPreferredRect(); + bool matchesKey(const LLSD& key) override; }; -#endif // LL_LLFLOATERWEBPROFILE_H - +#endif // LL_LLFLOATERCLASSIFIED_H diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp index 1a784223c2..ba91277c79 100644 --- a/indra/newview/llfloatercolorpicker.cpp +++ b/indra/newview/llfloatercolorpicker.cpp @@ -173,7 +173,6 @@ void LLFloaterColorPicker::showUI () openFloater(getKey()); setVisible ( TRUE ); setFocus ( TRUE ); - setRevertOnCancel(FALSE); // HACK: if system color picker is required - close the SL one we made and use default system dialog if ( gSavedSettings.getBOOL ( "UseDefaultColorPicker" ) ) @@ -185,15 +184,23 @@ void LLFloaterColorPicker::showUI () // code that will get switched in for default system color picker if ( swatch ) { + // Todo: this needs to be threaded for viewer not to timeout LLColor4 curCol = swatch->get (); send_agent_pause(); - getWindow()->dialogColorPicker( &curCol [ 0 ], &curCol [ 1 ], &curCol [ 2 ] ); + bool commit = getWindow()->dialogColorPicker( &curCol [ 0 ], &curCol [ 1 ], &curCol [ 2 ] ); send_agent_resume(); - setOrigRgb ( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] ); - setCurRgb( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] ); + if (commit) + { + setOrigRgb(curCol[0], curCol[1], curCol[2]); + setCurRgb(curCol[0], curCol[1], curCol[2]); - LLColorSwatchCtrl::onColorChanged ( swatch, LLColorSwatchCtrl::COLOR_CHANGE ); + LLColorSwatchCtrl::onColorChanged(swatch, LLColorSwatchCtrl::COLOR_SELECT); + } + else + { + LLColorSwatchCtrl::onColorChanged(swatch, LLColorSwatchCtrl::COLOR_CANCEL); + } } closeFloater(); @@ -391,10 +398,7 @@ void LLFloaterColorPicker::onClickCancel ( void* data ) if ( self ) { - if(self->getRevertOnCancel()) - { - self->cancelSelection (); - } + self->cancelSelection(); self->closeFloater(); } } diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h index 16974a872e..39dbc5b763 100644 --- a/indra/newview/llfloatercolorpicker.h +++ b/indra/newview/llfloatercolorpicker.h @@ -104,9 +104,6 @@ class LLFloaterColorPicker void setMouseDownInSwatch (BOOL mouse_down_in_swatch); BOOL getMouseDownInSwatch () { return mMouseDownInSwatch; } - void setRevertOnCancel (BOOL revertOnCancel) { mRevertOnCancel = revertOnCancel; }; - BOOL getRevertOnCancel () { return mRevertOnCancel; } - BOOL isColorChanged (); // called when text entries (RGB/HSL etc.) are changed by user @@ -149,8 +146,6 @@ class LLFloaterColorPicker BOOL mMouseDownInHueRegion; BOOL mMouseDownInSwatch; - BOOL mRevertOnCancel; - const S32 mRGBViewerImageLeft; const S32 mRGBViewerImageTop; const S32 mRGBViewerImageWidth; diff --git a/indra/newview/llfloatercreatelandmark.cpp b/indra/newview/llfloatercreatelandmark.cpp index 6b1d9306fb..7def855d83 100644 --- a/indra/newview/llfloatercreatelandmark.cpp +++ b/indra/newview/llfloatercreatelandmark.cpp @@ -46,19 +46,60 @@ typedef std::pair folder_pair_t; -class LLLandmarksInventoryObserver : public LLInventoryAddedObserver +class LLLandmarksInventoryObserver : public LLInventoryObserver { public: LLLandmarksInventoryObserver(LLFloaterCreateLandmark* create_landmark_floater) : mFloater(create_landmark_floater) {} + void changed(U32 mask) override + { + if (mFloater->getItem()) + { + checkChanged(mask); + } + else + { + checkCreated(mask); + } + } + protected: - /*virtual*/ void done() + void checkCreated(U32 mask) { + if (gInventory.getAddedIDs().empty()) + { + return; + } + + if (!(mask & LLInventoryObserver::ADD) || + !(mask & LLInventoryObserver::CREATE) || + !(mask & LLInventoryObserver::UPDATE_CREATE)) + { + return; + } + mFloater->setItem(gInventory.getAddedIDs()); } + void checkChanged(U32 mask) + { + if (gInventory.getChangedIDs().empty()) + { + return; + } + + if ((mask & LLInventoryObserver::LABEL) || + (mask & LLInventoryObserver::INTERNAL) || + (mask & LLInventoryObserver::REMOVE) || + (mask & LLInventoryObserver::STRUCTURE) || + (mask & LLInventoryObserver::REBUILD)) + { + mFloater->updateItem(gInventory.getChangedIDs(), mask); + } + } + private: LLFloaterCreateLandmark* mFloater; }; @@ -85,6 +126,9 @@ BOOL LLFloaterCreateLandmark::postBuild() getChild("ok_btn")->setClickedCallback(boost::bind(&LLFloaterCreateLandmark::onSaveClicked, this)); getChild("cancel_btn")->setClickedCallback(boost::bind(&LLFloaterCreateLandmark::onCancelClicked, this)); + mLandmarkTitleEditor->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTextChanges(); }); + mNotesEditor->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) { onCommitTextChanges(); }); + mLandmarksID = gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK); return TRUE; @@ -204,6 +248,33 @@ void LLFloaterCreateLandmark::populateFoldersList(const LLUUID &folder_id) } } +void LLFloaterCreateLandmark::onCommitTextChanges() +{ + if (mItem.isNull()) + { + return; + } + std::string current_title_value = mLandmarkTitleEditor->getText(); + std::string item_title_value = mItem->getName(); + std::string current_notes_value = mNotesEditor->getText(); + std::string item_notes_value = mItem->getDescription(); + + LLStringUtil::trim(current_title_value); + LLStringUtil::trim(current_notes_value); + + if (!current_title_value.empty() && + (item_title_value != current_title_value || item_notes_value != current_notes_value)) + { + LLPointer new_item = new LLViewerInventoryItem(mItem); + new_item->rename(current_title_value); + new_item->setDescription(current_notes_value); + LLPointer cb; + LLInventoryModel::LLCategoryUpdate up(mItem->getParentUUID(), 0); + gInventory.accountForUpdate(up); + update_inventory_item(new_item, cb); + } +} + void LLFloaterCreateLandmark::onCreateFolderClicked() { LLNotificationsUtil::add("CreateLandmarkFolder", LLSD(), LLSD(), @@ -278,6 +349,8 @@ void LLFloaterCreateLandmark::onSaveClicked() new_item->updateParentOnServer(FALSE); } + removeObserver(); + gInventory.updateItem(new_item); gInventory.notifyObservers(); @@ -286,6 +359,7 @@ void LLFloaterCreateLandmark::onSaveClicked() void LLFloaterCreateLandmark::onCancelClicked() { + removeObserver(); if (!mItem.isNull()) { LLUUID item_id = mItem->getUUID(); @@ -314,10 +388,59 @@ void LLFloaterCreateLandmark::setItem(const uuid_set_t& items) { if(!getItem()) { - removeObserver(); mItem = item; + mAssetID = mItem->getAssetUUID(); + setVisibleAndFrontmost(true); break; } } } } + +void LLFloaterCreateLandmark::updateItem(const uuid_set_t& items, U32 mask) +{ + if (!getItem()) + { + return; + } + + LLUUID landmark_id = getItem()->getUUID(); + + for (uuid_set_t::const_iterator item_iter = items.begin(); + item_iter != items.end(); + ++item_iter) + { + const LLUUID& item_id = (*item_iter); + if (landmark_id == item_id) + { + if (getItem() != gInventory.getItem(item_id)) + { + // item is obsolete or removed + closeFloater(); + } + + LLUUID folder_id = mFolderCombo->getValue().asUUID(); + if (folder_id != mItem->getParentUUID()) + { + // user moved landmark in inventory, + // assume that we are done all other changes should already be commited + closeFloater(); + } + + if ((mask & LLInventoryObserver::INTERNAL) && mAssetID != mItem->getAssetUUID()) + { + closeFloater(); + } + + if (mask & LLInventoryObserver::LABEL) + { + mLandmarkTitleEditor->setText(mItem->getName()); + } + + if (mask & LLInventoryObserver::INTERNAL) + { + mNotesEditor->setText(mItem->getDescription()); + } + } + } +} diff --git a/indra/newview/llfloatercreatelandmark.h b/indra/newview/llfloatercreatelandmark.h index 74ac5e651c..d84f5ae1fc 100644 --- a/indra/newview/llfloatercreatelandmark.h +++ b/indra/newview/llfloatercreatelandmark.h @@ -49,6 +49,7 @@ public: void onOpen(const LLSD& key); void setItem(const uuid_set_t& items); + void updateItem(const uuid_set_t& items, U32 mask); LLInventoryItem* getItem() { return mItem; } @@ -56,6 +57,7 @@ private: void setLandmarkInfo(const LLUUID &folder_id); void removeObserver(); void populateFoldersList(const LLUUID &folder_id = LLUUID::null); + void onCommitTextChanges(); void onCreateFolderClicked(); void onSaveClicked(); void onCancelClicked(); @@ -66,6 +68,7 @@ private: LLLineEditor* mLandmarkTitleEditor; LLTextEditor* mNotesEditor; LLUUID mLandmarksID; + LLUUID mAssetID; LLLandmarksInventoryObserver* mInventoryObserver; LLPointer mItem; diff --git a/indra/newview/llfloaterdisplayname.cpp b/indra/newview/llfloaterdisplayname.cpp new file mode 100644 index 0000000000..3b0c67415a --- /dev/null +++ b/indra/newview/llfloaterdisplayname.cpp @@ -0,0 +1,200 @@ +/** + * @file llfloaterdisplayname.cpp + * @author Leyla Farazha + * @brief Implementation of the LLFloaterDisplayName class. + * + * $LicenseInfo:firstyear=2002&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 "llfloaterreg.h" +#include "llfloater.h" + +#include "llnotificationsutil.h" +#include "llviewerdisplayname.h" + +#include "llnotifications.h" +#include "llfloaterdisplayname.h" +#include "llavatarnamecache.h" + +#include "llagent.h" + + +class LLFloaterDisplayName : public LLFloater +{ +public: + LLFloaterDisplayName(const LLSD& key); + virtual ~LLFloaterDisplayName() { } + /*virtual*/ BOOL postBuild(); + void onSave(); + void onCancel(); + /*virtual*/ void onOpen(const LLSD& key); + +private: + + void onCacheSetName(bool success, + const std::string& reason, + const LLSD& content); +}; + +LLFloaterDisplayName::LLFloaterDisplayName(const LLSD& key) : + LLFloater(key) +{ +} + +void LLFloaterDisplayName::onOpen(const LLSD& key) +{ + getChild("display_name_editor")->clear(); + getChild("display_name_confirm")->clear(); + + LLAvatarName av_name; + LLAvatarNameCache::get(gAgent.getID(), &av_name); + + F64 now_secs = LLDate::now().secondsSinceEpoch(); + + if (now_secs < av_name.mNextUpdate) + { + // ...can't update until some time in the future + F64 next_update_local_secs = + av_name.mNextUpdate - LLStringOps::getLocalTimeOffset(); + LLDate next_update_local(next_update_local_secs); + // display as "July 18 12:17 PM" + std::string next_update_string = + next_update_local.toHTTPDateString("%B %d %I:%M %p"); + getChild("lockout_text")->setTextArg("[TIME]", next_update_string); + getChild("lockout_text")->setVisible(true); + getChild("save_btn")->setEnabled(false); + getChild("display_name_editor")->setEnabled(false); + getChild("display_name_confirm")->setEnabled(false); + getChild("cancel_btn")->setFocus(TRUE); + + } + else + { + getChild("lockout_text")->setVisible(false); + getChild("save_btn")->setEnabled(true); + getChild("display_name_editor")->setEnabled(true); + getChild("display_name_confirm")->setEnabled(true); + + } +} + +BOOL LLFloaterDisplayName::postBuild() +{ + getChild("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onCancel, this)); + getChild("save_btn")->setCommitCallback(boost::bind(&LLFloaterDisplayName::onSave, this)); + + center(); + + return TRUE; +} + +void LLFloaterDisplayName::onCacheSetName(bool success, + const std::string& reason, + const LLSD& content) +{ + if (success) + { + // Inform the user that the change took place, but will take a while + // to percolate. + LLSD args; + args["DISPLAY_NAME"] = content["display_name"]; + LLNotificationsUtil::add("SetDisplayNameSuccess", args); + return; + } + + // Request failed, notify the user + std::string error_tag = content["error_tag"].asString(); + LL_INFOS() << "set name failure error_tag " << error_tag << LL_ENDL; + + // We might have a localized string for this message + // error_args will usually be empty from the server. + if (!error_tag.empty() + && LLNotifications::getInstance()->templateExists(error_tag)) + { + LLNotificationsUtil::add(error_tag); + return; + } + + // The server error might have a localized message for us + std::string lang_code = LLUI::getLanguage(); + LLSD error_desc = content["error_description"]; + if (error_desc.has( lang_code )) + { + LLSD args; + args["MESSAGE"] = error_desc[lang_code].asString(); + LLNotificationsUtil::add("GenericAlert", args); + return; + } + + // No specific error, throw a generic one + LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); +} + +void LLFloaterDisplayName::onCancel() +{ + setVisible(false); +} + +void LLFloaterDisplayName::onSave() +{ + std::string display_name_utf8 = getChild("display_name_editor")->getValue().asString(); + std::string display_name_confirm = getChild("display_name_confirm")->getValue().asString(); + + if (display_name_utf8.compare(display_name_confirm)) + { + LLNotificationsUtil::add("SetDisplayNameMismatch"); + return; + } + + const U32 DISPLAY_NAME_MAX_LENGTH = 31; // characters, not bytes + LLWString display_name_wstr = utf8string_to_wstring(display_name_utf8); + if (display_name_wstr.size() > DISPLAY_NAME_MAX_LENGTH) + { + LLSD args; + args["LENGTH"] = llformat("%d", DISPLAY_NAME_MAX_LENGTH); + LLNotificationsUtil::add("SetDisplayNameFailedLength", args); + return; + } + + if (LLAvatarNameCache::getInstance()->hasNameLookupURL()) + { + LLViewerDisplayName::set(display_name_utf8,boost::bind(&LLFloaterDisplayName::onCacheSetName, this, _1, _2, _3)); + } + else + { + LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); + } + + setVisible(false); +} + + +////////////////////////////////////////////////////////////////////////////// +// LLInspectObjectUtil +////////////////////////////////////////////////////////////////////////////// +void LLFloaterDisplayNameUtil::registerFloater() +{ + LLFloaterReg::add("display_name", "floater_display_name.xml", + &LLFloaterReg::build); +} diff --git a/indra/newview/llpanelme.h b/indra/newview/llfloaterdisplayname.h similarity index 65% rename from indra/newview/llpanelme.h rename to indra/newview/llfloaterdisplayname.h index 60e9d4317d..a00bf56712 100644 --- a/indra/newview/llpanelme.h +++ b/indra/newview/llfloaterdisplayname.h @@ -1,6 +1,5 @@ /** - * @file llpanelme.h - * @brief Side tray "Me" (My Profile) panel + * @file llfloaterdisplayname.h * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code @@ -24,27 +23,16 @@ * $/LicenseInfo$ */ -#ifndef LL_LLPANELMEPROFILE_H -#define LL_LLPANELMEPROFILE_H +#ifndef LLFLOATERDISPLAYNAME_H +#define LLFLOATERDISPLAYNAME_H -#include "llpanel.h" -#include "llpanelprofile.h" -/** -* Panel for displaying Agent's Picks and Classifieds panel. -* LLPanelMe allows user to edit his picks and classifieds. -*/ -class LLPanelMe : public LLPanelProfile +namespace LLFloaterDisplayNameUtil { - LOG_CLASS(LLPanelMe); + // Register with LLFloaterReg + void registerFloater(); +} -public: - LLPanelMe(); - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ BOOL postBuild(); -}; - -#endif // LL_LLPANELMEPROFILE_H +#endif diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index 24673d5a7c..297ad24359 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -665,6 +665,7 @@ void LLFloaterEditExtDayCycle::onButtonApply(LLUICtrl *ctrl, const LLSD &data) if (ctrl_action == ACTION_SAVE) { doApplyUpdateInventory(dayclone); + clearDirtyFlag(); } else if (ctrl_action == ACTION_SAVEAS) { diff --git a/indra/newview/llfloaterfixedenvironment.cpp b/indra/newview/llfloaterfixedenvironment.cpp index fec218ca3b..aa9a2c164a 100644 --- a/indra/newview/llfloaterfixedenvironment.cpp +++ b/indra/newview/llfloaterfixedenvironment.cpp @@ -296,6 +296,7 @@ void LLFloaterFixedEnvironment::onButtonApply(LLUICtrl *ctrl, const LLSD &data) if (ctrl_action == ACTION_SAVE) { doApplyUpdateInventory(setting_clone); + clearDirtyFlag(); } else if (ctrl_action == ACTION_SAVEAS) { diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index 6e326ff3cf..d17889bed1 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -122,7 +122,7 @@ LLFloaterGesture::LLFloaterGesture(const LLSD& key) mObserver = new LLFloaterGestureObserver(this); LLGestureMgr::instance().addObserver(mObserver); - mCommitCallbackRegistrar.add("Gesture.Action.ToogleActiveState", boost::bind(&LLFloaterGesture::onActivateBtnClick, this)); + mCommitCallbackRegistrar.add("Gesture.Action.ToggleActiveState", boost::bind(&LLFloaterGesture::onActivateBtnClick, this)); mCommitCallbackRegistrar.add("Gesture.Action.ShowPreview", boost::bind(&LLFloaterGesture::onClickEdit, this)); mCommitCallbackRegistrar.add("Gesture.Action.CopyPaste", boost::bind(&LLFloaterGesture::onCopyPasteAction, this, _2)); mCommitCallbackRegistrar.add("Gesture.Action.SaveToCOF", boost::bind(&LLFloaterGesture::addToCurrentOutFit, this)); diff --git a/indra/newview/llfloaterhoverheight.cpp b/indra/newview/llfloaterhoverheight.cpp index 42c5e40761..a00fc4aa84 100644 --- a/indra/newview/llfloaterhoverheight.cpp +++ b/indra/newview/llfloaterhoverheight.cpp @@ -100,11 +100,14 @@ void LLFloaterHoverHeight::onClose(bool app_quitting) // static void LLFloaterHoverHeight::onSliderMoved(LLUICtrl* ctrl, void* userData) { - LLSliderCtrl* sldrCtrl = static_cast(ctrl); - F32 value = sldrCtrl->getValueF32(); - LLVector3 offset(0.0, 0.0, llclamp(value,MIN_HOVER_Z,MAX_HOVER_Z)); - LL_INFOS("Avatar") << "setting hover from slider moved" << offset[2] << LL_ENDL; - gAgentAvatarp->setHoverOffset(offset, false); + if (isAgentAvatarValid()) + { + LLSliderCtrl* sldrCtrl = static_cast(ctrl); + F32 value = sldrCtrl->getValueF32(); + LLVector3 offset(0.0, 0.0, llclamp(value, MIN_HOVER_Z, MAX_HOVER_Z)); + LL_INFOS("Avatar") << "setting hover from slider moved" << offset[2] << LL_ENDL; + gAgentAvatarp->setHoverOffset(offset, false); + } } // Do send-to-the-server work when slider drag completes, or new diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 1525bb9952..703b5d0011 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -45,6 +45,7 @@ #include "llflashtimer.h" #include "llfloateravatarpicker.h" #include "llfloaterpreference.h" +#include "llfloaterreporter.h" #include "llimview.h" #include "llnotificationsutil.h" #include "lltoolbarview.h" @@ -1242,6 +1243,18 @@ void LLFloaterIMContainer::doToParticipants(const std::string& command, uuid_vec { LLAvatarActions::pay(userID); } + else if ("report_abuse" == command) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(userID, &av_name)) + { + LLFloaterReporter::showFromAvatar(userID, av_name.getCompleteName()); + } + else + { + LLFloaterReporter::showFromAvatar(userID, "not avaliable"); + } + } else if ("block_unblock" == command) { LLAvatarActions::toggleMute(userID, LLMute::flagVoiceChat); @@ -1507,7 +1520,11 @@ bool LLFloaterIMContainer::enableContextMenuItem(const std::string& item, uuid_v } // Handle all other options - if (("can_invite" == item) || ("can_chat_history" == item) || ("can_share" == item) || ("can_pay" == item)) + if (("can_invite" == item) + || ("can_chat_history" == item) + || ("can_share" == item) + || ("can_pay" == item) + || ("report_abuse" == item)) { // Those menu items are enable only if a single avatar is selected return is_single_select; diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index 3a850d4b68..4cceddeefb 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -698,7 +698,6 @@ void LLFloaterIMNearbyChat::sendChatFromViewer(const std::string &utf8text, ECha void LLFloaterIMNearbyChat::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate) { - LLUIUsage::instance().logCommand("Chat.Send"); // pseuo-command // Look for "/20 foo" channel chats. S32 channel = 0; LLWString out_text = stripChannelNumber(wtext, &channel); @@ -858,6 +857,12 @@ LLWString LLFloaterIMNearbyChat::stripChannelNumber(const LLWString &mesg, S32* void send_chat_from_viewer(const std::string& utf8_out_text, EChatType type, S32 channel) { + LL_DEBUGS("UIUsage") << "Nearby chat, text " << utf8_out_text << " type " << type << " channel " << channel << LL_ENDL; + if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP) // prune back some redundant logging + { + LLUIUsage::instance().logCommand("Chat.SendNearby"); // pseuo-command + } + LLMessageSystem* msg = gMessageSystem; if (channel >= 0) diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 34499ac170..93a0b39e02 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -551,7 +551,7 @@ void LLFloaterIMSessionTab::removeConversationViewParticipant(const LLUUID& part void LLFloaterIMSessionTab::updateConversationViewParticipant(const LLUUID& participant_id) { LLFolderViewItem* widget = get_ptr_in_map(mConversationsWidgets,participant_id); - if (widget) + if (widget && widget->getViewModelItem()) { widget->refresh(); } @@ -576,8 +576,11 @@ void LLFloaterIMSessionTab::refreshConversation() { participants_uuids.push_back(widget_it->first); } - widget_it->second->refresh(); - widget_it->second->setVisible(TRUE); + if (widget_it->second->getViewModelItem()) + { + widget_it->second->refresh(); + widget_it->second->setVisible(TRUE); + } ++widget_it; } if (is_ad_hoc || mIsP2PChat) @@ -1126,7 +1129,10 @@ void LLFloaterIMSessionTab::getSelectedUUIDs(uuid_vec_t& selected_uuids) for (; it != it_end; ++it) { LLConversationItem* conversation_item = static_cast((*it)->getViewModelItem()); - selected_uuids.push_back(conversation_item->getUUID()); + if (conversation_item) + { + selected_uuids.push_back(conversation_item->getUUID()); + } } } diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index d78f80ad12..1a98ab9d76 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -3056,7 +3056,8 @@ BOOL LLPanelLandCovenant::postBuild() { mLastRegionID = LLUUID::null; mNextUpdateTime = 0; - + mTextEstateOwner = getChild("estate_owner_text"); + mTextEstateOwner->setIsFriendCallback(LLAvatarActions::isFriend); return TRUE; } @@ -3164,8 +3165,7 @@ void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name) LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant(); if (self) { - LLTextBox* editor = self->getChild("estate_owner_text"); - if (editor) editor->setText(name); + self->mTextEstateOwner->setText(name); } } diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index 5d9b411f04..684950d88b 100644 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -413,6 +413,7 @@ protected: private: LLUUID mLastRegionID; F64 mNextUpdateTime; //seconds since client start + LLTextBox* mTextEstateOwner; }; #endif diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp old mode 100644 new mode 100755 index fc2da772f3..fd1af7ccc0 --- a/indra/newview/llfloatermap.cpp +++ b/indra/newview/llfloatermap.cpp @@ -51,7 +51,7 @@ // The minor cardinal direction labels are hidden if their height is more // than this proportion of the map. -const F32 MAP_MINOR_DIR_THRESHOLD = 0.07f; +const F32 MAP_MINOR_DIR_THRESHOLD = 0.035f; // // Member functions @@ -77,35 +77,44 @@ LLFloaterMap::~LLFloaterMap() BOOL LLFloaterMap::postBuild() { - mMap = getChild("Net Map"); - if (gSavedSettings.getBOOL("DoubleClickTeleport")) - { - mMap->setToolTipMsg(getString("AltToolTipMsg")); - } - else if (gSavedSettings.getBOOL("DoubleClickShowWorldMap")) - { - mMap->setToolTipMsg(getString("ToolTipMsg")); - } - sendChildToBack(mMap); - - mTextBoxNorth = getChild ("floater_map_north"); - mTextBoxEast = getChild ("floater_map_east"); - mTextBoxWest = getChild ("floater_map_west"); - mTextBoxSouth = getChild ("floater_map_south"); - mTextBoxSouthEast = getChild ("floater_map_southeast"); - mTextBoxNorthEast = getChild ("floater_map_northeast"); - mTextBoxSouthWest = getChild ("floater_map_southwest"); - mTextBoxNorthWest = getChild ("floater_map_northwest"); + mMap = getChild("Net Map"); + mMap->setToolTipMsg(getString("ToolTipMsg")); + mMap->setParcelNameMsg(getString("ParcelNameMsg")); + mMap->setParcelSalePriceMsg(getString("ParcelSalePriceMsg")); + mMap->setParcelSaleAreaMsg(getString("ParcelSaleAreaMsg")); + mMap->setParcelOwnerMsg(getString("ParcelOwnerMsg")); + mMap->setRegionNameMsg(getString("RegionNameMsg")); + mMap->setToolTipHintMsg(getString("ToolTipHintMsg")); + mMap->setAltToolTipHintMsg(getString("AltToolTipHintMsg")); + sendChildToBack(mMap); - updateMinorDirections(); + mTextBoxNorth = getChild("floater_map_north"); + mTextBoxEast = getChild("floater_map_east"); + mTextBoxWest = getChild("floater_map_west"); + mTextBoxSouth = getChild("floater_map_south"); + mTextBoxSouthEast = getChild("floater_map_southeast"); + mTextBoxNorthEast = getChild("floater_map_northeast"); + mTextBoxSouthWest = getChild("floater_map_southwest"); + mTextBoxNorthWest = getChild("floater_map_northwest"); - // Get the drag handle all the way in back - sendChildToBack(getDragHandle()); + mTextBoxNorth->reshapeToFitText(); + mTextBoxEast->reshapeToFitText(); + mTextBoxWest->reshapeToFitText(); + mTextBoxSouth->reshapeToFitText(); + mTextBoxSouthEast->reshapeToFitText(); + mTextBoxNorthEast->reshapeToFitText(); + mTextBoxSouthWest->reshapeToFitText(); + mTextBoxNorthWest->reshapeToFitText(); - // keep onscreen - gFloaterView->adjustToFitScreen(this, FALSE); + updateMinorDirections(); - return TRUE; + // Get the drag handle all the way in back + sendChildToBack(getDragHandle()); + + // keep onscreen + gFloaterView->adjustToFitScreen(this, false); + + return true; } BOOL LLFloaterMap::handleDoubleClick(S32 x, S32 y, MASK mask) @@ -138,23 +147,44 @@ BOOL LLFloaterMap::handleDoubleClick(S32 x, S32 y, MASK mask) return TRUE; } -void LLFloaterMap::setDirectionPos( LLTextBox* text_box, F32 rotation ) +void LLFloaterMap::setDirectionPos(LLTextBox *text_box, F32 rotation) { - // Rotation is in radians. - // Rotation of 0 means x = 1, y = 0 on the unit circle. + // Rotation is in radians. + // Rotation of 0 means x = 1, y = 0 on the unit circle. - F32 map_half_height = (F32)(getRect().getHeight() / 2) - getHeaderHeight()/2; - F32 map_half_width = (F32)(getRect().getWidth() / 2) ; - F32 text_half_height = (F32)(text_box->getRect().getHeight() / 2); - F32 text_half_width = (F32)(text_box->getRect().getWidth() / 2); - F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width ); + F32 map_half_height = (F32) (getRect().getHeight() / 2) - (getHeaderHeight() / 2); + F32 map_half_width = (F32) (getRect().getWidth() / 2); + F32 text_half_height = (F32) (text_box->getRect().getHeight() / 2); + F32 text_half_width = (F32) (text_box->getRect().getWidth() / 2); + F32 extra_padding = (F32) (mTextBoxNorth->getRect().getWidth() / 2); + F32 pos_half_height = map_half_height - text_half_height - extra_padding; + F32 pos_half_width = map_half_width - text_half_width - extra_padding; - // Inset by a little to account for position display. - radius -= 8.f; + F32 corner_angle = atan2(pos_half_height, pos_half_width); + F32 rotation_mirrored_into_top = abs(fmodf(rotation, F_PI)); + if (rotation < 0) + { + rotation_mirrored_into_top = F_PI - rotation_mirrored_into_top; + } + F32 rotation_mirrored_into_top_right = (F_PI_BY_TWO - abs(rotation_mirrored_into_top - F_PI_BY_TWO)); + bool at_left_right_edge = rotation_mirrored_into_top_right < corner_angle; - text_box->setOrigin( - ll_round(map_half_width - text_half_width + radius * cos( rotation )), - ll_round(map_half_height - text_half_height + radius * sin( rotation )) ); + F32 part_x = cos(rotation); + F32 part_y = sin(rotation); + F32 y; + F32 x; + if (at_left_right_edge) + { + x = std::copysign(pos_half_width, part_x); + y = x * part_y / part_x; + } + else + { + y = std::copysign(pos_half_height, part_y); + x = y * part_x / part_y; + } + + text_box->setOrigin(ll_round(map_half_width + x - text_half_width), ll_round(map_half_height + y - text_half_height)); } void LLFloaterMap::updateMinorDirections() @@ -218,32 +248,6 @@ void LLFloaterMap::reshape(S32 width, S32 height, BOOL called_from_parent) updateMinorDirections(); } -void LLFloaterMap::handleZoom(const LLSD& userdata) -{ - std::string level = userdata.asString(); - - F32 scale = 0.0f; - if (level == std::string("default")) - { - LLControlVariable *pvar = gSavedSettings.getControl("MiniMapScale"); - if(pvar) - { - pvar->resetToDefault(); - scale = gSavedSettings.getF32("MiniMapScale"); - } - } - else if (level == std::string("close")) - scale = LLNetMap::MAP_SCALE_MAX; - else if (level == std::string("medium")) - scale = LLNetMap::MAP_SCALE_MID; - else if (level == std::string("far")) - scale = LLNetMap::MAP_SCALE_MIN; - if (scale != 0.0f) - { - mMap->setScale(scale); - } -} - LLFloaterMap* LLFloaterMap::getInstance() { return LLFloaterReg::getTypedInstance("mini_map"); diff --git a/indra/newview/llfloatermap.h b/indra/newview/llfloatermap.h index ff2fb20535..929b1795aa 100644 --- a/indra/newview/llfloatermap.h +++ b/indra/newview/llfloatermap.h @@ -48,7 +48,6 @@ public: /*virtual*/ void draw(); private: - void handleZoom(const LLSD& userdata); void setDirectionPos( LLTextBox* text_box, F32 rotation ); void updateMinorDirections(); diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp index 524162ba51..e755e9924c 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -579,7 +579,25 @@ void LLFloaterMarketplaceListings::updateView() // Update the top message or flip to the tabs and folders view // *TODO : check those messages and create better appropriate ones in strings.xml - if (mRootFolderId.notNull()) + if (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE) + { + std::string reason = LLMarketplaceData::instance().getSLMConnectionfailureReason(); + if (reason.empty()) + { + text = LLTrans::getString("InventoryMarketplaceConnectionError"); + } + else + { + LLSD args; + args["[REASON]"] = reason; + text = LLTrans::getString("InventoryMarketplaceConnectionErrorReason", args); + } + + title = LLTrans::getString("InventoryOutboxErrorTitle"); + tooltip = LLTrans::getString("InventoryOutboxErrorTooltip"); + LL_WARNS() << "Marketplace status code: " << mkt_status << LL_ENDL; + } + else if (mRootFolderId.notNull()) { // "Marketplace listings is empty!" message strings text = LLTrans::getString("InventoryMarketplaceListingsNoItems", subs); diff --git a/indra/newview/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp index 2afd889609..b34961e8a2 100644 --- a/indra/newview/llfloatermediasettings.cpp +++ b/indra/newview/llfloatermediasettings.cpp @@ -156,6 +156,18 @@ void LLFloaterMediaSettings::apply() } } +//////////////////////////////////////////////////////////////////////////////// +void LLFloaterMediaSettings::onOpen(const LLSD& key) +{ + if (mPanelMediaSettingsGeneral) + { + // media is expensive, so only load it when nessesary. + // If we need to preload it, set volume to 0 and any pause + // if applicable, then unpause here + mPanelMediaSettingsGeneral->updateMediaPreview(); + } +} + //////////////////////////////////////////////////////////////////////////////// void LLFloaterMediaSettings::onClose(bool app_quitting) { diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h index f93512eb3a..151e43e6b9 100644 --- a/indra/newview/llfloatermediasettings.h +++ b/indra/newview/llfloatermediasettings.h @@ -42,6 +42,7 @@ public: ~LLFloaterMediaSettings(); /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void onClose(bool app_quitting); static LLFloaterMediaSettings* getInstance(); diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp index fe5120376c..66a245b779 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -467,22 +467,25 @@ void LLFloaterModelPreview::loadHighLodModel() loadModel(3); } -void LLFloaterModelPreview::loadModel(S32 lod) +void LLFloaterModelPreview::prepareToLoadModel(S32 lod) { mModelPreview->mLoading = true; if (lod == LLModel::LOD_PHYSICS) { // loading physics from file mModelPreview->mPhysicsSearchLOD = lod; + mModelPreview->mWarnOfUnmatchedPhyicsMeshes = false; } - +} +void LLFloaterModelPreview::loadModel(S32 lod) +{ + prepareToLoadModel(lod); (new LLMeshFilePicker(mModelPreview, lod))->getFile(); } void LLFloaterModelPreview::loadModel(S32 lod, const std::string& file_name, bool force_disable_slm) { - mModelPreview->mLoading = true; - + prepareToLoadModel(lod); mModelPreview->loadModel(file_name, lod, force_disable_slm); } @@ -507,6 +510,15 @@ void LLFloaterModelPreview::onClickCalculateBtn() toggleCalculateButton(false); mUploadBtn->setEnabled(false); + + //disable "simplification" UI + LLPanel* simplification_panel = getChild("physics simplification"); + LLView* child = simplification_panel->getFirstChild(); + while (child) + { + child->setEnabled(false); + child = simplification_panel->findNextSibling(child); + } } // Modified cell_params, make sure to clear values if you have to reuse cell_params outside of this function @@ -741,7 +753,7 @@ void LLFloaterModelPreview::onLODParamCommit(S32 lod, bool enforce_tri_limit) { case LLModelPreview::MESH_OPTIMIZER_AUTO: case LLModelPreview::MESH_OPTIMIZER_SLOPPY: - case LLModelPreview::MESH_OPTIMIZER_COMBINE: + case LLModelPreview::MESH_OPTIMIZER_PRECISE: mModelPreview->onLODMeshOptimizerParamCommit(lod, enforce_tri_limit, mode); break; default: @@ -1063,11 +1075,18 @@ void LLFloaterModelPreview::onPhysicsUseLOD(LLUICtrl* ctrl, void* userdata) } S32 file_mode = iface->getItemCount() - 1; - if (which_mode < file_mode) + S32 cube_mode = file_mode - 1; + if (which_mode < cube_mode) { S32 which_lod = num_lods - which_mode; sInstance->mModelPreview->setPhysicsFromLOD(which_lod); } + else if (which_mode == cube_mode) + { + std::string path = gDirUtilp->getAppRODataDir(); + gDirUtilp->append(path, "cube.dae"); + sInstance->loadModel(LLModel::LOD_PHYSICS, path); + } LLModelPreview *model_preview = sInstance->mModelPreview; if (model_preview) @@ -1662,15 +1681,15 @@ LLFloaterModelPreview::DecompRequest::DecompRequest(const std::string& stage, LL void LLFloaterModelPreview::setCtrlLoadFromFile(S32 lod) { if (lod == LLModel::LOD_PHYSICS) - { + { LLComboBox* lod_combo = findChild("physics_lod_combo"); if (lod_combo) { - lod_combo->setCurrentByIndex(5); + lod_combo->setCurrentByIndex(lod_combo->getItemCount() - 1); } } else -{ + { LLComboBox* lod_combo = findChild("lod_source_" + lod_name[lod]); if (lod_combo) { @@ -1745,7 +1764,7 @@ void LLFloaterModelPreview::onLoDSourceCommit(S32 lod) S32 index = lod_source_combo->getCurrentIndex(); if (index == LLModelPreview::MESH_OPTIMIZER_AUTO || index == LLModelPreview::MESH_OPTIMIZER_SLOPPY - || index == LLModelPreview::MESH_OPTIMIZER_COMBINE) + || index == LLModelPreview::MESH_OPTIMIZER_PRECISE) { //rebuild LoD to update triangle counts onLODParamCommit(lod, true); } diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h index 1e147cd555..bda042186b 100644 --- a/indra/newview/llfloatermodelpreview.h +++ b/indra/newview/llfloatermodelpreview.h @@ -221,6 +221,7 @@ private: void resetUploadOptions(); void clearLogTab(); + void prepareToLoadModel(S32 lod); void createSmoothComboBox(LLComboBox* combo_box, float min, float max); diff --git a/indra/newview/llfloateroutfitsnapshot.cpp b/indra/newview/llfloateroutfitsnapshot.cpp index dccef88e41..ad5e97e067 100644 --- a/indra/newview/llfloateroutfitsnapshot.cpp +++ b/indra/newview/llfloateroutfitsnapshot.cpp @@ -42,7 +42,6 @@ #include "llviewercontrol.h" #include "lltoolfocus.h" #include "lltoolmgr.h" -#include "llwebprofile.h" ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs diff --git a/indra/newview/llfloaterpay.cpp b/indra/newview/llfloaterpay.cpp index 87973c2286..94261b2e4e 100644 --- a/indra/newview/llfloaterpay.cpp +++ b/indra/newview/llfloaterpay.cpp @@ -72,7 +72,7 @@ struct LLGiveMoneyInfo mFloater(floater), mAmount(amount){} }; -typedef boost::shared_ptr give_money_ptr; +typedef std::shared_ptr give_money_ptr; ///---------------------------------------------------------------------------- /// Class LLFloaterPay diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 8d19aa36bb..273810e8d4 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -333,60 +333,59 @@ void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType t const LLAvatarData* pAvatarData = static_cast( pData ); if (pAvatarData && (gAgent.getID() == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null)) { - storeAvatarProperties( pAvatarData ); - processProfileProperties( pAvatarData ); + mAllowPublish = (bool)(pAvatarData->flags & AVATAR_ALLOW_PUBLISH); + mAvatarDataInitialized = true; + getChild("online_searchresults")->setValue(mAllowPublish); } } } -void LLFloaterPreference::storeAvatarProperties( const LLAvatarData* pAvatarData ) -{ - if (LLStartUp::getStartupState() == STATE_STARTED) - { - mAvatarProperties.avatar_id = pAvatarData->avatar_id; - mAvatarProperties.image_id = pAvatarData->image_id; - mAvatarProperties.fl_image_id = pAvatarData->fl_image_id; - mAvatarProperties.about_text = pAvatarData->about_text; - mAvatarProperties.fl_about_text = pAvatarData->fl_about_text; - mAvatarProperties.profile_url = pAvatarData->profile_url; - mAvatarProperties.flags = pAvatarData->flags; - mAvatarProperties.allow_publish = pAvatarData->flags & AVATAR_ALLOW_PUBLISH; - - mAvatarDataInitialized = true; - } -} - -void LLFloaterPreference::processProfileProperties(const LLAvatarData* pAvatarData ) -{ - getChild("online_searchresults")->setValue( (bool)(pAvatarData->flags & AVATAR_ALLOW_PUBLISH) ); -} - void LLFloaterPreference::saveAvatarProperties( void ) { - const BOOL allowPublish = getChild("online_searchresults")->getValue(); + const bool allowPublish = getChild("online_searchresults")->getValue(); - if (allowPublish) - { - mAvatarProperties.flags |= AVATAR_ALLOW_PUBLISH; - } + if ((LLStartUp::getStartupState() == STATE_STARTED) + && mAvatarDataInitialized + && (allowPublish != mAllowPublish)) + { + std::string cap_url = gAgent.getRegionCapability("AgentProfile"); + if (!cap_url.empty()) + { + mAllowPublish = allowPublish; - // - // NOTE: We really don't want to send the avatar properties unless we absolutely - // need to so we can avoid the accidental profile reset bug, so, if we're - // logged in, the avatar data has been initialized and we have a state change - // for the "allow publish" flag, then set the flag to its new value and send - // the properties update. - // - // NOTE: The only reason we can not remove this update altogether is because of the - // "allow publish" flag, the last remaining profile setting in the viewer - // that doesn't exist in the web profile. - // - if ((LLStartUp::getStartupState() == STATE_STARTED) && mAvatarDataInitialized && (allowPublish != mAvatarProperties.allow_publish)) - { - mAvatarProperties.allow_publish = allowPublish; + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(saveAvatarPropertiesCoro, cap_url, allowPublish)); + } + } +} - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPropertiesUpdate( &mAvatarProperties ); - } +void LLFloaterPreference::saveAvatarPropertiesCoro(const std::string cap_url, bool allow_publish) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_avatar_properties_coro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders; + + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + httpOpts->setFollowRedirects(true); + + std::string finalUrl = cap_url + "/" + gAgentID.asString(); + LLSD data; + data["allow_publish"] = allow_publish; + + LLSD result = httpAdapter->putAndSuspend(httpRequest, finalUrl, data, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS("Preferences") << "Failed to put agent information " << data << " for id " << gAgentID << LL_ENDL; + return; + } + + LL_DEBUGS("Preferences") << "Agent id: " << gAgentID << " Data: " << data << " Result: " << httpResults << LL_ENDL; } BOOL LLFloaterPreference::postBuild() @@ -938,7 +937,7 @@ void LLFloaterPreference::onBtnOK(const LLSD& userdata) else { // Show beep, pop up dialog, etc. - LL_INFOS() << "Can't close preferences!" << LL_ENDL; + LL_INFOS("Preferences") << "Can't close preferences!" << LL_ENDL; } LLPanelLogin::updateLocationSelectorsVisibility(); @@ -1200,7 +1199,6 @@ void LLFloaterPreference::refreshEnabledState() BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && bumpshiny && shaders && - gGLManager.mHasFramebufferObject && (ctrl_wind_light->get()) ? TRUE : FALSE; ctrl_deferred->setEnabled(enabled); @@ -1251,7 +1249,6 @@ void LLFloaterPreferenceGraphicsAdvanced::refreshEnabledState() BOOL enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && ((bumpshiny_ctrl && bumpshiny_ctrl->get()) ? TRUE : FALSE) && - gGLManager.mHasFramebufferObject && (ctrl_wind_light->get()) ? TRUE : FALSE; ctrl_deferred->setEnabled(enabled); @@ -1385,8 +1382,7 @@ void LLFloaterPreferenceGraphicsAdvanced::disableUnavailableSettings() } // disabled deferred - if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") || - !gGLManager.mHasFramebufferObject) + if (!LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred")) { ctrl_shadows->setEnabled(FALSE); ctrl_shadows->setValue(0); @@ -1793,13 +1789,13 @@ bool LLFloaterPreference::loadFromFilename(const std::string& filename, std::map if (!LLXMLNode::parseFile(filename, root, NULL)) { - LL_WARNS() << "Unable to parse file " << filename << LL_ENDL; + LL_WARNS("Preferences") << "Unable to parse file " << filename << LL_ENDL; return false; } if (!root->hasName("labels")) { - LL_WARNS() << filename << " is not a valid definition file" << LL_ENDL; + LL_WARNS("Preferences") << filename << " is not a valid definition file" << LL_ENDL; return false; } @@ -1819,7 +1815,7 @@ bool LLFloaterPreference::loadFromFilename(const std::string& filename, std::map } else { - LL_WARNS() << filename << " failed to load" << LL_ENDL; + LL_WARNS("Preferences") << filename << " failed to load" << LL_ENDL; return false; } @@ -2725,7 +2721,7 @@ bool LLPanelPreferenceControls::addControlTableColumns(const std::string &filena LLScrollListCtrl::Contents contents; if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) { - LL_WARNS() << "Failed to load " << filename << LL_ENDL; + LL_WARNS("Preferences") << "Failed to load " << filename << LL_ENDL; return false; } LLXUIParser parser; @@ -2752,7 +2748,7 @@ bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) LLScrollListCtrl::Contents contents; if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) { - LL_WARNS() << "Failed to load " << filename << LL_ENDL; + LL_WARNS("Preferences") << "Failed to load " << filename << LL_ENDL; return false; } LLXUIParser parser; @@ -2858,7 +2854,7 @@ void LLPanelPreferenceControls::populateControlTable() { // Either unknown mode or MODE_SAVED_SETTINGS // It doesn't have UI or actual settings yet - LL_WARNS() << "Unimplemented mode" << LL_ENDL; + LL_WARNS("Preferences") << "Unimplemented mode" << LL_ENDL; // Searchable columns were removed, mark searchables for an update LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); @@ -2898,7 +2894,7 @@ void LLPanelPreferenceControls::populateControlTable() } else { - LL_WARNS() << "Unimplemented mode" << LL_ENDL; + LL_WARNS("Preferences") << "Unimplemented mode" << LL_ENDL; } // explicit update to make sure table is ready for llsearchableui @@ -2953,10 +2949,15 @@ void LLPanelPreferenceControls::cancel() if (mConflictHandler[i].hasUnsavedChanges()) { mConflictHandler[i].clear(); + if (mEditingMode == i) + { + // cancel() can be called either when preferences floater closes + // or when child floater closes (like advanced graphical settings) + // in which case we need to clear and repopulate table + regenerateControls(); + } } } - pControlsTable->clearRows(); - pControlsTable->clearColumns(); } void LLPanelPreferenceControls::saveSettings() diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 5ef2ca68d3..542df18ddb 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -100,9 +100,8 @@ public: static void updateShowFavoritesCheckbox(bool val); void processProperties( void* pData, EAvatarProcessorType type ); - void processProfileProperties(const LLAvatarData* pAvatarData ); - void storeAvatarProperties( const LLAvatarData* pAvatarData ); void saveAvatarProperties( void ); + static void saveAvatarPropertiesCoro(const std::string url, bool allow_publish); void selectPrivacyPanel(); void selectChatPanel(); void getControlNames(std::vector& names); @@ -213,7 +212,7 @@ private: bool mOriginalHideOnlineStatus; std::string mDirectoryVisibility; - LLAvatarData mAvatarProperties; + bool mAllowPublish; // Allow showing agent in search std::string mSavedCameraPreset; std::string mSavedGraphicsPreset; LOG_CLASS(LLFloaterPreference); diff --git a/indra/newview/llfloaterprofile.cpp b/indra/newview/llfloaterprofile.cpp new file mode 100644 index 0000000000..6ccdace6c5 --- /dev/null +++ b/indra/newview/llfloaterprofile.cpp @@ -0,0 +1,170 @@ +/** + * @file llfloaterprofile.cpp + * @brief Avatar profile floater. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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 "llfloaterprofile.h" + +#include "llagent.h" //gAgent +#include "llnotificationsutil.h" +#include "llpanelavatar.h" +#include "llpanelprofile.h" + +static const std::string PANEL_PROFILE_VIEW = "panel_profile_view"; + +LLFloaterProfile::LLFloaterProfile(const LLSD& key) + : LLFloater(key), + mAvatarId(key["id"].asUUID()), + mNameCallbackConnection() +{ + mDefaultRectForGroup = false; +} + +LLFloaterProfile::~LLFloaterProfile() +{ + if (mNameCallbackConnection.connected()) + { + mNameCallbackConnection.disconnect(); + } +} + +void LLFloaterProfile::onOpen(const LLSD& key) +{ + mPanelProfile->onOpen(key); + + // Update the avatar name. + mNameCallbackConnection = LLAvatarNameCache::get(mAvatarId, boost::bind(&LLFloaterProfile::onAvatarNameCache, this, _1, _2)); +} + +BOOL LLFloaterProfile::postBuild() +{ + mPanelProfile = findChild(PANEL_PROFILE_VIEW); + + return TRUE; +} + +void LLFloaterProfile::onClickCloseBtn(bool app_quitting) +{ + if (!app_quitting) + { + if (mPanelProfile->hasUnpublishedClassifieds()) + { + LLNotificationsUtil::add("ProfileUnpublishedClassified", LLSD(), LLSD(), + boost::bind(&LLFloaterProfile::onUnsavedChangesCallback, this, _1, _2, false)); + } + else if (mPanelProfile->hasUnsavedChanges()) + { + LLNotificationsUtil::add("ProfileUnsavedChanges", LLSD(), LLSD(), + boost::bind(&LLFloaterProfile::onUnsavedChangesCallback, this, _1, _2, true)); + } + else + { + closeFloater(); + } + } + else + { + closeFloater(); + } +} + +void LLFloaterProfile::onUnsavedChangesCallback(const LLSD& notification, const LLSD& response, bool can_save) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (can_save) + { + // savable content + + if (option == 0) // Save + { + mPanelProfile->commitUnsavedChanges(); + closeFloater(); + } + if (option == 1) // Discard + { + closeFloater(); + } + // else cancel + } + else + { + // classifieds + + if (option == 0) // Ok + { + closeFloater(); + } + // else cancel + } + +} + +void LLFloaterProfile::createPick(const LLPickData &data) +{ + mPanelProfile->createPick(data); +} + +void LLFloaterProfile::showPick(const LLUUID& pick_id) +{ + mPanelProfile->showPick(pick_id); +} + +bool LLFloaterProfile::isPickTabSelected() +{ + return mPanelProfile->isPickTabSelected(); +} + +void LLFloaterProfile::refreshName() +{ + if (!mNameCallbackConnection.connected()) + { + mNameCallbackConnection = LLAvatarNameCache::get(mAvatarId, boost::bind(&LLFloaterProfile::onAvatarNameCache, this, _1, _2)); + } + + LLPanelProfileSecondLife *panel = findChild("panel_profile_secondlife"); + if (panel) + { + panel->refreshName(); + } +} + +void LLFloaterProfile::showClassified(const LLUUID& classified_id, bool edit) +{ + mPanelProfile->showClassified(classified_id, edit); +} + +void LLFloaterProfile::createClassified() +{ + mPanelProfile->createClassified(); +} + +void LLFloaterProfile::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + mNameCallbackConnection.disconnect(); + setTitle(av_name.getCompleteName()); +} + +// eof diff --git a/indra/newview/llfloaterprofile.h b/indra/newview/llfloaterprofile.h new file mode 100644 index 0000000000..b3ed02fc2c --- /dev/null +++ b/indra/newview/llfloaterprofile.h @@ -0,0 +1,66 @@ +/** + * @file llfloaterprofile.h + * @brief Avatar profile floater. + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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_LLFLOATERPROFILE_H +#define LL_LLFLOATERPROFILE_H + +#include "llavatarnamecache.h" +#include "llavatarpropertiesprocessor.h" +#include "llfloater.h" + +class LLPanelProfile; + +class LLFloaterProfile : public LLFloater +{ + LOG_CLASS(LLFloaterProfile); +public: + LLFloaterProfile(const LLSD& key); + virtual ~LLFloaterProfile(); + + BOOL postBuild() override; + + void onOpen(const LLSD& key) override; + void onClickCloseBtn(bool app_quitting = false) override; + void onUnsavedChangesCallback(const LLSD& notification, const LLSD& response, bool can_save); + + void createPick(const LLPickData &data); + void showPick(const LLUUID& pick_id = LLUUID::null); + bool isPickTabSelected(); + void refreshName(); + + void showClassified(const LLUUID& classified_id = LLUUID::null, bool edit = false); + void createClassified(); + +private: + LLAvatarNameCache::callback_connection_t mNameCallbackConnection; + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + + LLPanelProfile* mPanelProfile; + + LLUUID mAvatarId; +}; + +#endif // LL_LLFLOATERPROFILE_H diff --git a/indra/newview/llfloaterprofiletexture.cpp b/indra/newview/llfloaterprofiletexture.cpp new file mode 100644 index 0000000000..bf1f56a6d1 --- /dev/null +++ b/indra/newview/llfloaterprofiletexture.cpp @@ -0,0 +1,223 @@ +/** + * @file llfloaterprofiletexture.cpp + * @brief LLFloaterProfileTexture class implementation + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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 "llfloaterprofiletexture.h" + +#include "llbutton.h" +#include "llfloaterreg.h" +#include "llpreview.h" // fors constants +#include "lltrans.h" +#include "llviewercontrol.h" +#include "lltextureview.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" + + + +LLFloaterProfileTexture::LLFloaterProfileTexture(LLView* owner) + : LLFloater(LLSD()) + , mUpdateDimensions(TRUE) + , mLastHeight(0) + , mLastWidth(0) + , mImage(NULL) + , mImageOldBoostLevel(LLGLTexture::BOOST_NONE) + , mOwnerHandle(owner->getHandle()) +{ + buildFromFile("floater_profile_texture.xml"); +} + +LLFloaterProfileTexture::~LLFloaterProfileTexture() +{ + if (mImage.notNull()) + { + mImage->setBoostLevel(mImageOldBoostLevel); + mImage = NULL; + } +} + +// virtual +BOOL LLFloaterProfileTexture::postBuild() +{ + mProfileIcon = getChild("profile_pic"); + + mCloseButton = getChild("close_btn"); + mCloseButton->setCommitCallback([this](LLUICtrl*, void*) { closeFloater(); }, nullptr); + + return TRUE; +} + +// virtual +void LLFloaterProfileTexture::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLFloater::reshape(width, height, called_from_parent); +} + +// It takes a while until we get height and width information. +// When we receive it, reshape the window accordingly. +void LLFloaterProfileTexture::updateDimensions() +{ + if (mImage.isNull()) + { + return; + } + if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0) + { + return; + } + + S32 img_width = mImage->getFullWidth(); + S32 img_height = mImage->getFullHeight(); + + if (mAssetStatus != LLPreview::PREVIEW_ASSET_LOADED + || mLastWidth != img_width + || mLastHeight != img_height) + { + mAssetStatus = LLPreview::PREVIEW_ASSET_LOADED; + // Asset has been fully loaded + mUpdateDimensions = TRUE; + } + + mLastHeight = img_height; + mLastWidth = img_width; + + // Reshape the floater only when required + if (mUpdateDimensions) + { + mUpdateDimensions = FALSE; + + LLRect old_floater_rect = getRect(); + LLRect old_image_rect = mProfileIcon->getRect(); + S32 width = old_floater_rect.getWidth() - old_image_rect.getWidth() + mLastWidth; + S32 height = old_floater_rect.getHeight() - old_image_rect.getHeight() + mLastHeight; + + const F32 MAX_DIMENTIONS = 512; // most profiles are supposed to be 256x256 + + S32 biggest_dim = llmax(width, height); + if (biggest_dim > MAX_DIMENTIONS) + { + F32 scale_down = MAX_DIMENTIONS / (F32)biggest_dim; + width *= scale_down; + height *= scale_down; + } + + //reshape floater + reshape(width, height); + + gFloaterView->adjustToFitScreen(this, FALSE); + } +} + +void LLFloaterProfileTexture::draw() +{ + // drawFrustum + LLView *owner = mOwnerHandle.get(); + static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); + drawConeToOwner(mContextConeOpacity, max_opacity, owner); + + LLFloater::draw(); +} + +void LLFloaterProfileTexture::onOpen(const LLSD& key) +{ + mCloseButton->setFocus(true); +} + +void LLFloaterProfileTexture::resetAsset() +{ + mProfileIcon->setValue("Generic_Person_Large"); + mImageID = LLUUID::null; + if (mImage.notNull()) + { + mImage->setBoostLevel(mImageOldBoostLevel); + mImage = NULL; + } +} +void LLFloaterProfileTexture::loadAsset(const LLUUID &image_id) +{ + if (mImageID != image_id) + { + if (mImage.notNull()) + { + mImage->setBoostLevel(mImageOldBoostLevel); + mImage = NULL; + } + } + else + { + return; + } + + mProfileIcon->setValue(image_id); + mImageID = image_id; + mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + mImageOldBoostLevel = mImage->getBoostLevel(); + + if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0) + { + mImage->setLoadedCallback(LLFloaterProfileTexture::onTextureLoaded, + 0, TRUE, FALSE, new LLHandle(getHandle()), &mCallbackTextureList); + + mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + mAssetStatus = LLPreview::PREVIEW_ASSET_LOADING; + } + else + { + mAssetStatus = LLPreview::PREVIEW_ASSET_LOADED; + } + + mUpdateDimensions = TRUE; + updateDimensions(); +} + +// static +void LLFloaterProfileTexture::onTextureLoaded( + BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata) +{ + LLHandle* handle = (LLHandle*)userdata; + + if (!handle->isDead()) + { + LLFloaterProfileTexture* floater = static_cast(handle->get()); + if (floater && success) + { + floater->mUpdateDimensions = TRUE; + floater->updateDimensions(); + } + } + + if (final || !success) + { + delete handle; + } +} diff --git a/indra/newview/llfloaterprofiletexture.h b/indra/newview/llfloaterprofiletexture.h new file mode 100644 index 0000000000..66a61213dd --- /dev/null +++ b/indra/newview/llfloaterprofiletexture.h @@ -0,0 +1,81 @@ +/** + * @file llfloaterprofiletexture.h + * @brief LLFloaterProfileTexture class definition + * + * $LicenseInfo:firstyear=2022&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2022, 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_LLFLOATERPROFILETEXTURE_H +#define LL_LLFLOATERPROFILETEXTURE_H + +#include "llfloater.h" +#include "llviewertexture.h" + +class LLButton; +class LLImageRaw; +class LLIconCtrl; + +class LLFloaterProfileTexture : public LLFloater +{ +public: + LLFloaterProfileTexture(LLView* owner); + ~LLFloaterProfileTexture(); + + void draw() override; + void onOpen(const LLSD& key) override; + + void resetAsset(); + void loadAsset(const LLUUID &image_id); + + + static void onTextureLoaded( + BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata); + + void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) override; +protected: + BOOL postBuild() override; + +private: + void updateDimensions(); + + LLUUID mImageID; + LLPointer mImage; + S32 mImageOldBoostLevel; + S32 mAssetStatus; + F32 mContextConeOpacity; + S32 mLastHeight; + S32 mLastWidth; + BOOL mUpdateDimensions; + + LLHandle mOwnerHandle; + LLIconCtrl* mProfileIcon; + LLButton* mCloseButton; + + LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList; +}; +#endif // LL_LLFLOATERPROFILETEXTURE_H diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 296e155d28..07d4dcae38 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -47,6 +47,7 @@ #include "llagent.h" #include "llappviewer.h" +#include "llavataractions.h" #include "llavatarname.h" #include "llfloateravatarpicker.h" #include "llbutton.h" @@ -1322,6 +1323,7 @@ void LLPanelRegionDebugInfo::onClickDebugConsole(void* data) BOOL LLPanelRegionTerrainInfo::validateTextureSizes() { + static const S32 MAX_TERRAIN_TEXTURE_SIZE = 1024; for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i) { std::string buffer; @@ -1343,17 +1345,19 @@ BOOL LLPanelRegionTerrainInfo::validateTextureSizes() LLSD args; args["TEXTURE_NUM"] = i+1; args["TEXTURE_BIT_DEPTH"] = llformat("%d",components * 8); + args["MAX_SIZE"] = MAX_TERRAIN_TEXTURE_SIZE; LLNotificationsUtil::add("InvalidTerrainBitDepth", args); return FALSE; } - if (width > 512 || height > 512) + if (width > MAX_TERRAIN_TEXTURE_SIZE || height > MAX_TERRAIN_TEXTURE_SIZE) { LLSD args; args["TEXTURE_NUM"] = i+1; args["TEXTURE_SIZE_X"] = width; args["TEXTURE_SIZE_Y"] = height; + args["MAX_SIZE"] = MAX_TERRAIN_TEXTURE_SIZE; LLNotificationsUtil::add("InvalidTerrainSize", args); return FALSE; @@ -1826,7 +1830,7 @@ void LLPanelEstateInfo::updateControls(LLViewerRegion* region) setCtrlsEnabled(god || owner || manager); getChildView("apply_btn")->setEnabled(FALSE); - + getChildView("estate_owner")->setEnabled(TRUE); getChildView("message_estate_btn")->setEnabled(god || owner || manager); getChildView("kick_user_from_estate_btn")->setEnabled(god || owner || manager); @@ -1888,6 +1892,8 @@ BOOL LLPanelEstateInfo::postBuild() getChild("externally_visible_radio")->setFocus(TRUE); + getChild("estate_owner")->setIsFriendCallback(LLAvatarActions::isFriend); + return LLPanelRegionInfo::postBuild(); } @@ -2134,6 +2140,7 @@ BOOL LLPanelEstateCovenant::postBuild() { mEstateNameText = getChild("estate_name_text"); mEstateOwnerText = getChild("estate_owner_text"); + mEstateOwnerText->setIsFriendCallback(LLAvatarActions::isFriend); mLastModifiedText = getChild("covenant_timestamp_text"); mEditor = getChild("covenant_editor"); LLButton* reset_button = getChild("reset_covenant"); @@ -3683,7 +3690,7 @@ void LLPanelEstateAccess::searchAgent(LLNameListCtrl* listCtrl, const std::strin if (!search_string.empty()) { listCtrl->setSearchColumn(0); // name column - listCtrl->selectItemByPrefix(search_string, FALSE); + listCtrl->searchItems(search_string, false, true); } else { diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index b73755cf4e..2df4ca973d 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -54,6 +54,7 @@ #include "llbutton.h" #include "llfloaterreg.h" #include "lltexturectrl.h" +#include "lltexteditor.h" #include "llscrolllistctrl.h" #include "lldispatcher.h" #include "llviewerobject.h" @@ -250,9 +251,6 @@ LLFloaterReporter::~LLFloaterReporter() mPosition.setVec(0.0f, 0.0f, 0.0f); - std::for_each(mMCDList.begin(), mMCDList.end(), DeletePointer() ); - mMCDList.clear(); - delete mResourceDatap; } @@ -661,6 +659,23 @@ void LLFloaterReporter::showFromAvatar(const LLUUID& avatar_id, const std::strin show(avatar_id, avatar_name); } +// static +void LLFloaterReporter::showFromChat(const LLUUID& avatar_id, const std::string& avatar_name, const std::string& time, const std::string& description) +{ + show(avatar_id, avatar_name); + + LLStringUtil::format_map_t args; + args["[MSG_TIME]"] = time; + args["[MSG_DESCRIPTION]"] = description; + + LLFloaterReporter *self = LLFloaterReg::findTypedInstance("reporter"); + if (self) + { + std::string description = self->getString("chat_report_format", args); + self->getChild("details_edit")->setValue(description); + } +} + void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id) { getChild("object_name")->setValue(object_name); @@ -1028,37 +1043,3 @@ void LLFloaterReporter::onClose(bool app_quitting) mSnapshotTimer.stop(); gSavedPerAccountSettings.setBOOL("PreviousScreenshotForReport", app_quitting); } - - -// void LLFloaterReporter::setDescription(const std::string& description, LLMeanCollisionData *mcd) -// { -// LLFloaterReporter *self = LLFloaterReg::findTypedInstance("reporter"); -// if (self) -// { -// self->getChild("details_edit")->setValue(description); - -// for_each(self->mMCDList.begin(), self->mMCDList.end(), DeletePointer()); -// self->mMCDList.clear(); -// if (mcd) -// { -// self->mMCDList.push_back(new LLMeanCollisionData(mcd)); -// } -// } -// } - -// void LLFloaterReporter::addDescription(const std::string& description, LLMeanCollisionData *mcd) -// { -// LLFloaterReporter *self = LLFloaterReg::findTypedInstance("reporter"); -// if (self) -// { -// LLTextEditor* text = self->getChild("details_edit"); -// if (text) -// { -// text->insertText(description); -// } -// if (mcd) -// { -// self->mMCDList.push_back(new LLMeanCollisionData(mcd)); -// } -// } -// } diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h index c678df7155..b6c70e866d 100644 --- a/indra/newview/llfloaterreporter.h +++ b/indra/newview/llfloaterreporter.h @@ -93,6 +93,7 @@ public: static void showFromObject(const LLUUID& object_id, const LLUUID& experience_id = LLUUID::null); static void showFromAvatar(const LLUUID& avatar_id, const std::string avatar_name); + static void showFromChat(const LLUUID& avatar_id, const std::string& avatar_name, const std::string& time, const std::string& description); static void showFromExperience(const LLUUID& experience_id); static void onClickSend (void *userdata); @@ -101,8 +102,6 @@ public: void onClickSelectAbuser (); static void closePickTool (void *userdata); static void uploadDoneCallback(const LLUUID &uuid, void* user_data, S32 result, LLExtStat ext_status); - static void addDescription(const std::string& description, LLMeanCollisionData *mcd = NULL); - static void setDescription(const std::string& description, LLMeanCollisionData *mcd = NULL); void setPickedObjectProperties(const std::string& object_name, const std::string& owner_name, const LLUUID owner_id); @@ -114,10 +113,8 @@ private: static void show(const LLUUID& object_id, const std::string& avatar_name = LLStringUtil::null, const LLUUID& experience_id = LLUUID::null); void takeScreenshot(bool use_prev_screenshot = false); - void sendReportViaCaps(std::string url); void uploadImage(); bool validateReport(); - void setReporterID(); LLSD gatherReport(); void sendReportViaLegacy(const LLSD & report); void sendReportViaCaps(std::string url, std::string sshot_url, const LLSD & report); @@ -144,7 +141,6 @@ private: BOOL mPicking; LLVector3 mPosition; BOOL mCopyrightWarningSeen; - std::list mMCDList; std::string mDefaultSummary; LLResourceData* mResourceDatap; boost::signals2::connection mAvatarNameCacheConnection; diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index 2e1fbb09e0..bb3ed77772 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -57,10 +57,10 @@ public: const size_t parts = tokens.size(); // get the (optional) category for the search - std::string category; + std::string collection; if (parts > 0) { - category = tokens[0].asString(); + collection = tokens[0].asString(); } // get the (optional) search string @@ -72,7 +72,7 @@ public: // create the LLSD arguments for the search floater LLFloaterSearch::Params p; - p.search.category = category; + p.search.collection = collection; p.search.query = LLURI::unescape(search_text); // open the search floater and perform the requested search @@ -83,8 +83,9 @@ public: LLSearchHandler gSearchHandler; LLFloaterSearch::SearchQuery::SearchQuery() -: category("category", ""), - query("query") +: category("category", ""), + collection("collection", ""), + query("query") {} LLFloaterSearch::LLFloaterSearch(const Params& key) : @@ -93,16 +94,16 @@ LLFloaterSearch::LLFloaterSearch(const Params& key) : { // declare a map that transforms a category name into // the URL suffix that is used to search that category - mCategoryPaths = LLSD::emptyMap(); - mCategoryPaths["all"] = "search"; - mCategoryPaths["people"] = "search/people"; - mCategoryPaths["places"] = "search/places"; - mCategoryPaths["events"] = "search/events"; - mCategoryPaths["groups"] = "search/groups"; - mCategoryPaths["wiki"] = "search/wiki"; - mCategoryPaths["land"] = "land"; - mCategoryPaths["destinations"] = "destinations"; - mCategoryPaths["classifieds"] = "classifieds"; + + mSearchType.insert("standard"); + mSearchType.insert("land"); + mSearchType.insert("classified"); + + mCollectionType.insert("events"); + mCollectionType.insert("destinations"); + mCollectionType.insert("places"); + mCollectionType.insert("groups"); + mCollectionType.insert("people"); } BOOL LLFloaterSearch::postBuild() @@ -157,31 +158,49 @@ void LLFloaterSearch::search(const SearchQuery &p) // work out the subdir to use based on the requested category LLSD subs; - if (mCategoryPaths.has(p.category)) + if (mSearchType.find(p.category) != mSearchType.end()) { - subs["CATEGORY"] = mCategoryPaths[p.category].asString(); + subs["TYPE"] = p.category; } else { - subs["CATEGORY"] = mCategoryPaths["all"].asString(); + subs["TYPE"] = "standard"; } // add the search query string subs["QUERY"] = LLURI::escape(p.query); + subs["COLLECTION"] = ""; + if (subs["TYPE"] == "standard") + { + if (mCollectionType.find(p.collection) != mCollectionType.end()) + { + subs["COLLECTION"] = "&collection_chosen=" + std::string(p.collection); + } + else + { + std::string collection_args(""); + for (std::set::iterator it = mCollectionType.begin(); it != mCollectionType.end(); ++it) + { + collection_args += "&collection_chosen=" + std::string(*it); + } + subs["COLLECTION"] = collection_args; + } + } + // add the user's preferred maturity (can be changed via prefs) std::string maturity; if (gAgent.prefersAdult()) { - maturity = "42"; // PG,Mature,Adult + maturity = "gma"; // PG,Mature,Adult } else if (gAgent.prefersMature()) { - maturity = "21"; // PG,Mature + maturity = "gm"; // PG,Mature } else { - maturity = "13"; // PG + maturity = "g"; // PG } subs["MATURITY"] = maturity; diff --git a/indra/newview/llfloatersearch.h b/indra/newview/llfloatersearch.h index 35b268e1b2..cc77ce696f 100644 --- a/indra/newview/llfloatersearch.h +++ b/indra/newview/llfloatersearch.h @@ -49,6 +49,7 @@ public: struct SearchQuery : public LLInitParam::Block { Optional category; + Optional collection; Optional query; SearchQuery(); @@ -84,7 +85,8 @@ public: private: /*virtual*/ BOOL postBuild(); - LLSD mCategoryPaths; + std::set mSearchType; + std::set mCollectionType; U8 mSearchGodLevel; }; diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 0429749e11..b6acba6558 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -46,7 +46,6 @@ #include "llfloaterreg.h" #include "llfocusmgr.h" #include "llmediaentry.h" -#include "llmediactrl.h" #include "llmenugl.h" #include "llnotificationsutil.h" #include "llpanelcontents.h" @@ -240,7 +239,6 @@ BOOL LLFloaterTools::postBuild() mRadioGroupMove = getChild("move_radio_group"); mRadioGroupEdit = getChild("edit_radio_group"); mBtnGridOptions = getChild("Options..."); - mTitleMedia = getChild("title_media"); mBtnLink = getChild("link_btn"); mBtnUnlink = getChild("unlink_btn"); @@ -329,7 +327,6 @@ LLFloaterTools::LLFloaterTools(const LLSD& key) mCheckSnapToGrid(NULL), mBtnGridOptions(NULL), - mTitleMedia(NULL), mComboGridMode(NULL), mCheckStretchUniform(NULL), mCheckStretchTexture(NULL), @@ -369,8 +366,7 @@ LLFloaterTools::LLFloaterTools(const LLSD& key) mLandImpactsObserver(NULL), mDirty(TRUE), - mHasSelection(TRUE), - mNeedMediaTitle(TRUE) + mHasSelection(TRUE) { gFloaterTools = this; @@ -394,9 +390,6 @@ LLFloaterTools::LLFloaterTools(const LLSD& key) mCommitCallbackRegistrar.add("BuildTool.applyToSelection", boost::bind(&click_apply_to_selection, this)); mCommitCallbackRegistrar.add("BuildTool.commitRadioLand", boost::bind(&commit_radio_group_land,_1)); mCommitCallbackRegistrar.add("BuildTool.LandBrushForce", boost::bind(&commit_slider_dozer_force,_1)); - mCommitCallbackRegistrar.add("BuildTool.AddMedia", boost::bind(&LLFloaterTools::onClickBtnAddMedia,this)); - mCommitCallbackRegistrar.add("BuildTool.DeleteMedia", boost::bind(&LLFloaterTools::onClickBtnDeleteMedia,this)); - mCommitCallbackRegistrar.add("BuildTool.EditMedia", boost::bind(&LLFloaterTools::onClickBtnEditMedia,this)); mCommitCallbackRegistrar.add("BuildTool.LinkObjects", boost::bind(&LLSelectMgr::linkObjects, LLSelectMgr::getInstance())); mCommitCallbackRegistrar.add("BuildTool.UnlinkObjects", boost::bind(&LLSelectMgr::unlinkObjects, LLSelectMgr::getInstance())); @@ -480,30 +473,61 @@ void LLFloaterTools::refresh() else #endif { - F32 link_cost = LLSelectMgr::getInstance()->getSelection()->getSelectedLinksetCost(); - S32 link_count = LLSelectMgr::getInstance()->getSelection()->getRootObjectCount(); + LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); + F32 link_cost = selection->getSelectedLinksetCost(); + S32 link_count = selection->getRootObjectCount(); + S32 object_count = selection->getObjectCount(); - LLCrossParcelFunctor func; - if (LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, true)) - { - // Selection crosses parcel bounds. - // We don't display remaining land capacity in this case. - const LLStringExplicit empty_str(""); - childSetTextArg("remaining_capacity", "[CAPACITY_STRING]", empty_str); - } - else - { - LLViewerObject* selected_object = mObjectSelection->getFirstObject(); - if (selected_object) - { - // Select a parcel at the currently selected object's position. - LLViewerParcelMgr::getInstance()->selectParcelAt(selected_object->getPositionGlobal()); - } - else - { - LL_WARNS() << "Failed to get selected object" << LL_ENDL; - } - } + LLCrossParcelFunctor func; + if (!LLSelectMgr::getInstance()->getSelection()->applyToRootObjects(&func, true)) + { + // Unless multiple parcels selected, higlight parcel object is at. + LLViewerObject* selected_object = mObjectSelection->getFirstObject(); + if (selected_object) + { + // Select a parcel at the currently selected object's position. + LLViewerParcelMgr::getInstance()->selectParcelAt(selected_object->getPositionGlobal()); + } + else + { + LL_WARNS() << "Failed to get selected object" << LL_ENDL; + } + } + + if (object_count == 1) + { + // "selection_faces" shouldn't be visible if not LLToolFace::getInstance() + // But still need to be populated in case user switches + + std::string faces_str = ""; + + for (LLObjectSelection::iterator iter = selection->begin(); iter != selection->end();) + { + LLObjectSelection::iterator nextiter = iter++; // not strictly needed, we have only one object + LLSelectNode* node = *nextiter; + LLViewerObject* object = (*nextiter)->getObject(); + if (!object) + continue; + S32 num_tes = llmin((S32)object->getNumTEs(), (S32)object->getNumFaces()); + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + if (!faces_str.empty()) + { + faces_str += ", "; + } + faces_str += llformat("%d", te); + } + } + } + + childSetTextArg("selection_faces", "[FACES_STRING]", faces_str); + } + + bool show_faces = (object_count == 1) + && LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool(); + getChildView("selection_faces")->setVisible(show_faces); LLStringUtil::format_map_t selection_args; selection_args["OBJ_COUNT"] = llformat("%.1d", link_count); @@ -522,7 +546,7 @@ void LLFloaterTools::refresh() mPanelObject->refresh(); mPanelVolume->refresh(); mPanelFace->refresh(); - refreshMedia(); + mPanelFace->refreshMedia(); mPanelContents->refresh(); mPanelLandInfo->refresh(); @@ -549,9 +573,6 @@ void LLFloaterTools::draw() mDirty = FALSE; } - // grab media name/title and update the UI widget - updateMediaTitle(); - // mCheckSelectIndividual->set(gSavedSettings.getBOOL("EditLinkedParts")); LLFloater::draw(); } @@ -824,7 +845,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) bool have_selection = !LLSelectMgr::getInstance()->getSelection()->isEmpty(); getChildView("selection_count")->setVisible(!land_visible && have_selection); - getChildView("remaining_capacity")->setVisible(!land_visible && have_selection); + getChildView("selection_faces")->setVisible(LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool() + && LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1); getChildView("selection_empty")->setVisible(!land_visible && !have_selection); mTab->setVisible(!land_visible); @@ -874,8 +896,7 @@ void LLFloaterTools::onClose(bool app_quitting) LLViewerJoystick::getInstance()->moveAvatar(false); // destroy media source used to grab media title - if( mTitleMedia ) - mTitleMedia->unloadMediaSource(); + mPanelFace->unloadMedia(); // Different from handle_reset_view in that it doesn't actually // move the camera if EditCameraMovement is not set. @@ -1095,7 +1116,7 @@ void LLFloaterTools::onClickGridOptions() { LLFloater* floaterp = LLFloaterReg::showInstance("build_options"); // position floater next to build tools, not over - floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp)); + floaterp->setShape(gFloaterView->findNeighboringPosition(this, floaterp), true); } // static @@ -1128,51 +1149,6 @@ void LLFloaterTools::onFocusReceived() LLFloater::onFocusReceived(); } -// Media stuff -void LLFloaterTools::refreshMedia() -{ - getMediaState(); -} - -bool LLFloaterTools::selectedMediaEditable() -{ - U32 owner_mask_on; - U32 owner_mask_off; - U32 valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_OWNER, - &owner_mask_on, &owner_mask_off ); - U32 group_mask_on; - U32 group_mask_off; - U32 valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_GROUP, - &group_mask_on, &group_mask_off ); - U32 everyone_mask_on; - U32 everyone_mask_off; - S32 valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_EVERYONE, - &everyone_mask_on, &everyone_mask_off ); - - bool selected_Media_editable = false; - - // if perms we got back are valid - if ( valid_owner_perms && - valid_group_perms && - valid_everyone_perms ) - { - - if ( ( owner_mask_on & PERM_MODIFY ) || - ( group_mask_on & PERM_MODIFY ) || - ( group_mask_on & PERM_MODIFY ) ) - { - selected_Media_editable = true; - } - else - // user is NOT allowed to press the RESET button - { - selected_Media_editable = false; - }; - }; - - return selected_Media_editable; -} - void LLFloaterTools::updateLandImpacts() { LLParcel *parcel = mParcelSelection->getParcel(); @@ -1181,26 +1157,6 @@ void LLFloaterTools::updateLandImpacts() return; } - S32 rezzed_prims = parcel->getSimWidePrimCount(); - S32 total_capacity = parcel->getSimWideMaxPrimCapacity(); - LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); - if (region) - { - S32 max_tasks_per_region = (S32)region->getMaxTasks(); - total_capacity = llmin(total_capacity, max_tasks_per_region); - } - std::string remaining_capacity_str = ""; - - bool show_mesh_cost = gMeshRepo.meshRezEnabled(); - if (show_mesh_cost) - { - LLStringUtil::format_map_t remaining_capacity_args; - remaining_capacity_args["LAND_CAPACITY"] = llformat("%d", total_capacity - rezzed_prims); - remaining_capacity_str = getString("status_remaining_capacity", remaining_capacity_args); - } - - childSetTextArg("remaining_capacity", "[CAPACITY_STRING]", remaining_capacity_str); - // Update land impacts info in the weights floater LLFloaterObjectWeights* object_weights_floater = LLFloaterReg::findTypedInstance("object_weights"); if(object_weights_floater) @@ -1209,784 +1165,3 @@ void LLFloaterTools::updateLandImpacts() } } -void LLFloaterTools::getMediaState() -{ - LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); - LLViewerObject* first_object = selected_objects->getFirstObject(); - LLTextBox* media_info = getChild("media_info"); - - if( !(first_object - && first_object->getPCode() == LL_PCODE_VOLUME - &&first_object->permModify() - )) - { - getChildView("add_media")->setEnabled(FALSE); - media_info->clear(); - clearMediaSettings(); - return; - } - - std::string url = first_object->getRegion()->getCapability("ObjectMedia"); - bool has_media_capability = (!url.empty()); - - if(!has_media_capability) - { - getChildView("add_media")->setEnabled(FALSE); - LL_WARNS("LLFloaterToolsMedia") << "Media not enabled (no capability) in this region!" << LL_ENDL; - clearMediaSettings(); - return; - } - - BOOL is_nonpermanent_enforced = (LLSelectMgr::getInstance()->getSelection()->getFirstRootNode() - && LLSelectMgr::getInstance()->selectGetRootsNonPermanentEnforced()) - || LLSelectMgr::getInstance()->selectGetNonPermanentEnforced(); - bool editable = is_nonpermanent_enforced && (first_object->permModify() || selectedMediaEditable()); - - // Check modify permissions and whether any selected objects are in - // the process of being fetched. If they are, then we're not editable - if (editable) - { - LLObjectSelection::iterator iter = selected_objects->begin(); - LLObjectSelection::iterator end = selected_objects->end(); - for ( ; iter != end; ++iter) - { - LLSelectNode* node = *iter; - LLVOVolume* object = dynamic_cast(node->getObject()); - if (NULL != object) - { - if (!object->permModify()) - { - LL_INFOS("LLFloaterToolsMedia") - << "Selection not editable due to lack of modify permissions on object id " - << object->getID() << LL_ENDL; - - editable = false; - break; - } - // XXX DISABLE this for now, because when the fetch finally - // does come in, the state of this floater doesn't properly - // update. Re-selecting fixes the problem, but there is - // contention as to whether this is a sufficient solution. -// if (object->isMediaDataBeingFetched()) -// { -// LL_INFOS("LLFloaterToolsMedia") -// << "Selection not editable due to media data being fetched for object id " -// << object->getID() << LL_ENDL; -// -// editable = false; -// break; -// } - } - } - } - - // Media settings - bool bool_has_media = false; - struct media_functor : public LLSelectedTEGetFunctor - { - bool get(LLViewerObject* object, S32 face) - { - LLTextureEntry *te = object->getTE(face); - if (te) - { - return te->hasMedia(); - } - return false; - } - } func; - - - // check if all faces have media(or, all dont have media) - LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue( &func, bool_has_media ); - - const LLMediaEntry default_media_data; - - struct functor_getter_media_data : public LLSelectedTEGetFunctor< LLMediaEntry> - { - functor_getter_media_data(const LLMediaEntry& entry): mMediaEntry(entry) {} - - LLMediaEntry get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return *(object->getTE(face)->getMediaData()); - return mMediaEntry; - }; - - const LLMediaEntry& mMediaEntry; - - } func_media_data(default_media_data); - - LLMediaEntry media_data_get; - LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue( &func_media_data, media_data_get )); - - std::string multi_media_info_str = LLTrans::getString("Multiple Media"); - std::string media_title = ""; - // update UI depending on whether "object" (prim or face) has media - // and whether or not you are allowed to edit it. - - getChildView("add_media")->setEnabled(editable); - // IF all the faces have media (or all dont have media) - if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) - { - // TODO: get media title and set it. - media_info->clear(); - // if identical is set, all faces are same (whether all empty or has the same media) - if(!(LLFloaterMediaSettings::getInstance()->mMultipleMedia) ) - { - // Media data is valid - if(media_data_get!=default_media_data) - { - // initial media title is the media URL (until we get the name) - media_title = media_data_get.getHomeURL(); - } - // else all faces might be empty. - } - else // there' re Different Medias' been set on on the faces. - { - media_title = multi_media_info_str; - } - - getChildView("delete_media")->setEnabled(bool_has_media && editable ); - // TODO: display a list of all media on the face - use 'identical' flag - } - else // not all face has media but at least one does. - { - // seleted faces have not identical value - LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data ); - - if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) - { - media_title = multi_media_info_str; - } - else - { - // Media data is valid - if(media_data_get!=default_media_data) - { - // initial media title is the media URL (until we get the name) - media_title = media_data_get.getHomeURL(); - } - } - - getChildView("delete_media")->setEnabled(TRUE); - } - - navigateToTitleMedia(media_title); - media_info->setText(media_title); - - // load values for media settings - updateMediaSettings(); - - LLFloaterMediaSettings::initValues(mMediaSettings, editable ); -} - - -////////////////////////////////////////////////////////////////////////////// -// called when a user wants to add media to a prim or prim face -void LLFloaterTools::onClickBtnAddMedia() -{ - // check if multiple faces are selected - if(LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) - { - LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm); - } - else - { - onClickBtnEditMedia(); - } -} - -// static -bool LLFloaterTools::multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - switch( option ) - { - case 0: // "Yes" - gFloaterTools->onClickBtnEditMedia(); - break; - case 1: // "No" - default: - break; - } - return false; -} - -////////////////////////////////////////////////////////////////////////////// -// called when a user wants to edit existing media settings on a prim or prim face -// TODO: test if there is media on the item and only allow editing if present -void LLFloaterTools::onClickBtnEditMedia() -{ - refreshMedia(); - LLFloaterReg::showInstance("media_settings"); -} - -////////////////////////////////////////////////////////////////////////////// -// called when a user wants to delete media from a prim or prim face -void LLFloaterTools::onClickBtnDeleteMedia() -{ - LLNotificationsUtil::add("DeleteMedia", LLSD(), LLSD(), deleteMediaConfirm); -} - - -// static -bool LLFloaterTools::deleteMediaConfirm(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - switch( option ) - { - case 0: // "Yes" - LLSelectMgr::getInstance()->selectionSetMedia( 0, LLSD() ); - if(LLFloaterReg::instanceVisible("media_settings")) - { - LLFloaterReg::hideInstance("media_settings"); - } - break; - - case 1: // "No" - default: - break; - } - return false; -} - -////////////////////////////////////////////////////////////////////////////// -// -void LLFloaterTools::clearMediaSettings() -{ - LLFloaterMediaSettings::clearValues(false); -} - -////////////////////////////////////////////////////////////////////////////// -// -void LLFloaterTools::navigateToTitleMedia( const std::string url ) -{ - std::string multi_media_info_str = LLTrans::getString("Multiple Media"); - if (url.empty() || multi_media_info_str == url) - { - // nothing to show - mNeedMediaTitle = false; - } - else if (mTitleMedia) - { - LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); - - if ( media_plugin ) // Shouldn't this be after navigateTo creates plugin? - { - // if it's a movie, we don't want to hear it - media_plugin->setVolume( 0 ); - }; - - // check if url changed or if we need a new media source - if (mTitleMedia->getCurrentNavUrl() != url || media_plugin == NULL) - { - mTitleMedia->navigateTo( url ); - } - - // flag that we need to update the title (even if no request were made) - mNeedMediaTitle = true; - } -} - -////////////////////////////////////////////////////////////////////////////// -// -void LLFloaterTools::updateMediaTitle() -{ - // only get the media name if we need it - if ( ! mNeedMediaTitle ) - return; - - // get plugin impl - LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); - if ( media_plugin ) - { - // get the media name (asynchronous - must call repeatedly) - std::string media_title = media_plugin->getMediaName(); - - // only replace the title if what we get contains something - if ( ! media_title.empty() ) - { - // update the UI widget - LLTextBox* media_title_field = getChild("media_info"); - if ( media_title_field ) - { - media_title_field->setText( media_title ); - - // stop looking for a title when we get one - // FIXME: check this is the right approach - mNeedMediaTitle = false; - }; - }; - }; -} - -////////////////////////////////////////////////////////////////////////////// -// -void LLFloaterTools::updateMediaSettings() -{ - bool identical( false ); - std::string base_key( "" ); - std::string value_str( "" ); - int value_int = 0; - bool value_bool = false; - LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); - // TODO: (CP) refactor this using something clever or boost or both !! - - const LLMediaEntry default_media_data; - - // controls - U8 value_u8 = default_media_data.getControls(); - struct functor_getter_controls : public LLSelectedTEGetFunctor< U8 > - { - functor_getter_controls(const LLMediaEntry &entry) : mMediaEntry(entry) {} - - U8 get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getControls(); - return mMediaEntry.getControls(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_controls(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_controls, value_u8 ); - base_key = std::string( LLMediaEntry::CONTROLS_KEY ); - mMediaSettings[ base_key ] = value_u8; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // First click (formerly left click) - value_bool = default_media_data.getFirstClickInteract(); - struct functor_getter_first_click : public LLSelectedTEGetFunctor< bool > - { - functor_getter_first_click(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getFirstClickInteract(); - return mMediaEntry.getFirstClickInteract(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_first_click(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_first_click, value_bool ); - base_key = std::string( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Home URL - value_str = default_media_data.getHomeURL(); - struct functor_getter_home_url : public LLSelectedTEGetFunctor< std::string > - { - functor_getter_home_url(const LLMediaEntry& entry): mMediaEntry(entry) {} - - std::string get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getHomeURL(); - return mMediaEntry.getHomeURL(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_home_url(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_home_url, value_str ); - base_key = std::string( LLMediaEntry::HOME_URL_KEY ); - mMediaSettings[ base_key ] = value_str; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Current URL - value_str = default_media_data.getCurrentURL(); - struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string > - { - functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {} - - std::string get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getCurrentURL(); - return mMediaEntry.getCurrentURL(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_current_url(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_current_url, value_str ); - base_key = std::string( LLMediaEntry::CURRENT_URL_KEY ); - mMediaSettings[ base_key ] = value_str; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Auto zoom - value_bool = default_media_data.getAutoZoom(); - struct functor_getter_auto_zoom : public LLSelectedTEGetFunctor< bool > - { - - functor_getter_auto_zoom(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getAutoZoom(); - return mMediaEntry.getAutoZoom(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_auto_zoom(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_auto_zoom, value_bool ); - base_key = std::string( LLMediaEntry::AUTO_ZOOM_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Auto play - //value_bool = default_media_data.getAutoPlay(); - // set default to auto play TRUE -- angela EXT-5172 - value_bool = true; - struct functor_getter_auto_play : public LLSelectedTEGetFunctor< bool > - { - functor_getter_auto_play(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getAutoPlay(); - //return mMediaEntry.getAutoPlay(); set default to auto play TRUE -- angela EXT-5172 - return true; - }; - - const LLMediaEntry &mMediaEntry; - - } func_auto_play(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_auto_play, value_bool ); - base_key = std::string( LLMediaEntry::AUTO_PLAY_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - - // Auto scale - // set default to auto scale TRUE -- angela EXT-5172 - //value_bool = default_media_data.getAutoScale(); - value_bool = true; - struct functor_getter_auto_scale : public LLSelectedTEGetFunctor< bool > - { - functor_getter_auto_scale(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getAutoScale(); - // return mMediaEntry.getAutoScale(); set default to auto scale TRUE -- angela EXT-5172 - return true; - }; - - const LLMediaEntry &mMediaEntry; - - } func_auto_scale(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_auto_scale, value_bool ); - base_key = std::string( LLMediaEntry::AUTO_SCALE_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Auto loop - value_bool = default_media_data.getAutoLoop(); - struct functor_getter_auto_loop : public LLSelectedTEGetFunctor< bool > - { - functor_getter_auto_loop(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getAutoLoop(); - return mMediaEntry.getAutoLoop(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_auto_loop(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_auto_loop, value_bool ); - base_key = std::string( LLMediaEntry::AUTO_LOOP_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // width pixels (if not auto scaled) - value_int = default_media_data.getWidthPixels(); - struct functor_getter_width_pixels : public LLSelectedTEGetFunctor< int > - { - functor_getter_width_pixels(const LLMediaEntry& entry): mMediaEntry(entry) {} - - int get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getWidthPixels(); - return mMediaEntry.getWidthPixels(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_width_pixels(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_width_pixels, value_int ); - base_key = std::string( LLMediaEntry::WIDTH_PIXELS_KEY ); - mMediaSettings[ base_key ] = value_int; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // height pixels (if not auto scaled) - value_int = default_media_data.getHeightPixels(); - struct functor_getter_height_pixels : public LLSelectedTEGetFunctor< int > - { - functor_getter_height_pixels(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - int get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getHeightPixels(); - return mMediaEntry.getHeightPixels(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_height_pixels(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_height_pixels, value_int ); - base_key = std::string( LLMediaEntry::HEIGHT_PIXELS_KEY ); - mMediaSettings[ base_key ] = value_int; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Enable Alt image - value_bool = default_media_data.getAltImageEnable(); - struct functor_getter_enable_alt_image : public LLSelectedTEGetFunctor< bool > - { - functor_getter_enable_alt_image(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getAltImageEnable(); - return mMediaEntry.getAltImageEnable(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_enable_alt_image(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_enable_alt_image, value_bool ); - base_key = std::string( LLMediaEntry::ALT_IMAGE_ENABLE_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Perms - owner interact - value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_OWNER ); - struct functor_getter_perms_owner_interact : public LLSelectedTEGetFunctor< bool > - { - functor_getter_perms_owner_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_OWNER)); - return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_OWNER ); - }; - - const LLMediaEntry &mMediaEntry; - - } func_perms_owner_interact(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_perms_owner_interact, value_bool ); - base_key = std::string( LLPanelContents::PERMS_OWNER_INTERACT_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Perms - owner control - value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_OWNER ); - struct functor_getter_perms_owner_control : public LLSelectedTEGetFunctor< bool > - { - functor_getter_perms_owner_control(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_OWNER)); - return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_OWNER ); - }; - - const LLMediaEntry &mMediaEntry; - - } func_perms_owner_control(default_media_data); - identical = selected_objects ->getSelectedTEValue( &func_perms_owner_control, value_bool ); - base_key = std::string( LLPanelContents::PERMS_OWNER_CONTROL_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Perms - group interact - value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_GROUP ); - struct functor_getter_perms_group_interact : public LLSelectedTEGetFunctor< bool > - { - functor_getter_perms_group_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_GROUP)); - return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_GROUP ); - }; - - const LLMediaEntry &mMediaEntry; - - } func_perms_group_interact(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_perms_group_interact, value_bool ); - base_key = std::string( LLPanelContents::PERMS_GROUP_INTERACT_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Perms - group control - value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_GROUP ); - struct functor_getter_perms_group_control : public LLSelectedTEGetFunctor< bool > - { - functor_getter_perms_group_control(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_GROUP)); - return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_GROUP ); - }; - - const LLMediaEntry &mMediaEntry; - - } func_perms_group_control(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_perms_group_control, value_bool ); - base_key = std::string( LLPanelContents::PERMS_GROUP_CONTROL_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Perms - anyone interact - value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); - struct functor_getter_perms_anyone_interact : public LLSelectedTEGetFunctor< bool > - { - functor_getter_perms_anyone_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_ANYONE)); - return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); - }; - - const LLMediaEntry &mMediaEntry; - - } func_perms_anyone_interact(default_media_data); - identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_perms_anyone_interact, value_bool ); - base_key = std::string( LLPanelContents::PERMS_ANYONE_INTERACT_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // Perms - anyone control - value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_ANYONE ); - struct functor_getter_perms_anyone_control : public LLSelectedTEGetFunctor< bool > - { - functor_getter_perms_anyone_control(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_ANYONE)); - return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_ANYONE ); - }; - - const LLMediaEntry &mMediaEntry; - - } func_perms_anyone_control(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_perms_anyone_control, value_bool ); - base_key = std::string( LLPanelContents::PERMS_ANYONE_CONTROL_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // security - whitelist enable - value_bool = default_media_data.getWhiteListEnable(); - struct functor_getter_whitelist_enable : public LLSelectedTEGetFunctor< bool > - { - functor_getter_whitelist_enable(const LLMediaEntry& entry) : mMediaEntry(entry) {} - - bool get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getWhiteListEnable(); - return mMediaEntry.getWhiteListEnable(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_whitelist_enable(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_whitelist_enable, value_bool ); - base_key = std::string( LLMediaEntry::WHITELIST_ENABLE_KEY ); - mMediaSettings[ base_key ] = value_bool; - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; - - // security - whitelist URLs - std::vector value_vector_str = default_media_data.getWhiteList(); - struct functor_getter_whitelist_urls : public LLSelectedTEGetFunctor< std::vector > - { - functor_getter_whitelist_urls(const LLMediaEntry& entry): mMediaEntry(entry) {} - - std::vector get( LLViewerObject* object, S32 face ) - { - if ( object ) - if ( object->getTE(face) ) - if ( object->getTE(face)->getMediaData() ) - return object->getTE(face)->getMediaData()->getWhiteList(); - return mMediaEntry.getWhiteList(); - }; - - const LLMediaEntry &mMediaEntry; - - } func_whitelist_urls(default_media_data); - identical = selected_objects->getSelectedTEValue( &func_whitelist_urls, value_vector_str ); - base_key = std::string( LLMediaEntry::WHITELIST_KEY ); - mMediaSettings[ base_key ].clear(); - std::vector< std::string >::iterator iter = value_vector_str.begin(); - while( iter != value_vector_str.end() ) - { - std::string white_list_url = *iter; - mMediaSettings[ base_key ].append( white_list_url ); - ++iter; - }; - - mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; -} - diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h index ffff564ad4..3bb6492a6e 100644 --- a/indra/newview/llfloatertools.h +++ b/indra/newview/llfloatertools.h @@ -44,7 +44,6 @@ class LLRadioGroup; class LLSlider; class LLTabContainer; class LLTextBox; -class LLMediaCtrl; class LLTool; class LLParcelSelection; class LLObjectSelection; @@ -98,11 +97,6 @@ public: static void setEditTool(void* data); void setTool(const LLSD& user_data); void saveLastTool(); - void onClickBtnDeleteMedia(); - void onClickBtnAddMedia(); - void onClickBtnEditMedia(); - void clearMediaSettings(); - bool selectedMediaEditable(); void updateLandImpacts(); static void setGridMode(S32 mode); @@ -111,13 +105,6 @@ public: private: void refresh(); - void refreshMedia(); - void getMediaState(); - void updateMediaSettings(); - void navigateToTitleMedia( const std::string url ); // navigate if changed - void updateMediaTitle(); - static bool deleteMediaConfirm(const LLSD& notification, const LLSD& response); - static bool multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response); static void setObjectType( LLPCode pcode ); void onClickGridOptions(); @@ -193,19 +180,12 @@ public: LLParcelSelectionHandle mParcelSelection; LLObjectSelectionHandle mObjectSelection; - LLMediaCtrl *mTitleMedia; - bool mNeedMediaTitle; - private: BOOL mDirty; BOOL mHasSelection; std::map mStatusText; - -protected: - LLSD mMediaSettings; - public: static bool sShowObjectCost; static bool sPreviousFocusOnAvatar; diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index d5c2ad5f81..917d6dfcd0 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -112,16 +112,6 @@ void LLFloaterURLEntry::headerFetchComplete(S32 status, const std::string& mime_ panel_media->setMediaType(mime_type); panel_media->setMediaURL(mMediaURLEdit->getValue().asString()); } - else - { - LLPanelFace* panel_face = dynamic_cast(mPanelLandMediaHandle.get()); - if(panel_face) - { - panel_face->setMediaType(mime_type); - panel_face->setMediaURL(mMediaURLEdit->getValue().asString()); - } - - } getChildView("loading_label")->setVisible( false); closeFloater(); diff --git a/indra/newview/llfloatervoicevolume.cpp b/indra/newview/llfloatervoicevolume.cpp index 59e1f49f81..23f19dd5aa 100644 --- a/indra/newview/llfloatervoicevolume.cpp +++ b/indra/newview/llfloatervoicevolume.cpp @@ -127,7 +127,7 @@ void LLFloaterVoiceVolume::onOpen(const LLSD& data) // Extract appropriate avatar id mAvatarID = data["avatar_id"]; - LLUI::getInstance()->positionViewNearMouse(this); + LLInspect::repositionInspector(data); getChild("avatar_name")->setValue(""); updateVolumeControls(); diff --git a/indra/newview/llfloaterwebprofile.cpp b/indra/newview/llfloaterwebprofile.cpp deleted file mode 100644 index 891bb90c0e..0000000000 --- a/indra/newview/llfloaterwebprofile.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @file llfloaterwebprofile.cpp - * @brief Avatar profile floater. - * - * $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 "llfloaterwebprofile.h" - -#include "llviewercontrol.h" - -LLFloaterWebProfile::LLFloaterWebProfile(const Params& key) : - LLFloaterWebContent(key) -{ -} - -void LLFloaterWebProfile::onOpen(const LLSD& key) -{ - Params p(key); - p.show_chrome(true); - p.window_class("profile"); - p.allow_address_entry(false); - p.trusted_content(true); - LLFloaterWebContent::onOpen(p); - applyPreferredRect(); -} - -// virtual -void LLFloaterWebProfile::handleReshape(const LLRect& new_rect, bool by_user) -{ - LL_DEBUGS() << "handleReshape: " << new_rect << LL_ENDL; - - if (by_user && !isMinimized()) - { - LL_DEBUGS() << "Storing new rect" << LL_ENDL; - gSavedSettings.setRect("WebProfileFloaterRect", new_rect); - } - - LLFloaterWebContent::handleReshape(new_rect, by_user); -} - -LLFloater* LLFloaterWebProfile::create(const LLSD& key) -{ - LLFloaterWebContent::Params p(key); - preCreate(p); - return new LLFloaterWebProfile(p); -} - -void LLFloaterWebProfile::applyPreferredRect() -{ - const LLRect preferred_rect = gSavedSettings.getRect("WebProfileFloaterRect"); - LL_DEBUGS() << "Applying preferred rect: " << preferred_rect << LL_ENDL; - - // Don't override position that may have been set by floater stacking code. - LLRect new_rect = getRect(); - new_rect.setLeftTopAndSize( - new_rect.mLeft, new_rect.mTop, - preferred_rect.getWidth(), preferred_rect.getHeight()); - setShape(new_rect); -} diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp old mode 100644 new mode 100755 index 2c84cd1f93..01bfae8934 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -45,6 +45,7 @@ //#include "llfirstuse.h" #include "llfloaterreg.h" // getTypedInstance() #include "llfocusmgr.h" +#include "lliconctrl.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" #include "llinventorymodelbackgroundfetch.h" @@ -81,7 +82,6 @@ //--------------------------------------------------------------------------- // Constants //--------------------------------------------------------------------------- -static const F32 MAP_ZOOM_TIME = 0.2f; // Merov: we switched from using the "world size" (which varies depending where the user went) to a fixed // width of 512 regions max visible at a time. This makes the zoom slider works in a consistent way across @@ -284,7 +284,7 @@ void* LLFloaterWorldMap::createWorldMapView(void* data) BOOL LLFloaterWorldMap::postBuild() { - mPanel = getChild("objects_mapview"); + mMapView = dynamic_cast(getChild("objects_mapview")); LLComboBox *avatar_combo = getChild("friend combo"); avatar_combo->selectFirstItem(); @@ -305,13 +305,13 @@ BOOL LLFloaterWorldMap::postBuild() landmark_combo->setTextChangedCallback( boost::bind(&LLFloaterWorldMap::onComboTextEntry, this) ); mListLandmarkCombo = dynamic_cast(landmark_combo); - mCurZoomVal = log(LLWorldMapView::sMapScale/256.f)/log(2.f); - getChild("zoom slider")->setValue(mCurZoomVal); + F32 slider_zoom = mMapView->getZoom(); + getChild("zoom slider")->setValue(slider_zoom); + + getChild("expand_btn_panel")->setMouseDownCallback(boost::bind(&LLFloaterWorldMap::onExpandCollapseBtn, this)); setDefaultBtn(NULL); - mZoomTimer.stop(); - onChangeMaturity(); return TRUE; @@ -321,7 +321,7 @@ BOOL LLFloaterWorldMap::postBuild() LLFloaterWorldMap::~LLFloaterWorldMap() { // All cleaned up by LLView destructor - mPanel = NULL; + mMapView = NULL; // Inventory deletes all observers on shutdown mInventory = NULL; @@ -359,17 +359,15 @@ void LLFloaterWorldMap::onOpen(const LLSD& key) mIsClosing = FALSE; - LLWorldMapView* map_panel; - map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel; - map_panel->clearLastClick(); + mMapView->clearLastClick(); { // reset pan on show, so it centers on you again if (!center_on_target) { - LLWorldMapView::setPan(0, 0, TRUE); + mMapView->setPan(0, 0, true); } - map_panel->updateVisibleBlocks(); + mMapView->updateVisibleBlocks(); // Reload items as they may have changed LLWorldMap::getInstance()->reloadItems(); @@ -417,18 +415,21 @@ BOOL LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask) BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if (!isMinimized() && isFrontmost()) - { - if(mPanel->pointInView(x, y)) - { - F32 slider_value = (F32)getChild("zoom slider")->getValue().asReal(); - slider_value += ((F32)clicks * -0.3333f); - getChild("zoom slider")->setValue(LLSD(slider_value)); - return TRUE; - } - } - - return LLFloater::handleScrollWheel(x, y, clicks); + if (!isMinimized() && isFrontmost()) + { + S32 map_x = x - mMapView->getRect().mLeft; + S32 map_y = y - mMapView->getRect().mBottom; + if (mMapView->pointInView(map_x, map_y)) + { + F32 old_slider_zoom = (F32) getChild("zoom slider")->getValue().asReal(); + F32 slider_zoom = old_slider_zoom + ((F32) clicks * -0.3333f); + getChild("zoom slider")->setValue(LLSD(slider_zoom)); + mMapView->zoomWithPivot(slider_zoom, map_x, map_y); + return true; + } + } + + return LLFloater::handleScrollWheel(x, y, clicks); } @@ -507,26 +508,13 @@ void LLFloaterWorldMap::draw() setMouseOpaque(TRUE); getDragHandle()->setMouseOpaque(TRUE); - - //RN: snaps to zoom value because interpolation caused jitter in the text rendering - if (!mZoomTimer.getStarted() && mCurZoomVal != (F32)getChild("zoom slider")->getValue().asReal()) - { - mZoomTimer.start(); - } - F32 interp = mZoomTimer.getElapsedTimeF32() / MAP_ZOOM_TIME; - if (interp > 1.f) - { - interp = 1.f; - mZoomTimer.stop(); - } - mCurZoomVal = lerp(mCurZoomVal, (F32)getChild("zoom slider")->getValue().asReal(), interp); - F32 map_scale = 256.f*pow(2.f, mCurZoomVal); - LLWorldMapView::setScale( map_scale ); + + mMapView->zoom((F32)getChild("zoom slider")->getValue().asReal()); // Enable/disable checkboxes depending on the zoom level // If above threshold level (i.e. low res) -> Disable all checkboxes // If under threshold level (i.e. high res) -> Enable all checkboxes - bool enable = LLWorldMapView::showRegionInfo(); + bool enable = mMapView->showRegionInfo(); getChildView("people_chk")->setEnabled(enable); getChildView("infohub_chk")->setEnabled(enable); getChildView("telehub_chk")->setEnabled(enable); @@ -1005,7 +993,7 @@ void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui) { mTrackedStatus = LLTracker::TRACKING_NOTHING; LLCtrlListInterface *list = mListFriendCombo; - if (list) + if (list && list->getSelectedValue().asString() != "None") { list->selectByValue( "None" ); } @@ -1025,9 +1013,7 @@ void LLFloaterWorldMap::adjustZoomSliderBounds() S32 world_height_regions = MAX_VISIBLE_REGIONS; // Find how much space we have to display the world - LLWorldMapView* map_panel; - map_panel = (LLWorldMapView*)mPanel; - LLRect view_rect = map_panel->getRect(); + LLRect view_rect = mMapView->getRect(); // View size in pixels S32 view_width = view_rect.getWidth(); @@ -1295,9 +1281,9 @@ void LLFloaterWorldMap::onShowTargetBtn() void LLFloaterWorldMap::onShowAgentBtn() { - LLWorldMapView::setPan( 0, 0, FALSE); // FALSE == animate - // Set flag so user's location will be displayed if not tracking anything else - mSetToUserPosition = TRUE; + mMapView->setPanWithInterpTime(0, 0, false, 0.1f); // false == animate + // Set flag so user's location will be displayed if not tracking anything else + mSetToUserPosition = true; } void LLFloaterWorldMap::onClickTeleportBtn() @@ -1315,6 +1301,22 @@ void LLFloaterWorldMap::onCopySLURL() LLNotificationsUtil::add("CopySLURL", args); } +void LLFloaterWorldMap::onExpandCollapseBtn() +{ + LLLayoutStack* floater_stack = getChild("floater_map_stack"); + LLLayoutPanel* controls_panel = getChild("controls_lp"); + + bool toggle_collapse = !controls_panel->isCollapsed(); + floater_stack->collapsePanel(controls_panel, toggle_collapse); + floater_stack->updateLayout(); + + std::string image_name = getString(toggle_collapse ? "expand_icon" : "collapse_icon"); + std::string tooltip = getString(toggle_collapse ? "expand_tooltip" : "collapse_tooltip"); + getChild("expand_collapse_icon")->setImage(LLUI::getUIImage(image_name)); + getChild("expand_collapse_icon")->setToolTip(tooltip); + getChild("expand_btn_panel")->setToolTip(tooltip); +} + // protected void LLFloaterWorldMap::centerOnTarget(BOOL animate) { @@ -1349,9 +1351,10 @@ void LLFloaterWorldMap::centerOnTarget(BOOL animate) pos_global.clearVec(); } - LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), - -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sMapScale / REGION_WIDTH_METERS)), - !animate); + F64 map_scale = (F64)mMapView->getScale(); + mMapView->setPanWithInterpTime(-llfloor((F32)(pos_global.mdV[VX] * map_scale / REGION_WIDTH_METERS)), + -llfloor((F32)(pos_global.mdV[VY] * map_scale / REGION_WIDTH_METERS)), + !animate, 0.1f); mWaitingForTracker = FALSE; } @@ -1581,7 +1584,7 @@ void LLFloaterWorldMap::onTeleportFinished() { if(isInVisibleChain()) { - LLWorldMapView::setPan(0, 0, TRUE); + mMapView->setPan(0, 0, TRUE); } } @@ -1656,9 +1659,8 @@ void LLFloaterWorldMap::onChangeMaturity() void LLFloaterWorldMap::onFocusLost() { - gViewerWindow->showCursor(); - LLWorldMapView* map_panel = (LLWorldMapView*)gFloaterWorldMap->mPanel; - map_panel->mPanning = FALSE; + gViewerWindow->showCursor(); + mMapView->mPanning = false; } LLPanelHideBeacon::LLPanelHideBeacon() : diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 14a9c26fb9..3702226d23 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -44,6 +44,7 @@ class LLInventoryObserver; class LLItemInfo; class LLLineEditor; class LLTabContainer; +class LLWorldMapView; class LLFloaterWorldMap : public LLFloater { @@ -130,6 +131,8 @@ protected: void onShowAgentBtn(); void onCopySLURL(); + void onExpandCollapseBtn(); + void centerOnTarget(BOOL animate); void updateLocation(); @@ -154,11 +157,7 @@ protected: void onTeleportFinished(); private: - LLPanel* mPanel; // Panel displaying the map - - // Ties to LLWorldMapView::sMapScale, in pixels per region - F32 mCurZoomVal; - LLFrameTimer mZoomTimer; + LLWorldMapView* mMapView; // Panel displaying the map // update display of teleport destination coordinates - pos is in global coordinates void updateTeleportCoordsDisplay( const LLVector3d& pos ); diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index 0be748ace9..e395da7f1e 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -327,22 +327,63 @@ void LLFriendCardsManager::syncFriendCardsFolders() /************************************************************************/ /* Private Methods */ /************************************************************************/ +const LLUUID& LLFriendCardsManager::findFirstCallingCardSubfolder(const LLUUID &parent_id) const +{ + if (parent_id.isNull()) + { + return LLUUID::null; + } + + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(parent_id, cats, items); + + if (!cats || !items || cats->size() == 0) + { + // call failed + return LLUUID::null; + } + + if (cats->size() > 1) + { + const LLViewerInventoryCategory* friendFolder = gInventory.getCategory(parent_id); + if (friendFolder) + { + LL_WARNS_ONCE() << friendFolder->getName() << " folder contains more than one folder" << LL_ENDL; + } + } + + for (LLInventoryModel::cat_array_t::const_iterator iter = cats->begin(); + iter != cats->end(); + ++iter) + { + const LLInventoryCategory* category = (*iter); + if (category->getPreferredType() == LLFolderType::FT_CALLINGCARD) + { + return category->getUUID(); + } + } + + return LLUUID::null; +} + +// Inventorry -> +// Calling Cards - > +// Friends - > (the only expected folder) +// All (the only expected folder) + const LLUUID& LLFriendCardsManager::findFriendFolderUUIDImpl() const { - const LLUUID callingCardsFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); + const LLUUID callingCardsFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD); - std::string friendFolderName = get_friend_folder_name(); - - return findChildFolderUUID(callingCardsFolderID, friendFolderName); + return findFirstCallingCardSubfolder(callingCardsFolderID); } const LLUUID& LLFriendCardsManager::findFriendAllSubfolderUUIDImpl() const { - LLUUID friendFolderUUID = findFriendFolderUUIDImpl(); + LLUUID friendFolderUUID = findFriendFolderUUIDImpl(); - std::string friendAllSubfolderName = get_friend_all_subfolder_name(); - - return findChildFolderUUID(friendFolderUUID, friendAllSubfolderName); + return findFirstCallingCardSubfolder(friendFolderUUID); } const LLUUID& LLFriendCardsManager::findChildFolderUUID(const LLUUID& parentFolderUUID, const std::string& nonLocalizedName) const diff --git a/indra/newview/llfriendcard.h b/indra/newview/llfriendcard.h index 2fb912a930..f5679d7d85 100644 --- a/indra/newview/llfriendcard.h +++ b/indra/newview/llfriendcard.h @@ -116,6 +116,7 @@ private: } const LLUUID& findChildFolderUUID(const LLUUID& parentFolderUUID, const std::string& nonLocalizedName) const; + const LLUUID& findFirstCallingCardSubfolder(const LLUUID &parent_id) const; const LLUUID& findFriendFolderUUIDImpl() const; const LLUUID& findFriendAllSubfolderUUIDImpl() const; const LLUUID& findFriendCardInventoryUUIDImpl(const LLUUID& avatarID); diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h index 91ab445273..7c8e8279c2 100644 --- a/indra/newview/llgesturemgr.h +++ b/indra/newview/llgesturemgr.h @@ -185,7 +185,7 @@ private: std::set mLoadingAssets; // LLEventHost interface - boost::shared_ptr mListener; + std::shared_ptr mListener; }; #endif diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index e7bc2a9268..84a1278767 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -196,7 +196,7 @@ LLFetchLeaveGroupData* gFetchLeaveGroupData = NULL; // static void LLGroupActions::search() { - LLFloaterReg::showInstance("search"); + LLFloaterReg::showInstance("search", LLSD().with("collection", "groups")); } // static diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 62414d3bbb..32af2592d3 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -45,7 +45,6 @@ #include "llvoiceclient.h" static LLDefaultChildRegistry::Register r("group_list"); -S32 LLGroupListItem::sIconWidth = 0; class LLGroupComparator : public LLFlatListView::ItemComparator { @@ -65,21 +64,81 @@ public: } }; -static const LLGroupComparator GROUP_COMPARATOR; +class LLSharedGroupComparator : public LLFlatListView::ItemComparator +{ +public: + LLSharedGroupComparator() {}; + /*virtual*/ bool compare(const LLPanel* item1, const LLPanel* item2) const + { + const LLGroupListItem* group_item1 = static_cast(item1); + std::string name1 = group_item1->getGroupName(); + bool item1_shared = gAgent.isInGroup(group_item1->getGroupID(), true); + + const LLGroupListItem* group_item2 = static_cast(item2); + std::string name2 = group_item2->getGroupName(); + bool item2_shared = gAgent.isInGroup(group_item2->getGroupID(), true); + + if (item2_shared != item1_shared) + { + return item1_shared; + } + + LLStringUtil::toUpper(name1); + LLStringUtil::toUpper(name2); + + return name1 < name2; + } +}; + +static LLGroupComparator GROUP_COMPARATOR; +static LLSharedGroupComparator SHARED_GROUP_COMPARATOR; + +LLGroupList::Params::Params() +: for_agent("for_agent", true) +{ +} LLGroupList::LLGroupList(const Params& p) : LLFlatListViewEx(p) + , mForAgent(p.for_agent) , mDirty(true) // to force initial update + , mShowIcons(false) + , mShowNone(true) { - // Listen for agent group changes. - gAgent.addListener(this, "new group"); - - mShowIcons = gSavedSettings.getBOOL("GroupListShowIcons"); setCommitOnSelectionChange(true); // Set default sort order. - setComparator(&GROUP_COMPARATOR); + if (mForAgent) + { + setComparator(&GROUP_COMPARATOR); + } + else + { + // shared groups first + setComparator(&SHARED_GROUP_COMPARATOR); + } + + if (mForAgent) + { + enableForAgent(true); + } +} + +LLGroupList::~LLGroupList() +{ + if (mForAgent) gAgent.removeListener(this); + if (mContextMenuHandle.get()) mContextMenuHandle.get()->die(); +} + +void LLGroupList::enableForAgent(bool show_icons) +{ + mForAgent = true; + + mShowIcons = mForAgent && gSavedSettings.getBOOL("GroupListShowIcons") && show_icons; + + // Listen for agent group changes. + gAgent.addListener(this, "new group"); // Set up context menu. LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; @@ -94,12 +153,6 @@ LLGroupList::LLGroupList(const Params& p) mContextMenuHandle = context_menu->getHandle(); } -LLGroupList::~LLGroupList() -{ - gAgent.removeListener(this); - if (mContextMenuHandle.get()) mContextMenuHandle.get()->die(); -} - // virtual void LLGroupList::draw() { @@ -114,12 +167,15 @@ BOOL LLGroupList::handleRightMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask); - LLToggleableMenu* context_menu = mContextMenuHandle.get(); - if (context_menu && size() > 0) - { - context_menu->buildDrawLabels(); - context_menu->updateParent(LLMenuGL::sMenuContainer); - LLMenuGL::showPopup(this, context_menu, x, y); + if (mForAgent) + { + LLToggleableMenu* context_menu = mContextMenuHandle.get(); + if (context_menu && size() > 0) + { + context_menu->buildDrawLabels(); + context_menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this, context_menu, x, y); + } } return handled; @@ -132,7 +188,7 @@ BOOL LLGroupList::handleDoubleClick(S32 x, S32 y, MASK mask) // Handle double click only for the selected item in the list, skip clicks on empty space. if (handled) { - if (mDoubleClickSignal) + if (mDoubleClickSignal && getItemsRect().pointInRect(x, y)) { (*mDoubleClickSignal)(this, x, y, mask); } @@ -164,34 +220,49 @@ static bool findInsensitive(std::string haystack, const std::string& needle_uppe void LLGroupList::refresh() { - const LLUUID& highlight_id = gAgent.getGroupID(); - S32 count = gAgent.mGroups.size(); - LLUUID id; - bool have_filter = !mNameFilter.empty(); + if (mForAgent) + { + const LLUUID& highlight_id = gAgent.getGroupID(); + S32 count = gAgent.mGroups.size(); + LLUUID id; + bool have_filter = !mNameFilter.empty(); - clear(); + clear(); - for(S32 i = 0; i < count; ++i) - { - id = gAgent.mGroups.at(i).mID; - const LLGroupData& group_data = gAgent.mGroups.at(i); - if (have_filter && !findInsensitive(group_data.mName, mNameFilter)) - continue; - addNewItem(id, group_data.mName, group_data.mInsigniaID, ADD_BOTTOM); - } + for(S32 i = 0; i < count; ++i) + { + id = gAgent.mGroups.at(i).mID; + const LLGroupData& group_data = gAgent.mGroups.at(i); + if (have_filter && !findInsensitive(group_data.mName, mNameFilter)) + continue; + addNewItem(id, group_data.mName, group_data.mInsigniaID, ADD_BOTTOM, group_data.mListInProfile); + } - // Sort the list. - sort(); + // Sort the list. + sort(); - // Add "none" to list at top if filter not set (what's the point of filtering "none"?). - // but only if some real groups exists. EXT-4838 - if (!have_filter && count > 0) - { - std::string loc_none = LLTrans::getString("GroupsNone"); - addNewItem(LLUUID::null, loc_none, LLUUID::null, ADD_TOP); - } + // Add "none" to list at top if filter not set (what's the point of filtering "none"?). + // but only if some real groups exists. EXT-4838 + if (!have_filter && count > 0 && mShowNone) + { + std::string loc_none = LLTrans::getString("GroupsNone"); + addNewItem(LLUUID::null, loc_none, LLUUID::null, ADD_TOP); + } - selectItemByUUID(highlight_id); + selectItemByUUID(highlight_id); + } + else + { + clear(); + + for (group_map_t::iterator it = mGroups.begin(); it != mGroups.end(); ++it) + { + addNewItem(it->second, it->first, LLUUID::null, ADD_BOTTOM); + } + + // Sort the list. + sort(); + } setDirty(false); onCommit(); @@ -212,13 +283,19 @@ void LLGroupList::toggleIcons() } } +void LLGroupList::setGroups(const std::map< std::string,LLUUID> group_list) +{ + mGroups = group_list; + setDirty(true); +} + ////////////////////////////////////////////////////////////////////////// // PRIVATE Section ////////////////////////////////////////////////////////////////////////// -void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos) +void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile) { - LLGroupListItem* item = new LLGroupListItem(); + LLGroupListItem* item = new LLGroupListItem(mForAgent, mShowIcons); item->setGroupID(id); item->setName(name, mNameFilter); @@ -227,7 +304,10 @@ void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LL item->getChildView("info_btn")->setVisible( false); item->getChildView("profile_btn")->setVisible( false); item->setGroupIconVisible(mShowIcons); - + if (!mShowIcons) + { + item->setVisibleInProfile(visible_in_profile); + } addItem(item, id, pos); // setCommentVisible(false); @@ -243,6 +323,29 @@ bool LLGroupList::handleEvent(LLPointer event, const LLSD& return true; } + if (event->desc() == "value_changed") + { + LLSD data = event->getValue(); + if (data.has("group_id") && data.has("visible")) + { + LLUUID group_id = data["group_id"].asUUID(); + bool visible = data["visible"].asBoolean(); + + std::vector items; + getItems(items); + for (std::vector::iterator it = items.begin(); it != items.end(); ++it) + { + LLGroupListItem* item = dynamic_cast(*it); + if (item && item->getGroupID() == group_id) + { + item->setVisibleInProfile(visible); + break; + } + } + } + return true; + } + return false; } @@ -294,21 +397,25 @@ bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata) /* LLGroupListItem implementation */ /************************************************************************/ -LLGroupListItem::LLGroupListItem() +LLGroupListItem::LLGroupListItem(bool for_agent, bool show_icons) : LLPanel(), mGroupIcon(NULL), mGroupNameBox(NULL), mInfoBtn(NULL), -mGroupID(LLUUID::null) +mProfileBtn(NULL), +mVisibilityHideBtn(NULL), +mVisibilityShowBtn(NULL), +mGroupID(LLUUID::null), +mForAgent(for_agent) { - buildFromFile( "panel_group_list_item.xml"); - - // Remember group icon width including its padding from the name text box, - // so that we can hide and show the icon again later. - if (!sIconWidth) - { - sIconWidth = mGroupNameBox->getRect().mLeft - mGroupIcon->getRect().mLeft; - } + if (show_icons) + { + buildFromFile( "panel_group_list_item.xml"); + } + else + { + buildFromFile( "panel_group_list_item_short.xml"); + } } LLGroupListItem::~LLGroupListItem() @@ -325,7 +432,25 @@ BOOL LLGroupListItem::postBuild() mInfoBtn = getChild("info_btn"); mInfoBtn->setClickedCallback(boost::bind(&LLGroupListItem::onInfoBtnClick, this)); - childSetAction("profile_btn", boost::bind(&LLGroupListItem::onProfileBtnClick, this)); + mProfileBtn = getChild("profile_btn"); + mProfileBtn->setClickedCallback([this](LLUICtrl *, const LLSD &) { onProfileBtnClick(); }); + + mVisibilityHideBtn = findChild("visibility_hide_btn"); + if (mVisibilityHideBtn) + { + mVisibilityHideBtn->setClickedCallback([this](LLUICtrl *, const LLSD &) { onVisibilityBtnClick(false); }); + } + mVisibilityShowBtn = findChild("visibility_show_btn"); + if (mVisibilityShowBtn) + { + mVisibilityShowBtn->setClickedCallback([this](LLUICtrl *, const LLSD &) { onVisibilityBtnClick(true); }); + } + + // Remember group icon width including its padding from the name text box, + // so that we can hide and show the icon again later. + // Also note that panel_group_list_item and panel_group_list_item_short + // have icons of different sizes so we need to figure it per file. + mIconWidth = mGroupNameBox->getRect().mLeft - mGroupIcon->getRect().mLeft; return TRUE; } @@ -344,7 +469,16 @@ void LLGroupListItem::onMouseEnter(S32 x, S32 y, MASK mask) if (mGroupID.notNull()) // don't show the info button for the "none" group { mInfoBtn->setVisible(true); - getChildView("profile_btn")->setVisible( true); + mProfileBtn->setVisible(true); + if (mForAgent && mVisibilityHideBtn) + { + LLGroupData agent_gdatap; + if (gAgent.getGroupData(mGroupID, agent_gdatap)) + { + mVisibilityHideBtn->setVisible(agent_gdatap.mListInProfile); + mVisibilityShowBtn->setVisible(!agent_gdatap.mListInProfile); + } + } } LLPanel::onMouseEnter(x, y, mask); @@ -354,7 +488,12 @@ void LLGroupListItem::onMouseLeave(S32 x, S32 y, MASK mask) { getChildView("hovered_icon")->setVisible( false); mInfoBtn->setVisible(false); - getChildView("profile_btn")->setVisible( false); + mProfileBtn->setVisible(false); + if (mVisibilityHideBtn) + { + mVisibilityHideBtn->setVisible(false); + mVisibilityShowBtn->setVisible(false); + } LLPanel::onMouseLeave(x, y, mask); } @@ -372,7 +511,17 @@ void LLGroupListItem::setGroupID(const LLUUID& group_id) mID = group_id; mGroupID = group_id; - setActive(group_id == gAgent.getGroupID()); + + if (mForAgent) + { + // Active group should be bold. + setBold(group_id == gAgent.getGroupID()); + } + else + { + // Groups shared with the agent should be bold + setBold(gAgent.isInGroup(group_id, true)); + } LLGroupMgr::getInstance()->addObserver(this); } @@ -393,24 +542,28 @@ void LLGroupListItem::setGroupIconVisible(bool visible) // Move the group name horizontally by icon size + its distance from the group name. LLRect name_rect = mGroupNameBox->getRect(); - name_rect.mLeft += visible ? sIconWidth : -sIconWidth; + name_rect.mLeft += visible ? mIconWidth : -mIconWidth; mGroupNameBox->setRect(name_rect); } +void LLGroupListItem::setVisibleInProfile(bool visible) +{ + mGroupNameBox->setColor(LLUIColorTable::instance().getColor((visible ? "GroupVisibleInProfile" : "GroupHiddenInProfile"), LLColor4::red).get()); +} + ////////////////////////////////////////////////////////////////////////// // Private Section ////////////////////////////////////////////////////////////////////////// -void LLGroupListItem::setActive(bool active) +void LLGroupListItem::setBold(bool bold) { // *BUG: setName() overrides the style params. - // Active group should be bold. LLFontDescriptor new_desc(mGroupNameBox->getFont()->getFontDesc()); // *NOTE dzaporozhan // On Windows LLFontGL::NORMAL will not remove LLFontGL::BOLD if font // is predefined as bold (SansSerifSmallBold, for example) - new_desc.setStyle(active ? LLFontGL::BOLD : LLFontGL::NORMAL); + new_desc.setStyle(bold ? LLFontGL::BOLD : LLFontGL::NORMAL); LLFontGL* new_font = LLFontGL::getFont(new_desc); mGroupNameStyle.font = new_font; @@ -430,11 +583,25 @@ void LLGroupListItem::onProfileBtnClick() LLGroupActions::show(mGroupID); } +void LLGroupListItem::onVisibilityBtnClick(bool new_visibility) +{ + LLGroupData agent_gdatap; + if (gAgent.getGroupData(mGroupID, agent_gdatap)) + { + gAgent.setUserGroupFlags(mGroupID, agent_gdatap.mAcceptNotices, new_visibility); + setVisibleInProfile(new_visibility); + mVisibilityHideBtn->setVisible(new_visibility); + mVisibilityShowBtn->setVisible(!new_visibility); + } +} + void LLGroupListItem::changed(LLGroupChange gc) { LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(mID); - if(group_data) - setGroupIconID(group_data->mInsigniaID); + if ((gc == GC_ALL || gc == GC_PROPERTIES) && group_data) + { + setGroupIconID(group_data->mInsigniaID); + } } //EOF diff --git a/indra/newview/llgrouplist.h b/indra/newview/llgrouplist.h index 171b77fb00..5cbabb712f 100644 --- a/indra/newview/llgrouplist.h +++ b/indra/newview/llgrouplist.h @@ -50,12 +50,15 @@ class LLGroupList: public LLFlatListViewEx, public LLOldEvents::LLSimpleListener public: struct Params : public LLInitParam::Block { - Params(){}; + Optional for_agent; + Params(); }; LLGroupList(const Params& p); virtual ~LLGroupList(); + void enableForAgent(bool show_icons); + virtual void draw(); // from LLView /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); // from LLView /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); // from LLView @@ -63,13 +66,16 @@ public: void setNameFilter(const std::string& filter); void toggleIcons(); bool getIconsVisible() const { return mShowIcons; } + void setIconsVisible(bool show_icons) { mShowIcons = show_icons; } + void setShowNone(bool show_none) { mShowNone = show_none; } + void setGroups(const std::map< std::string,LLUUID> group_list); LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); } private: void setDirty(bool val = true) { mDirty = val; } void refresh(); - void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM); + void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true); bool handleEvent(LLPointer event, const LLSD& userdata); // called on agent group list changes bool onContextMenuItemClick(const LLSD& userdata); @@ -80,6 +86,11 @@ private: bool mShowIcons; bool mDirty; std::string mNameFilter; + + bool mForAgent; + bool mShowNone; + typedef std::map< std::string,LLUUID> group_map_t; + group_map_t mGroups; }; class LLButton; @@ -90,7 +101,7 @@ class LLGroupListItem : public LLPanel , public LLGroupMgrObserver { public: - LLGroupListItem(); + LLGroupListItem(bool for_agent, bool show_icons); ~LLGroupListItem(); /*virtual*/ BOOL postBuild(); /*virtual*/ void setValue(const LLSD& value); @@ -106,19 +117,26 @@ public: void setGroupIconVisible(bool visible); virtual void changed(LLGroupChange gc); + + void setVisibleInProfile(bool visible); private: - void setActive(bool active); + void setBold(bool bold); void onInfoBtnClick(); void onProfileBtnClick(); + void onVisibilityBtnClick(bool new_visibility); LLTextBox* mGroupNameBox; LLUUID mGroupID; LLGroupIconCtrl* mGroupIcon; - LLButton* mInfoBtn; + LLButton* mInfoBtn; + LLButton* mProfileBtn; + LLButton* mVisibilityHideBtn; + LLButton* mVisibilityShowBtn; std::string mGroupName; + bool mForAgent; LLStyle::Params mGroupNameStyle; - static S32 sIconWidth; // icon width + padding + S32 mIconWidth; }; #endif // LL_LLGROUPLIST_H diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 7f65153879..a9e5e55451 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -53,6 +53,7 @@ #include "llviewerregion.h" #include #include "llcorehttputil.h" +#include "lluiusage.h" #if LL_MSVC @@ -1859,6 +1860,9 @@ void LLGroupMgr::sendGroupRoleMemberChanges(const LLUUID& group_id) //static void LLGroupMgr::sendGroupMemberJoin(const LLUUID& group_id) { + + LLUIUsage::instance().logCommand("Group.Join"); + LLMessageSystem *msg = gMessageSystem; msg->newMessageFast(_PREHASH_JoinGroupRequest); diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index 7be35ac260..5952edfc44 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -330,7 +330,7 @@ void LLHUDText::updateVisibility() if (!mSourceObject) { - LL_WARNS() << "HUD text: mSourceObject is NULL, mOnHUDAttachment: " << mOnHUDAttachment << LL_ENDL; + // Beacons mVisible = TRUE; if (mOnHUDAttachment) { diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index b7e0a6a794..4d6ebf9cbb 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -69,7 +69,7 @@ #include "message.h" #include "llviewerregion.h" #include "llcorehttputil.h" - +#include "lluiusage.h" const static std::string ADHOC_NAME_SUFFIX(" Conference"); @@ -532,7 +532,6 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& mSessionInitialized(false), mCallBackEnabled(true), mTextIMPossible(true), - mOtherParticipantIsAvatar(true), mStartCallOnInitialize(false), mStartedAsIMCall(voice), mIsDNDsend(false), @@ -544,13 +543,6 @@ LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& if (IM_NOTHING_SPECIAL == mType || IM_SESSION_P2P_INVITE == mType) { mVoiceChannel = new LLVoiceChannelP2P(session_id, name, other_participant_id); - mOtherParticipantIsAvatar = LLVoiceClient::getInstance()->isParticipantAvatar(mSessionID); - - // check if it was AVALINE call - if (!mOtherParticipantIsAvatar) - { - mSessionType = AVALINE_SESSION; - } } else { @@ -651,9 +643,6 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES switch(mSessionType) { - case AVALINE_SESSION: - // no text notifications - break; case P2P_SESSION: LLAvatarNameCache::get(mOtherParticipantID, &av_name); other_avatar_name = av_name.getUserName(); @@ -781,6 +770,23 @@ void LLIMModel::LLIMSession::addMessage(const std::string& from, const LLUUID& f message["index"] = (LLSD::Integer)mMsgs.size(); message["is_history"] = is_history; + LL_DEBUGS("UIUsage") << "addMessage " << " from " << from << " from_id " << from_id << " utf8_text " << utf8_text << " time " << time << " is_history " << is_history << " session mType " << mType << LL_ENDL; + if (from_id == gAgent.getID()) + { + if (mType == IM_SESSION_GROUP_START) + { + LLUIUsage::instance().logCommand("Chat.SendGroup"); + } + else if (mType == IM_NOTHING_SPECIAL) + { + LLUIUsage::instance().logCommand("Chat.SendIM"); + } + else + { + LLUIUsage::instance().logCommand("Chat.SendOther"); + } + } + mMsgs.push_front(message); if (mSpeakers && from_id.notNull()) @@ -913,11 +919,6 @@ bool LLIMModel::LLIMSession::isGroupChat() return IM_SESSION_GROUP_START == mType || (IM_SESSION_INVITE == mType && gAgent.isInGroup(mSessionID, TRUE)); } -bool LLIMModel::LLIMSession::isOtherParticipantAvaline() -{ - return !mOtherParticipantIsAvatar; -} - LLUUID LLIMModel::LLIMSession::generateOutgoingAdHocHash() const { LLUUID hash = LLUUID::null; @@ -1795,7 +1796,6 @@ LLIMMgr::onConfirmForceCloseError( LLCallDialogManager::LLCallDialogManager(): mPreviousSessionlName(""), -mPreviousSessionType(LLIMModel::LLIMSession::P2P_SESSION), mCurrentSessionlName(""), mSession(NULL), mOldState(LLVoiceChannel::STATE_READY) @@ -1826,12 +1826,6 @@ void LLCallDialogManager::onVoiceChannelChangedInt(const LLUUID &session_id) mCurrentSessionlName = ""; // Empty string results in "Nearby Voice Chat" after substitution return; } - - if (mSession) - { - // store previous session type to process Avaline calls in dialogs - mPreviousSessionType = mSession->mSessionType; - } mSession = session; @@ -1857,7 +1851,6 @@ void LLCallDialogManager::onVoiceChannelChangedInt(const LLUUID &session_id) mCallDialogPayload["session_name"] = mSession->mName; mCallDialogPayload["other_user_id"] = mSession->mOtherParticipantID; mCallDialogPayload["old_channel_name"] = mPreviousSessionlName; - mCallDialogPayload["old_session_type"] = mPreviousSessionType; mCallDialogPayload["state"] = LLVoiceChannel::STATE_CALL_STARTED; mCallDialogPayload["disconnected_channel_name"] = mSession->mName; mCallDialogPayload["session_type"] = mSession->mSessionType; @@ -1893,7 +1886,6 @@ void LLCallDialogManager::onVoiceChannelStateChangedInt(const LLVoiceChannel::ES mCallDialogPayload["session_name"] = mSession->mName; mCallDialogPayload["other_user_id"] = mSession->mOtherParticipantID; mCallDialogPayload["old_channel_name"] = mPreviousSessionlName; - mCallDialogPayload["old_session_type"] = mPreviousSessionType; mCallDialogPayload["state"] = new_state; mCallDialogPayload["disconnected_channel_name"] = mSession->mName; mCallDialogPayload["session_type"] = mSession->mSessionType; @@ -1910,8 +1902,7 @@ void LLCallDialogManager::onVoiceChannelStateChangedInt(const LLVoiceChannel::ES break; case LLVoiceChannel::STATE_HUNG_UP: - // this state is coming before session is changed, so, put it into payload map - mCallDialogPayload["old_session_type"] = mSession->mSessionType; + // this state is coming before session is changed break; case LLVoiceChannel::STATE_CONNECTED : @@ -2031,7 +2022,6 @@ void LLCallDialog::onOpen(const LLSD& key) void LLCallDialog::setIcon(const LLSD& session_id, const LLSD& participant_id) { - // *NOTE: 12/28/2009: check avaline calls: LLVoiceClient::isParticipantAvatar returns false for them bool participant_is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); bool is_group = participant_is_avatar && gAgent.isInGroup(session_id, TRUE); @@ -2052,8 +2042,8 @@ void LLCallDialog::setIcon(const LLSD& session_id, const LLSD& participant_id) } else { - avatar_icon->setValue("Avaline_Icon"); - avatar_icon->setToolTip(std::string("")); + LL_WARNS() << "Participant neither avatar nor group" << LL_ENDL; + group_icon->setValue(session_id); } } @@ -2097,13 +2087,7 @@ void LLOutgoingCallDialog::show(const LLSD& key) // tell the user which voice channel they are leaving if (!mPayload["old_channel_name"].asString().empty()) { - bool was_avaline_call = LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["old_session_type"].asInteger(); - std::string old_caller_name = mPayload["old_channel_name"].asString(); - if (was_avaline_call) - { - old_caller_name = LLTextUtil::formatPhoneNumber(old_caller_name); - } getChild("leaving")->setTextArg("[CURRENT_CHAT]", old_caller_name); show_oldchannel = true; @@ -2116,10 +2100,6 @@ void LLOutgoingCallDialog::show(const LLSD& key) if (!mPayload["disconnected_channel_name"].asString().empty()) { std::string channel_name = mPayload["disconnected_channel_name"].asString(); - if (LLIMModel::LLIMSession::AVALINE_SESSION == mPayload["session_type"].asInteger()) - { - channel_name = LLTextUtil::formatPhoneNumber(channel_name); - } getChild("nearby")->setTextArg("[VOICE_CHANNEL_NAME]", channel_name); // skipping "You will now be reconnected to nearby" in notification when call is ended by disabling voice, @@ -2135,16 +2115,11 @@ void LLOutgoingCallDialog::show(const LLSD& key) std::string callee_name = mPayload["session_name"].asString(); LLUUID session_id = mPayload["session_id"].asUUID(); - bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); - if (callee_name == "anonymous") + if (callee_name == "anonymous") // obsolete? Likely was part of avaline support { callee_name = getString("anonymous"); } - else if (!is_avatar) - { - callee_name = LLTextUtil::formatPhoneNumber(callee_name); - } LLSD callee_id = mPayload["other_user_id"]; // Beautification: Since you know who you called, just show display name @@ -2344,18 +2319,11 @@ BOOL LLIncomingCallDialog::postBuild() call_type = getString(notify_box_type); } - // check to see if this is an Avaline call - bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); - if (caller_name == "anonymous") + if (caller_name == "anonymous") // obsolete? Likely was part of avaline support { caller_name = getString("anonymous"); setCallerName(caller_name, caller_name, call_type); } - else if (!is_avatar) - { - caller_name = LLTextUtil::formatPhoneNumber(caller_name); - setCallerName(caller_name, caller_name, call_type); - } else { // Get the full name information @@ -2375,7 +2343,7 @@ BOOL LLIncomingCallDialog::postBuild() if(notify_box_type != "VoiceInviteGroup" && notify_box_type != "VoiceInviteAdHoc") { - // starting notification's timer for P2P and AVALINE invitations + // starting notification's timer for P2P invitations mLifetimeTimer.start(); } else @@ -2384,7 +2352,7 @@ BOOL LLIncomingCallDialog::postBuild() } //it's not possible to connect to existing Ad-Hoc/Group chat through incoming ad-hoc call - //and no IM for avaline + bool is_avatar = LLVoiceClient::getInstance()->isParticipantAvatar(session_id); getChildView("Start IM")->setVisible( is_avatar && notify_box_type != "VoiceInviteAdHoc" && notify_box_type != "VoiceInviteGroup"); setCanDrag(FALSE); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 79c831ebb6..fdf9806e2e 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -72,7 +72,6 @@ public: P2P_SESSION, GROUP_SESSION, ADHOC_SESSION, - AVALINE_SESSION, NONE_SESSION, } SType; @@ -92,12 +91,10 @@ public: bool isAdHoc(); bool isP2P(); bool isGroupChat(); - bool isOtherParticipantAvaline(); bool isP2PSessionType() const { return mSessionType == P2P_SESSION;} bool isAdHocSessionType() const { return mSessionType == ADHOC_SESSION;} bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} - bool isAvalineSessionType() const { return mSessionType == AVALINE_SESSION;} LLUUID generateOutgoingAdHocHash() const; @@ -136,7 +133,6 @@ public: bool mCallBackEnabled; bool mTextIMPossible; - bool mOtherParticipantIsAvatar; bool mStartCallOnInitialize; //if IM session is created for a voice call @@ -516,7 +512,6 @@ private: protected: std::string mPreviousSessionlName; - LLIMModel::LLIMSession::SType mPreviousSessionType; std::string mCurrentSessionlName; LLIMModel::LLIMSession* mSession; LLVoiceChannel::EState mOldState; diff --git a/indra/newview/llinspect.cpp b/indra/newview/llinspect.cpp index 479e8f9abf..f382b5985f 100644 --- a/indra/newview/llinspect.cpp +++ b/indra/newview/llinspect.cpp @@ -147,3 +147,19 @@ bool LLInspect::childHasVisiblePopupMenu() } return false; } + +void LLInspect::repositionInspector(const LLSD& data) +{ + // Position the inspector relative to the mouse cursor + // Similar to how tooltips are positioned + // See LLToolTipMgr::createToolTip + if (data.has("pos")) + { + LLUI::getInstance()->positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger()); + } + else + { + LLUI::getInstance()->positionViewNearMouse(this); + } + applyRectControl(); +} diff --git a/indra/newview/llinspect.h b/indra/newview/llinspect.h index 1f6aafc7bd..6909aa3f16 100644 --- a/indra/newview/llinspect.h +++ b/indra/newview/llinspect.h @@ -49,6 +49,8 @@ public: /// Inspectors close themselves when they lose focus /*virtual*/ void onFocusLost(); + + void repositionInspector(const LLSD& data); protected: diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 10814ac076..b11c440015 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -45,7 +45,6 @@ #include "llfloater.h" #include "llfloaterreg.h" #include "lltextbox.h" -#include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" class LLFetchAvatarData; @@ -202,17 +201,7 @@ void LLInspectAvatar::onOpen(const LLSD& data) // Extract appropriate avatar id mAvatarID = data["avatar_id"]; - // Position the inspector relative to the mouse cursor - // Similar to how tooltips are positioned - // See LLToolTipMgr::createToolTip - if (data.has("pos")) - { - LLUI::getInstance()->positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger()); - } - else - { - LLUI::getInstance()->positionViewNearMouse(this); - } + LLInspect::repositionInspector(data); // Generate link to avatar profile. LLTextBase* avatar_profile_link = getChild("avatar_profile_link"); @@ -348,7 +337,7 @@ void LLInspectAvatar::onClickMuteVolume() LLMuteList* mute_list = LLMuteList::getInstance(); bool is_muted = mute_list->isMuted(mAvatarID, LLMute::flagVoiceChat); - LLMute mute(mAvatarID, mAvatarName.getDisplayName(), LLMute::AGENT); + LLMute mute(mAvatarID, mAvatarName.getUserName(), LLMute::AGENT); if (!is_muted) { mute_list->add(mute, LLMute::flagVoiceChat); diff --git a/indra/newview/llinspectgroup.cpp b/indra/newview/llinspectgroup.cpp index fa8a53c546..0a30ab9217 100644 --- a/indra/newview/llinspectgroup.cpp +++ b/indra/newview/llinspectgroup.cpp @@ -38,7 +38,6 @@ #include "llfloater.h" #include "llfloaterreg.h" #include "llresmgr.h" // getMonetaryString() -#include "lltooltip.h" // positionViewNearMouse() #include "lltrans.h" #include "lluictrl.h" #include "llgroupiconctrl.h" @@ -124,17 +123,7 @@ void LLInspectGroup::onOpen(const LLSD& data) setGroupID(data["group_id"]); - // Position the inspector relative to the mouse cursor - // Similar to how tooltips are positioned - // See LLToolTipMgr::createToolTip - if (data.has("pos")) - { - LLUI::getInstance()->positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger()); - } - else - { - LLUI::getInstance()->positionViewNearMouse(this); - } + LLInspect::repositionInspector(data); // can't call from constructor as widgets are not built yet requestUpdate(); diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index cb7031971b..5329f10612 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -50,7 +50,6 @@ #include "lltextbox.h" // for description truncation #include "lltoggleablemenu.h" #include "lltrans.h" -#include "llui.h" // positionViewNearMouse() #include "lluictrl.h" class LLViewerObject; @@ -198,17 +197,8 @@ void LLInspectObject::onOpen(const LLSD& data) { mObjectFace = data["object_face"]; } - // Position the inspector relative to the mouse cursor - // Similar to how tooltips are positioned - // See LLToolTipMgr::createToolTip - if (data.has("pos")) - { - LLUI::getInstance()->positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger()); - } - else - { - LLUI::getInstance()->positionViewNearMouse(this); - } + + LLInspect::repositionInspector(data); // Promote hovered object to a complete selection, which will also force // a request for selected object data off the network diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp index 272c8acbd5..77320510a6 100644 --- a/indra/newview/llinspectremoteobject.cpp +++ b/indra/newview/llinspectremoteobject.cpp @@ -111,17 +111,7 @@ void LLInspectRemoteObject::onOpen(const LLSD& data) // update the inspector with the current object state update(); - // Position the inspector relative to the mouse cursor - // Similar to how tooltips are positioned - // See LLToolTipMgr::createToolTip - if (data.has("pos")) - { - LLUI::getInstance()->positionViewNearMouse(this, data["pos"]["x"].asInteger(), data["pos"]["y"].asInteger()); - } - else - { - LLUI::getInstance()->positionViewNearMouse(this); - } + LLInspect::repositionInspector(data); } void LLInspectRemoteObject::onClickMap() diff --git a/indra/newview/llinspecttoast.cpp b/indra/newview/llinspecttoast.cpp index d0034eff13..68801b0895 100644 --- a/indra/newview/llinspecttoast.cpp +++ b/indra/newview/llinspecttoast.cpp @@ -110,7 +110,7 @@ void LLInspectToast::onOpen(const LLSD& notification_id) panel_rect = panel->getRect(); reshape(panel_rect.getWidth(), panel_rect.getHeight()); - LLUI::getInstance()->positionViewNearMouse(this); + LLInspect::repositionInspector(notification_id); } // virtual diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index a0de3a2af1..a0bc1035bf 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -792,6 +792,19 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Copy")); } + if (isAgentInventory()) + { + items.push_back(std::string("New folder from selected")); + items.push_back(std::string("Subfolder Separator")); + std::set selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); + uuid_vec_t ids; + std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); + if (!is_only_items_selected(ids) && !is_only_cats_selected(ids)) + { + disabled_items.push_back(std::string("New folder from selected")); + } + } + if (obj->getIsLinkType()) { items.push_back(std::string("Find Original")); @@ -1102,7 +1115,10 @@ void LLInvFVBridge::addMarketplaceContextMenuOptions(U32 flags, LLInventoryModel::cat_array_t categories; LLInventoryModel::item_array_t items; gInventory.collectDescendents(local_version_folder_id, categories, items, FALSE); - if (categories.size() >= gSavedSettings.getU32("InventoryOutboxMaxFolderCount")) + LLCachedControl max_depth(gSavedSettings, "InventoryOutboxMaxFolderDepth", 4); + LLCachedControl max_count(gSavedSettings, "InventoryOutboxMaxFolderCount", 20); + if (categories.size() >= max_count + || depth > (max_depth + 1)) { disabled_items.push_back(std::string("New Folder")); } @@ -3798,7 +3814,20 @@ void LLFolderBridge::perform_pasteFromClipboard() { if (item && can_move_to_landmarks(item)) { - dropToFavorites(item); + if (LLClipboard::instance().isCutMode()) + { + LLViewerInventoryItem* viitem = dynamic_cast(item); + llassert(viitem); + if (viitem) + { + //changeItemParent() implicity calls dirtyFilter + changeItemParent(model, viitem, parent_id, FALSE); + } + } + else + { + dropToFavorites(item); + } } } else if (LLClipboard::instance().isCutMode()) @@ -4266,7 +4295,16 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items.push_back(std::string("Conference Chat Folder")); items.push_back(std::string("IM All Contacts In Folder")); } + + if (((flags & ITEM_IN_MULTI_SELECTION) == 0) && hasChildren()) + { + items.push_back(std::string("Ungroup folder items")); + } } + else + { + disabled_items.push_back(std::string("New folder from selected")); + } #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory) diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index d239b23e83..27edc8148e 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -1868,6 +1868,86 @@ void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) } } +void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLInventoryItem* inv_item = gInventory.getItem(*it); + if (inv_item) + { + change_item_parent(*it, new_cat_uuid); + } + else + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (inv_cat && !LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + gInventory.changeCategoryParent((LLViewerInventoryCategory*)inv_cat, new_cat_uuid, false); + } + } + } + + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + if (!floater_inventory) + { + LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; + return; + } + LLSidepanelInventory *sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + if (sidepanel_inventory) + { + if (sidepanel_inventory->getActivePanel()) + { + sidepanel_inventory->getActivePanel()->setSelection(new_cat_uuid, TAKE_FOCUS_YES); + LLFolderViewItem* fv_folder = sidepanel_inventory->getActivePanel()->getItemByID(new_cat_uuid); + if (fv_folder) + { + fv_folder->setOpen(TRUE); + } + } + } +} + +bool is_only_cats_selected(const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (!inv_cat) + { + return false; + } + } + return true; +} + +bool is_only_items_selected(const uuid_vec_t& selected_uuids) +{ + for (uuid_vec_t::const_iterator it = selected_uuids.begin(); it != selected_uuids.end(); ++it) + { + LLViewerInventoryItem* inv_item = gInventory.getItem(*it); + if (!inv_item) + { + return false; + } + } + return true; +} + + +void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::string& folder_name) +{ + LLInventoryObject* first_item = gInventory.getObject(*selected_uuids.begin()); + if (!first_item) + { + return; + } + + inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids); + gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func); + +} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -2522,6 +2602,81 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { (new LLDirPickerThread(boost::bind(&LLInventoryAction::saveMultipleTextures, _1, selected_items, model), std::string()))->getFile(); } + else if ("new_folder_from_selected" == action) + { + + LLInventoryObject* first_item = gInventory.getObject(*ids.begin()); + if (!first_item) + { + return; + } + const LLUUID& parent_uuid = first_item->getParentUUID(); + for (uuid_vec_t::const_iterator it = ids.begin(); it != ids.end(); ++it) + { + LLInventoryObject *item = gInventory.getObject(*it); + if (!item || item->getParentUUID() != parent_uuid) + { + LLNotificationsUtil::add("SameFolderRequired"); + return; + } + } + + LLSD args; + args["DESC"] = LLTrans::getString("New Folder"); + + LLNotificationsUtil::add("CreateSubfolder", args, LLSD(), + [ids](const LLSD& notification, const LLSD& response) + { + S32 opt = LLNotificationsUtil::getSelectedOption(notification, response); + if (opt == 0) + { + std::string settings_name = response["message"].asString(); + + LLInventoryObject::correctInventoryName(settings_name); + if (settings_name.empty()) + { + settings_name = LLTrans::getString("New Folder"); + } + move_items_to_new_subfolder(ids, settings_name); + } + }); + } + else if ("ungroup_folder_items" == action) + { + if (selected_uuid_set.size() == 1) + { + LLInventoryCategory* inv_cat = gInventory.getCategory(*ids.begin()); + if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + return; + } + const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); + LLInventoryModel::cat_array_t cats = *cat_array; + LLInventoryModel::item_array_t items = *item_array; + + for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) + { + LLViewerInventoryCategory* cat = *cat_iter; + if (cat) + { + gInventory.changeCategoryParent(cat, new_cat_uuid, false); + } + } + for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) + { + LLViewerInventoryItem* item = *item_iter; + if(item) + { + gInventory.changeItemParent(item, new_cat_uuid, false); + } + } + gInventory.removeCategory(inv_cat->getUUID()); + gInventory.notifyObservers(); + } + } else { std::set::iterator set_iter; diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 8915bfa1e0..ba9f157e47 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -93,6 +93,10 @@ LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id); +void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::string& folder_name); +void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected_uuids); +bool is_only_cats_selected(const uuid_vec_t& selected_uuids); +bool is_only_items_selected(const uuid_vec_t& selected_uuids); /** Miscellaneous global functions ** ** diff --git a/indra/newview/llinventorylistitem.cpp b/indra/newview/llinventorylistitem.cpp index 12bb609df8..de6a850b57 100644 --- a/indra/newview/llinventorylistitem.cpp +++ b/indra/newview/llinventorylistitem.cpp @@ -298,31 +298,41 @@ LLPanelInventoryListItemBase::LLPanelInventoryListItemBase(LLViewerInventoryItem applyXUILayout(icon_params, this); mIconCtrl = LLUICtrlFactory::create(icon_params); - if (mIconCtrl) - { - addChild(mIconCtrl); - } - else + if (!mIconCtrl) { LLIconCtrl::Params icon_params; icon_params.name = "item_icon"; mIconCtrl = LLUICtrlFactory::create(icon_params); } + if (mIconCtrl) + { + addChild(mIconCtrl); + } + else + { + LL_ERRS() << "Failed to create mIconCtrl" << LL_ENDL; + } + LLTextBox::Params text_params(params.item_name); applyXUILayout(text_params, this); mTitleCtrl = LLUICtrlFactory::create(text_params); - if (mTitleCtrl) + if (!mTitleCtrl) { - addChild(mTitleCtrl); - } - else - { - LLTextBox::Params text_aprams; + LLTextBox::Params text_params; text_params.name = "item_title"; mTitleCtrl = LLUICtrlFactory::create(text_params); } + + if (mTitleCtrl) + { + addChild(mTitleCtrl); + } + else + { + LL_ERRS() << "Failed to create mTitleCtrl" << LL_ENDL; + } } class WidgetVisibilityChanger diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 37500176ea..fab7ae8f1a 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -135,15 +135,7 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) ///---------------------------------------------------------------------------- /// Class LLInventoryValidationInfo ///---------------------------------------------------------------------------- -LLInventoryValidationInfo::LLInventoryValidationInfo(): - mFatalErrorCount(0), - mWarningCount(0), - mLoopCount(0), - mOrphanedCount(0), - mInitialized(false), - mFatalNoRootFolder(false), - mFatalNoLibraryRootFolder(false), - mFatalQADebugMode(false) +LLInventoryValidationInfo::LLInventoryValidationInfo() { } @@ -165,7 +157,6 @@ std::ostream& operator<<(std::ostream& os, const LLInventoryValidationInfo& v) void LLInventoryValidationInfo::asLLSD(LLSD& sd) const { sd["fatal_error_count"] = mFatalErrorCount; - sd["warning_count"] = mWarningCount; sd["loop_count"] = mLoopCount; sd["orphaned_count"] = mOrphanedCount; sd["initialized"] = mInitialized; @@ -173,6 +164,20 @@ void LLInventoryValidationInfo::asLLSD(LLSD& sd) const sd["fatal_no_root_folder"] = mFatalNoRootFolder; sd["fatal_no_library_root_folder"] = mFatalNoLibraryRootFolder; sd["fatal_qa_debug_mode"] = mFatalQADebugMode; + + sd["warning_count"] = mWarningCount; + if (mWarningCount>0) + { + sd["warnings"] = LLSD::emptyArray(); + for (auto const& it : mWarnings) + { + S32 val =LLSD::Integer(it.second); + if (val>0) + { + sd["warnings"][it.first] = val; + } + } + } if (mMissingRequiredSystemFolders.size()>0) { sd["missing_system_folders"] = LLSD::emptyArray(); @@ -344,13 +349,13 @@ const LLViewerInventoryCategory* LLInventoryModel::getFirstDescendantOf(const LL return NULL; } -LLInventoryModel::EAnscestorResult LLInventoryModel::getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const +LLInventoryModel::EAncestorResult LLInventoryModel::getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const { LLInventoryObject *object = getObject(object_id); if (!object) { LL_WARNS(LOG_INV) << "Unable to trace topmost ancestor, initial object " << object_id << " does not exist" << LL_ENDL; - return ANSCESTOR_MISSING; + return ANCESTOR_MISSING; } std::set object_ids{ object_id }; // loop protection @@ -360,19 +365,19 @@ LLInventoryModel::EAnscestorResult LLInventoryModel::getObjectTopmostAncestor(co if (object_ids.find(parent_id) != object_ids.end()) { LL_WARNS(LOG_INV) << "Detected a loop on an object " << parent_id << " when searching for ancestor of " << object_id << LL_ENDL; - return ANSCESTOR_LOOP; + return ANCESTOR_LOOP; } object_ids.insert(parent_id); LLInventoryObject *parent_object = getObject(parent_id); if (!parent_object) { LL_WARNS(LOG_INV) << "unable to trace topmost ancestor of " << object_id << ", missing item for uuid " << parent_id << LL_ENDL; - return ANSCESTOR_MISSING; + return ANCESTOR_MISSING; } object = parent_object; } result = object->getUUID(); - return ANSCESTOR_OK; + return ANCESTOR_OK; } // Get the object by id. Returns NULL if not found. @@ -541,9 +546,18 @@ void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::E LLViewerInventoryCategory* cat = getCategory(*it); changeCategoryParent(cat, main_id, TRUE); } - + // Purge the emptied folder - removeCategory(folder_id); + // Note that this might be a system folder, don't validate removability + LLViewerInventoryCategory* cat = getCategory(folder_id); + if (cat) + { + const LLUUID trash_id = findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (trash_id.notNull()) + { + changeCategoryParent(cat, trash_id, TRUE); + } + } remove_inventory_category(folder_id, NULL); } } @@ -3132,7 +3146,6 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32 gInventory.accountForUpdate(update); } - U32 changes = 0x0; if (account) { mask |= LLInventoryObserver::CREATE; @@ -3140,7 +3153,7 @@ bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, U32 //as above, this loop never seems to loop more than once per call for (item_array_t::iterator it = items.begin(); it != items.end(); ++it) { - changes |= gInventory.updateItem(*it, mask); + gInventory.updateItem(*it, mask); } gInventory.notifyObservers(); gViewerWindow->getWindow()->decBusyCount(); @@ -3899,9 +3912,9 @@ LLPointer LLInventoryModel::validate() const { LLPointer validation_info = new LLInventoryValidationInfo; S32 fatal_errs = 0; - S32 warnings = 0; - S32 loops = 0; - S32 orphaned = 0; + S32 warning_count= 0; + S32 loop_count = 0; + S32 orphaned_count = 0; if (getRootFolderID().isNull()) { @@ -3922,7 +3935,9 @@ LLPointer LLInventoryModel::validate() const // ParentChild should be one larger because of the special entry for null uuid. LL_INFOS("Inventory") << "unexpected sizes: cat map size " << mCategoryMap.size() << " parent/child " << mParentChildCategoryTree.size() << LL_ENDL; - warnings++; + + validation_info->mWarnings["category_map_size"]++; + warning_count++; } S32 cat_lock = 0; S32 item_lock = 0; @@ -3941,32 +3956,35 @@ LLPointer LLInventoryModel::validate() const if (!cat) { LL_WARNS("Inventory") << "null cat" << LL_ENDL; - warnings++; + validation_info->mWarnings["null_cat"]++; + warning_count++; continue; } LLUUID topmost_ancestor_id; // Will leave as null uuid on failure - EAnscestorResult res = getObjectTopmostAncestor(cat_id, topmost_ancestor_id); + EAncestorResult res = getObjectTopmostAncestor(cat_id, topmost_ancestor_id); switch (res) { - case ANSCESTOR_MISSING: - orphaned++; + case ANCESTOR_MISSING: + orphaned_count++; break; - case ANSCESTOR_LOOP: - loops++; + case ANCESTOR_LOOP: + loop_count++; break; - case ANSCESTOR_OK: + case ANCESTOR_OK: break; default: LL_WARNS("Inventory") << "Unknown ancestor error for " << cat_id << LL_ENDL; - warnings++; + validation_info->mWarnings["unknown_ancestor_status"]++; + warning_count++; break; } if (cat_id != cat->getUUID()) { LL_WARNS("Inventory") << "cat id/index mismatch " << cat_id << " " << cat->getUUID() << LL_ENDL; - warnings++; + validation_info->mWarnings["cat_id_index_mismatch"]++; + warning_count++; } if (cat->getParentUUID().isNull()) @@ -3976,7 +3994,8 @@ LLPointer LLInventoryModel::validate() const LL_WARNS("Inventory") << "cat " << cat_id << " has no parent, but is not root (" << getRootFolderID() << ") or library root (" << getLibraryRootFolderID() << ")" << LL_ENDL; - warnings++; + validation_info->mWarnings["null_parent"]++; + warning_count++; } } cat_array_t* cats; @@ -3985,7 +4004,8 @@ LLPointer LLInventoryModel::validate() const if (!cats || !items) { LL_WARNS("Inventory") << "invalid direct descendents for " << cat_id << LL_ENDL; - warnings++; + validation_info->mWarnings["direct_descendents"]++; + warning_count++; continue; } if (cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) @@ -4003,7 +4023,8 @@ LLPointer LLInventoryModel::validate() const << " cached " << cat->getDescendentCount() << " expected " << cats->size() << "+" << items->size() << "=" << cats->size() +items->size() << LL_ENDL; - warnings++; + validation_info->mWarnings["invalid_descendent_count"]++; + warning_count++; } } if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) @@ -4027,7 +4048,8 @@ LLPointer LLInventoryModel::validate() const if (!item) { LL_WARNS("Inventory") << "null item at index " << i << " for cat " << cat_id << LL_ENDL; - warnings++; + validation_info->mWarnings["null_item_at_index"]++; + warning_count++; continue; } @@ -4038,7 +4060,8 @@ LLPointer LLInventoryModel::validate() const LL_WARNS("Inventory") << "wrong parent for " << item_id << " found " << item->getParentUUID() << " expected " << cat_id << LL_ENDL; - warnings++; + validation_info->mWarnings["wrong_parent_for_item"]++; + warning_count++; } @@ -4048,7 +4071,8 @@ LLPointer LLInventoryModel::validate() const { LL_WARNS("Inventory") << "item " << item_id << " found as child of " << cat_id << " but not in top level mItemMap" << LL_ENDL; - warnings++; + validation_info->mWarnings["item_not_in_top_map"]++; + warning_count++; } else { @@ -4062,11 +4086,12 @@ LLPointer LLInventoryModel::validate() const // Topmost ancestor should be root or library. LLUUID topmost_ancestor_id; - EAnscestorResult found = getObjectTopmostAncestor(item_id, topmost_ancestor_id); - if (found != ANSCESTOR_OK) + EAncestorResult found = getObjectTopmostAncestor(item_id, topmost_ancestor_id); + if (found != ANCESTOR_OK) { LL_WARNS("Inventory") << "unable to find topmost ancestor for " << item_id << LL_ENDL; - warnings++; + validation_info->mWarnings["topmost_ancestor_not_found"]++; + warning_count++; } else { @@ -4077,7 +4102,8 @@ LLPointer LLInventoryModel::validate() const << " got " << topmost_ancestor_id << " expected " << getRootFolderID() << " or " << getLibraryRootFolderID() << LL_ENDL; - warnings++; + validation_info->mWarnings["topmost_ancestor_not_recognized"]++; + warning_count++; } } } @@ -4093,7 +4119,7 @@ LLPointer LLInventoryModel::validate() const { LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName() << "] orphaned - no child cat array for alleged parent " << parent_id << LL_ENDL; - orphaned++; + orphaned_count++; } else { @@ -4111,7 +4137,7 @@ LLPointer LLInventoryModel::validate() const { LL_WARNS("Inventory") << "cat " << cat_id << " name [" << cat->getName() << "] orphaned - not found in child cat array of alleged parent " << parent_id << LL_ENDL; - orphaned++; + orphaned_count++; } } } @@ -4120,7 +4146,7 @@ LLPointer
0 1 2 0 2 3 4 5 6 4 6 7 8 9 10 8 10 11 12 13 14 12 14 15 16 17 18 16 18 19 20 21 22 20 22 23