diff --git a/autobuild.xml b/autobuild.xml index ccb5ac4e12..1d303b3e95 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 diff --git a/doc/contributions.txt b/doc/contributions.txt index 640ffb037e..2c1e5487ce 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 @@ -820,6 +821,7 @@ Jonathan Yap Kadah Coba STORM-1060 STORM-1843 + SL-10675 Jondan Lundquist Joosten Briebers MAINT-7074 diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index e0ebbb76bd..9e897a7ce8 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -276,7 +276,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; } @@ -827,7 +827,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 { @@ -1273,7 +1274,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), @@ -1339,7 +1340,7 @@ void LLAudioSource::update() void LLAudioSource::updatePriority() { - if (isAmbient()) + if (isForcedPriority()) { mPriority = 1.f; } diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index b5fd4c27a1..e12fb970ca 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -266,8 +266,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 +326,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; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index b0c87b0208..e5752d3dad 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -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_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 3bdd0302ee..305aa1ecb5 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -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/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/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/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/llsys.cpp b/indra/llcommon/llsys.cpp index 9b6bb3826c..26a0fa1b1c 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -597,6 +597,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 +614,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 +660,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 +695,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 +709,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; 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/llfilesystem/lldiskcache.cpp b/indra/llfilesystem/lldiskcache.cpp index ee43a599f7..01144d8b0d 100644 --- a/indra/llfilesystem/lldiskcache.cpp +++ b/indra/llfilesystem/lldiskcache.cpp @@ -354,6 +354,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/llvolume.cpp b/indra/llmath/llvolume.cpp index 4a069b0f63..93f1d508f3 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -684,7 +684,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= 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; } } @@ -6866,7 +6872,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/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/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/llmodel.cpp b/indra/llprimitive/llmodel.cpp index 285c5f656b..555164f3b0 100644 --- a/indra/llprimitive/llmodel.cpp +++ b/indra/llprimitive/llmodel.cpp @@ -843,7 +843,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) @@ -1553,6 +1553,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); @@ -1659,6 +1678,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 354ceb26b7..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); diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 9bd3a0a6b0..b9dc689d1a 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++) 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/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/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/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/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 b3616e4ea8..66f7e60371 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -1668,7 +1668,7 @@ void LLWindowMacOSX::hideCursor() void LLWindowMacOSX::showCursor() { - if(mCursorHidden) + if(mCursorHidden || !isCGCursorVisible()) { // LL_INFOS() << "showCursor: showing" << LL_ENDL; mCursorHidden = FALSE; 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 a996777fa4..776797da7c 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 @@ -296,10 +298,12 @@ set(viewer_SOURCE_FILES llfloaterperformance.cpp llfloaterperms.cpp llfloaterpostprocess.cpp + llfloaterprofile.cpp llfloaterpreference.cpp llfloaterpreferencesgraphicsadvanced.cpp llfloaterpreferenceviewadvanced.cpp llfloaterpreviewtrash.cpp + llfloaterprofiletexture.cpp llfloaterproperties.cpp llfloaterregiondebugconsole.cpp llfloaterregioninfo.cpp @@ -332,7 +336,6 @@ set(viewer_SOURCE_FILES llfloatervoiceeffect.cpp llfloatervoicevolume.cpp llfloaterwebcontent.cpp - llfloaterwebprofile.cpp llfloaterwhitelistentry.cpp llfloaterwindowsize.cpp llfloaterworldmap.cpp @@ -478,7 +481,6 @@ set(viewer_SOURCE_FILES llpanelmediasettingsgeneral.cpp llpanelmediasettingspermissions.cpp llpanelmediasettingssecurity.cpp - llpanelme.cpp llpanelnearbymedia.cpp llpanelobject.cpp llpanelobjectinventory.cpp @@ -488,8 +490,6 @@ set(viewer_SOURCE_FILES llpanelpeople.cpp llpanelpeoplemenus.cpp llpanelpermissions.cpp - llpanelpick.cpp - llpanelpicks.cpp llpanelplaceinfo.cpp llpanelplaceprofile.cpp llpanelplaces.cpp @@ -498,6 +498,8 @@ set(viewer_SOURCE_FILES llpanelpresetspulldown.cpp llpanelprimmediacontrols.cpp llpanelprofile.cpp + llpanelprofileclassifieds.cpp + llpanelprofilepicks.cpp llpanelsnapshot.cpp llpanelsnapshotinventory.cpp llpanelsnapshotlocal.cpp @@ -659,6 +661,7 @@ set(viewer_SOURCE_FILES llviewercontrol.cpp llviewercontrollistener.cpp llviewerdisplay.cpp + llviewerdisplayname.cpp llviewerfloaterreg.cpp llviewerfoldertype.cpp llviewergenericmessage.cpp @@ -872,12 +875,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 @@ -937,10 +942,12 @@ set(viewer_HEADER_FILES llfloaterperformance.h llfloaterperms.h llfloaterpostprocess.h + llfloaterprofile.h llfloaterpreference.h llfloaterpreferencesgraphicsadvanced.h llfloaterpreferenceviewadvanced.h llfloaterpreviewtrash.h + llfloaterprofiletexture.h llfloaterproperties.h llfloaterregiondebugconsole.h llfloaterregioninfo.h @@ -973,7 +980,6 @@ set(viewer_HEADER_FILES llfloatervoiceeffect.h llfloatervoicevolume.h llfloaterwebcontent.h - llfloaterwebprofile.h llfloaterwhitelistentry.h llfloaterwindowsize.h llfloaterworldmap.h @@ -1109,7 +1115,6 @@ set(viewer_HEADER_FILES llpanelmediasettingsgeneral.h llpanelmediasettingspermissions.h llpanelmediasettingssecurity.h - llpanelme.h llpanelnearbymedia.h llpanelobject.h llpanelobjectinventory.h @@ -1119,8 +1124,6 @@ set(viewer_HEADER_FILES llpanelpeople.h llpanelpeoplemenus.h llpanelpermissions.h - llpanelpick.h - llpanelpicks.h llpanelplaceinfo.h llpanelplaceprofile.h llpanelplaces.h @@ -1129,6 +1132,8 @@ set(viewer_HEADER_FILES llpanelpresetspulldown.h llpanelprimmediacontrols.h llpanelprofile.h + llpanelprofileclassifieds.h + llpanelprofilepicks.h llpanelsnapshot.h llpanelteleporthistory.h llpaneltiptoast.h @@ -1291,6 +1296,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 e411592c25..5dbe61b99e 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.6.3 +6.6.5 diff --git a/indra/newview/app_settings/commands.xml b/indra/newview/app_settings/commands.xml index 05a87d197d..4a3dfffde1 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" /> 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 @@ -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 @@ -15769,6 +15769,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/llagent.cpp b/indra/newview/llagent.cpp index a658459caa..78c726043e 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -716,6 +716,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); } 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/llappviewer.cpp b/indra/newview/llappviewer.cpp index 60797c87d9..d61a66c696 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -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()) @@ -3291,9 +3293,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 @@ -4256,6 +4267,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() { @@ -4285,12 +4305,16 @@ bool LLAppViewer::initCache() 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; } } @@ -4336,7 +4360,19 @@ bool LLAppViewer::initCache() 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(); @@ -5418,7 +5454,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..25ba7c365f 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" @@ -81,6 +85,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) { @@ -316,57 +333,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 +1094,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 +1104,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; } } 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..fa7d5139ae 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -640,6 +640,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/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/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/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/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; imSpecialRenderMode = 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/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/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/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/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 58fbdba315..90390de52a 100644 --- a/indra/newview/llfloatermodelpreview.cpp +++ b/indra/newview/llfloatermodelpreview.cpp @@ -507,6 +507,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 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 0e94d31d90..ac1dbe9867 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -332,60 +332,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() @@ -911,7 +910,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(); @@ -1502,13 +1501,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; } @@ -1528,7 +1527,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; } @@ -2435,7 +2434,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; @@ -2462,7 +2461,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; @@ -2568,7 +2567,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"); @@ -2608,7 +2607,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 @@ -2663,10 +2662,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 3e4c853a08..a089fde9ff 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); @@ -217,7 +216,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..293ccc344c 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -1322,6 +1322,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 +1344,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; @@ -3683,7 +3686,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 77a04bc5d7..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())); @@ -553,7 +546,7 @@ void LLFloaterTools::refresh() mPanelObject->refresh(); mPanelVolume->refresh(); mPanelFace->refresh(); - refreshMedia(); + mPanelFace->refreshMedia(); mPanelContents->refresh(); mPanelLandInfo->refresh(); @@ -580,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(); } @@ -906,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. @@ -1160,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(); @@ -1221,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 index 09235d8fb2..977023cfe4 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -1008,7 +1008,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" ); } 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/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..98c1d65f92 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -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(); @@ -913,11 +902,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 +1779,6 @@ LLIMMgr::onConfirmForceCloseError( LLCallDialogManager::LLCallDialogManager(): mPreviousSessionlName(""), -mPreviousSessionType(LLIMModel::LLIMSession::P2P_SESSION), mCurrentSessionlName(""), mSession(NULL), mOldState(LLVoiceChannel::STATE_READY) @@ -1826,12 +1809,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 +1834,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 +1869,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 +1885,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 +2005,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 +2025,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 +2070,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 +2083,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 +2098,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 +2302,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 +2326,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 +2335,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 f357899be0..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"); 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 884007d2a6..f42c954185 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -1115,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")); } diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 37500176ea..216a9f4c94 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); } } @@ -3899,9 +3913,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 +3936,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 +3957,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 +3995,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 +4005,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 +4024,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 +4049,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 +4061,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 +4072,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 +4087,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 +4103,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 +4120,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 +4138,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 +4147,7 @@ LLPointer LLInventoryModel::validate() const LLFolderType::EType folder_type = cat->getPreferredType(); bool cat_is_in_library = false; LLUUID topmost_id; - if (getObjectTopmostAncestor(cat->getUUID(),topmost_id) == ANSCESTOR_OK && topmost_id == getLibraryRootFolderID()) + if (getObjectTopmostAncestor(cat->getUUID(),topmost_id) == ANCESTOR_OK && topmost_id == getLibraryRootFolderID()) { cat_is_in_library = true; } @@ -4153,14 +4180,15 @@ LLPointer LLInventoryModel::validate() const if (item->getUUID() != item_id) { LL_WARNS("Inventory") << "item_id " << item_id << " does not match " << item->getUUID() << LL_ENDL; - warnings++; + validation_info->mWarnings["item_id_mismatch"]++; + warning_count++; } const LLUUID& parent_id = item->getParentUUID(); if (parent_id.isNull()) { LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() << "] has null parent id!" << LL_ENDL; - orphaned++; + orphaned_count++; } else { @@ -4171,7 +4199,7 @@ LLPointer LLInventoryModel::validate() const { LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() << "] orphaned - alleged parent has no child items list " << parent_id << LL_ENDL; - orphaned++; + orphaned_count++; } else { @@ -4188,7 +4216,7 @@ LLPointer LLInventoryModel::validate() const { LL_WARNS("Inventory") << "item " << item_id << " name [" << item->getName() << "] orphaned - not found as child of alleged parent " << parent_id << LL_ENDL; - orphaned++; + orphaned_count++; } } @@ -4206,18 +4234,18 @@ LLPointer LLInventoryModel::validate() const LL_WARNS("Inventory") << "link " << item->getUUID() << " type " << item->getActualType() << " missing backlink info at target_id " << target_id << LL_ENDL; - orphaned++; + orphaned_count++; } // Links should have referents. if (item->getActualType() == LLAssetType::AT_LINK && !target_item) { LL_WARNS("Inventory") << "broken item link " << item->getName() << " id " << item->getUUID() << LL_ENDL; - orphaned++; + orphaned_count++; } else if (item->getActualType() == LLAssetType::AT_LINK_FOLDER && !target_cat) { LL_WARNS("Inventory") << "broken folder link " << item->getName() << " id " << item->getUUID() << LL_ENDL; - orphaned++; + orphaned_count++; } if (target_item && target_item->getIsLinkType()) { @@ -4289,13 +4317,15 @@ LLPointer LLInventoryModel::validate() const if (is_automatic) { LL_WARNS("Inventory") << "Fatal inventory corruption: cannot create system folder of type " << ft << LL_ENDL; - fatal_errs++; validation_info->mMissingRequiredSystemFolders.insert(folder_type); + fatal_errs++; } else { // Can create, and will when needed. - warnings++; + // (Not sure this is really a warning, but worth logging) + validation_info->mWarnings["missing_system_folder_can_create"]++; + warning_count++; } } else if (count_under_root > 1) @@ -4306,6 +4336,7 @@ LLPointer LLInventoryModel::validate() const { // It is a fatal problem or can lead to fatal problems for COF, // outfits, trash and other non-automatic folders. + validation_info->mFatalSystemDuplicate++; fatal_errs++; } else @@ -4313,13 +4344,15 @@ LLPointer LLInventoryModel::validate() const // For automatic folders it's not a fatal issue and shouldn't // break inventory or other functionality further // Exception: FT_SETTINGS is not automatic, but only deserves a warning. - warnings++; + validation_info->mWarnings["non_fatal_system_duplicate_under_root"]++; + warning_count++; } } if (count_elsewhere > 0) { LL_WARNS("Inventory") << "Found " << count_elsewhere << " extra folders of type " << ft << " outside of root" << LL_ENDL; - warnings++; + validation_info->mWarnings["non_fatal_system_duplicate_elsewhere"]++; + warning_count++; } } } @@ -4341,12 +4374,12 @@ LLPointer LLInventoryModel::validate() const // FIXME need to fail login and tell user to retry, contact support if problem persists. bool valid = (fatal_errs == 0); - LL_INFOS("Inventory") << "Validate done, fatal errors: " << fatal_errs << ", warnings: " << warnings << ", valid: " << valid << LL_ENDL; + LL_INFOS("Inventory") << "Validate done, fatal errors: " << fatal_errs << ", warnings: " << warning_count << ", valid: " << valid << LL_ENDL; validation_info->mFatalErrorCount = fatal_errs; - validation_info->mWarningCount = warnings; - validation_info->mLoopCount = loops; - validation_info->mOrphanedCount = orphaned; + validation_info->mWarningCount = warning_count; + validation_info->mLoopCount = loop_count; + validation_info->mOrphanedCount = orphaned_count; return validation_info; } diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index eeec89bfb0..c4133ff9bb 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -65,15 +65,19 @@ public: void toOstream(std::ostream& os) const; void asLLSD(LLSD& sd) const; + bool mInitialized{false}; + S32 mWarningCount{0}; + std::map mWarnings; + + S32 mLoopCount{0}; // Presence of folders whose ancestors loop onto themselves + S32 mOrphanedCount{0}; // Missing or orphaned items, links and folders + + S32 mFatalErrorCount{0}; + bool mFatalNoRootFolder{false}; + S32 mFatalSystemDuplicate{0}; + bool mFatalNoLibraryRootFolder{false}; + bool mFatalQADebugMode{false}; - S32 mFatalErrorCount; - S32 mWarningCount; - S32 mLoopCount; // Presence of folders whose ansestors loop onto themselves - S32 mOrphanedCount; // Missing or orphaned items, links and folders - bool mInitialized; - bool mFatalNoRootFolder; - bool mFatalNoLibraryRootFolder; - bool mFatalQADebugMode; std::set mMissingRequiredSystemFolders; std::set mDuplicateRequiredSystemFolders; }; @@ -286,13 +290,13 @@ public: // Check if one object has a parent chain up to the category specified by UUID. BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id) const; - enum EAnscestorResult{ - ANSCESTOR_OK = 0, - ANSCESTOR_MISSING = 1, - ANSCESTOR_LOOP = 2, + enum EAncestorResult{ + ANCESTOR_OK = 0, + ANCESTOR_MISSING = 1, + ANCESTOR_LOOP = 2, }; // Follow parent chain to the top. - EAnscestorResult getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const; + EAncestorResult getObjectTopmostAncestor(const LLUUID& object_id, LLUUID& result) const; //-------------------------------------------------------------------- // Find diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index 5a17332fde..de8db69e19 100644 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -919,6 +919,36 @@ LLLocalBitmapMgr::~LLLocalBitmapMgr() mBitmapList.clear(); } +LLUUID LLLocalBitmapMgr::addUnit(const std::string &filename) +{ + if (!checkTextureDimensions(filename)) + { + return LLUUID::null; + } + + LLLocalBitmap* unit = new LLLocalBitmap(filename); + + if (unit->getValid()) + { + mBitmapList.push_back(unit); + return unit->getTrackingID(); + } + else + { + LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n" + << "Filename: " << filename << LL_ENDL; + + LLSD notif_args; + notif_args["FNAME"] = filename; + LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args); + + delete unit; + unit = NULL; + } + + return LLUUID::null; +} + bool LLLocalBitmapMgr::addUnit() { bool add_successful = false; @@ -931,32 +961,10 @@ bool LLLocalBitmapMgr::addUnit() std::string filename = picker.getFirstFile(); while(!filename.empty()) { - if(!checkTextureDimensions(filename)) - { - filename = picker.getNextFile(); - continue; - } - - LLLocalBitmap* unit = new LLLocalBitmap(filename); - - if (unit->getValid()) - { - mBitmapList.push_back(unit); - add_successful = true; - } - else - { - LL_WARNS() << "Attempted to add invalid or unreadable image file, attempt cancelled.\n" - << "Filename: " << filename << LL_ENDL; - - LLSD notif_args; - notif_args["FNAME"] = filename; - LLNotificationsUtil::add("LocalBitmapsVerifyFail", notif_args); - - delete unit; - unit = NULL; - } - + if (addUnit(filename).notNull()) + { + add_successful = true; + } filename = picker.getNextFile(); } diff --git a/indra/newview/lllocalbitmaps.h b/indra/newview/lllocalbitmaps.h index def5a6bd6e..02b8834c16 100644 --- a/indra/newview/lllocalbitmaps.h +++ b/indra/newview/lllocalbitmaps.h @@ -116,6 +116,7 @@ class LLLocalBitmapMgr : public LLSingleton ~LLLocalBitmapMgr(); public: bool addUnit(); + LLUUID addUnit(const std::string &filename); void delUnit(LLUUID tracking_id); bool checkTextureDimensions(std::string filename); diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 79dbe17982..af2a9f6afd 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -109,6 +109,8 @@ public: LLLineEditor* getTextEntry() const { return mTextEntry; } void handleLoginComplete(); + bool isNavMeshDirty() { return mIsNavMeshDirty; } + private: enum EParcelIcon diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 044c76ce2c..dd4ae4d201 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -758,7 +758,14 @@ void LLMarketplaceData::initializeSLM(const status_updated_signal_t::slot_type& if (mMarketPlaceStatus != MarketplaceStatusCodes::MARKET_PLACE_NOT_INITIALIZED) { // If already initialized, just confirm the status so the callback gets called - setSLMStatus(mMarketPlaceStatus); + if (mMarketPlaceFailureReason.empty()) + { + setSLMStatus(mMarketPlaceStatus); + } + else + { + setSLMConnectionFailure(mMarketPlaceFailureReason); + } } else { @@ -799,28 +806,27 @@ void LLMarketplaceData::getMerchantStatusCoro() if (httpCode == HTTP_NOT_FOUND) { log_SLM_infos("Get /merchant", httpCode, std::string("User is not a merchant")); - setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MERCHANT); } else if (httpCode == HTTP_SERVICE_UNAVAILABLE) { log_SLM_infos("Get /merchant", httpCode, std::string("Merchant is not migrated")); - setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT); + LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_NOT_MIGRATED_MERCHANT); } - else if (httpCode == HTTP_INTERNAL_ERROR) + else { - // 499 includes timeout and ssl error - marketplace is down or having issues, we do not show it in this request according to MAINT-5938 LL_WARNS("SLM") << "SLM Merchant Request failed with status: " << httpCode << ", reason : " << status.toString() << ", code : " << result["error_code"].asString() << ", description : " << result["error_description"].asString() << LL_ENDL; - LLMarketplaceData::instance().setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); - } - else - { - std::string err_code = result["error_code"].asString(); - //std::string err_description = result["error_description"].asString(); - log_SLM_warning("Get /merchant", httpCode, status.toString(), err_code, result["error_description"]); - setSLMStatus(MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE); + std::string reason = status.toString(); + if (reason.empty()) + { + reason = result["error_code"].asString(); + } + // Since user might not even have a marketplace, there is no reason to report the error + // to the user, instead write it down into listings' floater + LLMarketplaceData::instance().setSLMConnectionFailure(reason); } return; } @@ -1298,6 +1304,17 @@ std::string LLMarketplaceData::getSLMConnectURL(const std::string& route) void LLMarketplaceData::setSLMStatus(U32 status) { mMarketPlaceStatus = status; + mMarketPlaceFailureReason.clear(); + if (mStatusUpdatedSignal) + { + (*mStatusUpdatedSignal)(); + } +} + +void LLMarketplaceData::setSLMConnectionFailure(const std::string& reason) +{ + mMarketPlaceStatus = MarketplaceStatusCodes::MARKET_PLACE_CONNECTION_FAILURE; + mMarketPlaceFailureReason = reason; if (mStatusUpdatedSignal) { (*mStatusUpdatedSignal)(); diff --git a/indra/newview/llmarketplacefunctions.h b/indra/newview/llmarketplacefunctions.h index fee9225f77..088507d850 100644 --- a/indra/newview/llmarketplacefunctions.h +++ b/indra/newview/llmarketplacefunctions.h @@ -198,7 +198,9 @@ public: typedef boost::signals2::signal status_updated_signal_t; void initializeSLM(const status_updated_signal_t::slot_type& cb); U32 getSLMStatus() const { return mMarketPlaceStatus; } + std::string getSLMConnectionfailureReason() { return mMarketPlaceFailureReason; } void setSLMStatus(U32 status); + void setSLMConnectionFailure(const std::string& reason); void getSLMListings(); bool isEmpty() { return (mMarketplaceItems.size() == 0); } void setDataFetchedSignal(const status_updated_signal_t::slot_type& cb); @@ -272,6 +274,7 @@ private: // Handling Marketplace connection and inventory connection U32 mMarketPlaceStatus; + std::string mMarketPlaceFailureReason; status_updated_signal_t* mStatusUpdatedSignal; LLInventoryObserver* mInventoryObserver; bool mDirtyCount; // If true, stock count value need to be updated at the next check diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp index bc45eb6d3a..9d0f62a30d 100644 --- a/indra/newview/llmediadataclient.cpp +++ b/indra/newview/llmediadataclient.cpp @@ -154,8 +154,7 @@ void mark_dead_and_remove_if(T &c, const PredicateMatchRequest &matchPred) if (matchPred(*it)) { (*it)->markDead(); - // *TDOO: When C++11 is in change the following line to: it = c.erase(it); - c.erase(it++); + it = c.erase(it); } else { diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index d28e929b48..4dd0543693 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -383,6 +383,9 @@ U32 LLMeshRepository::sLODPending = 0; U32 LLMeshRepository::sCacheBytesRead = 0; U32 LLMeshRepository::sCacheBytesWritten = 0; +U32 LLMeshRepository::sCacheBytesHeaders = 0; +U32 LLMeshRepository::sCacheBytesSkins = 0; +U32 LLMeshRepository::sCacheBytesDecomps = 0; U32 LLMeshRepository::sCacheReads = 0; U32 LLMeshRepository::sCacheWrites = 0; U32 LLMeshRepository::sMaxLockHoldoffs = 0; @@ -1877,6 +1880,7 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes LLMutexLock lock(mHeaderMutex); mMeshHeaderSize[mesh_id] = header_size; mMeshHeader[mesh_id] = header; + LLMeshRepository::sCacheBytesHeaders += header_size; } @@ -3019,27 +3023,6 @@ S32 LLMeshRepository::getActualMeshLOD(LLSD& header, S32 lod) return -1; } -void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) -{ - mThread->mMeshHeader[data.mUUID] = header; - - // we cache the mesh for default parameters - LLVolumeParams volume_params; - volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); - volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH); - - for (U32 i = 0; i < 4; i++) - { - if (data.mModel[i].notNull()) - { - LLPointer volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i)); - volume->copyVolumeFaces(data.mModel[i]); - volume->setMeshAssetLoaded(TRUE); - } - } - -} - // Handle failed or successful requests for mesh assets. // // Support for 200 responses was added for several reasons. One, @@ -3957,6 +3940,8 @@ void LLMeshRepository::notifyLoadedMeshes() void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info) { mSkinMap[info.mMeshID] = info; + // Alternative: We can get skin size from header + sCacheBytesSkins += info.sizeBytes(); skin_load_map::iterator iter = mLoadingSkins.find(info.mMeshID); if (iter != mLoadingSkins.end()) @@ -3980,10 +3965,14 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom { //just insert decomp into map mDecompositionMap[decomp->mMeshID] = decomp; mLoadingDecompositions.erase(decomp->mMeshID); + sCacheBytesDecomps += decomp->sizeBytes(); } else { //merge decomp with existing entry + sCacheBytesDecomps -= iter->second->sizeBytes(); iter->second->merge(decomp); + sCacheBytesDecomps += iter->second->sizeBytes(); + mLoadingDecompositions.erase(decomp->mMeshID); delete decomp; } diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 1989350303..f61da3e571 100644 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -554,6 +554,9 @@ public: static U32 sLODProcessing; static U32 sCacheBytesRead; static U32 sCacheBytesWritten; + static U32 sCacheBytesHeaders; + static U32 sCacheBytesSkins; + static U32 sCacheBytesDecomps; static U32 sCacheReads; static U32 sCacheWrites; static U32 sMaxLockHoldoffs; // Maximum sequential locking failures @@ -643,8 +646,6 @@ public: std::queue mPendingPhysicsShapeRequests; U32 mMeshThreadCount; - - void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header); LLMeshRepoThread* mThread; std::vector mUploads; diff --git a/indra/newview/llmodelpreview.cpp b/indra/newview/llmodelpreview.cpp index 3bca4fde83..3401587450 100644 --- a/indra/newview/llmodelpreview.cpp +++ b/indra/newview/llmodelpreview.cpp @@ -2714,7 +2714,6 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) continue; } - LLModel* base_mdl = *base_iter; base_iter++; S32 num_faces = mdl->getNumVolumeFaces(); @@ -2789,7 +2788,7 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) //find closest weight to vf.mVertices[i].mPosition LLVector3 pos(vf.mPositions[i].getF32ptr()); - const LLModel::weight_list& weight_list = base_mdl->getJointInfluences(pos); + const LLModel::weight_list& weight_list = mdl->getJointInfluences(pos); llassert(weight_list.size()>0 && weight_list.size() <= 4); // LLModel::loadModel() should guarantee this LLVector4 w(0, 0, 0, 0); @@ -2916,6 +2915,20 @@ void LLModelPreview::loadedCallback( { pPreview->lookupLODModelFiles(lod); } + + const LLVOAvatar* avatarp = pPreview->getPreviewAvatar(); + if (avatarp) { // set up ground plane for possible rendering + const LLVector3 root_pos = avatarp->mRoot->getPosition(); + const LLVector4a* ext = avatarp->mDrawable->getSpatialExtents(); + const LLVector4a min = ext[0], max = ext[1]; + const F32 center = (max[2] - min[2]) * 0.5f; + const F32 ground = root_pos[2] - center; + auto plane = pPreview->mGroundPlane; + plane[0] = {min[0], min[1], ground}; + plane[1] = {max[0], min[1], ground}; + plane[2] = {max[0], max[1], ground}; + plane[3] = {min[0], max[1], ground}; + } } } @@ -3123,6 +3136,9 @@ BOOL LLModelPreview::render() // (note: all these UI updates need to be somewhere that is not render) fmp->childSetValue("upload_skin", true); mFirstSkinUpdate = false; + upload_skin = true; + skin_weight = true; + mViewOption["show_skin_weight"] = true; } fmp->enableViewOption("show_skin_weight"); @@ -3727,6 +3743,7 @@ BOOL LLModelPreview::render() { getPreviewAvatar()->renderBones(); } + renderGroundPlane(mPelvisZOffset); if (shader) { shader->bind(); @@ -3748,6 +3765,28 @@ BOOL LLModelPreview::render() return TRUE; } +void LLModelPreview::renderGroundPlane(float z_offset) +{ // Not necesarilly general - beware - but it seems to meet the needs of LLModelPreview::render + + gGL.diffuseColor3f( 1.0f, 0.0f, 1.0f ); + + gGL.begin(LLRender::LINES); + gGL.vertex3fv(mGroundPlane[0].mV); + gGL.vertex3fv(mGroundPlane[1].mV); + + gGL.vertex3fv(mGroundPlane[1].mV); + gGL.vertex3fv(mGroundPlane[2].mV); + + gGL.vertex3fv(mGroundPlane[2].mV); + gGL.vertex3fv(mGroundPlane[3].mV); + + gGL.vertex3fv(mGroundPlane[3].mV); + gGL.vertex3fv(mGroundPlane[0].mV); + + gGL.end(); +} + + //----------------------------------------------------------------------------- // refresh() //----------------------------------------------------------------------------- diff --git a/indra/newview/llmodelpreview.h b/indra/newview/llmodelpreview.h index 215f44357f..7cb5fd6845 100644 --- a/indra/newview/llmodelpreview.h +++ b/indra/newview/llmodelpreview.h @@ -224,6 +224,8 @@ private: LLVOAvatar* getPreviewAvatar(void) { return mPreviewAvatar; } // Count amount of original models, excluding sub-models static U32 countRootModels(LLModelLoader::model_list models); + LLVector3 mGroundPlane[4]; + void renderGroundPlane(float z_offset = 0.0f); typedef enum { diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 4a8ef53a8b..bf00d77dea 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -352,7 +352,7 @@ BOOL LLMuteList::add(const LLMute& mute, U32 flags) void LLMuteList::updateAdd(const LLMute& mute) { - // External mutes (e.g. Avaline callers) are local only, don't send them to the server. + // External mutes are local only, don't send them to the server. if (mute.mType == LLMute::EXTERNAL) { return; diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index e3ef407f76..d7d6fa1893 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -280,6 +280,25 @@ BOOL LLNameListCtrl::handleToolTip(S32 x, S32 y, MASK mask) return handled; } +// virtual +BOOL LLNameListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + LLNameListItem* hit_item = dynamic_cast(hitItem(x, y)); + LLFloater* floater = gFloaterView->getParentFloater(this); + if (floater && floater->isFrontmost() && hit_item) + { + if(hit_item->isGroup()) + { + ContextMenuType prev_menu = getContextMenuType(); + setContextMenu(MENU_GROUP); + BOOL handled = LLScrollListCtrl::handleRightMouseDown(x, y, mask); + setContextMenu(prev_menu); + return handled; + } + } + return LLScrollListCtrl::handleRightMouseDown(x, y, mask); +} + // public void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos, BOOL enabled) diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h index d7e991c94d..4a4bd4ba09 100644 --- a/indra/newview/llnamelistctrl.h +++ b/indra/newview/llnamelistctrl.h @@ -180,6 +180,8 @@ public: /*virtual*/ void mouseOverHighlightNthItem( S32 index ); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + bool isSpecialType() { return (mNameListType == SPECIAL); } void setNameListType(e_name_type type) { mNameListType = type; } diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 19dbbeb60e..2dd7cfab27 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -713,7 +713,7 @@ void LLNavigationBar::resizeLayoutPanel() } void LLNavigationBar::invokeSearch(std::string search_text) { - LLFloaterReg::showInstance("search", LLSD().with("category", "all").with("query", LLSD(search_text))); + LLFloaterReg::showInstance("search", LLSD().with("category", "standard").with("query", LLSD(search_text))); } void LLNavigationBar::clearHistoryCache() @@ -733,3 +733,8 @@ int LLNavigationBar::getDefFavBarHeight() { return mDefaultFpRect.getHeight(); } + +bool LLNavigationBar::isRebakeNavMeshAvailable() +{ + return mCmbLocation->isNavMeshDirty(); +} diff --git a/indra/newview/llnavigationbar.h b/indra/newview/llnavigationbar.h index 646911a62c..11c671294a 100755 --- a/indra/newview/llnavigationbar.h +++ b/indra/newview/llnavigationbar.h @@ -102,6 +102,8 @@ public: int getDefNavBarHeight(); int getDefFavBarHeight(); + + bool isRebakeNavMeshAvailable(); private: // the distance between navigation panel and favorites panel in pixels diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 4febb72c6c..f419e2e06d 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -1374,6 +1374,7 @@ void LLOutfitGallery::onSelectPhoto(LLUUID selected_outfit_id) texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLOutfitGallery::onTexturePickerCommit, this, _1, _2)); texture_floaterp->setOnUpdateImageStatsCallback(boost::bind(&LLOutfitGallery::onTexturePickerUpdateImageStats, this, _1)); texture_floaterp->setLocalTextureEnabled(FALSE); + texture_floaterp->setBakeTextureEnabled(FALSE); texture_floaterp->setCanApply(false, true); } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 37ed4bc74c..ff33efe4aa 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llpanelavatar.cpp * @brief LLPanelAvatar and related class implementations * * $LicenseInfo:firstyear=2004&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$ */ @@ -28,173 +28,127 @@ #include "llpanelavatar.h" #include "llagent.h" -#include "llavataractions.h" -#include "llcallingcard.h" -#include "llcombobox.h" -#include "lldateutil.h" // ageFromDate() -#include "llimview.h" -#include "llmenubutton.h" -#include "llnotificationsutil.h" -#include "llslurl.h" -#include "lltexteditor.h" -#include "lltexturectrl.h" -#include "lltoggleablemenu.h" +#include "llloadingindicator.h" #include "lltooldraganddrop.h" -#include "llscrollcontainer.h" -#include "llavatariconctrl.h" -#include "llfloaterreg.h" -#include "llnotificationsutil.h" -#include "llviewermenu.h" // is_agent_mappable -#include "llvoiceclient.h" -#include "lltextbox.h" -#include "lltrans.h" -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLDropTarget -// -// This handy class is a simple way to drop something on another -// view. It handles drop events, always setting itself to the size of -// its parent. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +////////////////////////////////////////////////////////////////////////// +// LLProfileDropTarget -class LLDropTarget : public LLView -{ -public: - struct Params : public LLInitParam::Block - { - Optional agent_id; - Params() - : agent_id("agent_id") - { - changeDefault(mouse_opaque, false); - changeDefault(follows.flags, FOLLOWS_ALL); - } - }; - - LLDropTarget(const Params&); - ~LLDropTarget(); - - void doDrop(EDragAndDropType cargo_type, void* cargo_data); - - // - // LLView functionality - virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg); - void setAgentID(const LLUUID &agent_id) { mAgentID = agent_id; } -protected: - LLUUID mAgentID; -}; - -LLDropTarget::LLDropTarget(const LLDropTarget::Params& p) -: LLView(p), - mAgentID(p.agent_id) +LLProfileDropTarget::LLProfileDropTarget(const LLProfileDropTarget::Params& p) +: LLView(p), + mAgentID(p.agent_id) {} -LLDropTarget::~LLDropTarget() -{} - -void LLDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data) +void LLProfileDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data) { - LL_INFOS() << "LLDropTarget::doDrop()" << LL_ENDL; + LL_INFOS() << "LLProfileDropTarget::doDrop()" << LL_ENDL; } -BOOL LLDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) +BOOL LLProfileDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) { - if(getParent()) - { - LLToolDragAndDrop::handleGiveDragAndDrop(mAgentID, LLUUID::null, drop, - cargo_type, cargo_data, accept); + if (getParent()) + { + LLToolDragAndDrop::handleGiveDragAndDrop(mAgentID, LLUUID::null, drop, + cargo_type, cargo_data, accept); - return TRUE; - } + return TRUE; + } - return FALSE; + return FALSE; } -static LLDefaultChildRegistry::Register r("drop_target"); +static LLDefaultChildRegistry::Register r("profile_drop_target"); ////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// +// LLPanelProfileTab LLPanelProfileTab::LLPanelProfileTab() : LLPanel() , mAvatarId(LLUUID::null) +, mLoadingState(PROFILE_INIT) +, mSelfProfile(false) { } LLPanelProfileTab::~LLPanelProfileTab() { - if(getAvatarId().notNull()) - { - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this); - } } -void LLPanelProfileTab::setAvatarId(const LLUUID& id) +void LLPanelProfileTab::setAvatarId(const LLUUID& avatar_id) { - if(id.notNull()) - { - if(getAvatarId().notNull()) - { - LLAvatarPropertiesProcessor::getInstance()->removeObserver(mAvatarId,this); - } - mAvatarId = id; - LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(),this); - } + if (avatar_id.notNull()) + { + mAvatarId = avatar_id; + mSelfProfile = (getAvatarId() == gAgentID); + } } void LLPanelProfileTab::onOpen(const LLSD& key) { - // Don't reset panel if we are opening it for same avatar. - if(getAvatarId() != key.asUUID()) - { - resetControls(); - resetData(); + // Update data even if we are viewing same avatar profile as some data might been changed. + setAvatarId(key.asUUID()); - scrollToTop(); - } - - // Update data even if we are viewing same avatar profile as some data might been changed. - setAvatarId(key.asUUID()); - updateData(); - updateButtons(); + setApplyProgress(true); } -void LLPanelProfileTab::scrollToTop() +void LLPanelProfileTab::setLoaded() { - LLScrollContainer* scrollContainer = findChild("profile_scroll"); - if (scrollContainer) - scrollContainer->goToTop(); + setApplyProgress(false); + + mLoadingState = PROFILE_LOADED; } -void LLPanelProfileTab::onMapButtonClick() +void LLPanelProfileTab::setApplyProgress(bool started) { - LLAvatarActions::showOnMap(getAvatarId()); + LLLoadingIndicator* indicator = findChild("progress_indicator"); + + if (indicator) + { + indicator->setVisible(started); + + if (started) + { + indicator->start(); + } + else + { + indicator->stop(); + } + } + + LLView* panel = findChild("indicator_stack"); + if (panel) + { + panel->setVisible(started); + } } -void LLPanelProfileTab::updateButtons() +LLPanelProfilePropertiesProcessorTab::LLPanelProfilePropertiesProcessorTab() + : LLPanelProfileTab() { - bool is_buddy_online = LLAvatarTracker::instance().isBuddyOnline(getAvatarId()); - - if(LLAvatarActions::isFriend(getAvatarId())) - { - getChildView("teleport")->setEnabled(is_buddy_online); - } - else - { - getChildView("teleport")->setEnabled(true); - } - - bool enable_map_btn = (is_buddy_online && - is_agent_mappable(getAvatarId())) - || gAgent.isGodlike(); - getChildView("show_on_map_btn")->setEnabled(enable_map_btn); +} + +LLPanelProfilePropertiesProcessorTab::~LLPanelProfilePropertiesProcessorTab() +{ + if (getAvatarId().notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); + } +} + +void LLPanelProfilePropertiesProcessorTab::setAvatarId(const LLUUID & avatar_id) +{ + if (avatar_id.notNull()) + { + if (getAvatarId().notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); + } + LLPanelProfileTab::setAvatarId(avatar_id); + LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); + } } diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index e33a850cfa..f182660c8e 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -1,25 +1,25 @@ -/** +/** * @file llpanelavatar.h - * @brief LLPanelAvatar and related class definitions + * @brief Legacy profile panel base class * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * + * Copyright (C) 2019, 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$ */ @@ -29,80 +29,141 @@ #include "llpanel.h" #include "llavatarpropertiesprocessor.h" -#include "llcallingcard.h" -#include "llvoiceclient.h" #include "llavatarnamecache.h" class LLComboBox; class LLLineEditor; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLProfileDropTarget +// +// This handy class is a simple way to drop something on another +// view. It handles drop events, always setting itself to the size of +// its parent. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class LLProfileDropTarget : public LLView +{ +public: + struct Params : public LLInitParam::Block + { + Optional agent_id; + Params() + : agent_id("agent_id") + { + changeDefault(mouse_opaque, false); + changeDefault(follows.flags, FOLLOWS_ALL); + } + }; + + LLProfileDropTarget(const Params&); + ~LLProfileDropTarget() {} + + void doDrop(EDragAndDropType cargo_type, void* cargo_data); + + // + // LLView functionality + virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + + void setAgentID(const LLUUID &agent_id) { mAgentID = agent_id; } + +protected: + LLUUID mAgentID; +}; + + /** * Base class for any Profile View. */ class LLPanelProfileTab - : public LLPanel - , public LLAvatarPropertiesObserver + : public LLPanel { public: - /** - * Sets avatar ID, sets panel as observer of avatar related info replies from server. - */ - virtual void setAvatarId(const LLUUID& id); + /** + * Sets avatar ID, sets panel as observer of avatar related info replies from server. + */ + virtual void setAvatarId(const LLUUID& avatar_id); - /** - * Returns avatar ID. - */ - virtual const LLUUID& getAvatarId() { return mAvatarId; } + /** + * Returns avatar ID. + */ + virtual const LLUUID& getAvatarId() { return mAvatarId; } - /** - * Sends update data request to server. - */ - virtual void updateData() = 0; + /** + * Sends update data request to server. + */ + virtual void updateData() {}; - /** - * Clears panel data if viewing avatar info for first time and sends update data request. - */ - virtual void onOpen(const LLSD& key); + /** + * Clears panel data if viewing avatar info for first time and sends update data request. + */ + virtual void onOpen(const LLSD& key); - /** - * Profile tabs should close any opened panels here. - * - * Called from LLPanelProfile::onOpen() before opening new profile. - * See LLPanelPicks::onClosePanel for example. LLPanelPicks closes picture info panel - * before new profile is displayed, otherwise new profile will - * be hidden behind picture info panel. - */ - virtual void onClosePanel() {} + /** + * Clears all data received from server. + */ + virtual void resetData(){}; - /** - * Resets controls visibility, state, etc. - */ - virtual void resetControls(){}; - - /** - * Clears all data received from server. - */ - virtual void resetData(){}; - - /*virtual*/ ~LLPanelProfileTab(); + /*virtual*/ ~LLPanelProfileTab(); protected: - LLPanelProfileTab(); + LLPanelProfileTab(); - /** - * Scrolls panel to top when viewing avatar info for first time. - */ - void scrollToTop(); + enum ELoadingState + { + PROFILE_INIT, + PROFILE_LOADING, + PROFILE_LOADED, + }; - virtual void onMapButtonClick(); - virtual void updateButtons(); + // mLoading: false: Initial state, can request + // true: Data requested, skip duplicate requests (happens due to LLUI's habit of repeated callbacks) + // mLoaded: false: Initial state, show loading indicator + // true: Data recieved, which comes in a single message, hide indicator + ELoadingState getLoadingState() { return mLoadingState; } + virtual void setLoaded(); + void setApplyProgress(bool started); + + const bool getSelfProfile() const { return mSelfProfile; } + +public: + void setIsLoading() { mLoadingState = PROFILE_LOADING; } + void resetLoading() { mLoadingState = PROFILE_INIT; } + + bool getStarted() { return mLoadingState != PROFILE_INIT; } + bool getIsLoaded() { return mLoadingState == PROFILE_LOADED; } + + virtual bool hasUnsavedChanges() { return false; } + virtual void commitUnsavedChanges() {} private: - LLUUID mAvatarId; + LLUUID mAvatarId; + ELoadingState mLoadingState; + bool mSelfProfile; +}; + +class LLPanelProfilePropertiesProcessorTab + : public LLPanelProfileTab + , public LLAvatarPropertiesObserver +{ +public: + LLPanelProfilePropertiesProcessorTab(); + ~LLPanelProfilePropertiesProcessorTab(); + + /*virtual*/ void setAvatarId(const LLUUID& avatar_id); + + /** + * Processes data received from server via LLAvatarPropertiesObserver. + */ + virtual void processProperties(void* data, EAvatarProcessorType type) = 0; }; #endif // LL_LLPANELAVATAR_H diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index c0342eef4e..183000ceac 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -1,10 +1,10 @@ /** * @file llpanelclassified.cpp - * @brief LLPanelClassified class implementation + * @brief LLPanelClassifiedInfo class implementation * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2021, 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 @@ -34,33 +34,21 @@ #include "lldispatcher.h" #include "llfloaterreg.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" #include "llparcel.h" #include "llagent.h" #include "llclassifiedflags.h" -#include "llcommandhandler.h" // for classified HTML detail page click tracking #include "lliconctrl.h" -#include "lllineeditor.h" -#include "llcombobox.h" #include "lltexturectrl.h" -#include "lltexteditor.h" -#include "llviewerparcelmgr.h" #include "llfloaterworldmap.h" #include "llviewergenericmessage.h" // send_generic_message #include "llviewerregion.h" -#include "llviewertexture.h" -#include "lltrans.h" #include "llscrollcontainer.h" -#include "llstatusbar.h" -#include "llviewertexture.h" #include "llcorehttputil.h" -const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$ - //static LLPanelClassifiedInfo::panel_list_t LLPanelClassifiedInfo::sAllPanels; +static LLPanelInjector t_panel_panel_classified_info("panel_classified_info"); // "classifiedclickthrough" // strings[0] = classified_id @@ -118,17 +106,8 @@ LLPanelClassifiedInfo::~LLPanelClassifiedInfo() sAllPanels.remove(this); } -// static -LLPanelClassifiedInfo* LLPanelClassifiedInfo::create() -{ - LLPanelClassifiedInfo* panel = new LLPanelClassifiedInfo(); - panel->buildFromFile("panel_classified_info.xml"); - return panel; -} - BOOL LLPanelClassifiedInfo::postBuild() { - childSetAction("back_btn", boost::bind(&LLPanelClassifiedInfo::onExit, this)); childSetAction("show_on_map_btn", boost::bind(&LLPanelClassifiedInfo::onMapClick, this)); childSetAction("teleport_btn", boost::bind(&LLPanelClassifiedInfo::onTeleportClick, this)); @@ -144,16 +123,6 @@ BOOL LLPanelClassifiedInfo::postBuild() return TRUE; } -void LLPanelClassifiedInfo::setExitCallback(const commit_callback_t& cb) -{ - getChild("back_btn")->setClickedCallback(cb); -} - -void LLPanelClassifiedInfo::setEditClassifiedCallback(const commit_callback_t& cb) -{ - getChild("edit_btn")->setClickedCallback(cb); -} - void LLPanelClassifiedInfo::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) { LLPanel::reshape(width, height, called_from_parent); @@ -286,6 +255,8 @@ void LLPanelClassifiedInfo::processProperties(void* data, EAvatarProcessorType t getChild("creation_date")->setValue(date_str); setInfoLoaded(true); + + LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); } } } @@ -590,588 +561,4 @@ void LLPanelClassifiedInfo::onTeleportClick() } } -void LLPanelClassifiedInfo::onExit() -{ - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); - gGenericDispatcher.addHandler("classifiedclickthrough", NULL); // deregister our handler -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -static const S32 CB_ITEM_MATURE = 0; -static const S32 CB_ITEM_PG = 1; - -LLPanelClassifiedEdit::LLPanelClassifiedEdit() - : LLPanelClassifiedInfo() - , mIsNew(false) - , mIsNewWithErrors(false) - , mCanClose(false) - , mPublishFloater(NULL) -{ -} - -LLPanelClassifiedEdit::~LLPanelClassifiedEdit() -{ -} - -//static -LLPanelClassifiedEdit* LLPanelClassifiedEdit::create() -{ - LLPanelClassifiedEdit* panel = new LLPanelClassifiedEdit(); - panel->buildFromFile("panel_edit_classified.xml"); - return panel; -} - -BOOL LLPanelClassifiedEdit::postBuild() -{ - LLPanelClassifiedInfo::postBuild(); - - LLUICtrl* edit_icon = getChild("edit_icon"); - mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelClassifiedEdit::onTexturePickerMouseEnter, this, edit_icon)); - mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelClassifiedEdit::onTexturePickerMouseLeave, this, edit_icon)); - edit_icon->setVisible(false); - - LLLineEditor* line_edit = getChild("classified_name"); - line_edit->setKeystrokeCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL); - - LLTextEditor* text_edit = getChild("classified_desc"); - text_edit->setKeystrokeCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this)); - - LLComboBox* combobox = getChild( "category"); - LLClassifiedInfo::cat_map::iterator iter; - for (iter = LLClassifiedInfo::sCategories.begin(); - iter != LLClassifiedInfo::sCategories.end(); - iter++) - { - combobox->add(LLTrans::getString(iter->second)); - } - - combobox->setCommitCallback(boost::bind(&LLPanelClassifiedEdit::onChange, this)); - - childSetCommitCallback("content_type", boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL); - childSetCommitCallback("price_for_listing", boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL); - childSetCommitCallback("auto_renew", boost::bind(&LLPanelClassifiedEdit::onChange, this), NULL); - - childSetAction("save_changes_btn", boost::bind(&LLPanelClassifiedEdit::onSaveClick, this)); - childSetAction("set_to_curr_location_btn", boost::bind(&LLPanelClassifiedEdit::onSetLocationClick, this)); - - mSnapshotCtrl->setOnSelectCallback(boost::bind(&LLPanelClassifiedEdit::onTextureSelected, this)); - - return TRUE; -} - -void LLPanelClassifiedEdit::fillIn(const LLSD& key) -{ - setAvatarId(gAgent.getID()); - - if(key.isUndefined()) - { - setPosGlobal(gAgent.getPositionGlobal()); - - LLUUID snapshot_id = LLUUID::null; - std::string desc; - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if(parcel) - { - desc = parcel->getDesc(); - snapshot_id = parcel->getSnapshotID(); - } - - std::string region_name = LLTrans::getString("ClassifiedUpdateAfterPublish"); - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - region_name = region->getName(); - } - - getChild("classified_name")->setValue(makeClassifiedName()); - getChild("classified_desc")->setValue(desc); - setSnapshotId(snapshot_id); - setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal())); - // server will set valid parcel id - setParcelId(LLUUID::null); - } - else - { - setClassifiedId(key["classified_id"]); - setClassifiedName(key["name"]); - setDescription(key["desc"]); - setSnapshotId(key["snapshot_id"]); - setCategory((U32)key["category"].asInteger()); - setContentType((U32)key["content_type"].asInteger()); - setClassifiedLocation(key["location_text"]); - getChild("auto_renew")->setValue(key["auto_renew"]); - getChild("price_for_listing")->setValue(key["price_for_listing"].asInteger()); - } -} - -void LLPanelClassifiedEdit::onOpen(const LLSD& key) -{ - mIsNew = key.isUndefined(); - - scrollToTop(); - - // classified is not created yet - bool is_new = isNew() || isNewWithErrors(); - - if(is_new) - { - resetData(); - resetControls(); - - fillIn(key); - - if(isNew()) - { - LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); - } - } - else - { - LLPanelClassifiedInfo::onOpen(key); - } - - std::string save_btn_label = is_new ? getString("publish_label") : getString("save_label"); - getChild("save_changes_btn")->setLabelArg("[LABEL]", save_btn_label); - - enableVerbs(is_new); - enableEditing(is_new); - showEditing(!is_new); - resetDirty(); - setInfoLoaded(false); -} - -void LLPanelClassifiedEdit::processProperties(void* data, EAvatarProcessorType type) -{ - if(APT_CLASSIFIED_INFO == type) - { - LLAvatarClassifiedInfo* c_info = static_cast(data); - if(c_info && getClassifiedId() == c_info->classified_id) - { - // see LLPanelClassifiedEdit::sendUpdate() for notes - mIsNewWithErrors = false; - // for just created classified - panel will probably be closed when we get here. - if(!getVisible()) - { - return; - } - - enableEditing(true); - - setClassifiedName(c_info->name); - setDescription(c_info->description); - setSnapshotId(c_info->snapshot_id); - setParcelId(c_info->parcel_id); - setPosGlobal(c_info->pos_global); - - setClassifiedLocation(createLocationText(c_info->parcel_name, c_info->sim_name, c_info->pos_global)); - // *HACK see LLPanelClassifiedEdit::sendUpdate() - setCategory(c_info->category - 1); - - bool mature = is_cf_mature(c_info->flags); - bool auto_renew = is_cf_auto_renew(c_info->flags); - - setContentType(mature ? CB_ITEM_MATURE : CB_ITEM_PG); - getChild("auto_renew")->setValue(auto_renew); - getChild("price_for_listing")->setValue(c_info->price_for_listing); - getChildView("price_for_listing")->setEnabled(isNew()); - - resetDirty(); - setInfoLoaded(true); - enableVerbs(false); - - // for just created classified - in case user opened edit panel before processProperties() callback - getChild("save_changes_btn")->setLabelArg("[LABEL]", getString("save_label")); - } - } -} - -BOOL LLPanelClassifiedEdit::isDirty() const -{ - if(mIsNew) - { - return TRUE; - } - - BOOL dirty = false; - - dirty |= LLPanelClassifiedInfo::isDirty(); - dirty |= getChild("classified_snapshot")->isDirty(); - dirty |= getChild("classified_name")->isDirty(); - dirty |= getChild("classified_desc")->isDirty(); - dirty |= getChild("category")->isDirty(); - dirty |= getChild("content_type")->isDirty(); - dirty |= getChild("auto_renew")->isDirty(); - dirty |= getChild("price_for_listing")->isDirty(); - - return dirty; -} - -void LLPanelClassifiedEdit::resetDirty() -{ - LLPanelClassifiedInfo::resetDirty(); - getChild("classified_snapshot")->resetDirty(); - getChild("classified_name")->resetDirty(); - - LLTextEditor* desc = getChild("classified_desc"); - // call blockUndo() to really reset dirty(and make isDirty work as intended) - desc->blockUndo(); - desc->resetDirty(); - - getChild("category")->resetDirty(); - getChild("content_type")->resetDirty(); - getChild("auto_renew")->resetDirty(); - getChild("price_for_listing")->resetDirty(); -} - -void LLPanelClassifiedEdit::setSaveCallback(const commit_signal_t::slot_type& cb) -{ - mSaveButtonClickedSignal.connect(cb); -} - -void LLPanelClassifiedEdit::setCancelCallback(const commit_signal_t::slot_type& cb) -{ - getChild("cancel_btn")->setClickedCallback(cb); -} - -void LLPanelClassifiedEdit::resetControls() -{ - LLPanelClassifiedInfo::resetControls(); - - getChild("category")->setCurrentByIndex(0); - getChild("content_type")->setCurrentByIndex(0); - getChild("auto_renew")->setValue(false); - getChild("price_for_listing")->setValue(MINIMUM_PRICE_FOR_LISTING); - getChildView("price_for_listing")->setEnabled(TRUE); -} - -bool LLPanelClassifiedEdit::canClose() -{ - return mCanClose; -} - -void LLPanelClassifiedEdit::draw() -{ - LLPanel::draw(); - - // Need to re-stretch on every draw because LLTextureCtrl::onSelectCallback - // does not trigger callbacks when user navigates through images. - stretchSnapshot(); -} - -void LLPanelClassifiedEdit::stretchSnapshot() -{ - LLPanelClassifiedInfo::stretchSnapshot(); - - getChild("edit_icon")->setShape(mSnapshotCtrl->getRect()); -} - -U32 LLPanelClassifiedEdit::getContentType() -{ - LLComboBox* ct_cb = getChild("content_type"); - return ct_cb->getCurrentIndex(); -} - -void LLPanelClassifiedEdit::setContentType(U32 content_type) -{ - LLComboBox* ct_cb = getChild("content_type"); - ct_cb->setCurrentByIndex(content_type); - ct_cb->resetDirty(); -} - -bool LLPanelClassifiedEdit::getAutoRenew() -{ - return getChild("auto_renew")->getValue().asBoolean(); -} - -void LLPanelClassifiedEdit::sendUpdate() -{ - LLAvatarClassifiedInfo c_data; - - if(getClassifiedId().isNull()) - { - setClassifiedId(LLUUID::generateNewID()); - } - - c_data.agent_id = gAgent.getID(); - c_data.classified_id = getClassifiedId(); - // *HACK - // Categories on server start with 1 while combo-box index starts with 0 - c_data.category = getCategory() + 1; - c_data.name = getClassifiedName(); - c_data.description = getDescription(); - c_data.parcel_id = getParcelId(); - c_data.snapshot_id = getSnapshotId(); - c_data.pos_global = getPosGlobal(); - c_data.flags = getFlags(); - c_data.price_for_listing = getPriceForListing(); - - LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoUpdate(&c_data); - - if(isNew()) - { - // Lets assume there will be some error. - // Successful sendClassifiedInfoUpdate will trigger processProperties and - // let us know there was no error. - mIsNewWithErrors = true; - } -} - -U32 LLPanelClassifiedEdit::getCategory() -{ - LLComboBox* cat_cb = getChild("category"); - return cat_cb->getCurrentIndex(); -} - -void LLPanelClassifiedEdit::setCategory(U32 category) -{ - LLComboBox* cat_cb = getChild("category"); - cat_cb->setCurrentByIndex(category); - cat_cb->resetDirty(); -} - -U8 LLPanelClassifiedEdit::getFlags() -{ - bool auto_renew = getChild("auto_renew")->getValue().asBoolean(); - - LLComboBox* content_cb = getChild("content_type"); - bool mature = content_cb->getCurrentIndex() == CB_ITEM_MATURE; - - return pack_classified_flags_request(auto_renew, false, mature, false); -} - -void LLPanelClassifiedEdit::enableVerbs(bool enable) -{ - getChildView("save_changes_btn")->setEnabled(enable); -} - -void LLPanelClassifiedEdit::enableEditing(bool enable) -{ - getChildView("classified_snapshot")->setEnabled(enable); - getChildView("classified_name")->setEnabled(enable); - getChildView("classified_desc")->setEnabled(enable); - getChildView("set_to_curr_location_btn")->setEnabled(enable); - getChildView("category")->setEnabled(enable); - getChildView("content_type")->setEnabled(enable); - getChildView("price_for_listing")->setEnabled(enable); - getChildView("auto_renew")->setEnabled(enable); -} - -void LLPanelClassifiedEdit::showEditing(bool show) -{ - getChildView("price_for_listing_label")->setVisible( show); - getChildView("price_for_listing")->setVisible( show); -} - -std::string LLPanelClassifiedEdit::makeClassifiedName() -{ - std::string name; - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(parcel) - { - name = parcel->getName(); - } - - if(!name.empty()) - { - return name; - } - - LLViewerRegion* region = gAgent.getRegion(); - if(region) - { - name = region->getName(); - } - - return name; -} - -S32 LLPanelClassifiedEdit::getPriceForListing() -{ - return getChild("price_for_listing")->getValue().asInteger(); -} - -void LLPanelClassifiedEdit::setPriceForListing(S32 price) -{ - getChild("price_for_listing")->setValue(price); -} - -void LLPanelClassifiedEdit::onSetLocationClick() -{ - setPosGlobal(gAgent.getPositionGlobal()); - setParcelId(LLUUID::null); - - std::string region_name = LLTrans::getString("ClassifiedUpdateAfterPublish"); - LLViewerRegion* region = gAgent.getRegion(); - if (region) - { - region_name = region->getName(); - } - - setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal())); - - // mark classified as dirty - setValue(LLSD()); - - onChange(); -} - -void LLPanelClassifiedEdit::onChange() -{ - enableVerbs(isDirty()); -} - -void LLPanelClassifiedEdit::onSaveClick() -{ - mCanClose = false; - - if(!isValidName()) - { - notifyInvalidName(); - return; - } - if(isNew() || isNewWithErrors()) - { - if(gStatusBar->getBalance() < getPriceForListing()) - { - LLNotificationsUtil::add("ClassifiedInsufficientFunds"); - return; - } - - mPublishFloater = LLFloaterReg::findTypedInstance( - "publish_classified", LLSD()); - - if(!mPublishFloater) - { - mPublishFloater = LLFloaterReg::getTypedInstance( - "publish_classified", LLSD()); - - mPublishFloater->setPublishClickedCallback(boost::bind - (&LLPanelClassifiedEdit::onPublishFloaterPublishClicked, this)); - } - - // set spinner value before it has focus or value wont be set - mPublishFloater->setPrice(getPriceForListing()); - mPublishFloater->openFloater(mPublishFloater->getKey()); - mPublishFloater->center(); - } - else - { - doSave(); - } -} - -void LLPanelClassifiedEdit::doSave() -{ - mCanClose = true; - sendUpdate(); - resetDirty(); - - mSaveButtonClickedSignal(this, LLSD()); -} - -void LLPanelClassifiedEdit::onPublishFloaterPublishClicked() -{ - setPriceForListing(mPublishFloater->getPrice()); - - doSave(); -} - -std::string LLPanelClassifiedEdit::getLocationNotice() -{ - static std::string location_notice = getString("location_notice"); - return location_notice; -} - -bool LLPanelClassifiedEdit::isValidName() -{ - std::string name = getClassifiedName(); - if (name.empty()) - { - return false; - } - if (!isalnum(name[0])) - { - return false; - } - - return true; -} - -void LLPanelClassifiedEdit::notifyInvalidName() -{ - std::string name = getClassifiedName(); - if (name.empty()) - { - LLNotificationsUtil::add("BlankClassifiedName"); - } - else if (!isalnum(name[0])) - { - LLNotificationsUtil::add("ClassifiedMustBeAlphanumeric"); - } -} - -void LLPanelClassifiedEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl) -{ - ctrl->setVisible(TRUE); -} - -void LLPanelClassifiedEdit::onTexturePickerMouseLeave(LLUICtrl* ctrl) -{ - ctrl->setVisible(FALSE); -} - -void LLPanelClassifiedEdit::onTextureSelected() -{ - setSnapshotId(mSnapshotCtrl->getValue().asUUID()); - onChange(); -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -LLPublishClassifiedFloater::LLPublishClassifiedFloater(const LLSD& key) - : LLFloater(key) -{ -} - -LLPublishClassifiedFloater::~LLPublishClassifiedFloater() -{ -} - -BOOL LLPublishClassifiedFloater::postBuild() -{ - LLFloater::postBuild(); - - childSetAction("publish_btn", boost::bind(&LLFloater::closeFloater, this, false)); - childSetAction("cancel_btn", boost::bind(&LLFloater::closeFloater, this, false)); - - return TRUE; -} - -void LLPublishClassifiedFloater::setPrice(S32 price) -{ - getChild("price_for_listing")->setValue(price); -} - -S32 LLPublishClassifiedFloater::getPrice() -{ - return getChild("price_for_listing")->getValue().asInteger(); -} - -void LLPublishClassifiedFloater::setPublishClickedCallback(const commit_signal_t::slot_type& cb) -{ - getChild("publish_btn")->setClickedCallback(cb); -} - -void LLPublishClassifiedFloater::setCancelClickedCallback(const commit_signal_t::slot_type& cb) -{ - getChild("cancel_btn")->setClickedCallback(cb); -} - //EOF diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h index b292782615..471becd0f7 100644 --- a/indra/newview/llpanelclassified.h +++ b/indra/newview/llpanelclassified.h @@ -1,10 +1,10 @@ /** * @file llpanelclassified.h - * @brief LLPanelClassified class definition + * @brief LLPanelClassifiedInfo class definition * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2021, 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 @@ -35,39 +35,16 @@ #include "llfloater.h" #include "llpanel.h" #include "llrect.h" -#include "lluuid.h" -#include "v3dmath.h" -#include "llcoros.h" -#include "lleventcoro.h" class LLScrollContainer; class LLTextureCtrl; -class LLUICtrl; - -class LLPublishClassifiedFloater : public LLFloater -{ -public: - LLPublishClassifiedFloater(const LLSD& key); - virtual ~LLPublishClassifiedFloater(); - - /*virtual*/ BOOL postBuild(); - - void setPrice(S32 price); - S32 getPrice(); - - void setPublishClickedCallback(const commit_signal_t::slot_type& cb); - void setCancelClickedCallback(const commit_signal_t::slot_type& cb); - -private: -}; class LLPanelClassifiedInfo : public LLPanel, public LLAvatarPropertiesObserver { LOG_CLASS(LLPanelClassifiedInfo); public: - static LLPanelClassifiedInfo* create(); - + LLPanelClassifiedInfo(); virtual ~LLPanelClassifiedInfo(); /*virtual*/ void onOpen(const LLSD& key); @@ -135,18 +112,12 @@ public: const LLVector3d& global_pos, const std::string& sim_name); - void setExitCallback(const commit_callback_t& cb); - - void setEditClassifiedCallback(const commit_callback_t& cb); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); /*virtual*/ void draw(); protected: - LLPanelClassifiedInfo(); - virtual void resetData(); virtual void resetControls(); @@ -165,7 +136,6 @@ protected: void onMapClick(); void onTeleportClick(); - void onExit(); bool mSnapshotStreched; LLRect mSnapshotRect; @@ -202,100 +172,4 @@ private: static panel_list_t sAllPanels; }; -class LLPanelClassifiedEdit : public LLPanelClassifiedInfo -{ - LOG_CLASS(LLPanelClassifiedEdit); -public: - - static LLPanelClassifiedEdit* create(); - - virtual ~LLPanelClassifiedEdit(); - - /*virtual*/ BOOL postBuild(); - - void fillIn(const LLSD& key); - - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - /*virtual*/ BOOL isDirty() const; - - /*virtual*/ void resetDirty(); - - void setSaveCallback(const commit_signal_t::slot_type& cb); - - void setCancelCallback(const commit_signal_t::slot_type& cb); - - /*virtual*/ void resetControls(); - - bool isNew() { return mIsNew; } - - bool isNewWithErrors() { return mIsNewWithErrors; } - - bool canClose(); - - void draw(); - - void stretchSnapshot(); - - U32 getCategory(); - - void setCategory(U32 category); - - U32 getContentType(); - - void setContentType(U32 content_type); - - bool getAutoRenew(); - - S32 getPriceForListing(); - -protected: - - LLPanelClassifiedEdit(); - - void sendUpdate(); - - void enableVerbs(bool enable); - - void enableEditing(bool enable); - - void showEditing(bool show); - - std::string makeClassifiedName(); - - void setPriceForListing(S32 price); - - U8 getFlags(); - - std::string getLocationNotice(); - - bool isValidName(); - - void notifyInvalidName(); - - void onSetLocationClick(); - void onChange(); - void onSaveClick(); - - void doSave(); - - void onPublishFloaterPublishClicked(); - - void onTexturePickerMouseEnter(LLUICtrl* ctrl); - void onTexturePickerMouseLeave(LLUICtrl* ctrl); - - void onTextureSelected(); - -private: - bool mIsNew; - bool mIsNewWithErrors; - bool mCanClose; - - LLPublishClassifiedFloater* mPublishFloater; - - commit_signal_t mSaveButtonClickedSignal; -}; - #endif // LL_LLPANELCLASSIFIED_H diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 6e897e2c7e..ea10aa75ae 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1308,7 +1308,8 @@ void LLPanelEditWearable::changeCamera(U8 subpart) gMorphView->setCameraOffset( subpart_entry->mCameraOffset ); if (gSavedSettings.getBOOL("AppearanceCameraMovement")) { - gAgentCamera.setFocusOnAvatar(FALSE, FALSE); + // Unlock focus from avatar but don't stop animation to not interrupt ANIM_AGENT_CUSTOMIZE + gAgentCamera.setFocusOnAvatar(FALSE, gAgentCamera.getCameraAnimating()); gMorphView->updateCamera(); } } diff --git a/indra/newview/llpanelexperiences.h b/indra/newview/llpanelexperiences.h index 9d5afd1a6a..11111f2a2e 100644 --- a/indra/newview/llpanelexperiences.h +++ b/indra/newview/llpanelexperiences.h @@ -29,7 +29,6 @@ #include "llaccordionctrltab.h" #include "llflatlistview.h" -#include "llpanelavatar.h" class LLExperienceItem; class LLPanelProfile; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index 7fe5b1dd3f..178aba11a3 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -49,11 +49,15 @@ #include "llinventoryfunctions.h" #include "llinventorymodel.h" // gInventory #include "llinventorymodelbackgroundfetch.h" +#include "llfloatermediasettings.h" +#include "llfloaterreg.h" #include "lllineeditor.h" #include "llmaterialmgr.h" +#include "llmediactrl.h" #include "llmediaentry.h" #include "llmenubutton.h" #include "llnotificationsutil.h" +#include "llpanelcontents.h" #include "llradiogroup.h" #include "llresmgr.h" #include "llselectmgr.h" @@ -99,10 +103,9 @@ std::string USE_TEXTURE; LLRender::eTexIndex LLPanelFace::getTextureChannelToEdit() { - LLComboBox* combobox_matmedia = getChild("combobox matmedia"); LLRadioGroup* radio_mat_type = getChild("radio_material_type"); - LLRender::eTexIndex channel_to_edit = (combobox_matmedia && combobox_matmedia->getCurrentIndex() == MATMEDIA_MATERIAL) ? + LLRender::eTexIndex channel_to_edit = (mComboMatMedia && mComboMatMedia->getCurrentIndex() == MATMEDIA_MATERIAL) ? (radio_mat_type ? (LLRender::eTexIndex)radio_mat_type->getSelectedIndex() : LLRender::DIFFUSE_MAP) : LLRender::DIFFUSE_MAP; channel_to_edit = (channel_to_edit == LLRender::NORMAL_MAP) ? (getCurrentNormalMap().isNull() ? LLRender::DIFFUSE_MAP : channel_to_edit) : channel_to_edit; @@ -161,6 +164,8 @@ BOOL LLPanelFace::postBuild() childSetCommitCallback("glossiness",&LLPanelFace::onCommitMaterialGloss, this); childSetCommitCallback("environment",&LLPanelFace::onCommitMaterialEnv, this); childSetCommitCallback("maskcutoff",&LLPanelFace::onCommitMaterialMaskCutoff, this); + childSetCommitCallback("add_media", &LLPanelFace::onClickBtnAddMedia, this); + childSetCommitCallback("delete_media", &LLPanelFace::onClickBtnDeleteMedia, this); childSetAction("button align",&LLPanelFace::onClickAutoFix,this); childSetAction("button align textures", &LLPanelFace::onAlignTexture, this); @@ -172,7 +177,6 @@ BOOL LLPanelFace::postBuild() LLColorSwatchCtrl* mShinyColorSwatch; LLComboBox* mComboTexGen; - LLComboBox* mComboMatMedia; LLCheckBoxCtrl *mCheckFullbright; @@ -308,6 +312,9 @@ BOOL LLPanelFace::postBuild() mMenuClipboardColor = getChild("clipboard_color_params_btn"); mMenuClipboardTexture = getChild("clipboard_texture_params_btn"); + + mTitleMedia = getChild("title_media"); + mTitleMediaText = getChild("media_info"); clearCtrls(); @@ -316,24 +323,31 @@ BOOL LLPanelFace::postBuild() LLPanelFace::LLPanelFace() : LLPanel(), - mIsAlpha(false) + mIsAlpha(false), + mComboMatMedia(NULL), + mTitleMedia(NULL), + mTitleMediaText(NULL), + mNeedMediaTitle(true) { USE_TEXTURE = LLTrans::getString("use_texture"); mCommitCallbackRegistrar.add("PanelFace.menuDoToSelected", boost::bind(&LLPanelFace::menuDoToSelected, this, _2)); mEnableCallbackRegistrar.add("PanelFace.menuEnable", boost::bind(&LLPanelFace::menuEnableItem, this, _2)); } - LLPanelFace::~LLPanelFace() { - // Children all cleaned up by default view destructor. + unloadMedia(); } - void LLPanelFace::draw() { updateCopyTexButton(); + // grab media name/title and update the UI widget + // Todo: move it, it's preferable not to update + // labels inside draw + updateMediaTitle(); + LLPanel::draw(); } @@ -824,21 +838,20 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) BOOL editable = objectp->permModify() && !objectp->isPermanentEnforced(); // only turn on auto-adjust button if there is a media renderer and the media is loaded - getChildView("button align")->setEnabled(editable); + childSetEnabled("button align", editable); - LLComboBox* combobox_matmedia = getChild("combobox matmedia"); - if (combobox_matmedia) + if (mComboMatMedia) { - if (combobox_matmedia->getCurrentIndex() < MATMEDIA_MATERIAL) + if (mComboMatMedia->getCurrentIndex() < MATMEDIA_MATERIAL) { - combobox_matmedia->selectNthItem(MATMEDIA_MATERIAL); + mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL); } + mComboMatMedia->setEnabled(editable); } else { LL_WARNS() << "failed getChild for 'combobox matmedia'" << LL_ENDL; } - getChildView("combobox matmedia")->setEnabled(editable); LLRadioGroup* radio_mat_type = getChild("radio_material_type"); if(radio_mat_type) @@ -847,7 +860,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) { radio_mat_type->selectNthItem(MATTYPE_DIFFUSE); } - } else { @@ -876,22 +888,22 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) { getChildView("color label")->setEnabled(editable); } - LLColorSwatchCtrl* mColorSwatch = getChild("colorswatch"); + LLColorSwatchCtrl* color_swatch = findChild("colorswatch"); LLColor4 color = LLColor4::white; bool identical_color = false; - if(mColorSwatch) + if(color_swatch) { LLSelectedTE::getColor(color, identical_color); - LLColor4 prev_color = mColorSwatch->get(); + LLColor4 prev_color = color_swatch->get(); - mColorSwatch->setOriginal(color); - mColorSwatch->set(color, force_set_values || (prev_color != color) || !editable); + color_swatch->setOriginal(color); + color_swatch->set(color, force_set_values || (prev_color != color) || !editable); - mColorSwatch->setValid(editable); - mColorSwatch->setEnabled( editable ); - mColorSwatch->setCanApplyImmediately( editable ); + color_swatch->setValid(editable); + color_swatch->setEnabled( editable ); + color_swatch->setCanApplyImmediately( editable ); } // Color transparency @@ -1374,7 +1386,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) BOOL identical_repeats = true; F32 repeats = 1.0f; - U32 material_type = (combobox_matmedia->getCurrentIndex() == MATMEDIA_MATERIAL) ? radio_mat_type->getSelectedIndex() : MATTYPE_DIFFUSE; + U32 material_type = (mComboMatMedia->getCurrentIndex() == MATMEDIA_MATERIAL) ? radio_mat_type->getSelectedIndex() : MATTYPE_DIFFUSE; LLSelectMgr::getInstance()->setTextureChannel(LLRender::eTexIndex(material_type)); switch (material_type) @@ -1606,6 +1618,755 @@ void LLPanelFace::refresh() getState(); } +void LLPanelFace::refreshMedia() +{ + LLObjectSelectionHandle selected_objects = LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* first_object = selected_objects->getFirstObject(); + + if (!(first_object + && first_object->getPCode() == LL_PCODE_VOLUME + && first_object->permModify() + )) + { + getChildView("add_media")->setEnabled(FALSE); + mTitleMediaText->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; + } + } + } + } + + // 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. + mTitleMediaText->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); + } + + U32 materials_media = mComboMatMedia->getCurrentIndex(); + if (materials_media == MATMEDIA_MEDIA) + { + // currently displaying media info, navigateTo and update title + navigateToTitleMedia(media_title); + } + else + { + // Media can be heavy, don't keep it around + // MAC specific: MAC doesn't support setVolume(0) so if not + // unloaded, it might keep playing audio until user closes editor + unloadMedia(); + mNeedMediaTitle = false; + } + + mTitleMediaText->setText(media_title); + + // load values for media settings + updateMediaSettings(); + + LLFloaterMediaSettings::initValues(mMediaSettings, editable); +} + +void LLPanelFace::unloadMedia() +{ + // destroy media source used to grab media title + if (mTitleMedia) + mTitleMedia->unloadMediaSource(); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLPanelFace::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(); + // check if url changed or if we need a new media source + if (mTitleMedia->getCurrentNavUrl() != url || media_plugin == NULL) + { + mTitleMedia->navigateTo( url ); + + LLViewerMediaImpl* impl = LLViewerMedia::getInstance()->getMediaImplFromTextureID(mTitleMedia->getTextureID()); + if (impl) + { + // if it's a page with a movie, we don't want to hear it + impl->setVolume(0); + }; + } + + // flag that we need to update the title (even if no request were made) + mNeedMediaTitle = true; + } +} + +bool LLPanelFace::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) || + (everyone_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 LLPanelFace::clearMediaSettings() +{ + LLFloaterMediaSettings::clearValues(false); +} + +void LLPanelFace::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; +} + +void LLPanelFace::updateMediaTitle() +{ + // only get the media name if we need it + if (!mNeedMediaTitle) + return; + + // get plugin impl + LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); + if (media_plugin && mTitleMedia->getCurrentNavUrl() == media_plugin->getNavigateURI()) + { + // 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 + if (mTitleMediaText) + { + mTitleMediaText->setText(media_title); + + // stop looking for a title when we get one + mNeedMediaTitle = false; + }; + }; + }; +} + // // Static functions // @@ -1664,30 +2425,29 @@ void LLPanelFace::onCommitMaterialsMedia(LLUICtrl* ctrl, void* userdata) self->updateShinyControls(false,true); self->updateBumpyControls(false,true); self->updateUI(); + self->refreshMedia(); } -// static void LLPanelFace::updateVisibility() { - LLComboBox* combo_matmedia = getChild("combobox matmedia"); LLRadioGroup* radio_mat_type = getChild("radio_material_type"); LLComboBox* combo_shininess = getChild("combobox shininess"); LLComboBox* combo_bumpiness = getChild("combobox bumpiness"); - if (!radio_mat_type || !combo_matmedia || !combo_shininess || !combo_bumpiness) + if (!radio_mat_type || !mComboMatMedia || !combo_shininess || !combo_bumpiness) { LL_WARNS("Materials") << "Combo box not found...exiting." << LL_ENDL; return; } - U32 materials_media = combo_matmedia->getCurrentIndex(); + U32 materials_media = mComboMatMedia->getCurrentIndex(); U32 material_type = radio_mat_type->getSelectedIndex(); - bool show_media = (materials_media == MATMEDIA_MEDIA) && combo_matmedia->getEnabled(); - bool show_texture = (show_media || ((material_type == MATTYPE_DIFFUSE) && combo_matmedia->getEnabled())); - bool show_bumpiness = (!show_media) && (material_type == MATTYPE_NORMAL) && combo_matmedia->getEnabled(); - bool show_shininess = (!show_media) && (material_type == MATTYPE_SPECULAR) && combo_matmedia->getEnabled(); + bool show_media = (materials_media == MATMEDIA_MEDIA) && mComboMatMedia->getEnabled(); + bool show_texture = (show_media || ((material_type == MATTYPE_DIFFUSE) && mComboMatMedia->getEnabled())); + bool show_bumpiness = (!show_media) && (material_type == MATTYPE_NORMAL) && mComboMatMedia->getEnabled(); + bool show_shininess = (!show_media) && (material_type == MATTYPE_SPECULAR) && mComboMatMedia->getEnabled(); getChildView("radio_material_type")->setVisible(!show_media); // Media controls - getChildView("media_info")->setVisible(show_media); + mTitleMediaText->setVisible(show_media); getChildView("add_media")->setVisible(show_media); getChildView("delete_media")->setVisible(show_media); getChildView("button align")->setVisible(show_media); @@ -1819,12 +2579,11 @@ void LLPanelFace::updateShinyControls(bool is_setting_texture, bool mess_with_sh } - LLComboBox* combo_matmedia = getChild("combobox matmedia"); LLRadioGroup* radio_mat_type = getChild("radio_material_type"); - U32 materials_media = combo_matmedia->getCurrentIndex(); + U32 materials_media = mComboMatMedia->getCurrentIndex(); U32 material_type = radio_mat_type->getSelectedIndex(); - bool show_media = (materials_media == MATMEDIA_MEDIA) && combo_matmedia->getEnabled(); - bool show_shininess = (!show_media) && (material_type == MATTYPE_SPECULAR) && combo_matmedia->getEnabled(); + bool show_media = (materials_media == MATMEDIA_MEDIA) && mComboMatMedia->getEnabled(); + bool show_shininess = (!show_media) && (material_type == MATTYPE_SPECULAR) && mComboMatMedia->getEnabled(); U32 shiny_value = comboShiny->getCurrentIndex(); bool show_shinyctrls = (shiny_value == SHINY_TEXTURE) && show_shininess; // Use texture getChildView("label glossiness")->setVisible(show_shinyctrls); @@ -1898,11 +2657,10 @@ void LLPanelFace::updateAlphaControls() U32 alpha_value = comboAlphaMode->getCurrentIndex(); bool show_alphactrls = (alpha_value == ALPHAMODE_MASK); // Alpha masking - LLComboBox* combobox_matmedia = getChild("combobox matmedia"); U32 mat_media = MATMEDIA_MATERIAL; - if (combobox_matmedia) + if (mComboMatMedia) { - mat_media = combobox_matmedia->getCurrentIndex(); + mat_media = mComboMatMedia->getCurrentIndex(); } U32 mat_type = MATTYPE_DIFFUSE; @@ -2059,6 +2817,77 @@ void LLPanelFace::onSelectNormalTexture(const LLSD& data) sendBump(nmap_id.isNull() ? 0 : BUMPY_TEXTURE); } +////////////////////////////////////////////////////////////////////////////// +// 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 LLPanelFace::onClickBtnEditMedia(LLUICtrl* ctrl, void* userdata) +{ + LLPanelFace* self = (LLPanelFace*)userdata; + self->refreshMedia(); + LLFloaterReg::showInstance("media_settings"); +} + +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to delete media from a prim or prim face +void LLPanelFace::onClickBtnDeleteMedia(LLUICtrl* ctrl, void* userdata) +{ + LLNotificationsUtil::add("DeleteMedia", LLSD(), LLSD(), deleteMediaConfirm); +} + +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to add media to a prim or prim face +void LLPanelFace::onClickBtnAddMedia(LLUICtrl* ctrl, void* userdata) +{ + // check if multiple faces are selected + if (LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) + { + LLPanelFace* self = (LLPanelFace*)userdata; + self->refreshMedia(); + LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm); + } + else + { + onClickBtnEditMedia(ctrl, userdata); + } +} + +// static +bool LLPanelFace::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; +} + +// static +bool LLPanelFace::multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch (option) + { + case 0: // "Yes" + LLFloaterReg::showInstance("media_settings"); + break; + case 1: // "No" + default: + break; + } + return false; +} + //static void LLPanelFace::syncOffsetX(LLPanelFace* self, F32 offsetU) { @@ -2436,10 +3265,9 @@ void LLPanelFace::onCommitRepeatsPerMeter(LLUICtrl* ctrl, void* userdata) LLPanelFace* self = (LLPanelFace*) userdata; LLUICtrl* repeats_ctrl = self->getChild("rptctrl"); - LLComboBox* combo_matmedia = self->getChild("combobox matmedia"); LLRadioGroup* radio_mat_type = self->getChild("radio_material_type"); - U32 materials_media = combo_matmedia->getCurrentIndex(); + U32 materials_media = self->mComboMatMedia->getCurrentIndex(); U32 material_type = (materials_media == MATMEDIA_MATERIAL) ? radio_mat_type->getSelectedIndex() : 0; F32 repeats_per_meter = repeats_ctrl->getValue().asReal(); @@ -3375,14 +4203,6 @@ bool LLPanelFace::menuEnableItem(const LLSD& userdata) } -// TODO: I don't know who put these in or what these are for??? -void LLPanelFace::setMediaURL(const std::string& url) -{ -} -void LLPanelFace::setMediaType(const std::string& mime_type) -{ -} - // static void LLPanelFace::onCommitPlanarAlign(LLUICtrl* ctrl, void* userdata) { diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index e3b925c1d4..44bc442bbb 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -47,6 +47,7 @@ class LLUICtrl; class LLViewerObject; class LLFloater; class LLMaterialID; +class LLMediaCtrl; class LLMenuButton; // Represents an edit for use in replicating the op across one or more materials in the selection set. @@ -97,11 +98,11 @@ public: LLPanelFace(); virtual ~LLPanelFace(); - void draw(); - void refresh(); - void setMediaURL(const std::string& url); - void setMediaType(const std::string& mime_type); + void refreshMedia(); + void unloadMedia(); + + /*virtual*/ void draw(); LLMaterialPtr createDefaultMaterial(LLMaterialPtr current_material) { @@ -117,6 +118,12 @@ public: LLRender::eTexIndex getTextureChannelToEdit(); protected: + void navigateToTitleMedia(const std::string url); + bool selectedMediaEditable(); + void clearMediaSettings(); + void updateMediaSettings(); + void updateMediaTitle(); + void getState(); void sendTexture(); // applies and sends texture @@ -155,6 +162,9 @@ protected: void onCloseTexturePicker(const LLSD& data); + static bool deleteMediaConfirm(const LLSD& notification, const LLSD& response); + static bool multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response); + // Make UI reflect state of currently selected material (refresh) // and UI mode (e.g. editing normal map v diffuse map) // @@ -199,6 +209,9 @@ protected: static void onCommitMaterialsMedia( LLUICtrl* ctrl, void* userdata); static void onCommitMaterialType( LLUICtrl* ctrl, void* userdata); + static void onClickBtnEditMedia(LLUICtrl* ctrl, void* userdata); + static void onClickBtnDeleteMedia(LLUICtrl* ctrl, void* userdata); + static void onClickBtnAddMedia(LLUICtrl* ctrl, void* userdata); static void onCommitBump( LLUICtrl* ctrl, void* userdata); static void onCommitTexGen( LLUICtrl* ctrl, void* userdata); static void onCommitShiny( LLUICtrl* ctrl, void* userdata); @@ -251,6 +264,10 @@ private: F32 getCurrentShinyOffsetU(); F32 getCurrentShinyOffsetV(); + LLComboBox *mComboMatMedia; + LLMediaCtrl *mTitleMedia; + LLTextBox *mTitleMediaText; + // Update visibility of controls to match current UI mode // (e.g. materials vs media editing) // @@ -258,10 +275,6 @@ private: // void updateVisibility(); - // Make material(s) reflect current state of UI (apply edit) - // - void updateMaterial(); - // Hey look everyone, a type-safe alternative to copy and paste! :) // @@ -439,6 +452,9 @@ private: LLSD mClipboardParams; + LLSD mMediaSettings; + bool mNeedMediaTitle; + public: #if defined(DEF_GET_MAT_STATE) #undef DEF_GET_MAT_STATE diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 389baa86cd..07a8641a92 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -1,6 +1,6 @@ /** - * @file llpanelavatar.cpp - * @brief LLPanelAvatar and related class implementations + * @file llpanelimcontrolpanel.cpp + * @brief LLPanelIMControlPanel and related class implementations * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code diff --git a/indra/newview/llpanellandaudio.cpp b/indra/newview/llpanellandaudio.cpp index e7bdc51b4a..9e3fc54477 100644 --- a/indra/newview/llpanellandaudio.cpp +++ b/indra/newview/llpanellandaudio.cpp @@ -97,6 +97,9 @@ BOOL LLPanelLandAudio::postBuild() mCheckAVSoundGroup = getChild("group av sound check"); childSetCommitCallback("group av sound check", onCommitAny, this); + mCheckObscureMOAP = getChild("obscure_moap"); + childSetCommitCallback("obscure_moap", onCommitAny, this); + return TRUE; } @@ -157,6 +160,9 @@ void LLPanelLandAudio::refresh() mCheckAVSoundGroup->set(parcel->getAllowGroupAVSounds() || parcel->getAllowAnyAVSounds()); // On if "Everyone" is on mCheckAVSoundGroup->setEnabled(can_change_av_sounds && !parcel->getAllowAnyAVSounds()); // Enabled if "Everyone" is off + + mCheckObscureMOAP->set(parcel->getObscureMOAP()); + mCheckObscureMOAP->setEnabled(can_change_media); } } // static @@ -184,6 +190,8 @@ void LLPanelLandAudio::onCommitAny(LLUICtrl*, void *userdata) group_av_sound = self->mCheckAVSoundGroup->get(); } + bool obscure_moap = self->mCheckObscureMOAP->get(); + // Remove leading/trailing whitespace (common when copying/pasting) LLStringUtil::trim(music_url); @@ -194,6 +202,7 @@ void LLPanelLandAudio::onCommitAny(LLUICtrl*, void *userdata) parcel->setMusicURL(music_url); parcel->setAllowAnyAVSounds(any_av_sound); parcel->setAllowGroupAVSounds(group_av_sound); + parcel->setObscureMOAP(obscure_moap); // Send current parcel data upstream to server LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel ); diff --git a/indra/newview/llpanellandaudio.h b/indra/newview/llpanellandaudio.h index 7e4fce80e4..b54fe62179 100644 --- a/indra/newview/llpanellandaudio.h +++ b/indra/newview/llpanellandaudio.h @@ -53,6 +53,7 @@ private: LLLineEditor* mMusicURLEdit; LLCheckBoxCtrl* mCheckAVSoundAny; LLCheckBoxCtrl* mCheckAVSoundGroup; + LLCheckBoxCtrl* mCheckObscureMOAP; LLSafeHandle& mParcel; }; diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp index ce17da3076..c3334605ae 100644 --- a/indra/newview/llpanellandmarks.cpp +++ b/indra/newview/llpanellandmarks.cpp @@ -29,6 +29,7 @@ #include "llpanellandmarks.h" #include "llbutton.h" +#include "llfloaterprofile.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" #include "llsdutil.h" @@ -1003,17 +1004,6 @@ bool LLLandmarksPanel::canItemBeModified(const std::string& command_name, LLFold return can_be_modified; } -void LLLandmarksPanel::onPickPanelExit( LLPanelPickEdit* pick_panel, LLView* owner, const LLSD& params) -{ - pick_panel->setVisible(FALSE); - owner->removeChild(pick_panel); - //we need remove observer to avoid processParcelInfo in the future. - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(params["parcel_id"].asUUID(), this); - - delete pick_panel; - pick_panel = NULL; -} - bool LLLandmarksPanel::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, void* cargo_data , EAcceptance* accept) { *accept = ACCEPT_NO; @@ -1080,49 +1070,21 @@ void LLLandmarksPanel::doProcessParcelInfo(LLLandmark* landmark, LLInventoryItem* inv_item, const LLParcelData& parcel_data) { - LLPanelPickEdit* panel_pick = LLPanelPickEdit::create(); LLVector3d landmark_global_pos; landmark->getGlobalPos(landmark_global_pos); - // let's toggle pick panel into panel places - LLPanel* panel_places = NULL; - LLFloaterSidePanelContainer* floaterp = LLFloaterReg::getTypedInstance("places"); - if (floaterp) - { - panel_places = floaterp->findChild("main_panel"); - } - - if (!panel_places) - { - llassert(NULL != panel_places); - return; - } - panel_places->addChild(panel_pick); - LLRect paren_rect(panel_places->getRect()); - panel_pick->reshape(paren_rect.getWidth(),paren_rect.getHeight(), TRUE); - panel_pick->setRect(paren_rect); - panel_pick->onOpen(LLSD()); - LLPickData data; data.pos_global = landmark_global_pos; data.name = inv_item->getName(); data.desc = inv_item->getDescription(); data.snapshot_id = parcel_data.snapshot_id; data.parcel_id = parcel_data.parcel_id; - panel_pick->setPickData(&data); - LLSD params; - params["parcel_id"] = parcel_data.parcel_id; - /* set exit callback to get back onto panel places - in callback we will make cleaning up( delete pick_panel instance, - remove landmark panel from observer list - */ - panel_pick->setExitCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, - panel_pick, panel_places,params)); - panel_pick->setSaveCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, - panel_pick, panel_places,params)); - panel_pick->setCancelCallback(boost::bind(&LLLandmarksPanel::onPickPanelExit,this, - panel_pick, panel_places,params)); + LLFloaterProfile* profile_floater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", gAgentID))); + if (profile_floater) + { + profile_floater->createPick(data); + } } void LLLandmarksPanel::doCreatePick(LLLandmark* landmark, const LLUUID &item_id) diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h index d7408269b5..16f3a5dc24 100644 --- a/indra/newview/llpanellandmarks.h +++ b/indra/newview/llpanellandmarks.h @@ -34,7 +34,6 @@ #include "llinventorymodel.h" #include "lllandmarklist.h" #include "llpanelplacestab.h" -#include "llpanelpick.h" #include "llremoteparcelrequest.h" class LLAccordionCtrlTab; @@ -136,7 +135,6 @@ private: * For now it checks cut/rename/delete/paste actions. */ bool canItemBeModified(const std::string& command_name, LLFolderViewItem* item) const; - void onPickPanelExit( LLPanelPickEdit* pick_panel, LLView* owner, const LLSD& params); /** * Landmark actions callbacks. Fire when a landmark is loaded from the list. diff --git a/indra/newview/llpanelme.cpp b/indra/newview/llpanelme.cpp deleted file mode 100644 index 55e4ffff5e..0000000000 --- a/indra/newview/llpanelme.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @file llpanelme.cpp - * @brief Side tray "Me" (My Profile) panel - * - * $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 "llpanelme.h" - -// Viewer includes -#include "llpanelprofile.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "llagentwearables.h" -#include "llfirstuse.h" -#include "llfloaterreg.h" -#include "llhints.h" -#include "llviewercontrol.h" - -// Linden libraries -#include "llavatarnamecache.h" // IDEVO -#include "lliconctrl.h" -#include "llnotifications.h" -#include "llnotificationsutil.h" // IDEVO -#include "lltabcontainer.h" -#include "lltexturectrl.h" - -static LLPanelInjector t_panel_me_profile("panel_me"); - -LLPanelMe::LLPanelMe(void) - : LLPanelProfile() -{ - setAvatarId(gAgent.getID()); -} - -BOOL LLPanelMe::postBuild() -{ - LLPanelProfile::postBuild(); - - return TRUE; -} - -void LLPanelMe::onOpen(const LLSD& key) -{ - LLPanelProfile::onOpen(key); -} diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp index 9730f0f16d..e1818cc68b 100644 --- a/indra/newview/llpanelmediasettingsgeneral.cpp +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -98,9 +98,6 @@ BOOL LLPanelMediaSettingsGeneral::postBuild() childSetCommitCallback( LLMediaEntry::HOME_URL_KEY, onCommitHomeURL, this); childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this); - // interrogates controls and updates widgets as required - updateMediaPreview(); - return true; } @@ -313,9 +310,6 @@ void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& _media data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); }; }; - - // interrogates controls and updates widgets as required - self->updateMediaPreview(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp deleted file mode 100644 index 40326cfb39..0000000000 --- a/indra/newview/llpanelpick.cpp +++ /dev/null @@ -1,620 +0,0 @@ -/** - * @file llpanelpick.cpp - * @brief LLPanelPick class implementation - * - * $LicenseInfo:firstyear=2004&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$ - */ - -// Display of a "Top Pick" used both for the global top picks in the -// Find directory, and also for each individual user's picks in their -// profile. - -#include "llviewerprecompiledheaders.h" - -#include "llpanelpick.h" - -#include "message.h" - -#include "llparcel.h" - -#include "llbutton.h" -#include "llfloaterreg.h" -#include "lliconctrl.h" -#include "lllineeditor.h" -#include "llpanel.h" -#include "llscrollcontainer.h" -#include "lltexteditor.h" - -#include "llagent.h" -#include "llagentpicksinfo.h" -#include "llavatarpropertiesprocessor.h" -#include "llfloaterworldmap.h" -#include "lltexturectrl.h" -#include "lluiconstants.h" -#include "llviewerparcelmgr.h" -#include "llviewerregion.h" -#include "llworldmap.h" - - -#define XML_PANEL_EDIT_PICK "panel_edit_pick.xml" -#define XML_PANEL_PICK_INFO "panel_pick_info.xml" - -#define XML_NAME "pick_name" -#define XML_DESC "pick_desc" -#define XML_SNAPSHOT "pick_snapshot" -#define XML_LOCATION "pick_location" - -#define XML_BTN_ON_TXTR "edit_icon" -#define XML_BTN_SAVE "save_changes_btn" - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -//static -LLPanelPickInfo* LLPanelPickInfo::create() -{ - LLPanelPickInfo* panel = new LLPanelPickInfo(); - panel->buildFromFile(XML_PANEL_PICK_INFO); - return panel; -} - -LLPanelPickInfo::LLPanelPickInfo() - : LLPanel() - , LLAvatarPropertiesObserver() - , LLRemoteParcelInfoObserver() - , mAvatarId(LLUUID::null) - , mSnapshotCtrl(NULL) - , mPickId(LLUUID::null) - , mParcelId(LLUUID::null) - , mRequestedId(LLUUID::null) - , mScrollingPanelMinHeight(0) - , mScrollingPanelWidth(0) - , mScrollingPanel(NULL) - , mScrollContainer(NULL) -{ -} - -LLPanelPickInfo::~LLPanelPickInfo() -{ - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); - - if (mParcelId.notNull()) - { - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelId, this); - } -} - -void LLPanelPickInfo::onOpen(const LLSD& key) -{ - LLUUID avatar_id = key["avatar_id"]; - if(avatar_id.isNull()) - { - return; - } - - if(getAvatarId().notNull()) - { - LLAvatarPropertiesProcessor::getInstance()->removeObserver( - getAvatarId(), this); - } - - setAvatarId(avatar_id); - - resetData(); - resetControls(); - - setPickId(key["pick_id"]); - setPickName(key["pick_name"]); - setPickDesc(key["pick_desc"]); - setSnapshotId(key["snapshot_id"]); - - LLAvatarPropertiesProcessor::getInstance()->addObserver( - getAvatarId(), this); - LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest( - getAvatarId(), getPickId()); -} - -BOOL LLPanelPickInfo::postBuild() -{ - mSnapshotCtrl = getChild(XML_SNAPSHOT); - - childSetAction("teleport_btn", boost::bind(&LLPanelPickInfo::onClickTeleport, this)); - childSetAction("show_on_map_btn", boost::bind(&LLPanelPickInfo::onClickMap, this)); - childSetAction("back_btn", boost::bind(&LLPanelPickInfo::onClickBack, this)); - - mScrollingPanel = getChild("scroll_content_panel"); - mScrollContainer = getChild("profile_scroll"); - - mScrollingPanelMinHeight = mScrollContainer->getScrolledViewRect().getHeight(); - mScrollingPanelWidth = mScrollingPanel->getRect().getWidth(); - - LLTextEditor* text_desc = getChild(XML_DESC); - text_desc->setContentTrusted(false); - - return TRUE; -} - -void LLPanelPickInfo::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLPanel::reshape(width, height, called_from_parent); - - if (!mScrollContainer || !mScrollingPanel) - return; - - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - - S32 scroll_height = mScrollContainer->getRect().getHeight(); - if (mScrollingPanelMinHeight >= scroll_height) - { - mScrollingPanel->reshape(mScrollingPanelWidth, mScrollingPanelMinHeight); - } - else - { - mScrollingPanel->reshape(mScrollingPanelWidth + scrollbar_size, scroll_height); - } -} - -void LLPanelPickInfo::processProperties(void* data, EAvatarProcessorType type) -{ - if(APT_PICK_INFO != type) - { - return; - } - LLPickData* pick_info = static_cast(data); - if(!pick_info - || pick_info->creator_id != getAvatarId() - || pick_info->pick_id != getPickId()) - { - return; - } - - mParcelId = pick_info->parcel_id; - setSnapshotId(pick_info->snapshot_id); - setPickName(pick_info->name); - setPickDesc(pick_info->desc); - setPosGlobal(pick_info->pos_global); - - // Send remote parcel info request to get parcel name and sim (region) name. - sendParcelInfoRequest(); - - // *NOTE dzaporozhan - // We want to keep listening to APT_PICK_INFO because user may - // edit the Pick and we have to update Pick info panel. - // revomeObserver is called from onClickBack -} - -void LLPanelPickInfo::sendParcelInfoRequest() -{ - if (mParcelId != mRequestedId) - { - LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this); - LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId); - - mRequestedId = mParcelId; - } -} - -void LLPanelPickInfo::setExitCallback(const commit_callback_t& cb) -{ - getChild("back_btn")->setClickedCallback(cb); -} - -void LLPanelPickInfo::processParcelInfo(const LLParcelData& parcel_data) -{ - setPickLocation(createLocationText(LLStringUtil::null, parcel_data.name, - parcel_data.sim_name, getPosGlobal())); - - // We have received parcel info for the requested ID so clear it now. - mRequestedId.setNull(); - - if (mParcelId.notNull()) - { - LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelId, this); - } -} - -void LLPanelPickInfo::setEditPickCallback(const commit_callback_t& cb) -{ - getChild("edit_btn")->setClickedCallback(cb); -} - -// PROTECTED AREA - -void LLPanelPickInfo::resetControls() -{ - if(getAvatarId() == gAgent.getID()) - { - getChildView("edit_btn")->setEnabled(TRUE); - getChildView("edit_btn")->setVisible( TRUE); - } - else - { - getChildView("edit_btn")->setEnabled(FALSE); - getChildView("edit_btn")->setVisible( FALSE); - } -} - -void LLPanelPickInfo::resetData() -{ - setPickName(LLStringUtil::null); - setPickDesc(LLStringUtil::null); - setPickLocation(LLStringUtil::null); - setPickId(LLUUID::null); - setSnapshotId(LLUUID::null); - mPosGlobal.clearVec(); - mParcelId.setNull(); - mRequestedId.setNull(); -} - -// static -std::string LLPanelPickInfo::createLocationText(const std::string& owner_name, const std::string& original_name, const std::string& sim_name, const LLVector3d& pos_global) -{ - std::string location_text; - location_text.append(owner_name); - if (!original_name.empty()) - { - if (!location_text.empty()) location_text.append(", "); - location_text.append(original_name); - - } - if (!sim_name.empty()) - { - if (!location_text.empty()) location_text.append(", "); - location_text.append(sim_name); - } - - if (!location_text.empty()) location_text.append(" "); - - if (!pos_global.isNull()) - { - S32 region_x = ll_round((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS; - S32 region_y = ll_round((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS; - S32 region_z = ll_round((F32)pos_global.mdV[VZ]); - location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); - } - return location_text; -} - -void LLPanelPickInfo::setSnapshotId(const LLUUID& id) -{ - mSnapshotCtrl->setImageAssetID(id); - mSnapshotCtrl->setValid(TRUE); -} - -void LLPanelPickInfo::setPickName(const std::string& name) -{ - getChild(XML_NAME)->setValue(name); -} - -void LLPanelPickInfo::setPickDesc(const std::string& desc) -{ - getChild(XML_DESC)->setValue(desc); -} - -void LLPanelPickInfo::setPickLocation(const std::string& location) -{ - getChild(XML_LOCATION)->setValue(location); -} - -void LLPanelPickInfo::onClickMap() -{ - LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); - LLFloaterReg::showInstance("world_map", "center"); -} - -void LLPanelPickInfo::onClickTeleport() -{ - if (!getPosGlobal().isExactlyZero()) - { - gAgent.teleportViaLocation(getPosGlobal()); - LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); - } -} - -void LLPanelPickInfo::onClickBack() -{ - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -//static -LLPanelPickEdit* LLPanelPickEdit::create() -{ - LLPanelPickEdit* panel = new LLPanelPickEdit(); - panel->buildFromFile(XML_PANEL_EDIT_PICK); - return panel; -} - -LLPanelPickEdit::LLPanelPickEdit() - : LLPanelPickInfo() - , mLocationChanged(false) - , mNeedData(true) - , mNewPick(false) -{ -} - -LLPanelPickEdit::~LLPanelPickEdit() -{ -} - -void LLPanelPickEdit::onOpen(const LLSD& key) -{ - LLUUID pick_id = key["pick_id"]; - mNeedData = true; - - // creating new Pick - if(pick_id.isNull()) - { - mNewPick = true; - - setAvatarId(gAgent.getID()); - - resetData(); - resetControls(); - - setPosGlobal(gAgent.getPositionGlobal()); - - LLUUID parcel_id = LLUUID::null, snapshot_id = LLUUID::null; - std::string pick_name, pick_desc, region_name; - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(parcel) - { - parcel_id = parcel->getID(); - pick_name = parcel->getName(); - pick_desc = parcel->getDesc(); - snapshot_id = parcel->getSnapshotID(); - } - - LLViewerRegion* region = gAgent.getRegion(); - if(region) - { - region_name = region->getName(); - } - - setParcelID(parcel_id); - getChild("pick_name")->setValue(pick_name.empty() ? region_name : pick_name); - getChild("pick_desc")->setValue(pick_desc); - setSnapshotId(snapshot_id); - setPickLocation(createLocationText(getLocationNotice(), pick_name, region_name, getPosGlobal())); - - enableSaveButton(true); - } - // editing existing pick - else - { - mNewPick = false; - LLPanelPickInfo::onOpen(key); - - enableSaveButton(false); - } - - resetDirty(); -} - -void LLPanelPickEdit::setPickData(const LLPickData* pick_data) -{ - if(!pick_data) - { - return; - } - - mNeedData = false; - - setParcelID(pick_data->parcel_id); - getChild("pick_name")->setValue(pick_data->name); - getChild("pick_desc")->setValue(pick_data->desc); - setSnapshotId(pick_data->snapshot_id); - setPosGlobal(pick_data->pos_global); - setPickLocation(createLocationText(LLStringUtil::null, pick_data->name, - pick_data->sim_name, pick_data->pos_global)); -} - -BOOL LLPanelPickEdit::postBuild() -{ - LLPanelPickInfo::postBuild(); - - mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelPickEdit::onSnapshotChanged, this)); - - LLLineEditor* line_edit = getChild("pick_name"); - line_edit->setKeystrokeCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1), NULL); - - LLTextEditor* text_edit = getChild("pick_desc"); - text_edit->setKeystrokeCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1)); - - childSetAction(XML_BTN_SAVE, boost::bind(&LLPanelPickEdit::onClickSave, this)); - childSetAction("set_to_curr_location_btn", boost::bind(&LLPanelPickEdit::onClickSetLocation, this)); - - initTexturePickerMouseEvents(); - - return TRUE; -} - -void LLPanelPickEdit::setSaveCallback(const commit_callback_t& cb) -{ - getChild("save_changes_btn")->setClickedCallback(cb); -} - -void LLPanelPickEdit::setCancelCallback(const commit_callback_t& cb) -{ - getChild("cancel_btn")->setClickedCallback(cb); -} - -void LLPanelPickEdit::resetDirty() -{ - LLPanelPickInfo::resetDirty(); - - getChild("pick_name")->resetDirty(); - getChild("pick_desc")->resetDirty(); - mSnapshotCtrl->resetDirty(); - mLocationChanged = false; -} - -BOOL LLPanelPickEdit::isDirty() const -{ - if( mNewPick - || LLPanelPickInfo::isDirty() - || mLocationChanged - || mSnapshotCtrl->isDirty() - || getChild("pick_name")->isDirty() - || getChild("pick_desc")->isDirty()) - { - return TRUE; - } - return FALSE; -} - -// PROTECTED AREA - -void LLPanelPickEdit::sendUpdate() -{ - LLPickData pick_data; - - // If we don't have a pick id yet, we'll need to generate one, - // otherwise we'll keep overwriting pick_id 00000 in the database. - if (getPickId().isNull()) - { - getPickId().generate(); - } - - pick_data.agent_id = gAgent.getID(); - pick_data.session_id = gAgent.getSessionID(); - pick_data.pick_id = getPickId(); - pick_data.creator_id = gAgent.getID();; - - //legacy var need to be deleted - pick_data.top_pick = FALSE; - pick_data.parcel_id = mParcelId; - pick_data.name = getChild(XML_NAME)->getValue().asString(); - pick_data.desc = getChild(XML_DESC)->getValue().asString(); - pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); - pick_data.pos_global = getPosGlobal(); - pick_data.sort_order = 0; - pick_data.enabled = TRUE; - - LLAvatarPropertiesProcessor::instance().sendPickInfoUpdate(&pick_data); - - if(mNewPick) - { - // Assume a successful create pick operation, make new number of picks - // available immediately. Actual number of picks will be requested in - // LLAvatarPropertiesProcessor::sendPickInfoUpdate and updated upon server respond. - LLAgentPicksInfo::getInstance()->incrementNumberOfPicks(); - } -} - -void LLPanelPickEdit::onSnapshotChanged() -{ - enableSaveButton(true); -} - -void LLPanelPickEdit::onPickChanged(LLUICtrl* ctrl) -{ - enableSaveButton(isDirty()); -} - -void LLPanelPickEdit::resetData() -{ - LLPanelPickInfo::resetData(); - mLocationChanged = false; -} - -void LLPanelPickEdit::enableSaveButton(bool enable) -{ - getChildView(XML_BTN_SAVE)->setEnabled(enable); -} - -void LLPanelPickEdit::onClickSetLocation() -{ - // Save location for later use. - setPosGlobal(gAgent.getPositionGlobal()); - - std::string parcel_name, region_name; - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (parcel) - { - mParcelId = parcel->getID(); - parcel_name = parcel->getName(); - } - - LLViewerRegion* region = gAgent.getRegion(); - if(region) - { - region_name = region->getName(); - } - - setPickLocation(createLocationText(getLocationNotice(), parcel_name, region_name, getPosGlobal())); - - mLocationChanged = true; - enableSaveButton(TRUE); -} - -void LLPanelPickEdit::onClickSave() -{ - sendUpdate(); - - mLocationChanged = false; - - LLSD params; - params["action"] = "save_new_pick"; - notifyParent(params); -} - -std::string LLPanelPickEdit::getLocationNotice() -{ - static std::string notice = getString("location_notice"); - return notice; -} - -void LLPanelPickEdit::processProperties(void* data, EAvatarProcessorType type) -{ - if(mNeedData) - { - LLPanelPickInfo::processProperties(data, type); - } -} - -// PRIVATE AREA - -void LLPanelPickEdit::initTexturePickerMouseEvents() -{ - text_icon = getChild(XML_BTN_ON_TXTR); - mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseEnter, this, _1)); - mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseLeave, this, _1)); - - text_icon->setVisible(FALSE); -} - -void LLPanelPickEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl) -{ - text_icon->setVisible(TRUE); -} - -void LLPanelPickEdit::onTexturePickerMouseLeave(LLUICtrl* ctrl) -{ - text_icon->setVisible(FALSE); -} diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h deleted file mode 100644 index 7a8bd66fcf..0000000000 --- a/indra/newview/llpanelpick.h +++ /dev/null @@ -1,264 +0,0 @@ -/** - * @file llpanelpick.h - * @brief LLPanelPick class definition - * - * $LicenseInfo:firstyear=2004&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$ - */ - -// Display of a "Top Pick" used both for the global top picks in the -// Find directory, and also for each individual user's picks in their -// profile. - -#ifndef LL_LLPANELPICK_H -#define LL_LLPANELPICK_H - -#include "llpanel.h" -#include "llremoteparcelrequest.h" -#include "llavatarpropertiesprocessor.h" - -class LLIconCtrl; -class LLTextureCtrl; -class LLScrollContainer; -class LLMessageSystem; -class LLAvatarPropertiesObserver; - -/** - * Panel for displaying Pick Information - snapshot, name, description, etc. - */ -class LLPanelPickInfo : public LLPanel, public LLAvatarPropertiesObserver, LLRemoteParcelInfoObserver -{ - LOG_CLASS(LLPanelPickInfo); -public: - - // Creates new panel - static LLPanelPickInfo* create(); - - virtual ~LLPanelPickInfo(); - - /** - * Initializes panel properties - * - * By default Pick will be created for current Agent location. - * Use setPickData to change Pick properties. - */ - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ BOOL postBuild(); - - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - /** - * Sends remote parcel info request to resolve parcel name from its ID. - */ - void sendParcelInfoRequest(); - - /** - * Sets "Back" button click callback - */ - virtual void setExitCallback(const commit_callback_t& cb); - - /** - * Sets "Edit" button click callback - */ - virtual void setEditPickCallback(const commit_callback_t& cb); - - //This stuff we got from LLRemoteParcelObserver, in the last one we intentionally do nothing - /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); - /*virtual*/ void setParcelID(const LLUUID& parcel_id) { mParcelId = parcel_id; } - /*virtual*/ void setErrorStatus(S32 status, const std::string& reason) {}; - -protected: - - LLPanelPickInfo(); - - /** - * Resets Pick information - */ - virtual void resetData(); - - /** - * Resets UI controls (visibility, values) - */ - virtual void resetControls(); - - /** - * "Location text" is actually the owner name, the original - * name that owner gave the parcel, and the location. - */ - static std::string createLocationText( - const std::string& owner_name, - const std::string& original_name, - const std::string& sim_name, - const LLVector3d& pos_global); - - virtual void setAvatarId(const LLUUID& avatar_id) { mAvatarId = avatar_id; } - virtual LLUUID& getAvatarId() { return mAvatarId; } - - /** - * Sets snapshot id. - * - * Will mark snapshot control as valid if id is not null. - * Will mark snapshot control as invalid if id is null. If null id is a valid value, - * you have to manually mark snapshot is valid. - */ - virtual void setSnapshotId(const LLUUID& id); - - virtual void setPickId(const LLUUID& id) { mPickId = id; } - virtual LLUUID& getPickId() { return mPickId; } - - virtual void setPickName(const std::string& name); - - virtual void setPickDesc(const std::string& desc); - - virtual void setPickLocation(const std::string& location); - - virtual void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } - virtual LLVector3d& getPosGlobal() { return mPosGlobal; } - - /** - * Callback for "Map" button, opens Map - */ - void onClickMap(); - - /** - * Callback for "Teleport" button, teleports user to Pick location. - */ - void onClickTeleport(); - - void onClickBack(); - -protected: - - S32 mScrollingPanelMinHeight; - S32 mScrollingPanelWidth; - LLScrollContainer* mScrollContainer; - LLPanel* mScrollingPanel; - LLTextureCtrl* mSnapshotCtrl; - - LLUUID mAvatarId; - LLVector3d mPosGlobal; - LLUUID mParcelId; - LLUUID mPickId; - LLUUID mRequestedId; -}; - -/** - * Panel for creating/editing Pick. - */ -class LLPanelPickEdit : public LLPanelPickInfo -{ - LOG_CLASS(LLPanelPickEdit); -public: - - /** - * Creates new panel - */ - static LLPanelPickEdit* create(); - - /*virtual*/ ~LLPanelPickEdit(); - - /*virtual*/ void onOpen(const LLSD& key); - - virtual void setPickData(const LLPickData* pick_data); - - /*virtual*/ BOOL postBuild(); - - /** - * Sets "Save" button click callback - */ - virtual void setSaveCallback(const commit_callback_t& cb); - - /** - * Sets "Cancel" button click callback - */ - virtual void setCancelCallback(const commit_callback_t& cb); - - /** - * Resets panel and all cantrols to unedited state - */ - /*virtual*/ void resetDirty(); - - /** - * Returns true if any of Pick properties was changed by user. - */ - /*virtual*/ BOOL isDirty() const; - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - -protected: - - LLPanelPickEdit(); - - /** - * Sends Pick properties to server. - */ - void sendUpdate(); - - /** - * Called when snapshot image changes. - */ - void onSnapshotChanged(); - - /** - * Callback for Pick snapshot, name and description changed event. - */ - void onPickChanged(LLUICtrl* ctrl); - - /*virtual*/ void resetData(); - - /** - * Enables/disables "Save" button - */ - void enableSaveButton(bool enable); - - /** - * Callback for "Set Location" button click - */ - void onClickSetLocation(); - - /** - * Callback for "Save" button click - */ - void onClickSave(); - - std::string getLocationNotice(); - -protected: - - bool mLocationChanged; - bool mNeedData; - bool mNewPick; - -private: - - void initTexturePickerMouseEvents(); - void onTexturePickerMouseEnter(LLUICtrl* ctrl); - void onTexturePickerMouseLeave(LLUICtrl* ctrl); - -private: - - LLIconCtrl* text_icon; -}; - -#endif // LL_LLPANELPICK_H diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp deleted file mode 100644 index 8294977f99..0000000000 --- a/indra/newview/llpanelpicks.cpp +++ /dev/null @@ -1,1484 +0,0 @@ -/** - * @file llpanelpicks.cpp - * @brief LLPanelPicks and related class implementations - * - * $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 "llpanelpicks.h" - -#include "llagent.h" -#include "llagentpicksinfo.h" -#include "llcommandhandler.h" -#include "lldispatcher.h" -#include "llflatlistview.h" -#include "llfloaterreg.h" -#include "llfloatersidepanelcontainer.h" -#include "llfloaterworldmap.h" -#include "llnotificationsutil.h" -#include "llstartup.h" -#include "lltexturectrl.h" -#include "lltoggleablemenu.h" -#include "lltrans.h" -#include "llviewergenericmessage.h" // send_generic_message -#include "llmenugl.h" -#include "llviewermenu.h" -#include "llregistry.h" - -#include "llaccordionctrl.h" -#include "llaccordionctrltab.h" -#include "llavatarpropertiesprocessor.h" -#include "llfloatersidepanelcontainer.h" -#include "llpanelavatar.h" -#include "llpanelprofile.h" -#include "llpanelpick.h" -#include "llpanelclassified.h" - -static const std::string XML_BTN_NEW = "new_btn"; -static const std::string XML_BTN_DELETE = "trash_btn"; -static const std::string XML_BTN_INFO = "info_btn"; -static const std::string XML_BTN_TELEPORT = "teleport_btn"; -static const std::string XML_BTN_SHOW_ON_MAP = "show_on_map_btn"; - -static const std::string PICK_ID("pick_id"); -static const std::string PICK_CREATOR_ID("pick_creator_id"); -static const std::string PICK_NAME("pick_name"); - -static const std::string CLASSIFIED_ID("classified_id"); -static const std::string CLASSIFIED_NAME("classified_name"); - - -static LLPanelInjector t_panel_picks("panel_picks"); - - -class LLPickHandler : public LLCommandHandler, - public LLAvatarPropertiesObserver -{ -public: - - std::set mPickIds; - - // requires trusted browser to trigger - LLPickHandler() : LLCommandHandler("pick", UNTRUSTED_THROTTLE) { } - - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) - { - if (LLStartUp::getStartupState() < STATE_STARTED) - { - return true; - } - - if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnablePicks")) - { - LLNotificationsUtil::add("NoPicks", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); - return true; - } - - // handle app/classified/create urls first - if (params.size() == 1 && params[0].asString() == "create") - { - createPick(); - return true; - } - - // then handle the general app/pick/{UUID}/{CMD} urls - if (params.size() < 2) - { - return false; - } - - // get the ID for the pick_id - LLUUID pick_id; - if (!pick_id.set(params[0], FALSE)) - { - return false; - } - - // edit the pick in the side tray. - // need to ask the server for more info first though... - const std::string verb = params[1].asString(); - if (verb == "edit") - { - mPickIds.insert(pick_id); - LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); - LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest(gAgent.getID(),pick_id); - return true; - } - else - { - LL_WARNS() << "unknown verb " << verb << LL_ENDL; - return false; - } - } - - void createPick() - { - // open the new pick panel on the Picks floater - LLFloater* picks_floater = LLFloaterReg::showInstance("picks"); - - LLPanelPicks* picks = picks_floater->findChild("panel_picks"); - if (picks) - { - picks->createNewPick(); - } - } - - void editPick(LLPickData* pick_info) - { - LLSD params; - params["open_tab_name"] = "panel_picks"; - params["show_tab_panel"] = "edit_pick"; - params["pick_id"] = pick_info->pick_id; - params["avatar_id"] = pick_info->creator_id; - params["snapshot_id"] = pick_info->snapshot_id; - params["pick_name"] = pick_info->name; - params["pick_desc"] = pick_info->desc; - LLFloaterSidePanelContainer::showPanel("picks", params); - } - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) - { - if (APT_PICK_INFO != type) - { - return; - } - - // is this the pick that we asked for? - LLPickData* pick_info = static_cast(data); - if (!pick_info || mPickIds.find(pick_info->pick_id) == mPickIds.end()) - { - return; - } - - // open the edit side tray for this pick - if (pick_info->creator_id == gAgent.getID()) - { - editPick(pick_info); - } - else - { - LL_WARNS() << "Can't edit a pick you did not create" << LL_ENDL; - } - - // remove our observer now that we're done - mPickIds.erase(pick_info->pick_id); - LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); - } -}; - -LLPickHandler gPickHandler; - -class LLClassifiedHandler : - public LLCommandHandler, - public LLAvatarPropertiesObserver -{ -public: - // throttle calls from untrusted browsers - LLClassifiedHandler() : LLCommandHandler("classified", UNTRUSTED_THROTTLE) {} - - std::set mClassifiedIds; - - std::string mRequestVerb; - - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) - { - if (LLStartUp::getStartupState() < STATE_STARTED) - { - return true; - } - - if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableClassifieds")) - { - LLNotificationsUtil::add("NoClassifieds", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); - return true; - } - - // handle app/classified/create urls first - if (params.size() == 1 && params[0].asString() == "create") - { - createClassified(); - return true; - } - - // then handle the general app/classified/{UUID}/{CMD} urls - if (params.size() < 2) - { - return false; - } - - // get the ID for the classified - LLUUID classified_id; - if (!classified_id.set(params[0], FALSE)) - { - return false; - } - - // show the classified in the side tray. - // need to ask the server for more info first though... - const std::string verb = params[1].asString(); - if (verb == "about") - { - mRequestVerb = verb; - mClassifiedIds.insert(classified_id); - LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); - LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); - return true; - } - else if (verb == "edit") - { - mRequestVerb = verb; - mClassifiedIds.insert(classified_id); - LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); - LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); - return true; - } - - return false; - } - - void createClassified() - { - // open the new classified panel on the Picks floater - LLFloater* picks_floater = LLFloaterReg::showInstance("picks"); - - LLPanelPicks* picks = picks_floater->findChild("panel_picks"); - if (picks) - { - picks->createNewClassified(); - } - } - - void openClassified(LLAvatarClassifiedInfo* c_info) - { - if (mRequestVerb == "about") - { - // open the classified info panel on the Me > Picks sidetray - LLSD params; - params["id"] = c_info->creator_id; - params["open_tab_name"] = "panel_picks"; - params["show_tab_panel"] = "classified_details"; - params["classified_id"] = c_info->classified_id; - params["classified_creator_id"] = c_info->creator_id; - params["classified_snapshot_id"] = c_info->snapshot_id; - params["classified_name"] = c_info->name; - params["classified_desc"] = c_info->description; - params["from_search"] = true; - LLFloaterSidePanelContainer::showPanel("picks", params); - } - else if (mRequestVerb == "edit") - { - if (c_info->creator_id == gAgent.getID()) - { - LL_WARNS() << "edit in progress" << LL_ENDL; - // open the new classified panel on the Me > Picks sidetray - LLSD params; - params["id"] = gAgent.getID(); - params["open_tab_name"] = "panel_picks"; - params["show_tab_panel"] = "edit_classified"; - params["classified_id"] = c_info->classified_id; - LLFloaterSidePanelContainer::showPanel("my_profile", params); - } - else - { - LL_WARNS() << "Can't edit a classified you did not create" << LL_ENDL; - } - } - } - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) - { - if (APT_CLASSIFIED_INFO != type) - { - return; - } - - // is this the classified that we asked for? - LLAvatarClassifiedInfo* c_info = static_cast(data); - if (!c_info || mClassifiedIds.find(c_info->classified_id) == mClassifiedIds.end()) - { - return; - } - - // open the detail side tray for this classified - openClassified(c_info); - - // remove our observer now that we're done - mClassifiedIds.erase(c_info->classified_id); - LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); - } - -}; -LLClassifiedHandler gClassifiedHandler; - -////////////////////////////////////////////////////////////////////////// - - -//----------------------------------------------------------------------------- -// LLPanelPicks -//----------------------------------------------------------------------------- -LLPanelPicks::LLPanelPicks() -: LLPanelProfileTab(), - mPopupMenu(NULL), - mProfilePanel(NULL), - mPickPanel(NULL), - mPicksList(NULL), - mClassifiedsList(NULL), - mPanelPickInfo(NULL), - mPanelPickEdit(NULL), - mPlusMenu(NULL), - mPicksAccTab(NULL), - mClassifiedsAccTab(NULL), - mPanelClassifiedInfo(NULL), - mNoClassifieds(false), - mNoPicks(false) -{ -} - -LLPanelPicks::~LLPanelPicks() -{ - if(getAvatarId().notNull()) - { - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this); - } -} - -void* LLPanelPicks::create(void* data /* = NULL */) -{ - return new LLPanelPicks(); -} - -void LLPanelPicks::updateData() -{ - // Send Picks request only when we need to, not on every onOpen(during tab switch). - if(isDirty()) - { - mNoPicks = false; - mNoClassifieds = false; - - mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); - mNoItemsLabel->setVisible(TRUE); - - mPicksList->clear(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId()); - - mClassifiedsList->clear(); - LLAvatarPropertiesProcessor::getInstance()->sendAvatarClassifiedsRequest(getAvatarId()); - } -} - -void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type) -{ - if(APT_PICKS == type) - { - LLAvatarPicks* avatar_picks = static_cast(data); - if(avatar_picks && getAvatarId() == avatar_picks->target_id) - { - LLAvatarName av_name; - LLAvatarNameCache::get(getAvatarId(), &av_name); - getChild("pick_title")->setTextArg("[NAME]", av_name.getUserName()); - - // Save selection, to be able to edit same item after saving changes. See EXT-3023. - LLUUID selected_id = mPicksList->getSelectedValue()[PICK_ID]; - - mPicksList->clear(); - - LLAvatarPicks::picks_list_t::const_iterator it = avatar_picks->picks_list.begin(); - for(; avatar_picks->picks_list.end() != it; ++it) - { - LLUUID pick_id = it->first; - std::string pick_name = it->second; - - LLPickItem* picture = LLPickItem::create(); - picture->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this)); - picture->setPickName(pick_name); - picture->setPickId(pick_id); - picture->setCreatorId(getAvatarId()); - - LLAvatarPropertiesProcessor::instance().addObserver(getAvatarId(), picture); - picture->update(); - - LLSD pick_value = LLSD(); - pick_value.insert(PICK_ID, pick_id); - pick_value.insert(PICK_NAME, pick_name); - pick_value.insert(PICK_CREATOR_ID, getAvatarId()); - - mPicksList->addItem(picture, pick_value); - - // Restore selection by item id. - if ( pick_id == selected_id ) - mPicksList->selectItemByValue(pick_value); - - picture->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickPickItem, this, _1)); - picture->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); - picture->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this)); - } - - showAccordion("tab_picks", mPicksList->size()); - - resetDirty(); - updateButtons(); - } - - mNoPicks = !mPicksList->size(); - } - else if((APT_CLASSIFIEDS == type) || (APT_CLASSIFIED_INFO == type)) - { - LLAvatarClassifieds* c_info = static_cast(data); - if(c_info && getAvatarId() == c_info->target_id) - { - // do not clear classified list in case we will receive two or more data packets. - // list has been cleared in updateData(). (fix for EXT-6436) - - LLAvatarClassifieds::classifieds_list_t::const_iterator it = c_info->classifieds_list.begin(); - for(; c_info->classifieds_list.end() != it; ++it) - { - LLAvatarClassifieds::classified_data c_data = *it; - - LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), c_data.classified_id); - c_item->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this)); - c_item->setClassifiedName(c_data.name); - - LLSD pick_value = LLSD(); - pick_value.insert(CLASSIFIED_ID, c_data.classified_id); - pick_value.insert(CLASSIFIED_NAME, c_data.name); - - if (!findClassifiedById(c_data.classified_id)) - { - mClassifiedsList->addItem(c_item, pick_value); - } - - c_item->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickClassifiedItem, this, _1)); - c_item->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); - c_item->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this)); - } - - showAccordion("tab_classifieds", mClassifiedsList->size()); - - resetDirty(); - updateButtons(); - } - - mNoClassifieds = !mClassifiedsList->size(); - } - - updateNoItemsLabel(); -} - -LLPickItem* LLPanelPicks::getSelectedPickItem() -{ - LLPanel* selected_item = mPicksList->getSelectedItem(); - if (!selected_item) return NULL; - - return dynamic_cast(selected_item); -} - -LLClassifiedItem* LLPanelPicks::getSelectedClassifiedItem() -{ - LLPanel* selected_item = mClassifiedsList->getSelectedItem(); - if (!selected_item) - { - return NULL; - } - return dynamic_cast(selected_item); -} - -BOOL LLPanelPicks::postBuild() -{ - mPicksList = getChild("picks_list"); - mClassifiedsList = getChild("classifieds_list"); - - mPicksList->setCommitOnSelectionChange(true); - mClassifiedsList->setCommitOnSelectionChange(true); - - mPicksList->setCommitCallback(boost::bind(&LLPanelPicks::onListCommit, this, mPicksList)); - mClassifiedsList->setCommitCallback(boost::bind(&LLPanelPicks::onListCommit, this, mClassifiedsList)); - - mPicksList->setNoItemsCommentText(getString("no_picks")); - mClassifiedsList->setNoItemsCommentText(getString("no_classifieds")); - - mNoItemsLabel = getChild("picks_panel_text"); - - childSetAction(XML_BTN_NEW, boost::bind(&LLPanelPicks::onClickPlusBtn, this)); - childSetAction(XML_BTN_DELETE, boost::bind(&LLPanelPicks::onClickDelete, this)); - childSetAction(XML_BTN_TELEPORT, boost::bind(&LLPanelPicks::onClickTeleport, this)); - childSetAction(XML_BTN_SHOW_ON_MAP, boost::bind(&LLPanelPicks::onClickMap, this)); - childSetAction(XML_BTN_INFO, boost::bind(&LLPanelPicks::onClickInfo, this)); - - mPicksAccTab = getChild("tab_picks"); - mPicksAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mPicksAccTab)); - mPicksAccTab->setDisplayChildren(true); - - mClassifiedsAccTab = getChild("tab_classifieds"); - mClassifiedsAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mClassifiedsAccTab)); - mClassifiedsAccTab->setDisplayChildren(false); - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registar; - registar.add("Pick.Info", boost::bind(&LLPanelPicks::onClickInfo, this)); - registar.add("Pick.Edit", boost::bind(&LLPanelPicks::onClickMenuEdit, this)); - registar.add("Pick.Teleport", boost::bind(&LLPanelPicks::onClickTeleport, this)); - registar.add("Pick.Map", boost::bind(&LLPanelPicks::onClickMap, this)); - registar.add("Pick.Delete", boost::bind(&LLPanelPicks::onClickDelete, this)); - LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registar; - enable_registar.add("Pick.Enable", boost::bind(&LLPanelPicks::onEnableMenuItem, this, _2)); - - mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_picks.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - - LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar; - plus_registar.add("Picks.Plus.Action", boost::bind(&LLPanelPicks::onPlusMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("Picks.Plus.Enable", boost::bind(&LLPanelPicks::isActionEnabled, this, _2)); - mPlusMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_picks_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - - return TRUE; -} - -void LLPanelPicks::onPlusMenuItemClicked(const LLSD& param) -{ - std::string value = param.asString(); - - if("new_pick" == value) - { - createNewPick(); - } - else if("new_classified" == value) - { - createNewClassified(); - } -} - -bool LLPanelPicks::isActionEnabled(const LLSD& userdata) const -{ - std::string command_name = userdata.asString(); - - if (command_name == "new_pick" && LLAgentPicksInfo::getInstance()->isPickLimitReached()) - { - return false; - } - - return true; -} - -bool LLPanelPicks::isClassifiedPublished(LLClassifiedItem* c_item) -{ - if(c_item) - { - LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; - if(panel) - { - return !panel->isNewWithErrors(); - } - - // we've got this classified from server - it's published - return true; - } - return false; -} - -void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab) -{ - if(!mPicksAccTab->getDisplayChildren()) - { - mPicksList->resetSelection(true); - } - if(!mClassifiedsAccTab->getDisplayChildren()) - { - mClassifiedsList->resetSelection(true); - } - - updateButtons(); -} - -void LLPanelPicks::onOpen(const LLSD& key) -{ - const LLUUID id(key.asUUID()); - BOOL self = (gAgent.getID() == id); - - // only agent can edit her picks - getChildView("edit_panel")->setEnabled(self); - getChildView("edit_panel")->setVisible( self); - - // Disable buttons when viewing profile for first time - if(getAvatarId() != id) - { - getChildView(XML_BTN_INFO)->setEnabled(FALSE); - getChildView(XML_BTN_TELEPORT)->setEnabled(FALSE); - getChildView(XML_BTN_SHOW_ON_MAP)->setEnabled(FALSE); - } - - // and see a special title - set as invisible by default in xml file - if (self) - { - getChildView("pick_title")->setVisible( !self); - getChildView("pick_title_agent")->setVisible( self); - - mPopupMenu->setItemVisible("pick_delete", TRUE); - mPopupMenu->setItemVisible("pick_edit", TRUE); - mPopupMenu->setItemVisible("pick_separator", TRUE); - } - - if(getAvatarId() != id) - { - showAccordion("tab_picks", false); - showAccordion("tab_classifieds", false); - - mPicksList->goToTop(); - // Set dummy value to make panel dirty and make it reload picks - setValue(LLSD()); - } - - LLPanelProfileTab::onOpen(key); -} - -void LLPanelPicks::onClosePanel() -{ - if (mPanelClassifiedInfo) - { - onPanelClassifiedClose(mPanelClassifiedInfo); - } - if (mPanelPickInfo) - { - onPanelPickClose(mPanelPickInfo); - } -} - -void LLPanelPicks::onListCommit(const LLFlatListView* f_list) -{ - // Make sure only one of the lists has selection. - if(f_list == mPicksList) - { - mClassifiedsList->resetSelection(true); - } - else if(f_list == mClassifiedsList) - { - mPicksList->resetSelection(true); - } - else - { - LL_WARNS() << "Unknown list" << LL_ENDL; - } - - updateButtons(); -} - -//static -void LLPanelPicks::onClickDelete() -{ - LLSD value = mPicksList->getSelectedValue(); - if (value.isDefined()) - { - LLSD args; - args["PICK"] = value[PICK_NAME]; - LLNotificationsUtil::add("DeleteAvatarPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackDeletePick, this, _1, _2)); - return; - } - - value = mClassifiedsList->getSelectedValue(); - if(value.isDefined()) - { - LLSD args; - args["NAME"] = value[CLASSIFIED_NAME]; - LLNotificationsUtil::add("DeleteClassified", args, LLSD(), boost::bind(&LLPanelPicks::callbackDeleteClassified, this, _1, _2)); - return; - } -} - -bool LLPanelPicks::callbackDeletePick(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - LLSD pick_value = mPicksList->getSelectedValue(); - - if (0 == option) - { - LLAvatarPropertiesProcessor::instance().sendPickDelete(pick_value[PICK_ID]); - mPicksList->removeItemByValue(pick_value); - - mNoPicks = !mPicksList->size(); - if (mNoPicks) - { - showAccordion("tab_picks", false); - } - updateNoItemsLabel(); - } - updateButtons(); - return false; -} - -bool LLPanelPicks::callbackDeleteClassified(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - LLSD value = mClassifiedsList->getSelectedValue(); - - if (0 == option) - { - LLAvatarPropertiesProcessor::instance().sendClassifiedDelete(value[CLASSIFIED_ID]); - mClassifiedsList->removeItemByValue(value); - - mNoClassifieds = !mClassifiedsList->size(); - if (mNoClassifieds) - { - showAccordion("tab_classifieds", false); - } - updateNoItemsLabel(); - } - updateButtons(); - return false; -} - -bool LLPanelPicks::callbackTeleport( const LLSD& notification, const LLSD& response ) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - - if (0 == option) - { - onClickTeleport(); - } - return false; -} - -//static -void LLPanelPicks::onClickTeleport() -{ - LLPickItem* pick_item = getSelectedPickItem(); - LLClassifiedItem* c_item = getSelectedClassifiedItem(); - - LLVector3d pos; - if(pick_item) - pos = pick_item->getPosGlobal(); - else if(c_item) - { - pos = c_item->getPosGlobal(); - LLPanelClassifiedInfo::sendClickMessage("teleport", false, - c_item->getClassifiedId(), LLUUID::null, pos, LLStringUtil::null); - } - - if (!pos.isExactlyZero()) - { - gAgent.teleportViaLocation(pos); - LLFloaterWorldMap::getInstance()->trackLocation(pos); - } -} - -//static -void LLPanelPicks::onClickMap() -{ - LLPickItem* pick_item = getSelectedPickItem(); - LLClassifiedItem* c_item = getSelectedClassifiedItem(); - - LLVector3d pos; - if (pick_item) - pos = pick_item->getPosGlobal(); - else if(c_item) - { - LLPanelClassifiedInfo::sendClickMessage("map", false, - c_item->getClassifiedId(), LLUUID::null, pos, LLStringUtil::null); - pos = c_item->getPosGlobal(); - } - - LLFloaterWorldMap::getInstance()->trackLocation(pos); - LLFloaterReg::showInstance("world_map", "center"); -} - - -void LLPanelPicks::onRightMouseUpItem(LLUICtrl* item, S32 x, S32 y, MASK mask) -{ - updateButtons(); - - if (mPopupMenu) - { - mPopupMenu->buildDrawLabels(); - mPopupMenu->updateParent(LLMenuGL::sMenuContainer); - ((LLContextMenu*)mPopupMenu)->show(x, y); - LLMenuGL::showPopup(item, mPopupMenu, x, y); - } -} - -void LLPanelPicks::onDoubleClickPickItem(LLUICtrl* item) -{ - LLSD pick_value = mPicksList->getSelectedValue(); - if (pick_value.isUndefined()) return; - - LLSD args; - args["PICK"] = pick_value[PICK_NAME]; - LLNotificationsUtil::add("TeleportToPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackTeleport, this, _1, _2)); -} - -void LLPanelPicks::onDoubleClickClassifiedItem(LLUICtrl* item) -{ - LLSD value = mClassifiedsList->getSelectedValue(); - if (value.isUndefined()) return; - - LLSD args; - args["CLASSIFIED"] = value[CLASSIFIED_NAME]; - LLNotificationsUtil::add("TeleportToClassified", args, LLSD(), boost::bind(&LLPanelPicks::callbackTeleport, this, _1, _2)); -} - -void LLPanelPicks::updateButtons() -{ - bool has_selected = mPicksList->numSelected() > 0 || mClassifiedsList->numSelected() > 0; - - if (getAvatarId() == gAgentID) - { - getChildView(XML_BTN_DELETE)->setEnabled(has_selected); - } - - getChildView(XML_BTN_INFO)->setEnabled(has_selected); - getChildView(XML_BTN_TELEPORT)->setEnabled(has_selected); - getChildView(XML_BTN_SHOW_ON_MAP)->setEnabled(has_selected); - - LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); - if(c_item) - { - getChildView(XML_BTN_INFO)->setEnabled(isClassifiedPublished(c_item)); - } -} - -void LLPanelPicks::updateNoItemsLabel() -{ - bool no_data = mNoPicks && mNoClassifieds; - mNoItemsLabel->setVisible(no_data); - if (no_data) - { - if (getAvatarId() == gAgentID) - { - mNoItemsLabel->setValue(LLTrans::getString("NoPicksClassifiedsText")); - } - else - { - mNoItemsLabel->setValue(LLTrans::getString("NoAvatarPicksClassifiedsText")); - } - } -} - -void LLPanelPicks::setProfilePanel(LLPanelProfile* profile_panel) -{ - mProfilePanel = profile_panel; -} - - -void LLPanelPicks::buildPickPanel() -{ -// if (mPickPanel == NULL) -// { -// mPickPanel = new LLPanelPick(); -// mPickPanel->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, NULL)); -// } -} - -void LLPanelPicks::onClickPlusBtn() -{ - LLRect rect(getChildView(XML_BTN_NEW)->getRect()); - - mPlusMenu->updateParent(LLMenuGL::sMenuContainer); - mPlusMenu->setButtonRect(rect, this); - LLMenuGL::showPopup(this, mPlusMenu, rect.mLeft, rect.mTop); -} - -void LLPanelPicks::createNewPick() -{ - createPickEditPanel(); - - getProfilePanel()->openPanel(mPanelPickEdit, LLSD()); -} - -void LLPanelPicks::createNewClassified() -{ - LLPanelClassifiedEdit* panel = NULL; - createClassifiedEditPanel(&panel); - - getProfilePanel()->openPanel(panel, LLSD()); -} - -void LLPanelPicks::onClickInfo() -{ - if(mPicksList->numSelected() > 0) - { - openPickInfo(); - } - else if(mClassifiedsList->numSelected() > 0) - { - openClassifiedInfo(); - } -} - -void LLPanelPicks::openPickInfo() -{ - LLSD selected_value = mPicksList->getSelectedValue(); - if (selected_value.isUndefined()) return; - - LLPickItem* pick = (LLPickItem*)mPicksList->getSelectedItem(); - - createPickInfoPanel(); - - LLSD params; - params["pick_id"] = pick->getPickId(); - params["avatar_id"] = pick->getCreatorId(); - params["snapshot_id"] = pick->getSnapshotId(); - params["pick_name"] = pick->getPickName(); - params["pick_desc"] = pick->getPickDesc(); - - getProfilePanel()->openPanel(mPanelPickInfo, params); -} - -void LLPanelPicks::openClassifiedInfo() -{ - LLSD selected_value = mClassifiedsList->getSelectedValue(); - if (selected_value.isUndefined()) return; - - LLClassifiedItem* c_item = getSelectedClassifiedItem(); - LLSD params; - params["classified_id"] = c_item->getClassifiedId(); - params["classified_creator_id"] = c_item->getAvatarId(); - params["classified_snapshot_id"] = c_item->getSnapshotId(); - params["classified_name"] = c_item->getClassifiedName(); - params["classified_desc"] = c_item->getDescription(); - params["from_search"] = false; - - openClassifiedInfo(params); -} - -void LLPanelPicks::openClassifiedInfo(const LLSD ¶ms) -{ - createClassifiedInfoPanel(); - getProfilePanel()->openPanel(mPanelClassifiedInfo, params); -} - -void LLPanelPicks::openClassifiedEdit(const LLSD& params) -{ - LLUUID classified_id = params["classified_id"].asUUID();; - LL_INFOS() << "opening classified " << classified_id << " for edit" << LL_ENDL; - editClassified(classified_id); -} - -void LLPanelPicks::showAccordion(const std::string& name, bool show) -{ - LLAccordionCtrlTab* tab = getChild(name); - tab->setVisible(show); - LLAccordionCtrl* acc = getChild("accordion"); - acc->arrange(); -} - -void LLPanelPicks::onPanelPickClose(LLPanel* panel) -{ - getProfilePanel()->closePanel(panel); -} - -void LLPanelPicks::onPanelPickSave(LLPanel* panel) -{ - onPanelPickClose(panel); - updateButtons(); -} - -void LLPanelPicks::onPanelClassifiedSave(LLPanelClassifiedEdit* panel) -{ - if(!panel->canClose()) - { - return; - } - - if(panel->isNew()) - { - mEditClassifiedPanels[panel->getClassifiedId()] = panel; - - LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), panel->getClassifiedId()); - c_item->fillIn(panel); - - LLSD c_value; - c_value.insert(CLASSIFIED_ID, c_item->getClassifiedId()); - c_value.insert(CLASSIFIED_NAME, c_item->getClassifiedName()); - mClassifiedsList->addItem(c_item, c_value, ADD_TOP); - - c_item->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickClassifiedItem, this, _1)); - c_item->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); - c_item->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this)); - c_item->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this)); - - // order does matter, showAccordion will invoke arrange for accordions. - mClassifiedsAccTab->changeOpenClose(false); - showAccordion("tab_classifieds", true); - } - else if(panel->isNewWithErrors()) - { - LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); - llassert(c_item); - if (c_item) - { - c_item->fillIn(panel); - } - } - else - { - onPanelClassifiedClose(panel); - return; - } - - onPanelPickClose(panel); - updateButtons(); -} - -void LLPanelPicks::onPanelClassifiedClose(LLPanelClassifiedInfo* panel) -{ - if(panel->getInfoLoaded() && !panel->isDirty()) - { - std::vector values; - mClassifiedsList->getValues(values); - for(size_t n = 0; n < values.size(); ++n) - { - LLUUID c_id = values[n][CLASSIFIED_ID].asUUID(); - if(panel->getClassifiedId() == c_id) - { - LLClassifiedItem* c_item = dynamic_cast( - mClassifiedsList->getItemByValue(values[n])); - llassert(c_item); - if (c_item) - { - c_item->setClassifiedName(panel->getClassifiedName()); - c_item->setDescription(panel->getDescription()); - c_item->setSnapshotId(panel->getSnapshotId()); - } - } - } - } - - onPanelPickClose(panel); - updateButtons(); -} - -void LLPanelPicks::createPickInfoPanel() -{ - if(!mPanelPickInfo) - { - mPanelPickInfo = LLPanelPickInfo::create(); - mPanelPickInfo->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickInfo)); - mPanelPickInfo->setEditPickCallback(boost::bind(&LLPanelPicks::onPanelPickEdit, this)); - mPanelPickInfo->setVisible(FALSE); - } -} - -void LLPanelPicks::createClassifiedInfoPanel() -{ - mPanelClassifiedInfo = LLPanelClassifiedInfo::create(); - mPanelClassifiedInfo->setExitCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, mPanelClassifiedInfo)); - mPanelClassifiedInfo->setEditClassifiedCallback(boost::bind(&LLPanelPicks::onPanelClassifiedEdit, this)); - mPanelClassifiedInfo->setVisible(FALSE); -} - -void LLPanelPicks::createClassifiedEditPanel(LLPanelClassifiedEdit** panel) -{ - if(panel) - { - LLPanelClassifiedEdit* new_panel = LLPanelClassifiedEdit::create(); - new_panel->setExitCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, new_panel)); - new_panel->setSaveCallback(boost::bind(&LLPanelPicks::onPanelClassifiedSave, this, new_panel)); - new_panel->setCancelCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, new_panel)); - new_panel->setVisible(FALSE); - *panel = new_panel; - } -} - -void LLPanelPicks::createPickEditPanel() -{ - mPanelPickEdit = LLPanelPickEdit::create(); - mPanelPickEdit->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickEdit)); - mPanelPickEdit->setSaveCallback(boost::bind(&LLPanelPicks::onPanelPickSave, this, mPanelPickEdit)); - mPanelPickEdit->setCancelCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickEdit)); - mPanelPickEdit->setVisible(FALSE); -} - -// void LLPanelPicks::openPickEditPanel(LLPickItem* pick) -// { -// if(!pick) -// { -// return; -// } -// } - -// void LLPanelPicks::openPickInfoPanel(LLPickItem* pick) -// { -// if(!mPanelPickInfo) -// { -// mPanelPickInfo = LLPanelPickInfo::create(); -// mPanelPickInfo->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickInfo)); -// mPanelPickInfo->setEditPickCallback(boost::bind(&LLPanelPicks::onPanelPickEdit, this)); -// mPanelPickInfo->setVisible(FALSE); -// } -// -// LLSD params; -// params["pick_id"] = pick->getPickId(); -// params["avatar_id"] = pick->getCreatorId(); -// params["snapshot_id"] = pick->getSnapshotId(); -// params["pick_name"] = pick->getPickName(); -// params["pick_desc"] = pick->getPickDesc(); -// -// getProfilePanel()->openPanel(mPanelPickInfo, params); -// } - -void LLPanelPicks::openPickEdit(const LLSD& params) -{ - createPickEditPanel(); - getProfilePanel()->openPanel(mPanelPickEdit, params); -} - -void LLPanelPicks::onPanelPickEdit() -{ - LLSD selected_value = mPicksList->getSelectedValue(); - if (selected_value.isUndefined()) return; - - LLPickItem* pick = dynamic_cast(mPicksList->getSelectedItem()); - - createPickEditPanel(); - - LLSD params; - params["pick_id"] = pick->getPickId(); - params["avatar_id"] = pick->getCreatorId(); - params["snapshot_id"] = pick->getSnapshotId(); - params["pick_name"] = pick->getPickName(); - params["pick_desc"] = pick->getPickDesc(); - - getProfilePanel()->openPanel(mPanelPickEdit, params); -} - -void LLPanelPicks::onPanelClassifiedEdit() -{ - LLSD selected_value = mClassifiedsList->getSelectedValue(); - if (selected_value.isUndefined()) - { - return; - } - - LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); - llassert(c_item); - if (!c_item) - { - return; - } - editClassified(c_item->getClassifiedId()); -} - -LLClassifiedItem *LLPanelPicks::findClassifiedById(const LLUUID& classified_id) -{ - // HACK - find item by classified id. Should be a better way. - std::vector items; - mClassifiedsList->getItems(items); - LLClassifiedItem* c_item = NULL; - for(std::vector::iterator it = items.begin(); it != items.end(); ++it) - { - LLClassifiedItem *test_item = dynamic_cast(*it); - if (test_item && test_item->getClassifiedId() == classified_id) - { - c_item = test_item; - break; - } - } - return c_item; -} - -void LLPanelPicks::editClassified(const LLUUID& classified_id) -{ - LLClassifiedItem* c_item = findClassifiedById(classified_id); - if (!c_item) - { - LL_WARNS() << "item not found for classified_id " << classified_id << LL_ENDL; - return; - } - - LLSD params; - params["classified_id"] = c_item->getClassifiedId(); - params["classified_creator_id"] = c_item->getAvatarId(); - params["snapshot_id"] = c_item->getSnapshotId(); - params["name"] = c_item->getClassifiedName(); - params["desc"] = c_item->getDescription(); - params["category"] = (S32)c_item->getCategory(); - params["content_type"] = (S32)c_item->getContentType(); - params["auto_renew"] = c_item->getAutoRenew(); - params["price_for_listing"] = c_item->getPriceForListing(); - params["location_text"] = c_item->getLocationText(); - - LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; - if(!panel) - { - createClassifiedEditPanel(&panel); - mEditClassifiedPanels[c_item->getClassifiedId()] = panel; - } - getProfilePanel()->openPanel(panel, params); - panel->setPosGlobal(c_item->getPosGlobal()); -} - -void LLPanelPicks::onClickMenuEdit() -{ - if(getSelectedPickItem()) - { - onPanelPickEdit(); - } - else if(getSelectedClassifiedItem()) - { - onPanelClassifiedEdit(); - } -} - -bool LLPanelPicks::onEnableMenuItem(const LLSD& user_data) -{ - std::string param = user_data.asString(); - - LLClassifiedItem* c_item = dynamic_cast(mClassifiedsList->getSelectedItem()); - if(c_item && "info" == param) - { - // dont show Info panel if classified was not created - return isClassifiedPublished(c_item); - } - - return true; -} - -inline LLPanelProfile* LLPanelPicks::getProfilePanel() -{ - llassert_always(NULL != mProfilePanel); - return mProfilePanel; -} - -//----------------------------------------------------------------------------- -// LLPanelPicks -//----------------------------------------------------------------------------- -LLPickItem::LLPickItem() -: LLPanel() -, mPickID(LLUUID::null) -, mCreatorID(LLUUID::null) -, mParcelID(LLUUID::null) -, mSnapshotID(LLUUID::null) -, mNeedData(true) -{ - buildFromFile("panel_pick_list_item.xml"); -} - -LLPickItem::~LLPickItem() -{ - if (mCreatorID.notNull()) - { - LLAvatarPropertiesProcessor::instance().removeObserver(mCreatorID, this); - } - -} - -LLPickItem* LLPickItem::create() -{ - return new LLPickItem(); -} - -void LLPickItem::init(LLPickData* pick_data) -{ - setPickDesc(pick_data->desc); - setSnapshotId(pick_data->snapshot_id); - mPosGlobal = pick_data->pos_global; - mSimName = pick_data->sim_name; - mPickDescription = pick_data->desc; - mUserName = pick_data->user_name; - mOriginalName = pick_data->original_name; - - LLTextureCtrl* picture = getChild("picture"); - picture->setImageAssetID(pick_data->snapshot_id); -} - -void LLPickItem::setPickName(const std::string& name) -{ - mPickName = name; - getChild("picture_name")->setValue(name); - -} - -const std::string& LLPickItem::getPickName() -{ - return mPickName; -} - -const LLUUID& LLPickItem::getCreatorId() -{ - return mCreatorID; -} - -const LLUUID& LLPickItem::getSnapshotId() -{ - return mSnapshotID; -} - -void LLPickItem::setPickDesc(const std::string& descr) -{ - getChild("picture_descr")->setValue(descr); -} - -void LLPickItem::setPickId(const LLUUID& id) -{ - mPickID = id; -} - -const LLUUID& LLPickItem::getPickId() -{ - return mPickID; -} - -const LLVector3d& LLPickItem::getPosGlobal() -{ - return mPosGlobal; -} - -const std::string LLPickItem::getDescription() -{ - return getChild("picture_descr")->getValue().asString(); -} - -void LLPickItem::update() -{ - setNeedData(true); - LLAvatarPropertiesProcessor::instance().sendPickInfoRequest(mCreatorID, mPickID); -} - -void LLPickItem::processProperties(void *data, EAvatarProcessorType type) -{ - if (APT_PICK_INFO != type) - { - return; - } - - LLPickData* pick_data = static_cast(data); - if (!pick_data || mPickID != pick_data->pick_id) - { - return; - } - - init(pick_data); - setNeedData(false); - LLAvatarPropertiesProcessor::instance().removeObserver(mCreatorID, this); -} - -void set_child_visible(LLView* parent, const std::string& child_name, bool visible) -{ - parent->getChildView(child_name)->setVisible(visible); -} - -BOOL LLPickItem::postBuild() -{ - setMouseEnterCallback(boost::bind(&set_child_visible, this, "hovered_icon", true)); - setMouseLeaveCallback(boost::bind(&set_child_visible, this, "hovered_icon", false)); - return TRUE; -} - -void LLPickItem::setValue(const LLSD& value) -{ - if (!value.isMap()) return;; - if (!value.has("selected")) return; - getChildView("selected_icon")->setVisible( value["selected"]); -} - -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////// - -LLClassifiedItem::LLClassifiedItem(const LLUUID& avatar_id, const LLUUID& classified_id) - : LLPanel() - , mAvatarId(avatar_id) - , mClassifiedId(classified_id) -{ - buildFromFile("panel_classifieds_list_item.xml"); - - LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); - LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(getClassifiedId()); -} - -LLClassifiedItem::~LLClassifiedItem() -{ - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); -} - -void LLClassifiedItem::processProperties(void* data, EAvatarProcessorType type) -{ - if(APT_CLASSIFIED_INFO != type) - { - return; - } - - LLAvatarClassifiedInfo* c_info = static_cast(data); - if( !c_info || c_info->classified_id != getClassifiedId() ) - { - return; - } - - setClassifiedName(c_info->name); - setDescription(c_info->description); - setSnapshotId(c_info->snapshot_id); - setPosGlobal(c_info->pos_global); - - LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); -} - -BOOL LLClassifiedItem::postBuild() -{ - setMouseEnterCallback(boost::bind(&set_child_visible, this, "hovered_icon", true)); - setMouseLeaveCallback(boost::bind(&set_child_visible, this, "hovered_icon", false)); - return TRUE; -} - -void LLClassifiedItem::setValue(const LLSD& value) -{ - if (!value.isMap()) return;; - if (!value.has("selected")) return; - getChildView("selected_icon")->setVisible( value["selected"]); -} - -void LLClassifiedItem::fillIn(LLPanelClassifiedEdit* panel) -{ - if(!panel) - { - return; - } - - setClassifiedName(panel->getClassifiedName()); - setDescription(panel->getDescription()); - setSnapshotId(panel->getSnapshotId()); - setCategory(panel->getCategory()); - setContentType(panel->getContentType()); - setAutoRenew(panel->getAutoRenew()); - setPriceForListing(panel->getPriceForListing()); - setPosGlobal(panel->getPosGlobal()); - setLocationText(panel->getClassifiedLocation()); -} - -void LLClassifiedItem::setClassifiedName(const std::string& name) -{ - getChild("name")->setValue(name); -} - -void LLClassifiedItem::setDescription(const std::string& desc) -{ - getChild("description")->setValue(desc); -} - -void LLClassifiedItem::setSnapshotId(const LLUUID& snapshot_id) -{ - getChild("picture")->setValue(snapshot_id); -} - -LLUUID LLClassifiedItem::getSnapshotId() -{ - return getChild("picture")->getValue(); -} - -//EOF diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h deleted file mode 100644 index fd7688b99d..0000000000 --- a/indra/newview/llpanelpicks.h +++ /dev/null @@ -1,315 +0,0 @@ -/** - * @file llpanelpicks.h - * @brief LLPanelPicks and related class definitions - * - * $LicenseInfo:firstyear=2009&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLPANELPICKS_H -#define LL_LLPANELPICKS_H - -#include "llpanel.h" -#include "v3dmath.h" -#include "lluuid.h" -#include "llavatarpropertiesprocessor.h" -#include "llpanelavatar.h" -#include "llregistry.h" - -class LLAccordionCtrlTab; -class LLPanelProfile; -class LLMessageSystem; -class LLVector3d; -class LLPanelProfileTab; -class LLAgent; -class LLMenuGL; -class LLPickItem; -class LLClassifiedItem; -class LLFlatListView; -class LLPanelPickInfo; -class LLPanelPickEdit; -class LLToggleableMenu; -class LLPanelClassifiedInfo; -class LLPanelClassifiedEdit; - -// *TODO -// Panel Picks has been consolidated with Classifieds (EXT-2095), give LLPanelPicks -// and corresponding files (cpp, h, xml) a new name. (new name is TBD at the moment) - -class LLPanelPicks - : public LLPanelProfileTab -{ -public: - LLPanelPicks(); - ~LLPanelPicks(); - - static void* create(void* data); - - /*virtual*/ BOOL postBuild(void); - - /*virtual*/ void onOpen(const LLSD& key); - - /*virtual*/ void onClosePanel(); - - void processProperties(void* data, EAvatarProcessorType type); - - void updateData(); - - // returns the selected pick item - LLPickItem* getSelectedPickItem(); - LLClassifiedItem* getSelectedClassifiedItem(); - LLClassifiedItem* findClassifiedById(const LLUUID& classified_id); - - //*NOTE top down approch when panel toggling is done only by - // parent panels failed to work (picks related code was in my profile panel) - void setProfilePanel(LLPanelProfile* profile_panel); - - void createNewPick(); - void createNewClassified(); - -protected: - /*virtual*/void updateButtons(); - void updateNoItemsLabel(); - -private: - void onClickDelete(); - void onClickTeleport(); - void onClickMap(); - - void onPlusMenuItemClicked(const LLSD& param); - bool isActionEnabled(const LLSD& userdata) const; - - bool isClassifiedPublished(LLClassifiedItem* c_item); - - void onListCommit(const LLFlatListView* f_list); - void onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab); - - //------------------------------------------------ - // Callbacks which require panel toggling - //------------------------------------------------ - void onClickPlusBtn(); - void onClickInfo(); - void onPanelPickClose(LLPanel* panel); - void onPanelPickSave(LLPanel* panel); - void onPanelClassifiedSave(LLPanelClassifiedEdit* panel); - void onPanelClassifiedClose(LLPanelClassifiedInfo* panel); - void openPickEdit(const LLSD& params); - void onPanelPickEdit(); - void onPanelClassifiedEdit(); - void editClassified(const LLUUID& classified_id); - void onClickMenuEdit(); - - bool onEnableMenuItem(const LLSD& user_data); - - void openPickInfo(); - void openClassifiedInfo(); - void openClassifiedInfo(const LLSD& params); - void openClassifiedEdit(const LLSD& params); - friend class LLPanelProfile; - - void showAccordion(const std::string& name, bool show); - - void buildPickPanel(); - - bool callbackDeletePick(const LLSD& notification, const LLSD& response); - bool callbackDeleteClassified(const LLSD& notification, const LLSD& response); - bool callbackTeleport(const LLSD& notification, const LLSD& response); - - - virtual void onDoubleClickPickItem(LLUICtrl* item); - virtual void onDoubleClickClassifiedItem(LLUICtrl* item); - virtual void onRightMouseUpItem(LLUICtrl* item, S32 x, S32 y, MASK mask); - - LLPanelProfile* getProfilePanel(); - - void createPickInfoPanel(); - void createPickEditPanel(); - void createClassifiedInfoPanel(); - void createClassifiedEditPanel(LLPanelClassifiedEdit** panel); - - LLMenuGL* mPopupMenu; - LLPanelProfile* mProfilePanel; - LLPanelPickInfo* mPickPanel; - LLFlatListView* mPicksList; - LLFlatListView* mClassifiedsList; - LLPanelPickInfo* mPanelPickInfo; - LLPanelClassifiedInfo* mPanelClassifiedInfo; - LLPanelPickEdit* mPanelPickEdit; - LLToggleableMenu* mPlusMenu; - LLUICtrl* mNoItemsLabel; - - // - typedef std::map panel_classified_edit_map_t; - - // This map is needed for newly created classifieds. The purpose of panel is to - // sit in this map and listen to LLPanelClassifiedEdit::processProperties callback. - panel_classified_edit_map_t mEditClassifiedPanels; - - LLAccordionCtrlTab* mPicksAccTab; - LLAccordionCtrlTab* mClassifiedsAccTab; - - //true if picks list is empty after processing picks - bool mNoPicks; - //true if classifieds list is empty after processing classifieds - bool mNoClassifieds; -}; - -class LLPickItem : public LLPanel, public LLAvatarPropertiesObserver -{ -public: - - LLPickItem(); - - static LLPickItem* create(); - - void init(LLPickData* pick_data); - - void setPickName(const std::string& name); - - void setPickDesc(const std::string& descr); - - void setPickId(const LLUUID& id); - - void setCreatorId(const LLUUID& id) {mCreatorID = id;}; - - void setSnapshotId(const LLUUID& id) {mSnapshotID = id;}; - - void setNeedData(bool need){mNeedData = need;}; - - const LLUUID& getPickId(); - - const std::string& getPickName(); - - const LLUUID& getCreatorId(); - - const LLUUID& getSnapshotId(); - - const LLVector3d& getPosGlobal(); - - const std::string getDescription(); - - const std::string& getSimName() { return mSimName; } - - const std::string& getUserName() { return mUserName; } - - const std::string& getOriginalName() { return mOriginalName; } - - const std::string& getPickDesc() { return mPickDescription; } - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - void update(); - - ~LLPickItem(); - - /*virtual*/ BOOL postBuild(); - - /** setting on/off background icon to indicate selected state */ - /*virtual*/ void setValue(const LLSD& value); - -protected: - - LLUUID mPickID; - LLUUID mCreatorID; - LLUUID mParcelID; - LLUUID mSnapshotID; - LLVector3d mPosGlobal; - bool mNeedData; - - std::string mPickName; - std::string mUserName; - std::string mOriginalName; - std::string mPickDescription; - std::string mSimName; -}; - -class LLClassifiedItem : public LLPanel, public LLAvatarPropertiesObserver -{ -public: - - LLClassifiedItem(const LLUUID& avatar_id, const LLUUID& classified_id); - - virtual ~LLClassifiedItem(); - - /*virtual*/ void processProperties(void* data, EAvatarProcessorType type); - - /*virtual*/ BOOL postBuild(); - - /*virtual*/ void setValue(const LLSD& value); - - void fillIn(LLPanelClassifiedEdit* panel); - - LLUUID getAvatarId() {return mAvatarId;} - - void setAvatarId(const LLUUID& avatar_id) {mAvatarId = avatar_id;} - - LLUUID getClassifiedId() {return mClassifiedId;} - - void setClassifiedId(const LLUUID& classified_id) {mClassifiedId = classified_id;} - - void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } - - const LLVector3d getPosGlobal() { return mPosGlobal; } - - void setLocationText(const std::string location) { mLocationText = location; } - - std::string getLocationText() { return mLocationText; } - - void setClassifiedName (const std::string& name); - - std::string getClassifiedName() { return getChild("name")->getValue().asString(); } - - void setDescription(const std::string& desc); - - std::string getDescription() { return getChild("description")->getValue().asString(); } - - void setSnapshotId(const LLUUID& snapshot_id); - - LLUUID getSnapshotId(); - - void setCategory(U32 cat) { mCategory = cat; } - - U32 getCategory() { return mCategory; } - - void setContentType(U32 ct) { mContentType = ct; } - - U32 getContentType() { return mContentType; } - - void setAutoRenew(U32 renew) { mAutoRenew = renew; } - - bool getAutoRenew() { return mAutoRenew; } - - void setPriceForListing(S32 price) { mPriceForListing = price; } - - S32 getPriceForListing() { return mPriceForListing; } - -private: - LLUUID mAvatarId; - LLUUID mClassifiedId; - LLVector3d mPosGlobal; - std::string mLocationText; - U32 mCategory; - U32 mContentType; - bool mAutoRenew; - S32 mPriceForListing; -}; - -#endif // LL_LLPANELPICKS_H diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index 9157df789f..fb5957ff8f 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -27,6 +27,8 @@ #include "llviewerprecompiledheaders.h" #include "llpanelplaceinfo.h" +#include "llfloaterprofile.h" +#include "llfloaterreg.h" #include "llavatarname.h" #include "llsdutil.h" @@ -42,7 +44,6 @@ #include "llagent.h" #include "llexpandabletextbox.h" -#include "llpanelpick.h" #include "llslurl.h" #include "lltexturectrl.h" #include "llviewerregion.h" @@ -287,7 +288,7 @@ void LLPanelPlaceInfo::reshape(S32 width, S32 height, BOOL called_from_parent) } } -void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPickEdit* pick_panel) +void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global) { LLPickData data; data.pos_global = pos_global; @@ -296,7 +297,12 @@ void LLPanelPlaceInfo::createPick(const LLVector3d& pos_global, LLPanelPickEdit* data.desc = mDescEditor->getText(); data.snapshot_id = mSnapshotCtrl->getImageAssetID(); data.parcel_id = mParcelID; - pick_panel->setPickData(&data); + + LLFloaterProfile* profile_floater = dynamic_cast(LLFloaterReg::showInstance("profile", LLSD().with("id", gAgentID))); + if (profile_floater) + { + profile_floater->createPick(data); + } } // static diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index 8bf67cfe7d..533215016a 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -38,7 +38,6 @@ class LLAvatarName; class LLExpandableTextBox; class LLIconCtrl; class LLInventoryItem; -class LLPanelPickEdit; class LLParcel; class LLScrollContainer; class LLTextBox; @@ -94,7 +93,7 @@ public: // Create a pick for the location specified // by global_pos. - void createPick(const LLVector3d& pos_global, LLPanelPickEdit* pick_panel); + void createPick(const LLVector3d& pos_global); protected: static void onNameCache(LLTextBox* text, const std::string& full_name); diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 69f181e1b3..74ec576554 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -63,7 +63,6 @@ #include "lllayoutstack.h" #include "llpanellandmarkinfo.h" #include "llpanellandmarks.h" -#include "llpanelpick.h" #include "llpanelplaceprofile.h" #include "llpanelteleporthistory.h" #include "llremoteparcelrequest.h" @@ -238,7 +237,6 @@ LLPanelPlaces::LLPanelPlaces() mFilterEditor(NULL), mPlaceProfile(NULL), mLandmarkInfo(NULL), - mPickPanel(NULL), mItem(NULL), mPlaceMenu(NULL), mLandmarkMenu(NULL), @@ -952,28 +950,11 @@ void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param) } else if (item == "pick") { - if (mPickPanel == NULL) - { - mPickPanel = LLPanelPickEdit::create(); - addChild(mPickPanel); - - mPickPanel->setExitCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE)); - mPickPanel->setCancelCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE)); - mPickPanel->setSaveCallback(boost::bind(&LLPanelPlaces::togglePickPanel, this, FALSE)); - } - - togglePickPanel(TRUE); - mPickPanel->onOpen(LLSD()); - LLPanelPlaceInfo* panel = getCurrentInfoPanel(); if (panel) { - panel->createPick(mPosGlobal, mPickPanel); + panel->createPick(mPosGlobal); } - - LLRect rect = getRect(); - mPickPanel->reshape(rect.getWidth(), rect.getHeight()); - mPickPanel->setRect(rect); } else if (item == "add_to_favbar") { @@ -1050,17 +1031,6 @@ bool LLPanelPlaces::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_t return false; } -void LLPanelPlaces::togglePickPanel(BOOL visible) -{ - if (mPickPanel) - { - mPickPanel->setVisible(visible); - mPlaceProfile->setVisible(!visible); - updateVerbs(); - } - -} - void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) { if (!mPlaceProfile || !mLandmarkInfo) @@ -1309,15 +1279,11 @@ void LLPanelPlaces::updateVerbs() bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE; bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE; - bool is_pick_panel_visible = false; - if(mPickPanel) - { - is_pick_panel_visible = mPickPanel->isInVisibleChain(); - } + bool have_3d_pos = ! mPosGlobal.isExactlyZero(); - mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); - mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn && !is_pick_panel_visible); + mTeleportBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn); + mShowOnMapBtn->setVisible(!is_create_landmark_visible && !isLandmarkEditModeOn); mSaveBtn->setVisible(isLandmarkEditModeOn); mCancelBtn->setVisible(isLandmarkEditModeOn); mCloseBtn->setVisible(is_create_landmark_visible && !isLandmarkEditModeOn); diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 3b87eb6cb9..e554099343 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -38,7 +38,6 @@ class LLLandmark; class LLPanelLandmarkInfo; class LLPanelPlaceProfile; -class LLPanelPickEdit; class LLPanelPlaceInfo; class LLPanelPlacesTab; class LLParcelSelection; @@ -95,7 +94,6 @@ private: void onOverflowButtonClicked(); void onOverflowMenuItemClicked(const LLSD& param); bool onOverflowMenuItemEnable(const LLSD& param); - void onCreateLandmarkButtonClicked(const LLUUID& folder_id); void onBackButtonClicked(); void onGearMenuClick(); void onSortingMenuClick(); @@ -103,9 +101,6 @@ private: void onRemoveButtonClicked(); bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept); - - void toggleMediaPanel(); - void togglePickPanel(BOOL visible); void togglePlaceInfoPanel(BOOL visible); /*virtual*/ void onVisibilityChange(BOOL new_visibility); @@ -122,7 +117,6 @@ private: LLPanelPlaceProfile* mPlaceProfile; LLPanelLandmarkInfo* mLandmarkInfo; - LLPanelPickEdit* mPickPanel; LLToggleableMenu* mPlaceMenu; LLToggleableMenu* mLandmarkMenu; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 5f13b223fb..f4eaa78f11 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llpanelprofile.cpp * @brief Profile panel implementation * -* $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$ */ @@ -27,32 +27,433 @@ #include "llviewerprecompiledheaders.h" #include "llpanelprofile.h" -#include "llagent.h" -#include "llavataractions.h" -#include "llfloaterreg.h" -#include "llcommandhandler.h" -#include "llnotificationsutil.h" -#include "llpanelpicks.h" +// Common +#include "llavatarnamecache.h" +#include "llsdutil.h" +#include "llslurl.h" +#include "lldateutil.h" //ageFromDate + +// UI +#include "llavatariconctrl.h" +#include "llclipboard.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "lllineeditor.h" +#include "llloadingindicator.h" +#include "llmenubutton.h" #include "lltabcontainer.h" -#include "llviewercontrol.h" -#include "llviewernetwork.h" +#include "lltextbox.h" +#include "lltexteditor.h" +#include "lltexturectrl.h" +#include "lltoggleablemenu.h" +#include "llgrouplist.h" +#include "llurlaction.h" + +// Image +#include "llimagej2c.h" + +// Newview +#include "llagent.h" //gAgent +#include "llagentpicksinfo.h" +#include "llavataractions.h" +#include "llavatarpropertiesprocessor.h" +#include "llcallingcard.h" +#include "llcommandhandler.h" +#include "llfloaterprofiletexture.h" +#include "llfloaterreg.h" +#include "llfloaterreporter.h" +#include "llfilepicker.h" +#include "llfirstuse.h" +#include "llgroupactions.h" +#include "lllogchat.h" #include "llmutelist.h" +#include "llnotificationsutil.h" #include "llpanelblockedlist.h" +#include "llpanelprofileclassifieds.h" +#include "llpanelprofilepicks.h" +#include "lltrans.h" +#include "llviewercontrol.h" +#include "llviewermenu.h" //is_agent_mappable +#include "llviewermenufile.h" +#include "llviewertexturelist.h" +#include "llvoiceclient.h" #include "llweb.h" -static const std::string PANEL_PICKS = "panel_picks"; -std::string getProfileURL(const std::string& agent_name) +static LLPanelInjector t_panel_profile_secondlife("panel_profile_secondlife"); +static LLPanelInjector t_panel_web("panel_profile_web"); +static LLPanelInjector t_panel_picks("panel_profile_picks"); +static LLPanelInjector t_panel_firstlife("panel_profile_firstlife"); +static LLPanelInjector t_panel_notes("panel_profile_notes"); +static LLPanelInjector t_panel_profile("panel_profile"); + +static const std::string PANEL_SECONDLIFE = "panel_profile_secondlife"; +static const std::string PANEL_WEB = "panel_profile_web"; +static const std::string PANEL_PICKS = "panel_profile_picks"; +static const std::string PANEL_CLASSIFIEDS = "panel_profile_classifieds"; +static const std::string PANEL_FIRSTLIFE = "panel_profile_firstlife"; +static const std::string PANEL_NOTES = "panel_profile_notes"; +static const std::string PANEL_PROFILE_VIEW = "panel_profile_view"; + +static const std::string PROFILE_PROPERTIES_CAP = "AgentProfile"; +static const std::string PROFILE_IMAGE_UPLOAD_CAP = "UploadAgentProfileImage"; + + +////////////////////////////////////////////////////////////////////////// + +void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id) { - std::string url = "[WEB_PROFILE_URL][AGENT_NAME]"; - LLSD subs; - subs["WEB_PROFILE_URL"] = LLGridManager::getInstance()->getWebProfileURL(); - subs["AGENT_NAME"] = agent_name; - url = LLWeb::expandURLSubstitutions(url, subs); - LLStringUtil::toLower(url); - return url; + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("request_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 + "/" + 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); + + LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Result: " << httpResults << LL_ENDL; + + if (!status + || !result.has("id") + || agent_id != result["id"].asUUID()) + { + LL_WARNS("AvatarProperties") << "Failed to get agent information for id " << agent_id << LL_ENDL; + return; + } + + LLFloater* floater_profile = LLFloaterReg::findInstance("profile", LLSD().with("id", agent_id)); + if (!floater_profile) + { + // floater is dead, so panels are dead as well + return; + } + + LLPanel *panel = floater_profile->findChild(PANEL_PROFILE_VIEW, TRUE); + LLPanelProfile *panel_profile = dynamic_cast(panel); + if (!panel_profile) + { + LL_WARNS() << PANEL_PROFILE_VIEW << " not found" << LL_ENDL; + return; + } + + + // Avatar Data + + LLAvatarData *avatar_data = &panel_profile->mAvatarData; + 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; + + if (result["online"].asBoolean()) + { + avatar_data->flags |= AVATAR_ONLINE; + } + if (result["allow_publish"].asBoolean()) + { + avatar_data->flags |= AVATAR_ALLOW_PUBLISH; + } + if (result["identified"].asBoolean()) + { + avatar_data->flags |= AVATAR_IDENTIFIED; + } + if (result["transacted"].asBoolean()) + { + avatar_data->flags |= AVATAR_TRANSACTED; + } + + avatar_data->caption_index = 0; + if (result.has("charter_member")) // won't be present if "caption" is set + { + avatar_data->caption_index = result["charter_member"].asInteger(); + } + else if (result.has("caption")) + { + avatar_data->caption_text = result["caption"].asString(); + } + + panel = floater_profile->findChild(PANEL_SECONDLIFE, TRUE); + LLPanelProfileSecondLife *panel_sl = dynamic_cast(panel); + if (panel_sl) + { + panel_sl->processProfileProperties(avatar_data); + } + + panel = floater_profile->findChild(PANEL_WEB, TRUE); + LLPanelProfileWeb *panel_web = dynamic_cast(panel); + if (panel_web) + { + panel_web->setLoaded(); + } + + panel = floater_profile->findChild(PANEL_FIRSTLIFE, TRUE); + LLPanelProfileFirstLife *panel_first = dynamic_cast(panel); + if (panel_first) + { + panel_first->processProperties(avatar_data); + } + + // 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()); + } + + panel = floater_profile->findChild(PANEL_PICKS, TRUE); + LLPanelProfilePicks *panel_picks = dynamic_cast(panel); + if (panel_picks) + { + // Refresh pick limit before processing + LLAgentPicksInfo::getInstance()->onServerRespond(&avatar_picks); + panel_picks->processProperties(&avatar_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); + } + + if (panel_sl) + { + panel_sl->processGroupProperties(&avatar_groups); + } + + // Notes + LLAvatarNotes avatar_notes; + + avatar_notes.agent_id = agent_id; + avatar_notes.target_id = agent_id; + avatar_notes.notes = result["notes"].asString(); + + panel = floater_profile->findChild(PANEL_NOTES, TRUE); + LLPanelProfileNotes *panel_notes = dynamic_cast(panel); + if (panel_notes) + { + panel_notes->processProperties(&avatar_notes); + } } +//TODO: changes take two minutes to propagate! +// Add some storage that holds updated data for two minutes +// for new instances to reuse the data +// Profile data is only relevant to won avatar, but notes +// are for everybody +void put_avatar_properties_coro(std::string cap_url, LLUUID agent_id, LLSD data) +{ + 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 + "/" + agent_id.asString(); + + 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("AvatarProperties") << "Failed to put agent information " << data << " for id " << agent_id << LL_ENDL; + return; + } + + LL_DEBUGS("AvatarProperties") << "Agent id: " << agent_id << " Data: " << data << " Result: " << httpResults << LL_ENDL; +} + +LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::string path_to_image, LLHandle *handle) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("post_profile_image_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); + + LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, first_data, httpOpts, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + // todo: notification? + LL_WARNS("AvatarProperties") << "Failed to get uploader cap " << status.toString() << LL_ENDL; + return LLUUID::null; + } + if (!result.has("uploader")) + { + // todo: notification? + LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL; + return LLUUID::null; + } + std::string uploader_cap = result["uploader"].asString(); + if (uploader_cap.empty()) + { + LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL; + return LLUUID::null; + } + + // Upload the image + + LLCore::HttpRequest::ptr_t uploaderhttpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t uploaderhttpHeaders(new LLCore::HttpHeaders); + LLCore::HttpOptions::ptr_t uploaderhttpOpts(new LLCore::HttpOptions); + S64 length; + + { + llifstream instream(path_to_image.c_str(), std::iostream::binary | std::iostream::ate); + if (!instream.is_open()) + { + LL_WARNS("AvatarProperties") << "Failed to open file " << path_to_image << LL_ENDL; + return LLUUID::null; + } + length = instream.tellg(); + } + + uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/jp2"); // optional + uploaderhttpHeaders->append(HTTP_OUT_HEADER_CONTENT_LENGTH, llformat("%d", length)); // required! + uploaderhttpOpts->setFollowRedirects(true); + + result = httpAdapter->postFileAndSuspend(uploaderhttpRequest, uploader_cap, path_to_image, uploaderhttpOpts, uploaderhttpHeaders); + + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + LL_WARNS("AvatarProperties") << result << LL_ENDL; + + if (!status) + { + LL_WARNS("AvatarProperties") << "Failed to upload image " << status.toString() << LL_ENDL; + return LLUUID::null; + } + + if (result["state"].asString() != "complete") + { + if (result.has("message")) + { + LL_WARNS("AvatarProperties") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL; + } + else + { + LL_WARNS("AvatarProperties") << "Failed to upload image " << result << LL_ENDL; + } + return LLUUID::null; + } + + return result["new_asset"].asUUID(); +} + +enum EProfileImageType +{ + PROFILE_IMAGE_SL, + PROFILE_IMAGE_FL, +}; + +void post_profile_image_coro(std::string cap_url, EProfileImageType type, std::string path_to_image, LLHandle *handle) +{ + LLSD data; + switch (type) + { + case PROFILE_IMAGE_SL: + data["profile-image-asset"] = "sl_image_id"; + break; + case PROFILE_IMAGE_FL: + data["profile-image-asset"] = "fl_image_id"; + break; + } + + LLUUID result = post_profile_image(cap_url, data, path_to_image, handle); + + // reset loading indicator + if (!handle->isDead()) + { + switch (type) + { + case PROFILE_IMAGE_SL: + { + LLPanelProfileSecondLife* panel = static_cast(handle->get()); + if (result.notNull()) + { + panel->setProfileImageUploaded(result); + } + else + { + // failure, just stop progress indicator + panel->setProfileImageUploading(false); + } + break; + } + case PROFILE_IMAGE_FL: + { + LLPanelProfileFirstLife* panel = static_cast(handle->get()); + if (result.notNull()) + { + panel->setProfileImageUploaded(result); + } + else + { + // failure, just stop progress indicator + panel->setProfileImageUploading(false); + } + break; + } + } + } + + // Cleanup + LLFile::remove(path_to_image); + delete handle; +} + +////////////////////////////////////////////////////////////////////////// +// LLProfileHandler + class LLProfileHandler : public LLCommandHandler { public: @@ -73,6 +474,10 @@ public: }; LLProfileHandler gProfileHandler; + +////////////////////////////////////////////////////////////////////////// +// LLAgentHandler + class LLAgentHandler : public LLCommandHandler { public: @@ -178,279 +583,2070 @@ public: } return true; } + + // reportAbuse is here due to convoluted avatar handling + // in LLScrollListCtrl and LLTextBase + if (verb == "reportAbuse" && web == NULL) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(avatar_id, &av_name)) + { + LLFloaterReporter::showFromAvatar(avatar_id, av_name.getCompleteName()); + } + else + { + LLFloaterReporter::showFromAvatar(avatar_id, "not avaliable"); + } + return true; + } return false; } }; LLAgentHandler gAgentHandler; -//-- LLPanelProfile::ChildStack begins ---------------------------------------- -LLPanelProfile::ChildStack::ChildStack() -: mParent(NULL) +///---------------------------------------------------------------------------- +/// LLFloaterProfilePermissions +///---------------------------------------------------------------------------- + +class LLFloaterProfilePermissions + : public LLFloater + , public LLFriendObserver +{ +public: + LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id); + ~LLFloaterProfilePermissions(); + BOOL postBuild() override; + void onOpen(const LLSD& key) override; + void draw() override; + void changed(U32 mask) override; // LLFriendObserver + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + bool hasUnsavedChanges() { return mHasUnsavedPermChanges; } + + void onApplyRights(); + +private: + void fillRightsData(); + void rightsConfirmationCallback(const LLSD& notification, const LLSD& response); + void confirmModifyRights(bool grant); + void onCommitSeeOnlineRights(); + void onCommitEditRights(); + void onCancel(); + + LLTextBase* mDescription; + LLCheckBoxCtrl* mOnlineStatus; + LLCheckBoxCtrl* mMapRights; + LLCheckBoxCtrl* mEditObjectRights; + LLButton* mOkBtn; + LLButton* mCancelBtn; + + LLUUID mAvatarID; + F32 mContextConeOpacity; + bool mHasUnsavedPermChanges; + LLHandle mOwnerHandle; + + boost::signals2::connection mAvatarNameCacheConnection; +}; + +LLFloaterProfilePermissions::LLFloaterProfilePermissions(LLView * owner, const LLUUID &avatar_id) + : LLFloater(LLSD()) + , mAvatarID(avatar_id) + , mContextConeOpacity(0.0f) + , mHasUnsavedPermChanges(false) + , mOwnerHandle(owner->getHandle()) +{ + buildFromFile("floater_profile_permissions.xml"); +} + +LLFloaterProfilePermissions::~LLFloaterProfilePermissions() +{ + mAvatarNameCacheConnection.disconnect(); + if (mAvatarID.notNull()) + { + LLAvatarTracker::instance().removeParticularFriendObserver(mAvatarID, this); + } +} + +BOOL LLFloaterProfilePermissions::postBuild() +{ + mDescription = getChild("perm_description"); + mOnlineStatus = getChild("online_check"); + mMapRights = getChild("map_check"); + mEditObjectRights = getChild("objects_check"); + mOkBtn = getChild("perms_btn_ok"); + mCancelBtn = getChild("perms_btn_cancel"); + + mOnlineStatus->setCommitCallback([this](LLUICtrl*, void*) { onCommitSeeOnlineRights(); }, nullptr); + mMapRights->setCommitCallback([this](LLUICtrl*, void*) { mHasUnsavedPermChanges = true; }, nullptr); + mEditObjectRights->setCommitCallback([this](LLUICtrl*, void*) { onCommitEditRights(); }, nullptr); + mOkBtn->setCommitCallback([this](LLUICtrl*, void*) { onApplyRights(); }, nullptr); + mCancelBtn->setCommitCallback([this](LLUICtrl*, void*) { onCancel(); }, nullptr); + + return TRUE; +} + +void LLFloaterProfilePermissions::onOpen(const LLSD& key) +{ + if (LLAvatarActions::isFriend(mAvatarID)) + { + LLAvatarTracker::instance().addParticularFriendObserver(mAvatarID, this); + fillRightsData(); + } + + mCancelBtn->setFocus(true); + + mAvatarNameCacheConnection = LLAvatarNameCache::get(mAvatarID, boost::bind(&LLFloaterProfilePermissions::onAvatarNameCache, this, _1, _2)); +} + +void LLFloaterProfilePermissions::draw() +{ + // drawFrustum + LLView *owner = mOwnerHandle.get(); + static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); + drawConeToOwner(mContextConeOpacity, max_opacity, owner); + LLFloater::draw(); +} + +void LLFloaterProfilePermissions::changed(U32 mask) +{ + if (mask != LLFriendObserver::ONLINE) + { + fillRightsData(); + } +} + +void LLFloaterProfilePermissions::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + + LLStringUtil::format_map_t args; + args["[AGENT_NAME]"] = av_name.getDisplayName(); + std::string descritpion = getString("description_string", args); + mDescription->setValue(descritpion); +} + +void LLFloaterProfilePermissions::fillRightsData() +{ + const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + // If true - we are viewing friend's profile, enable check boxes and set values. + if (relation) + { + S32 rights = relation->getRightsGrantedTo(); + + BOOL see_online = LLRelationship::GRANT_ONLINE_STATUS & rights ? TRUE : FALSE; + mOnlineStatus->setValue(see_online); + mMapRights->setEnabled(see_online); + mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights ? TRUE : FALSE); + mEditObjectRights->setValue(LLRelationship::GRANT_MODIFY_OBJECTS & rights ? TRUE : FALSE); + } + else + { + closeFloater(); + LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL; + } +} + +void LLFloaterProfilePermissions::rightsConfirmationCallback(const LLSD& notification, + const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) // canceled + { + mEditObjectRights->setValue(mEditObjectRights->getValue().asBoolean() ? FALSE : TRUE); + } + else + { + mHasUnsavedPermChanges = true; + } +} + +void LLFloaterProfilePermissions::confirmModifyRights(bool grant) +{ + LLSD args; + args["NAME"] = LLSLURL("agent", mAvatarID, "completename").getSLURLString(); + LLNotificationsUtil::add(grant ? "GrantModifyRights" : "RevokeModifyRights", args, LLSD(), + boost::bind(&LLFloaterProfilePermissions::rightsConfirmationCallback, this, _1, _2)); +} + +void LLFloaterProfilePermissions::onCommitSeeOnlineRights() +{ + bool see_online = mOnlineStatus->getValue().asBoolean(); + mMapRights->setEnabled(see_online); + if (see_online) + { + const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + if (relation) + { + S32 rights = relation->getRightsGrantedTo(); + mMapRights->setValue(LLRelationship::GRANT_MAP_LOCATION & rights ? TRUE : FALSE); + } + else + { + closeFloater(); + LL_INFOS("ProfilePermissions") << "Floater closing since agent is no longer a friend" << LL_ENDL; + } + } + else + { + mMapRights->setValue(FALSE); + } + mHasUnsavedPermChanges = true; +} + +void LLFloaterProfilePermissions::onCommitEditRights() +{ + const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + + if (!buddy_relationship) + { + LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Closing floater." << LL_ENDL; + closeFloater(); + return; + } + + bool allow_modify_objects = mEditObjectRights->getValue().asBoolean(); + + // if modify objects checkbox clicked + if (buddy_relationship->isRightGrantedTo( + LLRelationship::GRANT_MODIFY_OBJECTS) != allow_modify_objects) + { + confirmModifyRights(allow_modify_objects); + } +} + +void LLFloaterProfilePermissions::onApplyRights() +{ + const LLRelationship* buddy_relationship = LLAvatarTracker::instance().getBuddyInfo(mAvatarID); + + if (!buddy_relationship) + { + LL_WARNS("ProfilePermissions") << "Trying to modify rights for non-friend avatar. Skipped." << LL_ENDL; + return; + } + + S32 rights = 0; + + if (mOnlineStatus->getValue().asBoolean()) + { + rights |= LLRelationship::GRANT_ONLINE_STATUS; + } + if (mMapRights->getValue().asBoolean()) + { + rights |= LLRelationship::GRANT_MAP_LOCATION; + } + if (mEditObjectRights->getValue().asBoolean()) + { + rights |= LLRelationship::GRANT_MODIFY_OBJECTS; + } + + LLAvatarPropertiesProcessor::getInstance()->sendFriendRights(mAvatarID, rights); + + closeFloater(); +} + +void LLFloaterProfilePermissions::onCancel() +{ + closeFloater(); +} + +////////////////////////////////////////////////////////////////////////// +// LLPanelProfileSecondLife + +LLPanelProfileSecondLife::LLPanelProfileSecondLife() + : LLPanelProfileTab() + , mAvatarNameCacheConnection() + , mHasUnsavedDescriptionChanges(false) + , mWaitingForImageUpload(false) + , mAllowPublish(false) { } -LLPanelProfile::ChildStack::~ChildStack() +LLPanelProfileSecondLife::~LLPanelProfileSecondLife() { - while (mStack.size() != 0) - { - view_list_t& top = mStack.back(); - for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it) - { - LLView* viewp = *it; - if (viewp) - { - viewp->die(); - } - } - mStack.pop_back(); - } + if (getAvatarId().notNull()) + { + LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); + } + + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->removeObserver((LLVoiceClientStatusObserver*)this); + } + + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } } -void LLPanelProfile::ChildStack::setParent(LLPanel* parent) +BOOL LLPanelProfileSecondLife::postBuild() { - llassert_always(parent != NULL); - mParent = parent; + mGroupList = getChild("group_list"); + mShowInSearchCombo = getChild("show_in_search"); + mSecondLifePic = getChild("2nd_life_pic"); + mSecondLifePicLayout = getChild("image_panel"); + mDescriptionEdit = getChild("sl_description_edit"); + mAgentActionMenuButton = getChild("agent_actions_menu"); + mSaveDescriptionChanges = getChild("save_description_changes"); + mDiscardDescriptionChanges = getChild("discard_description_changes"); + mCanSeeOnlineIcon = getChild("can_see_online"); + mCantSeeOnlineIcon = getChild("cant_see_online"); + mCanSeeOnMapIcon = getChild("can_see_on_map"); + mCantSeeOnMapIcon = getChild("cant_see_on_map"); + mCanEditObjectsIcon = getChild("can_edit_objects"); + mCantEditObjectsIcon = getChild("cant_edit_objects"); + + mShowInSearchCombo->setCommitCallback([this](LLUICtrl*, void*) { onShowInSearchCallback(); }, nullptr); + mGroupList->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { LLPanelProfileSecondLife::openGroupProfile(); }); + mGroupList->setReturnCallback([this](LLUICtrl*, const LLSD&) { LLPanelProfileSecondLife::openGroupProfile(); }); + mSaveDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr); + mDiscardDescriptionChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr); + mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); }); + + mCanSeeOnlineIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); }); + mCantSeeOnlineIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); }); + mCanSeeOnMapIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); }); + mCantSeeOnMapIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); }); + mCanEditObjectsIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); }); + mCantEditObjectsIcon->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentPermissionsDialog(); }); + mSecondLifePic->setMouseUpCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onShowAgentProfileTexture(); }); + + return TRUE; } -/// Save current parent's child views and remove them from the child list. -bool LLPanelProfile::ChildStack::push() +void LLPanelProfileSecondLife::onOpen(const LLSD& key) { - view_list_t vlist = *mParent->getChildList(); + LLPanelProfileTab::onOpen(key); - for (view_list_t::const_iterator it = vlist.begin(); it != vlist.end(); ++it) - { - LLView* viewp = *it; - mParent->removeChild(viewp); - } + resetData(); - mStack.push_back(vlist); - dump(); - return true; + LLUUID avatar_id = getAvatarId(); + + BOOL own_profile = getSelfProfile(); + + mGroupList->setShowNone(!own_profile); + + childSetVisible("notes_panel", !own_profile); + childSetVisible("settings_panel", own_profile); + childSetVisible("about_buttons_panel", own_profile); + + if (own_profile) + { + // Group list control cannot toggle ForAgent loading + // Less than ideal, but viewing own profile via search is edge case + mGroupList->enableForAgent(false); + } + + // Init menu, menu needs to be created in scope of a registar to work correctly. + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar commit; + commit.add("Profile.Commit", [this](LLUICtrl*, const LLSD& userdata) { onCommitMenu(userdata); }); + + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable; + enable.add("Profile.EnableItem", [this](LLUICtrl*, const LLSD& userdata) { return onEnableMenu(userdata); }); + enable.add("Profile.CheckItem", [this](LLUICtrl*, const LLSD& userdata) { return onCheckMenu(userdata); }); + + if (own_profile) + { + mAgentActionMenuButton->setMenu("menu_profile_self.xml", LLMenuButton::MP_BOTTOM_RIGHT); + } + else + { + // Todo: use PeopleContextMenu instead? + mAgentActionMenuButton->setMenu("menu_profile_other.xml", LLMenuButton::MP_BOTTOM_RIGHT); + } + + mDescriptionEdit->setParseHTML(!own_profile); + + if (!own_profile) + { + mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(avatar_id) ? LLAvatarTracker::instance().isBuddyOnline(avatar_id) : TRUE); + updateOnlineStatus(); + fillRightsData(); + } + + mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2)); } -/// Restore saved children (adding them back to the child list). -bool LLPanelProfile::ChildStack::pop() +void LLPanelProfileSecondLife::updateData() { - if (mStack.size() == 0) - { - LL_WARNS() << "Empty stack" << LL_ENDL; - llassert(mStack.size() == 0); - return false; - } + LLUUID avatar_id = getAvatarId(); + if (!getStarted() && avatar_id.notNull()) + { + setIsLoading(); - view_list_t& top = mStack.back(); - for (view_list_t::const_iterator it = top.begin(); it != top.end(); ++it) - { - LLView* viewp = *it; - mParent->addChild(viewp); - } - - mStack.pop_back(); - dump(); - return true; + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(request_avatar_properties_coro, cap_url, avatar_id)); + } + else + { + LL_WARNS() << "Failed to update profile data, no cap found" << LL_ENDL; + } + } } -/// Temporarily add all saved children back. -void LLPanelProfile::ChildStack::preParentReshape() +void LLPanelProfileSecondLife::refreshName() { - mSavedStack = mStack; - while(mStack.size() > 0) - { - pop(); - } + if (!mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCache, this, _1, _2)); + } } -/// Add the temporarily saved children back. -void LLPanelProfile::ChildStack::postParentReshape() +void LLPanelProfileSecondLife::resetData() { - mStack = mSavedStack; - mSavedStack = stack_t(); + resetLoading(); - for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it) - { - const view_list_t& vlist = (*stack_it); - for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it) - { - LLView* viewp = *list_it; - LL_DEBUGS() << "removing " << viewp->getName() << LL_ENDL; - mParent->removeChild(viewp); - } - } + // Set default image and 1:1 dimensions for it + mSecondLifePic->setValue("Generic_Person_Large"); + mImageId = LLUUID::null; + + LLRect imageRect = mSecondLifePicLayout->getRect(); + mSecondLifePicLayout->reshape(imageRect.getHeight(), imageRect.getHeight()); + + setDescriptionText(LLStringUtil::null); + mGroups.clear(); + mGroupList->setGroups(mGroups); + + bool own_profile = getSelfProfile(); + mCanSeeOnlineIcon->setVisible(false); + mCantSeeOnlineIcon->setVisible(!own_profile); + mCanSeeOnMapIcon->setVisible(false); + mCantSeeOnMapIcon->setVisible(!own_profile); + mCanEditObjectsIcon->setVisible(false); + mCantEditObjectsIcon->setVisible(!own_profile); + + mCanSeeOnlineIcon->setEnabled(false); + mCantSeeOnlineIcon->setEnabled(false); + mCanSeeOnMapIcon->setEnabled(false); + mCantSeeOnMapIcon->setEnabled(false); + mCanEditObjectsIcon->setEnabled(false); + mCantEditObjectsIcon->setEnabled(false); + + childSetVisible("partner_layout", FALSE); } -void LLPanelProfile::ChildStack::dump() +void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data) { - unsigned lvl = 0; - LL_DEBUGS() << "child stack dump:" << LL_ENDL; - for (stack_t::const_iterator stack_it = mStack.begin(); stack_it != mStack.end(); ++stack_it, ++lvl) - { - std::ostringstream dbg_line; - dbg_line << "lvl #" << lvl << ":"; - const view_list_t& vlist = (*stack_it); - for (view_list_t::const_iterator list_it = vlist.begin(); list_it != vlist.end(); ++list_it) - { - dbg_line << " " << (*list_it)->getName(); - } - LL_DEBUGS() << dbg_line.str() << LL_ENDL; - } + LLUUID avatar_id = getAvatarId(); + const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); + if ((relationship != NULL || gAgent.isGodlike()) && !getSelfProfile()) + { + // Relies onto friend observer to get information about online status updates. + // Once SL-17506 gets implemented, condition might need to become: + // (gAgent.isGodlike() || isRightGrantedFrom || flags & AVATAR_ONLINE) + processOnlineStatus(relationship != NULL, + gAgent.isGodlike() || relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS), + (avatar_data->flags & AVATAR_ONLINE)); + } + + fillCommonData(avatar_data); + + fillPartnerData(avatar_data); + + fillAccountStatus(avatar_data); + + setLoaded(); } -//-- LLPanelProfile::ChildStack ends ------------------------------------------ +void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avatar_groups) +{ + + LLAvatarGroups::group_list_t::const_iterator it = avatar_groups->group_list.begin(); + const LLAvatarGroups::group_list_t::const_iterator it_end = avatar_groups->group_list.end(); + + for (; it_end != it; ++it) + { + LLAvatarGroups::LLGroupData group_data = *it; + mGroups[group_data.group_name] = group_data.group_id; + } + + mGroupList->setGroups(mGroups); +} + +void LLPanelProfileSecondLife::openGroupProfile() +{ + LLUUID group_id = mGroupList->getSelectedUUID(); + LLGroupActions::show(group_id); +} + +void LLPanelProfileSecondLife::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + getChild("display_name")->setValue(av_name.getDisplayName()); + getChild("user_name")->setValue(av_name.getAccountName()); +} + +void LLPanelProfileSecondLife::setProfileImageUploading(bool loading) +{ + LLLoadingIndicator* indicator = getChild("image_upload_indicator"); + indicator->setVisible(loading); + if (loading) + { + indicator->start(); + } + else + { + indicator->stop(); + } + mWaitingForImageUpload = loading; +} + +void LLPanelProfileSecondLife::setProfileImageUploaded(const LLUUID &image_asset_id) +{ + mSecondLifePic->setValue(image_asset_id); + mImageId = image_asset_id; + + LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(image_asset_id); + if (imagep->getFullHeight()) + { + onImageLoaded(true, imagep); + } + else + { + imagep->setLoadedCallback(onImageLoaded, + MAX_DISCARD_LEVEL, + FALSE, + FALSE, + new LLHandle(getHandle()), + NULL, + FALSE); + } + + LLFloater *floater = mFloaterProfileTextureHandle.get(); + if (floater) + { + LLFloaterProfileTexture * texture_view = dynamic_cast(floater); + if (mImageId.notNull()) + { + texture_view->loadAsset(mImageId); + } + else + { + texture_view->resetAsset(); + } + } + + setProfileImageUploading(false); +} + +bool LLPanelProfileSecondLife::hasUnsavedChanges() +{ + LLFloater *floater = mFloaterPermissionsHandle.get(); + if (floater) + { + LLFloaterProfilePermissions* perm = dynamic_cast(floater); + if (perm && perm->hasUnsavedChanges()) + { + return true; + } + } + // if floater + return mHasUnsavedDescriptionChanges; +} + +void LLPanelProfileSecondLife::commitUnsavedChanges() +{ + LLFloater *floater = mFloaterPermissionsHandle.get(); + if (floater) + { + LLFloaterProfilePermissions* perm = dynamic_cast(floater); + if (perm && perm->hasUnsavedChanges()) + { + perm->onApplyRights(); + } + } + if (mHasUnsavedDescriptionChanges) + { + onSaveDescriptionChanges(); + } +} + +void LLPanelProfileSecondLife::fillCommonData(const LLAvatarData* avatar_data) +{ + // Refresh avatar id in cache with new info to prevent re-requests + // and to make sure icons in text will be up to date + LLAvatarIconIDCache::getInstance()->add(avatar_data->avatar_id, avatar_data->image_id); + + fillAgeData(avatar_data->born_on); + + setDescriptionText(avatar_data->about_text); + + if (avatar_data->image_id.notNull()) + { + mSecondLifePic->setValue(avatar_data->image_id); + mImageId = avatar_data->image_id; + } + else + { + mSecondLifePic->setValue("Generic_Person_Large"); + mImageId = LLUUID::null; + } + + // Will be loaded as a LLViewerFetchedTexture::BOOST_UI due to mSecondLifePic + LLViewerFetchedTexture* imagep = LLViewerTextureManager::getFetchedTexture(avatar_data->image_id); + if (imagep->getFullHeight()) + { + onImageLoaded(true, imagep); + } + else + { + imagep->setLoadedCallback(onImageLoaded, + MAX_DISCARD_LEVEL, + FALSE, + FALSE, + new LLHandle(getHandle()), + NULL, + FALSE); + } + + if (getSelfProfile()) + { + mAllowPublish = avatar_data->flags & AVATAR_ALLOW_PUBLISH; + mShowInSearchCombo->setValue((BOOL)mAllowPublish); + } +} + +void LLPanelProfileSecondLife::fillPartnerData(const LLAvatarData* avatar_data) +{ + LLTextBox* partner_text_ctrl = getChild("partner_link"); + if (avatar_data->partner_id.notNull()) + { + childSetVisible("partner_layout", TRUE); + LLStringUtil::format_map_t args; + args["[LINK]"] = LLSLURL("agent", avatar_data->partner_id, "inspect").getSLURLString(); + std::string partner_text = getString("partner_text", args); + partner_text_ctrl->setText(partner_text); + } + else + { + childSetVisible("partner_layout", FALSE); + } +} + +void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data) +{ + LLStringUtil::format_map_t args; + args["[ACCTTYPE]"] = LLAvatarPropertiesProcessor::accountType(avatar_data); + args["[PAYMENTINFO]"] = LLAvatarPropertiesProcessor::paymentInfo(avatar_data); + + std::string caption_text = getString("CaptionTextAcctInfo", args); + getChild("account_info")->setValue(caption_text); +} + +void LLPanelProfileSecondLife::fillRightsData() +{ + if (getSelfProfile()) + { + return; + } + + const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); + // If true - we are viewing friend's profile, enable check boxes and set values. + if (relation) + { + S32 rights = relation->getRightsGrantedTo(); + bool can_see_online = LLRelationship::GRANT_ONLINE_STATUS & rights; + bool can_see_on_map = LLRelationship::GRANT_MAP_LOCATION & rights; + bool can_edit_objects = LLRelationship::GRANT_MODIFY_OBJECTS & rights; + + mCanSeeOnlineIcon->setVisible(can_see_online); + mCantSeeOnlineIcon->setVisible(!can_see_online); + mCanSeeOnMapIcon->setVisible(can_see_on_map); + mCantSeeOnMapIcon->setVisible(!can_see_on_map); + mCanEditObjectsIcon->setVisible(can_edit_objects); + mCantEditObjectsIcon->setVisible(!can_edit_objects); + + mCanSeeOnlineIcon->setEnabled(true); + mCantSeeOnlineIcon->setEnabled(true); + mCanSeeOnMapIcon->setEnabled(true); + mCantSeeOnMapIcon->setEnabled(true); + mCanEditObjectsIcon->setEnabled(true); + mCantEditObjectsIcon->setEnabled(true); + } + else + { + mCanSeeOnlineIcon->setVisible(false); + mCantSeeOnlineIcon->setVisible(false); + mCanSeeOnMapIcon->setVisible(false); + mCantSeeOnMapIcon->setVisible(false); + mCanEditObjectsIcon->setVisible(false); + mCantEditObjectsIcon->setVisible(false); + } +} + +void LLPanelProfileSecondLife::fillAgeData(const LLDate &born_on) +{ + std::string name_and_date = getString("date_format"); + LLSD args_name; + args_name["datetime"] = (S32)born_on.secondsSinceEpoch(); + LLStringUtil::format(name_and_date, args_name); + getChild("sl_birth_date")->setValue(name_and_date); + + std::string register_date = getString("age_format"); + LLSD args_age; + args_age["[AGE]"] = LLDateUtil::ageFromDate(born_on, LLDate::now()); + LLStringUtil::format(register_date, args_age); + getChild("user_age")->setValue(register_date); +} + +void LLPanelProfileSecondLife::onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep) +{ + LLRect imageRect = mSecondLifePicLayout->getRect(); + if (!success || imagep->getFullWidth() == imagep->getFullHeight()) + { + mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth()); + } + else + { + // assume 3:4, for sake of firestorm + mSecondLifePicLayout->reshape(imageRect.getWidth(), imageRect.getWidth() * 3 / 4); + } +} + +//static +void LLPanelProfileSecondLife::onImageLoaded(BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata) +{ + if (!userdata) return; + + LLHandle* handle = (LLHandle*)userdata; + + if (!handle->isDead()) + { + LLPanelProfileSecondLife* panel = static_cast(handle->get()); + if (panel) + { + panel->onImageLoaded(success, src_vi); + } + } + + if (final || !success) + { + delete handle; + } +} + +// virtual, called by LLAvatarTracker +void LLPanelProfileSecondLife::changed(U32 mask) +{ + updateOnlineStatus(); + if (mask != LLFriendObserver::ONLINE) + { + fillRightsData(); + } +} + +// virtual, called by LLVoiceClient +void LLPanelProfileSecondLife::onChange(EStatusType status, const std::string &channelURI, bool proximal) +{ + if(status == STATUS_JOINING || status == STATUS_LEFT_CHANNEL) + { + return; + } + + mVoiceStatus = LLAvatarActions::canCall() && (LLAvatarActions::isFriend(getAvatarId()) ? LLAvatarTracker::instance().isBuddyOnline(getAvatarId()) : TRUE); +} + +void LLPanelProfileSecondLife::setAvatarId(const LLUUID& avatar_id) +{ + if (avatar_id.notNull()) + { + if (getAvatarId().notNull()) + { + LLAvatarTracker::instance().removeParticularFriendObserver(getAvatarId(), this); + } + + LLPanelProfileTab::setAvatarId(avatar_id); + + if (LLAvatarActions::isFriend(getAvatarId())) + { + LLAvatarTracker::instance().addParticularFriendObserver(getAvatarId(), this); + } + } +} + +// method was disabled according to EXT-2022. Re-enabled & improved according to EXT-3880 +void LLPanelProfileSecondLife::updateOnlineStatus() +{ + const LLRelationship* relationship = LLAvatarTracker::instance().getBuddyInfo(getAvatarId()); + if (relationship != NULL) + { + // For friend let check if he allowed me to see his status + bool online = relationship->isOnline(); + bool perm_granted = relationship->isRightGrantedFrom(LLRelationship::GRANT_ONLINE_STATUS); + processOnlineStatus(true, perm_granted, online); + } + else + { + childSetVisible("frind_layout", false); + childSetVisible("online_layout", false); + childSetVisible("offline_layout", false); + } +} + +void LLPanelProfileSecondLife::processOnlineStatus(bool is_friend, bool show_online, bool online) +{ + childSetVisible("frind_layout", is_friend); + childSetVisible("online_layout", online && show_online); + childSetVisible("offline_layout", !online && show_online); +} + +void LLPanelProfileSecondLife::setLoaded() +{ + LLPanelProfileTab::setLoaded(); + + if (getSelfProfile()) + { + mShowInSearchCombo->setEnabled(TRUE); + mDescriptionEdit->setEnabled(TRUE); + } +} + + + +class LLProfileImagePicker : public LLFilePickerThread +{ +public: + LLProfileImagePicker(EProfileImageType type, LLHandle *handle); + ~LLProfileImagePicker(); + void notify(const std::vector& filenames) override; + +private: + LLHandle *mHandle; + EProfileImageType mType; +}; + +LLProfileImagePicker::LLProfileImagePicker(EProfileImageType type, LLHandle *handle) + : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE), + mHandle(handle), + mType(type) +{ +} + +LLProfileImagePicker::~LLProfileImagePicker() +{ + delete mHandle; +} + +void LLProfileImagePicker::notify(const std::vector& filenames) +{ + if (mHandle->isDead()) + { + return; + } + if (filenames.empty()) + { + return; + } + std::string file_path = filenames[0]; + if (file_path.empty()) + { + return; + } + + // generate a temp texture file for coroutine + std::string temp_file = gDirUtilp->getTempFilename(); + U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path)); + const S32 MAX_DIM = 256; + if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, MAX_DIM)) + { + //todo: image not supported notification + LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", failed to open image" << LL_ENDL; + return; + } + + std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP); + if (cap_url.empty()) + { + LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)PROFILE_IMAGE_SL << ", no cap found" << LL_ENDL; + return; + } + + switch (mType) + { + case PROFILE_IMAGE_SL: + { + LLPanelProfileSecondLife* panel = static_cast(mHandle->get()); + panel->setProfileImageUploading(true); + } + break; + case PROFILE_IMAGE_FL: + { + LLPanelProfileFirstLife* panel = static_cast(mHandle->get()); + panel->setProfileImageUploading(true); + } + break; + } + + LLCoros::instance().launch("postAgentUserImageCoro", + boost::bind(post_profile_image_coro, cap_url, mType, temp_file, mHandle)); + + mHandle = nullptr; // transferred to post_profile_image_coro +} + +void LLPanelProfileSecondLife::onCommitMenu(const LLSD& userdata) +{ + const std::string item_name = userdata.asString(); + const LLUUID agent_id = getAvatarId(); + // todo: consider moving this into LLAvatarActions::onCommit(name, id) + // and making all other flaoters, like people menu do the same + if (item_name == "im") + { + LLAvatarActions::startIM(agent_id); + } + else if (item_name == "offer_teleport") + { + LLAvatarActions::offerTeleport(agent_id); + } + else if (item_name == "request_teleport") + { + LLAvatarActions::teleportRequest(agent_id); + } + else if (item_name == "voice_call") + { + LLAvatarActions::startCall(agent_id); + } + else if (item_name == "chat_history") + { + LLAvatarActions::viewChatHistory(agent_id); + } + else if (item_name == "add_friend") + { + LLAvatarActions::requestFriendshipDialog(agent_id); + } + else if (item_name == "remove_friend") + { + LLAvatarActions::removeFriendDialog(agent_id); + } + else if (item_name == "invite_to_group") + { + LLAvatarActions::inviteToGroup(agent_id); + } + else if (item_name == "can_show_on_map") + { + LLAvatarActions::showOnMap(agent_id); + } + else if (item_name == "share") + { + LLAvatarActions::share(agent_id); + } + else if (item_name == "pay") + { + LLAvatarActions::pay(agent_id); + } + else if (item_name == "toggle_block_agent") + { + LLAvatarActions::toggleBlock(agent_id); + } + else if (item_name == "copy_user_id") + { + LLWString wstr = utf8str_to_wstring(getAvatarId().asString()); + LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size()); + } + else if (item_name == "agent_permissions") + { + onShowAgentPermissionsDialog(); + } + else if (item_name == "copy_display_name" + || item_name == "copy_username") + { + LLAvatarName av_name; + if (!LLAvatarNameCache::get(getAvatarId(), &av_name)) + { + // shouldn't happen, option is supposed to be invisible while name is fetching + LL_WARNS() << "Failed to get agent data" << LL_ENDL; + return; + } + LLWString wstr; + if (item_name == "copy_display_name") + { + wstr = utf8str_to_wstring(av_name.getDisplayName(true)); + } + else if (item_name == "copy_username") + { + wstr = utf8str_to_wstring(av_name.getUserName()); + } + LLClipboard::instance().copyToClipboard(wstr, 0, wstr.size()); + } + else if (item_name == "edit_display_name") + { + LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileSecondLife::onAvatarNameCacheSetName, this, _1, _2)); + LLFirstUse::setDisplayName(false); + } + else if (item_name == "edit_partner") + { + std::string url = "https://[GRID]/my/account/partners.php"; + LLSD subs; + url = LLWeb::expandURLSubstitutions(url, subs); + LLUrlAction::openURL(url); + } + else if (item_name == "upload_photo") + { + (new LLProfileImagePicker(PROFILE_IMAGE_SL, new LLHandle(getHandle())))->getFile(); + + LLFloater* floaterp = mFloaterTexturePickerHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } + } + else if (item_name == "change_photo") + { + onShowTexturePicker(); + } + else if (item_name == "remove_photo") + { + onCommitProfileImage(LLUUID::null); + + LLFloater* floaterp = mFloaterTexturePickerHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } + } +} + +bool LLPanelProfileSecondLife::onEnableMenu(const LLSD& userdata) +{ + const std::string item_name = userdata.asString(); + const LLUUID agent_id = getAvatarId(); + if (item_name == "offer_teleport" || item_name == "request_teleport") + { + return LLAvatarActions::canOfferTeleport(agent_id); + } + else if (item_name == "voice_call") + { + return mVoiceStatus; + } + else if (item_name == "chat_history") + { + return LLLogChat::isTranscriptExist(agent_id); + } + else if (item_name == "add_friend") + { + return !LLAvatarActions::isFriend(agent_id); + } + else if (item_name == "remove_friend") + { + return LLAvatarActions::isFriend(agent_id); + } + else if (item_name == "can_show_on_map") + { + return (LLAvatarTracker::instance().isBuddyOnline(agent_id) && is_agent_mappable(agent_id)) + || gAgent.isGodlike(); + } + else if (item_name == "toggle_block_agent") + { + return LLAvatarActions::canBlock(agent_id); + } + else if (item_name == "agent_permissions") + { + return LLAvatarActions::isFriend(agent_id); + } + else if (item_name == "copy_display_name" + || item_name == "copy_username") + { + return !mAvatarNameCacheConnection.connected(); + } + else if (item_name == "upload_photo" + || item_name == "change_photo") + { + std::string cap_url = gAgent.getRegionCapability(PROFILE_IMAGE_UPLOAD_CAP); + return !cap_url.empty() && !mWaitingForImageUpload && getIsLoaded(); + } + else if (item_name == "remove_photo") + { + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + return mImageId.notNull() && !cap_url.empty() && !mWaitingForImageUpload && getIsLoaded(); + } + + return false; +} + +bool LLPanelProfileSecondLife::onCheckMenu(const LLSD& userdata) +{ + const std::string item_name = userdata.asString(); + const LLUUID agent_id = getAvatarId(); + if (item_name == "toggle_block_agent") + { + return LLAvatarActions::isBlocked(agent_id); + } + return false; +} + +void LLPanelProfileSecondLife::onAvatarNameCacheSetName(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + if (av_name.getDisplayName().empty()) + { + // something is wrong, tell user to try again later + LLNotificationsUtil::add("SetDisplayNameFailedGeneric"); + return; + } + + LL_INFOS("LegacyProfile") << "name-change now " << LLDate::now() << " next_update " + << LLDate(av_name.mNextUpdate) << LL_ENDL; + F64 now_secs = LLDate::now().secondsSinceEpoch(); + + if (now_secs < av_name.mNextUpdate) + { + // if the update time is more than a year in the future, it means updates have been blocked + // show a more general message + static const S32 YEAR = 60*60*24*365; + if (now_secs + YEAR < av_name.mNextUpdate) + { + LLNotificationsUtil::add("SetDisplayNameBlocked"); + return; + } + } + + LLFloaterReg::showInstance("display_name"); +} + +void LLPanelProfileSecondLife::setDescriptionText(const std::string &text) +{ + mSaveDescriptionChanges->setEnabled(FALSE); + mDiscardDescriptionChanges->setEnabled(FALSE); + mHasUnsavedDescriptionChanges = false; + + mDescriptionText = text; + mDescriptionEdit->setValue(mDescriptionText); +} + +void LLPanelProfileSecondLife::onSetDescriptionDirty() +{ + mSaveDescriptionChanges->setEnabled(TRUE); + mDiscardDescriptionChanges->setEnabled(TRUE); + mHasUnsavedDescriptionChanges = true; +} + +void LLPanelProfileSecondLife::onShowInSearchCallback() +{ + S32 value = mShowInSearchCombo->getValue().asInteger(); + if (mAllowPublish == (bool)value) + { + return; + } + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + mAllowPublish = value; + LLSD data; + data["allow_publish"] = mAllowPublish; + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), data)); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } +} + +void LLPanelProfileSecondLife::onSaveDescriptionChanges() +{ + mDescriptionText = mDescriptionEdit->getValue().asString(); + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("sl_about_text", mDescriptionText))); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } + + mSaveDescriptionChanges->setEnabled(FALSE); + mDiscardDescriptionChanges->setEnabled(FALSE); + mHasUnsavedDescriptionChanges = false; +} + +void LLPanelProfileSecondLife::onDiscardDescriptionChanges() +{ + setDescriptionText(mDescriptionText); +} + +void LLPanelProfileSecondLife::onShowAgentPermissionsDialog() +{ + LLFloater *floater = mFloaterPermissionsHandle.get(); + if (!floater) + { + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + if (parent_floater) + { + LLFloaterProfilePermissions * perms = new LLFloaterProfilePermissions(parent_floater, getAvatarId()); + mFloaterPermissionsHandle = perms->getHandle(); + perms->openFloater(); + perms->setVisibleAndFrontmost(TRUE); + + parent_floater->addDependentFloater(mFloaterPermissionsHandle); + } + } + else // already open + { + floater->setMinimized(FALSE); + floater->setVisibleAndFrontmost(TRUE); + } +} + +void LLPanelProfileSecondLife::onShowAgentProfileTexture() +{ + if (!getIsLoaded()) + { + return; + } + + LLFloater *floater = mFloaterProfileTextureHandle.get(); + if (!floater) + { + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + if (parent_floater) + { + LLFloaterProfileTexture * texture_view = new LLFloaterProfileTexture(parent_floater); + mFloaterProfileTextureHandle = texture_view->getHandle(); + if (mImageId.notNull()) + { + texture_view->loadAsset(mImageId); + } + else + { + texture_view->resetAsset(); + } + texture_view->openFloater(); + texture_view->setVisibleAndFrontmost(TRUE); + + parent_floater->addDependentFloater(mFloaterProfileTextureHandle); + } + } + else // already open + { + LLFloaterProfileTexture * texture_view = dynamic_cast(floater); + texture_view->setMinimized(FALSE); + texture_view->setVisibleAndFrontmost(TRUE); + if (mImageId.notNull()) + { + texture_view->loadAsset(mImageId); + } + else + { + texture_view->resetAsset(); + } + } +} + +void LLPanelProfileSecondLife::onShowTexturePicker() +{ + LLFloater* floaterp = mFloaterTexturePickerHandle.get(); + + // Show the dialog + if (!floaterp) + { + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + if (parent_floater) + { + // because inventory construction is somewhat slow + getWindow()->setCursor(UI_CURSOR_WAIT); + LLFloaterTexturePicker* texture_floaterp = new LLFloaterTexturePicker( + this, + mImageId, + LLUUID::null, + mImageId, + FALSE, + FALSE, + "SELECT PHOTO", + PERM_NONE, + PERM_NONE, + PERM_NONE, + FALSE, + NULL); + + mFloaterTexturePickerHandle = texture_floaterp->getHandle(); + + texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id) + { + if (op == LLTextureCtrl::TEXTURE_SELECT) + { + LLUUID image_asset_id; + LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterTexturePickerHandle.get(); + if (floaterp) + { + if (id.notNull()) + { + image_asset_id = id; + } + else + { + image_asset_id = floaterp->getAssetID(); + } + } + + onCommitProfileImage(image_asset_id); + } + }); + texture_floaterp->setLocalTextureEnabled(FALSE); + texture_floaterp->setBakeTextureEnabled(FALSE); + texture_floaterp->setCanApply(false, true); + + parent_floater->addDependentFloater(mFloaterTexturePickerHandle); + + texture_floaterp->openFloater(); + texture_floaterp->setFocus(TRUE); + } + } + else + { + floaterp->setMinimized(FALSE); + floaterp->setVisibleAndFrontmost(TRUE); + } +} + +void LLPanelProfileSecondLife::onCommitProfileImage(const LLUUID& id) +{ + if (mImageId == id) + { + return; + } + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLSD params; + params["sl_image_id"] = id; + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); + + mImageId = id; + if (mImageId == LLUUID::null) + { + mSecondLifePic->setValue("Generic_Person_Large"); + } + else + { + mSecondLifePic->setValue(mImageId); + } + + LLFloater *floater = mFloaterProfileTextureHandle.get(); + if (floater) + { + LLFloaterProfileTexture * texture_view = dynamic_cast(floater); + if (mImageId == LLUUID::null) + { + texture_view->resetAsset(); + } + else + { + texture_view->loadAsset(mImageId); + } + } + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } +} + +////////////////////////////////////////////////////////////////////////// +// LLPanelProfileWeb + +LLPanelProfileWeb::LLPanelProfileWeb() + : LLPanelProfileTab() + , mWebBrowser(NULL) + , mAvatarNameCacheConnection() +{ +} + +LLPanelProfileWeb::~LLPanelProfileWeb() +{ + if (mAvatarNameCacheConnection.connected()) + { + mAvatarNameCacheConnection.disconnect(); + } +} + +void LLPanelProfileWeb::onOpen(const LLSD& key) +{ + LLPanelProfileTab::onOpen(key); + + resetData(); + + mAvatarNameCacheConnection = LLAvatarNameCache::get(getAvatarId(), boost::bind(&LLPanelProfileWeb::onAvatarNameCache, this, _1, _2)); +} + +BOOL LLPanelProfileWeb::postBuild() +{ + mWebBrowser = getChild("profile_html"); + mWebBrowser->addObserver(this); + mWebBrowser->setHomePageUrl("about:blank"); + + return TRUE; +} + +void LLPanelProfileWeb::resetData() +{ + mWebBrowser->navigateHome(); +} + +void LLPanelProfileWeb::updateData() +{ + LLUUID avatar_id = getAvatarId(); + if (!getStarted() && avatar_id.notNull() && !mURLWebProfile.empty()) + { + setIsLoading(); + + mWebBrowser->setVisible(TRUE); + mPerformanceTimer.start(); + mWebBrowser->navigateTo(mURLWebProfile, HTTP_CONTENT_TEXT_HTML); + } +} + +void LLPanelProfileWeb::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name) +{ + mAvatarNameCacheConnection.disconnect(); + + std::string username = av_name.getAccountName(); + if (username.empty()) + { + username = LLCacheName::buildUsername(av_name.getDisplayName()); + } + else + { + LLStringUtil::replaceChar(username, ' ', '.'); + } + + mURLWebProfile = getProfileURL(username, true); + if (mURLWebProfile.empty()) + { + return; + } + + //if the tab was opened before name was resolved, load the panel now + updateData(); +} + +void LLPanelProfileWeb::onCommitLoad(LLUICtrl* ctrl) +{ + if (!mURLHome.empty()) + { + LLSD::String valstr = ctrl->getValue().asString(); + if (valstr.empty()) + { + mWebBrowser->setVisible(TRUE); + mPerformanceTimer.start(); + mWebBrowser->navigateTo( mURLHome, HTTP_CONTENT_TEXT_HTML ); + } + else if (valstr == "popout") + { + // open in viewer's browser, new window + LLWeb::loadURLInternal(mURLHome); + } + else if (valstr == "external") + { + // open in external browser + LLWeb::loadURLExternal(mURLHome); + } + } +} + +void LLPanelProfileWeb::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + switch(event) + { + case MEDIA_EVENT_STATUS_TEXT_CHANGED: + childSetValue("status_text", LLSD( self->getStatusText() ) ); + break; + + case MEDIA_EVENT_NAVIGATE_BEGIN: + { + if (mFirstNavigate) + { + mFirstNavigate = false; + } + else + { + mPerformanceTimer.start(); + } + } + break; + + case MEDIA_EVENT_NAVIGATE_COMPLETE: + { + LLStringUtil::format_map_t args; + args["[TIME]"] = llformat("%.2f", mPerformanceTimer.getElapsedTimeF32()); + childSetValue("status_text", LLSD( getString("LoadTime", args)) ); + } + break; + + default: + // Having a default case makes the compiler happy. + break; + } +} + + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +LLPanelProfileFirstLife::LLPanelProfileFirstLife() + : LLPanelProfileTab() + , mHasUnsavedChanges(false) +{ +} + +LLPanelProfileFirstLife::~LLPanelProfileFirstLife() +{ +} + +BOOL LLPanelProfileFirstLife::postBuild() +{ + mDescriptionEdit = getChild("fl_description_edit"); + mPicture = getChild("real_world_pic"); + + mUploadPhoto = getChild("fl_upload_image"); + mChangePhoto = getChild("fl_change_image"); + mRemovePhoto = getChild("fl_remove_image"); + mSaveChanges = getChild("fl_save_changes"); + mDiscardChanges = getChild("fl_discard_changes"); + + mUploadPhoto->setCommitCallback([this](LLUICtrl*, void*) { onUploadPhoto(); }, nullptr); + mChangePhoto->setCommitCallback([this](LLUICtrl*, void*) { onChangePhoto(); }, nullptr); + mRemovePhoto->setCommitCallback([this](LLUICtrl*, void*) { onRemovePhoto(); }, nullptr); + mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveDescriptionChanges(); }, nullptr); + mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardDescriptionChanges(); }, nullptr); + mDescriptionEdit->setKeystrokeCallback([this](LLTextEditor* caller) { onSetDescriptionDirty(); }); + + return TRUE; +} + +void LLPanelProfileFirstLife::onOpen(const LLSD& key) +{ + LLPanelProfileTab::onOpen(key); + + if (!getSelfProfile()) + { + // Otherwise as the only focusable element it will be selected + mDescriptionEdit->setTabStop(FALSE); + } + + resetData(); +} + +void LLPanelProfileFirstLife::setProfileImageUploading(bool loading) +{ + mUploadPhoto->setEnabled(!loading); + mChangePhoto->setEnabled(!loading); + mRemovePhoto->setEnabled(!loading && mImageId.notNull()); + + LLLoadingIndicator* indicator = getChild("image_upload_indicator"); + indicator->setVisible(loading); + if (loading) + { + indicator->start(); + } + else + { + indicator->stop(); + } +} + +void LLPanelProfileFirstLife::setProfileImageUploaded(const LLUUID &image_asset_id) +{ + mPicture->setValue(image_asset_id); + mImageId = image_asset_id; + setProfileImageUploading(false); +} + +void LLPanelProfileFirstLife::commitUnsavedChanges() +{ + if (mHasUnsavedChanges) + { + onSaveDescriptionChanges(); + } +} + +void LLPanelProfileFirstLife::onUploadPhoto() +{ + (new LLProfileImagePicker(PROFILE_IMAGE_FL, new LLHandle(getHandle())))->getFile(); + + LLFloater* floaterp = mFloaterTexturePickerHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } +} + +void LLPanelProfileFirstLife::onChangePhoto() +{ + LLFloater* floaterp = mFloaterTexturePickerHandle.get(); + + // Show the dialog + if (!floaterp) + { + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + if (parent_floater) + { + // because inventory construction is somewhat slow + getWindow()->setCursor(UI_CURSOR_WAIT); + LLFloaterTexturePicker* texture_floaterp = new LLFloaterTexturePicker( + this, + mImageId, + LLUUID::null, + mImageId, + FALSE, + FALSE, + "SELECT PHOTO", + PERM_NONE, + PERM_NONE, + PERM_NONE, + FALSE, + NULL); + + mFloaterTexturePickerHandle = texture_floaterp->getHandle(); + + texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id) + { + if (op == LLTextureCtrl::TEXTURE_SELECT) + { + LLUUID image_asset_id; + LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterTexturePickerHandle.get(); + if (floaterp) + { + if (id.notNull()) + { + image_asset_id = id; + } + else + { + image_asset_id = floaterp->getAssetID(); + } + } + + onCommitPhoto(image_asset_id); + } + }); + texture_floaterp->setLocalTextureEnabled(FALSE); + texture_floaterp->setCanApply(false, true); + + parent_floater->addDependentFloater(mFloaterTexturePickerHandle); + + texture_floaterp->openFloater(); + texture_floaterp->setFocus(TRUE); + } + } + else + { + floaterp->setMinimized(FALSE); + floaterp->setVisibleAndFrontmost(TRUE); + } +} + +void LLPanelProfileFirstLife::onRemovePhoto() +{ + onCommitPhoto(LLUUID::null); + + LLFloater* floaterp = mFloaterTexturePickerHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } +} + +void LLPanelProfileFirstLife::onCommitPhoto(const LLUUID& id) +{ + if (mImageId == id) + { + return; + } + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLSD params; + params["fl_image_id"] = id; + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), params)); + + mImageId = id; + if (mImageId.notNull()) + { + mPicture->setValue(mImageId); + } + else + { + mPicture->setValue("Generic_Person_Large"); + } + + mRemovePhoto->setEnabled(mImageId.notNull()); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } +} + +void LLPanelProfileFirstLife::setDescriptionText(const std::string &text) +{ + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); + mHasUnsavedChanges = false; + + mCurrentDescription = text; + mDescriptionEdit->setValue(mCurrentDescription); +} + +void LLPanelProfileFirstLife::onSetDescriptionDirty() +{ + mSaveChanges->setEnabled(TRUE); + mDiscardChanges->setEnabled(TRUE); + mHasUnsavedChanges = true; +} + +void LLPanelProfileFirstLife::onSaveDescriptionChanges() +{ + mCurrentDescription = mDescriptionEdit->getValue().asString(); + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("fl_about_text", mCurrentDescription))); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } + + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); + mHasUnsavedChanges = false; +} + +void LLPanelProfileFirstLife::onDiscardDescriptionChanges() +{ + setDescriptionText(mCurrentDescription); +} + +void LLPanelProfileFirstLife::processProperties(const LLAvatarData* avatar_data) +{ + setDescriptionText(avatar_data->fl_about_text); + + mImageId = avatar_data->fl_image_id; + + if (mImageId.notNull()) + { + mPicture->setValue(mImageId); + } + else + { + mPicture->setValue("Generic_Person_Large"); + } + + setLoaded(); +} + +void LLPanelProfileFirstLife::resetData() +{ + setDescriptionText(std::string()); + mPicture->setValue("Generic_Person_Large"); + mImageId = LLUUID::null; + + mUploadPhoto->setVisible(getSelfProfile()); + mChangePhoto->setVisible(getSelfProfile()); + mRemovePhoto->setVisible(getSelfProfile()); + mSaveChanges->setVisible(getSelfProfile()); + mDiscardChanges->setVisible(getSelfProfile()); +} + +void LLPanelProfileFirstLife::setLoaded() +{ + LLPanelProfileTab::setLoaded(); + + if (getSelfProfile()) + { + mDescriptionEdit->setEnabled(TRUE); + mPicture->setEnabled(TRUE); + mRemovePhoto->setEnabled(mImageId.notNull()); + } +} + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +LLPanelProfileNotes::LLPanelProfileNotes() +: LLPanelProfileTab() + , mHasUnsavedChanges(false) +{ + +} + +LLPanelProfileNotes::~LLPanelProfileNotes() +{ +} + +void LLPanelProfileNotes::updateData() +{ + LLUUID avatar_id = getAvatarId(); + if (!getStarted() && avatar_id.notNull()) + { + setIsLoading(); + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(request_avatar_properties_coro, cap_url, avatar_id)); + } + } +} + +void LLPanelProfileNotes::commitUnsavedChanges() +{ + if (mHasUnsavedChanges) + { + onSaveNotesChanges(); + } +} + +BOOL LLPanelProfileNotes::postBuild() +{ + mNotesEditor = getChild("notes_edit"); + mSaveChanges = getChild("notes_save_changes"); + mDiscardChanges = getChild("notes_discard_changes"); + + mSaveChanges->setCommitCallback([this](LLUICtrl*, void*) { onSaveNotesChanges(); }, nullptr); + mDiscardChanges->setCommitCallback([this](LLUICtrl*, void*) { onDiscardNotesChanges(); }, nullptr); + mNotesEditor->setKeystrokeCallback([this](LLTextEditor* caller) { onSetNotesDirty(); }); + + return TRUE; +} + +void LLPanelProfileNotes::onOpen(const LLSD& key) +{ + LLPanelProfileTab::onOpen(key); + + resetData(); +} + +void LLPanelProfileNotes::setNotesText(const std::string &text) +{ + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); + mHasUnsavedChanges = false; + + mCurrentNotes = text; + mNotesEditor->setValue(mCurrentNotes); +} + +void LLPanelProfileNotes::onSetNotesDirty() +{ + mSaveChanges->setEnabled(TRUE); + mDiscardChanges->setEnabled(TRUE); + mHasUnsavedChanges = true; +} + +void LLPanelProfileNotes::onSaveNotesChanges() +{ + mCurrentNotes = mNotesEditor->getValue().asString(); + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("putAgentUserInfoCoro", + boost::bind(put_avatar_properties_coro, cap_url, getAvatarId(), LLSD().with("notes", mCurrentNotes))); + } + else + { + LL_WARNS("AvatarProperties") << "Failed to update profile data, no cap found" << LL_ENDL; + } + + mSaveChanges->setEnabled(FALSE); + mDiscardChanges->setEnabled(FALSE); + mHasUnsavedChanges = false; +} + +void LLPanelProfileNotes::onDiscardNotesChanges() +{ + setNotesText(mCurrentNotes); +} + +void LLPanelProfileNotes::processProperties(LLAvatarNotes* avatar_notes) +{ + setNotesText(avatar_notes->notes); + mNotesEditor->setEnabled(TRUE); + setLoaded(); +} + +void LLPanelProfileNotes::resetData() +{ + resetLoading(); + setNotesText(std::string()); +} + +void LLPanelProfileNotes::setAvatarId(const LLUUID& avatar_id) +{ + if (avatar_id.notNull()) + { + LLPanelProfileTab::setAvatarId(avatar_id); + } +} + + +////////////////////////////////////////////////////////////////////////// +// LLPanelProfile LLPanelProfile::LLPanelProfile() - : LLPanel() - , mAvatarId(LLUUID::null) + : LLPanelProfileTab() +{ +} + +LLPanelProfile::~LLPanelProfile() { - mChildStack.setParent(this); } BOOL LLPanelProfile::postBuild() { - LLPanelPicks* panel_picks = findChild(PANEL_PICKS); - panel_picks->setProfilePanel(this); - - getTabContainer()[PANEL_PICKS] = panel_picks; - - return TRUE; + return TRUE; } -// virtual -void LLPanelProfile::reshape(S32 width, S32 height, BOOL called_from_parent) +void LLPanelProfile::onTabChange() { - // Temporarily add saved children back and reshape them. - mChildStack.preParentReshape(); - LLPanel::reshape(width, height, called_from_parent); - mChildStack.postParentReshape(); + LLPanelProfileTab* active_panel = dynamic_cast(mTabContainer->getCurrentPanel()); + if (active_panel) + { + active_panel->updateData(); + } } void LLPanelProfile::onOpen(const LLSD& key) { - getTabContainer()[PANEL_PICKS]->onOpen(getAvatarId()); + LLUUID avatar_id = key["id"].asUUID(); - // support commands to open further pieces of UI - if (key.has("show_tab_panel")) - { - std::string panel = key["show_tab_panel"].asString(); - if (panel == "create_classified") - { - LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); - if (picks) - { - picks->createNewClassified(); - } - } - else if (panel == "classified_details") - { - LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); - if (picks) - { - LLSD params = key; - params.erase("show_tab_panel"); - params.erase("open_tab_name"); - picks->openClassifiedInfo(params); - } - } - else if (panel == "edit_classified") - { - LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); - if (picks) - { - LLSD params = key; - params.erase("show_tab_panel"); - params.erase("open_tab_name"); - picks->openClassifiedEdit(params); - } - } - else if (panel == "create_pick") - { - LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); - if (picks) - { - picks->createNewPick(); - } - } - else if (panel == "edit_pick") - { - LLPanelPicks* picks = dynamic_cast(getTabContainer()[PANEL_PICKS]); - if (picks) - { - LLSD params = key; - params.erase("show_tab_panel"); - params.erase("open_tab_name"); - picks->openPickEdit(params); - } - } - } + // Don't reload the same profile + if (getAvatarId() == avatar_id) + { + return; + } + + LLPanelProfileTab::onOpen(avatar_id); + + mTabContainer = getChild("panel_profile_tabs"); + mPanelSecondlife = findChild(PANEL_SECONDLIFE); + mPanelWeb = findChild(PANEL_WEB); + mPanelPicks = findChild(PANEL_PICKS); + mPanelClassifieds = findChild(PANEL_CLASSIFIEDS); + mPanelFirstlife = findChild(PANEL_FIRSTLIFE); + mPanelNotes = findChild(PANEL_NOTES); + + mPanelSecondlife->onOpen(avatar_id); + mPanelWeb->onOpen(avatar_id); + mPanelPicks->onOpen(avatar_id); + mPanelClassifieds->onOpen(avatar_id); + mPanelFirstlife->onOpen(avatar_id); + mPanelNotes->onOpen(avatar_id); + + // Always request the base profile info + resetLoading(); + updateData(); + + // Some tabs only request data when opened + mTabContainer->setCommitCallback(boost::bind(&LLPanelProfile::onTabChange, this)); } -void LLPanelProfile::onTabSelected(const LLSD& param) +void LLPanelProfile::updateData() { - std::string tab_name = param.asString(); - if (NULL != getTabContainer()[tab_name]) - { - getTabContainer()[tab_name]->onOpen(getAvatarId()); - } + LLUUID avatar_id = getAvatarId(); + // Todo: getIsloading functionality needs to be expanded to + // include 'inited' or 'data_provided' state to not rerequest + if (!getStarted() && avatar_id.notNull()) + { + setIsLoading(); + + mPanelSecondlife->setIsLoading(); + mPanelPicks->setIsLoading(); + mPanelFirstlife->setIsLoading(); + mPanelNotes->setIsLoading(); + + std::string cap_url = gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP); + if (!cap_url.empty()) + { + LLCoros::instance().launch("requestAgentUserInfoCoro", + boost::bind(request_avatar_properties_coro, cap_url, avatar_id)); + } + } } -void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params) +void LLPanelProfile::refreshName() { - // Hide currently visible panel (STORM-690). - mChildStack.push(); - - // Add the panel or bring it to front. - if (panel->getParent() != this) - { - addChild(panel); - } - else - { - sendChildToFront(panel); - } - - panel->setVisible(TRUE); - panel->setFocus(TRUE); // prevent losing focus by the floater - panel->onOpen(params); - - LLRect new_rect = getRect(); - panel->reshape(new_rect.getWidth(), new_rect.getHeight()); - new_rect.setLeftTopAndSize(0, new_rect.getHeight(), new_rect.getWidth(), new_rect.getHeight()); - panel->setRect(new_rect); + mPanelSecondlife->refreshName(); } -void LLPanelProfile::closePanel(LLPanel* panel) +void LLPanelProfile::createPick(const LLPickData &data) { - panel->setVisible(FALSE); - - if (panel->getParent() == this) - { - removeChild(panel); - - // Make the underlying panel visible. - mChildStack.pop(); - - // Prevent losing focus by the floater - const child_list_t* child_list = getChildList(); - if (child_list->size() > 0) - { - child_list->front()->setFocus(TRUE); - } - else - { - LL_WARNS() << "No underlying panel to focus." << LL_ENDL; - } - } + mTabContainer->selectTabPanel(mPanelPicks); + mPanelPicks->createPick(data); } -S32 LLPanelProfile::notifyParent(const LLSD& info) +void LLPanelProfile::showPick(const LLUUID& pick_id) { - std::string action = info["action"]; - // lets update Picks list after Pick was saved - if("save_new_pick" == action) - { - onOpen(info); - return 1; - } - - return LLPanel::notifyParent(info); + if (pick_id.notNull()) + { + mPanelPicks->selectPick(pick_id); + } + mTabContainer->selectTabPanel(mPanelPicks); } + +bool LLPanelProfile::isPickTabSelected() +{ + return (mTabContainer->getCurrentPanel() == mPanelPicks); +} + +bool LLPanelProfile::isNotesTabSelected() +{ + return (mTabContainer->getCurrentPanel() == mPanelNotes); +} + +bool LLPanelProfile::hasUnsavedChanges() +{ + return mPanelSecondlife->hasUnsavedChanges() + || mPanelPicks->hasUnsavedChanges() + || mPanelClassifieds->hasUnsavedChanges() + || mPanelFirstlife->hasUnsavedChanges() + || mPanelNotes->hasUnsavedChanges(); +} + +bool LLPanelProfile::hasUnpublishedClassifieds() +{ + return mPanelClassifieds->hasNewClassifieds(); +} + +void LLPanelProfile::commitUnsavedChanges() +{ + mPanelSecondlife->commitUnsavedChanges(); + mPanelPicks->commitUnsavedChanges(); + mPanelClassifieds->commitUnsavedChanges(); + mPanelFirstlife->commitUnsavedChanges(); + mPanelNotes->commitUnsavedChanges(); +} + +void LLPanelProfile::showClassified(const LLUUID& classified_id, bool edit) +{ + if (classified_id.notNull()) + { + mPanelClassifieds->selectClassified(classified_id, edit); + } + mTabContainer->selectTabPanel(mPanelClassifieds); +} + +void LLPanelProfile::createClassified() +{ + mPanelClassifieds->createClassified(); + mTabContainer->selectTabPanel(mPanelClassifieds); +} + diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index d97f60ed22..d32bb943bd 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -1,25 +1,25 @@ -/** +/** * @file llpanelprofile.h * @brief Profile panel * -* $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$ */ @@ -27,76 +27,383 @@ #ifndef LL_LLPANELPROFILE_H #define LL_LLPANELPROFILE_H +#include "llavatarpropertiesprocessor.h" +#include "llcallingcard.h" +#include "llfloater.h" #include "llpanel.h" #include "llpanelavatar.h" +#include "llmediactrl.h" +#include "llvoiceclient.h" +// class LLPanelProfileClassifieds; +// class LLTabContainer; + +// class LLPanelProfileSecondLife; +// class LLPanelProfileWeb; +// class LLPanelProfilePicks; +// class LLPanelProfileFirstLife; +// class LLPanelProfileNotes; + +class LLAvatarName; +class LLButton; +class LLCheckBoxCtrl; +class LLComboBox; +class LLIconCtrl; class LLTabContainer; +class LLTextBox; +class LLTextureCtrl; +class LLMediaCtrl; +class LLGroupList; +class LLTextBase; +class LLMenuButton; +class LLLineEditor; +class LLTextEditor; +class LLPanelProfileClassifieds; +class LLPanelProfilePicks; +class LLViewerFetchedTexture; -std::string getProfileURL(const std::string& agent_name); /** -* Base class for Profile View and My Profile. +* Panel for displaying Avatar's second life related info. */ -class LLPanelProfile : public LLPanel +class LLPanelProfileSecondLife + : public LLPanelProfileTab + , public LLFriendObserver + , public LLVoiceClientStatusObserver { - LOG_CLASS(LLPanelProfile); - public: - /*virtual*/ BOOL postBuild(); - /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - /*virtual*/ void onOpen(const LLSD& key); + LLPanelProfileSecondLife(); + /*virtual*/ ~LLPanelProfileSecondLife(); - virtual void openPanel(LLPanel* panel, const LLSD& params); + void onOpen(const LLSD& key) override; - virtual void closePanel(LLPanel* panel); + /** + * LLFriendObserver trigger + */ + void changed(U32 mask) override; - S32 notifyParent(const LLSD& info); + // Implements LLVoiceClientStatusObserver::onChange() to enable the call + // button when voice is available + void onChange(EStatusType status, const std::string &channelURI, bool proximal) override; + + void setAvatarId(const LLUUID& avatar_id) override; + + BOOL postBuild() override; + + void resetData() override; + + /** + * Sends update data request to server. + */ + void updateData() override; + void refreshName(); + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + + void setProfileImageUploading(bool loading); + void setProfileImageUploaded(const LLUUID &image_asset_id); + + bool hasUnsavedChanges() override; + void commitUnsavedChanges() override; + + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); protected: + /** + * Process profile related data received from server. + */ + void processProfileProperties(const LLAvatarData* avatar_data); - LLPanelProfile(); + /** + * Processes group related data received from server. + */ + void processGroupProperties(const LLAvatarGroups* avatar_groups); - virtual void onTabSelected(const LLSD& param); + /** + * Fills common for Avatar profile and My Profile fields. + */ + void fillCommonData(const LLAvatarData* avatar_data); - const LLUUID& getAvatarId() { return mAvatarId; } + /** + * Fills partner data. + */ + void fillPartnerData(const LLAvatarData* avatar_data); - void setAvatarId(const LLUUID& avatar_id) { mAvatarId = avatar_id; } + /** + * Fills account status. + */ + void fillAccountStatus(const LLAvatarData* avatar_data); - typedef std::map profile_tabs_t; + /** + * Sets permissions specific icon + */ + void fillRightsData(); - profile_tabs_t& getTabContainer() { return mTabContainer; } + /** + * Fills user name, display name, age. + */ + void fillAgeData(const LLDate &born_on); + + void onImageLoaded(BOOL success, LLViewerFetchedTexture *imagep); + static void onImageLoaded(BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata); + + /** + * Displays avatar's online status if possible. + * + * Requirements from EXT-3880: + * For friends: + * - Online when online and privacy settings allow to show + * - Offline when offline and privacy settings allow to show + * - Else: nothing + * For other avatars: + * - Online when online and was not set in Preferences/"Only Friends & Groups can see when I am online" + * - Else: Offline + */ + void updateOnlineStatus(); + void processOnlineStatus(bool is_friend, bool show_online, bool online); private: + void setLoaded() override; + void onCommitMenu(const LLSD& userdata); + bool onEnableMenu(const LLSD& userdata); + bool onCheckMenu(const LLSD& userdata); + void onAvatarNameCacheSetName(const LLUUID& id, const LLAvatarName& av_name); - //-- ChildStack begins ---------------------------------------------------- - class ChildStack - { - LOG_CLASS(LLPanelProfile::ChildStack); - public: - ChildStack(); - ~ChildStack(); - void setParent(LLPanel* parent); + void setDescriptionText(const std::string &text); + void onSetDescriptionDirty(); + void onShowInSearchCallback(); + void onSaveDescriptionChanges(); + void onDiscardDescriptionChanges(); + void onShowAgentPermissionsDialog(); + void onShowAgentProfileTexture(); + void onShowTexturePicker(); + void onCommitProfileImage(const LLUUID& id); - bool push(); - bool pop(); - void preParentReshape(); - void postParentReshape(); +private: + typedef std::map group_map_t; + group_map_t mGroups; + void openGroupProfile(); - private: - void dump(); + LLGroupList* mGroupList; + LLComboBox* mShowInSearchCombo; + LLIconCtrl* mSecondLifePic; + LLPanel* mSecondLifePicLayout; + LLTextEditor* mDescriptionEdit; + LLMenuButton* mAgentActionMenuButton; + LLButton* mSaveDescriptionChanges; + LLButton* mDiscardDescriptionChanges; + LLIconCtrl* mCanSeeOnlineIcon; + LLIconCtrl* mCantSeeOnlineIcon; + LLIconCtrl* mCanSeeOnMapIcon; + LLIconCtrl* mCantSeeOnMapIcon; + LLIconCtrl* mCanEditObjectsIcon; + LLIconCtrl* mCantEditObjectsIcon; - typedef LLView::child_list_t view_list_t; - typedef std::list stack_t; + LLHandle mFloaterPermissionsHandle; + LLHandle mFloaterProfileTextureHandle; + LLHandle mFloaterTexturePickerHandle; - stack_t mStack; - stack_t mSavedStack; - LLPanel* mParent; - }; - //-- ChildStack ends ------------------------------------------------------ + bool mHasUnsavedDescriptionChanges; + bool mVoiceStatus; + bool mWaitingForImageUpload; + bool mAllowPublish; + std::string mDescriptionText; + LLUUID mImageId; - profile_tabs_t mTabContainer; - ChildStack mChildStack; - LLUUID mAvatarId; + boost::signals2::connection mAvatarNameCacheConnection; +}; + + +/** +* Panel for displaying Avatar's web profile and home page. +*/ +class LLPanelProfileWeb + : public LLPanelProfileTab + , public LLViewerMediaObserver +{ +public: + LLPanelProfileWeb(); + /*virtual*/ ~LLPanelProfileWeb(); + + void onOpen(const LLSD& key) override; + + BOOL postBuild() override; + + void resetData() override; + + /** + * Loads web profile. + */ + void updateData() override; + + void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) override; + + void onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name); + + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + +protected: + void onCommitLoad(LLUICtrl* ctrl); + +private: + std::string mURLHome; + std::string mURLWebProfile; + LLMediaCtrl* mWebBrowser; + + LLFrameTimer mPerformanceTimer; + bool mFirstNavigate; + + boost::signals2::connection mAvatarNameCacheConnection; +}; + +/** +* Panel for displaying Avatar's first life related info. +*/ +class LLPanelProfileFirstLife + : public LLPanelProfileTab +{ +public: + LLPanelProfileFirstLife(); + /*virtual*/ ~LLPanelProfileFirstLife(); + + void onOpen(const LLSD& key) override; + + BOOL postBuild() override; + + void processProperties(const LLAvatarData* avatar_data); + + void resetData() override; + + void setProfileImageUploading(bool loading); + void setProfileImageUploaded(const LLUUID &image_asset_id); + + bool hasUnsavedChanges() override { return mHasUnsavedChanges; } + void commitUnsavedChanges() override; + + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + +protected: + void setLoaded() override; + + void onUploadPhoto(); + void onChangePhoto(); + void onRemovePhoto(); + void onCommitPhoto(const LLUUID& id); + void setDescriptionText(const std::string &text); + void onSetDescriptionDirty(); + void onSaveDescriptionChanges(); + void onDiscardDescriptionChanges(); + + LLTextEditor* mDescriptionEdit; + LLIconCtrl* mPicture; + LLButton* mUploadPhoto; + LLButton* mChangePhoto; + LLButton* mRemovePhoto; + LLButton* mSaveChanges; + LLButton* mDiscardChanges; + + LLHandle mFloaterTexturePickerHandle; + + std::string mCurrentDescription; + LLUUID mImageId; + bool mHasUnsavedChanges; +}; + +/** + * Panel for displaying Avatar's notes and modifying friend's rights. + */ +class LLPanelProfileNotes + : public LLPanelProfileTab +{ +public: + LLPanelProfileNotes(); + /*virtual*/ ~LLPanelProfileNotes(); + + void setAvatarId(const LLUUID& avatar_id) override; + + void onOpen(const LLSD& key) override; + + BOOL postBuild() override; + + void processProperties(LLAvatarNotes* avatar_notes); + + void resetData() override; + + void updateData() override; + + bool hasUnsavedChanges() override { return mHasUnsavedChanges; } + void commitUnsavedChanges() override; + +protected: + void setNotesText(const std::string &text); + void onSetNotesDirty(); + void onSaveNotesChanges(); + void onDiscardNotesChanges(); + + LLTextEditor* mNotesEditor; + LLButton* mSaveChanges; + LLButton* mDiscardChanges; + + std::string mCurrentNotes; + bool mHasUnsavedChanges; +}; + + +/** +* Container panel for the profile tabs +*/ +class LLPanelProfile + : public LLPanelProfileTab +{ +public: + LLPanelProfile(); + /*virtual*/ ~LLPanelProfile(); + + BOOL postBuild() override; + + void updateData() override; + void refreshName(); + + void onOpen(const LLSD& key) override; + + void createPick(const LLPickData &data); + void showPick(const LLUUID& pick_id = LLUUID::null); + bool isPickTabSelected(); + bool isNotesTabSelected(); + bool hasUnsavedChanges() override; + bool hasUnpublishedClassifieds(); + void commitUnsavedChanges() override; + + void showClassified(const LLUUID& classified_id = LLUUID::null, bool edit = false); + void createClassified(); + + LLAvatarData getAvatarData() { return mAvatarData; }; + + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + +private: + void onTabChange(); + + LLPanelProfileSecondLife* mPanelSecondlife; + LLPanelProfileWeb* mPanelWeb; + LLPanelProfilePicks* mPanelPicks; + LLPanelProfileClassifieds* mPanelClassifieds; + LLPanelProfileFirstLife* mPanelFirstlife; + LLPanelProfileNotes* mPanelNotes; + LLTabContainer* mTabContainer; + + // Todo: due to server taking minutes to update this needs a more long term storage + // to reuse recently saved values if user opens floater again + // Storage implementation depends onto how a cap will be implemented, if cap will be + // enought to fully update LLAvatarPropertiesProcessor, then this storage can be + // implemented there. + LLAvatarData mAvatarData; }; #endif //LL_LLPANELPROFILE_H diff --git a/indra/newview/llpanelprofileclassifieds.cpp b/indra/newview/llpanelprofileclassifieds.cpp new file mode 100644 index 0000000000..a3913ddc49 --- /dev/null +++ b/indra/newview/llpanelprofileclassifieds.cpp @@ -0,0 +1,1513 @@ +/** + * @file llpanelprofileclassifieds.cpp + * @brief LLPanelProfileClassifieds and related class implementations + * + * $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 "llpanelprofileclassifieds.h" + +#include "llagent.h" +#include "llavataractions.h" +#include "llavatarpropertiesprocessor.h" +#include "llclassifiedflags.h" +#include "llcombobox.h" +#include "llcommandhandler.h" // for classified HTML detail page click tracking +#include "llcorehttputil.h" +#include "lldispatcher.h" +#include "llfloaterclassified.h" +#include "llfloaterreg.h" +#include "llfloatersidepanelcontainer.h" +#include "llfloaterworldmap.h" +#include "lliconctrl.h" +#include "lllineeditor.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llpanelavatar.h" +#include "llparcel.h" +#include "llregistry.h" +#include "llscrollcontainer.h" +#include "llstartup.h" +#include "llstatusbar.h" +#include "lltabcontainer.h" +#include "lltexteditor.h" +#include "lltexturectrl.h" +#include "lltrans.h" +#include "llviewergenericmessage.h" // send_generic_message +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" +#include "llviewertexture.h" +#include "llviewertexture.h" + + +//*TODO: verify this limit +const S32 MAX_AVATAR_CLASSIFIEDS = 100; + +const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$ +const S32 DEFAULT_EDIT_CLASSIFIED_SCROLL_HEIGHT = 530; + +//static +LLPanelProfileClassified::panel_list_t LLPanelProfileClassified::sAllPanels; + +static LLPanelInjector t_panel_profile_classifieds("panel_profile_classifieds"); +static LLPanelInjector t_panel_profile_classified("panel_profile_classified"); + +class LLClassifiedHandler : public LLCommandHandler, public LLAvatarPropertiesObserver +{ +public: + // throttle calls from untrusted browsers + LLClassifiedHandler() : LLCommandHandler("classified", UNTRUSTED_THROTTLE) {} + + std::set mClassifiedIds; + std::string mRequestVerb; + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return true; + } + + if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableClassifieds")) + { + LLNotificationsUtil::add("NoClassifieds", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + } + + // handle app/classified/create urls first + if (params.size() == 1 && params[0].asString() == "create") + { + LLAvatarActions::createClassified(); + return true; + } + + // then handle the general app/classified/{UUID}/{CMD} urls + if (params.size() < 2) + { + return false; + } + + // get the ID for the classified + LLUUID classified_id; + if (!classified_id.set(params[0], FALSE)) + { + return false; + } + + // show the classified in the side tray. + // need to ask the server for more info first though... + const std::string verb = params[1].asString(); + if (verb == "about") + { + mRequestVerb = verb; + mClassifiedIds.insert(classified_id); + LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); + LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); + return true; + } + else if (verb == "edit") + { + LLAvatarActions::showClassified(gAgent.getID(), classified_id, true); + return true; + } + + return false; + } + + void openClassified(LLAvatarClassifiedInfo* c_info) + { + if (mRequestVerb == "about") + { + if (c_info->creator_id == gAgent.getID()) + { + LLAvatarActions::showClassified(gAgent.getID(), c_info->classified_id, false); + } + else + { + LLSD params; + params["id"] = c_info->creator_id; + params["classified_id"] = c_info->classified_id; + params["classified_creator_id"] = c_info->creator_id; + params["classified_snapshot_id"] = c_info->snapshot_id; + params["classified_name"] = c_info->name; + params["classified_desc"] = c_info->description; + params["from_search"] = true; + + LLFloaterClassified* floaterp = LLFloaterReg::getTypedInstance("classified", params); + if (floaterp) + { + floaterp->openFloater(params); + floaterp->setVisibleAndFrontmost(); + } + } + } + } + + void processProperties(void* data, EAvatarProcessorType type) + { + if (APT_CLASSIFIED_INFO != type) + { + return; + } + + // is this the classified that we asked for? + LLAvatarClassifiedInfo* c_info = static_cast(data); + if (!c_info || mClassifiedIds.find(c_info->classified_id) == mClassifiedIds.end()) + { + return; + } + + // open the detail side tray for this classified + openClassified(c_info); + + // remove our observer now that we're done + mClassifiedIds.erase(c_info->classified_id); + LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); + } +}; +LLClassifiedHandler gClassifiedHandler; + +////////////////////////////////////////////////////////////////////////// + + +//----------------------------------------------------------------------------- +// LLPanelProfileClassifieds +//----------------------------------------------------------------------------- + +LLPanelProfileClassifieds::LLPanelProfileClassifieds() + : LLPanelProfilePropertiesProcessorTab() + , mClassifiedToSelectOnLoad(LLUUID::null) + , mClassifiedEditOnLoad(false) + , mSheduledClassifiedCreation(false) +{ +} + +LLPanelProfileClassifieds::~LLPanelProfileClassifieds() +{ +} + +void LLPanelProfileClassifieds::onOpen(const LLSD& key) +{ + LLPanelProfilePropertiesProcessorTab::onOpen(key); + + resetData(); + + bool own_profile = getSelfProfile(); + if (own_profile) + { + mNewButton->setVisible(TRUE); + mNewButton->setEnabled(FALSE); + + mDeleteButton->setVisible(TRUE); + mDeleteButton->setEnabled(FALSE); + } + + childSetVisible("buttons_header", own_profile); + +} + +void LLPanelProfileClassifieds::selectClassified(const LLUUID& classified_id, bool edit) +{ + if (getIsLoaded()) + { + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfileClassified* classified_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (classified_panel) + { + if (classified_panel->getClassifiedId() == classified_id) + { + mTabContainer->selectTabPanel(classified_panel); + if (edit) + { + classified_panel->setEditMode(TRUE); + } + break; + } + } + } + } + else + { + mClassifiedToSelectOnLoad = classified_id; + mClassifiedEditOnLoad = edit; + } +} + +void LLPanelProfileClassifieds::createClassified() +{ + if (getIsLoaded()) + { + mNoItemsLabel->setVisible(FALSE); + LLPanelProfileClassified* classified_panel = LLPanelProfileClassified::create(); + classified_panel->onOpen(LLSD()); + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(classified_panel). + select_tab(true). + label(classified_panel->getClassifiedName())); + updateButtons(); + } + else + { + mSheduledClassifiedCreation = true; + } +} + +BOOL LLPanelProfileClassifieds::postBuild() +{ + mTabContainer = getChild("tab_classifieds"); + mNoItemsLabel = getChild("classifieds_panel_text"); + mNewButton = getChild("new_btn"); + mDeleteButton = getChild("delete_btn"); + + mNewButton->setCommitCallback(boost::bind(&LLPanelProfileClassifieds::onClickNewBtn, this)); + mDeleteButton->setCommitCallback(boost::bind(&LLPanelProfileClassifieds::onClickDelete, this)); + + return TRUE; +} + +void LLPanelProfileClassifieds::onClickNewBtn() +{ + mNoItemsLabel->setVisible(FALSE); + LLPanelProfileClassified* classified_panel = LLPanelProfileClassified::create(); + classified_panel->onOpen(LLSD()); + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(classified_panel). + select_tab(true). + label(classified_panel->getClassifiedName())); + updateButtons(); +} + +void LLPanelProfileClassifieds::onClickDelete() +{ + LLPanelProfileClassified* classified_panel = dynamic_cast(mTabContainer->getCurrentPanel()); + if (classified_panel) + { + LLUUID classified_id = classified_panel->getClassifiedId(); + LLSD args; + args["CLASSIFIED"] = classified_panel->getClassifiedName(); + LLSD payload; + payload["classified_id"] = classified_id; + payload["tab_idx"] = mTabContainer->getCurrentPanelIndex(); + LLNotificationsUtil::add("ProfileDeleteClassified", args, payload, + boost::bind(&LLPanelProfileClassifieds::callbackDeleteClassified, this, _1, _2)); + } +} + +void LLPanelProfileClassifieds::callbackDeleteClassified(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) + { + LLUUID classified_id = notification["payload"]["classified_id"].asUUID(); + S32 tab_idx = notification["payload"]["tab_idx"].asInteger(); + + LLPanelProfileClassified* classified_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (classified_panel && classified_panel->getClassifiedId() == classified_id) + { + mTabContainer->removeTabPanel(classified_panel); + } + + if (classified_id.notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->sendClassifiedDelete(classified_id); + } + + updateButtons(); + + BOOL no_data = !mTabContainer->getTabCount(); + mNoItemsLabel->setVisible(no_data); + } +} + +void LLPanelProfileClassifieds::processProperties(void* data, EAvatarProcessorType type) +{ + if ((APT_CLASSIFIEDS == type) || (APT_CLASSIFIED_INFO == type)) + { + LLUUID avatar_id = getAvatarId(); + + LLAvatarClassifieds* c_info = static_cast(data); + if (c_info && getAvatarId() == c_info->target_id) + { + // do not clear classified list in case we will receive two or more data packets. + // list has been cleared in updateData(). (fix for EXT-6436) + LLUUID selected_id = mClassifiedToSelectOnLoad; + bool has_selection = false; + + LLAvatarClassifieds::classifieds_list_t::const_iterator it = c_info->classifieds_list.begin(); + for (; c_info->classifieds_list.end() != it; ++it) + { + LLAvatarClassifieds::classified_data c_data = *it; + + LLPanelProfileClassified* classified_panel = LLPanelProfileClassified::create(); + + LLSD params; + params["classified_creator_id"] = avatar_id; + params["classified_id"] = c_data.classified_id; + params["classified_name"] = c_data.name; + params["from_search"] = (selected_id == c_data.classified_id); //SLURL handling and stats tracking + params["edit"] = (selected_id == c_data.classified_id) && mClassifiedEditOnLoad; + classified_panel->onOpen(params); + + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(classified_panel). + select_tab(selected_id == c_data.classified_id). + label(c_data.name)); + + if (selected_id == c_data.classified_id) + { + has_selection = true; + } + } + + if (mSheduledClassifiedCreation) + { + LLPanelProfileClassified* classified_panel = LLPanelProfileClassified::create(); + classified_panel->onOpen(LLSD()); + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(classified_panel). + select_tab(!has_selection). + label(classified_panel->getClassifiedName())); + has_selection = true; + } + + // reset 'do on load' values + mClassifiedToSelectOnLoad = LLUUID::null; + mClassifiedEditOnLoad = false; + mSheduledClassifiedCreation = false; + + // set even if not visible, user might delete own + // calassified and this string will need to be shown + if (getSelfProfile()) + { + mNoItemsLabel->setValue(LLTrans::getString("NoClassifiedsText")); + } + else + { + mNoItemsLabel->setValue(LLTrans::getString("NoAvatarClassifiedsText")); + } + + bool has_data = mTabContainer->getTabCount() > 0; + mNoItemsLabel->setVisible(!has_data); + if (has_data && !has_selection) + { + mTabContainer->selectFirstTab(); + } + + setLoaded(); + updateButtons(); + } + } +} + +void LLPanelProfileClassifieds::resetData() +{ + resetLoading(); + mTabContainer->deleteAllTabs(); +} + +void LLPanelProfileClassifieds::updateButtons() +{ + if (getSelfProfile()) + { + mNewButton->setEnabled(canAddNewClassified()); + mDeleteButton->setEnabled(canDeleteClassified()); + } +} + +void LLPanelProfileClassifieds::updateData() +{ + // Send picks request only once + LLUUID avatar_id = getAvatarId(); + if (!getStarted() && avatar_id.notNull()) + { + setIsLoading(); + mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); + mNoItemsLabel->setVisible(TRUE); + + LLAvatarPropertiesProcessor::getInstance()->sendAvatarClassifiedsRequest(avatar_id); + } +} + +bool LLPanelProfileClassifieds::hasNewClassifieds() +{ + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfileClassified* classified_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (classified_panel && classified_panel->isNew()) + { + return true; + } + } + return false; +} + +bool LLPanelProfileClassifieds::hasUnsavedChanges() +{ + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfileClassified* classified_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (classified_panel && classified_panel->isDirty()) // includes 'new' + { + return true; + } + } + return false; +} + +bool LLPanelProfileClassifieds::canAddNewClassified() +{ + return (mTabContainer->getTabCount() < MAX_AVATAR_CLASSIFIEDS); +} + +bool LLPanelProfileClassifieds::canDeleteClassified() +{ + return (mTabContainer->getTabCount() > 0); +} + +void LLPanelProfileClassifieds::commitUnsavedChanges() +{ + if (getIsLoaded()) + { + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfileClassified* classified_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (classified_panel && classified_panel->isDirty() && !classified_panel->isNew()) + { + classified_panel->doSave(); + } + } + } +} +//----------------------------------------------------------------------------- +// LLDispatchClassifiedClickThrough +//----------------------------------------------------------------------------- + +// "classifiedclickthrough" +// strings[0] = classified_id +// strings[1] = teleport_clicks +// strings[2] = map_clicks +// strings[3] = profile_clicks +class LLDispatchClassifiedClickThrough : public LLDispatchHandler +{ +public: + virtual bool operator()( + const LLDispatcher* dispatcher, + const std::string& key, + const LLUUID& invoice, + const sparam_t& strings) + { + if (strings.size() != 4) return false; + LLUUID classified_id(strings[0]); + S32 teleport_clicks = atoi(strings[1].c_str()); + S32 map_clicks = atoi(strings[2].c_str()); + S32 profile_clicks = atoi(strings[3].c_str()); + + LLPanelProfileClassified::setClickThrough( + classified_id, teleport_clicks, map_clicks, profile_clicks, false); + + return true; + } +}; +static LLDispatchClassifiedClickThrough sClassifiedClickThrough; + + +//----------------------------------------------------------------------------- +// LLPanelProfileClassified +//----------------------------------------------------------------------------- + +static const S32 CB_ITEM_MATURE = 0; +static const S32 CB_ITEM_PG = 1; + +LLPanelProfileClassified::LLPanelProfileClassified() + : LLPanelProfilePropertiesProcessorTab() + , mInfoLoaded(false) + , mTeleportClicksOld(0) + , mMapClicksOld(0) + , mProfileClicksOld(0) + , mTeleportClicksNew(0) + , mMapClicksNew(0) + , mProfileClicksNew(0) + , mPriceForListing(0) + , mSnapshotCtrl(NULL) + , mPublishFloater(NULL) + , mIsNew(false) + , mIsNewWithErrors(false) + , mCanClose(false) + , mEditMode(false) + , mEditOnLoad(false) +{ + sAllPanels.push_back(this); +} + +LLPanelProfileClassified::~LLPanelProfileClassified() +{ + sAllPanels.remove(this); + gGenericDispatcher.addHandler("classifiedclickthrough", NULL); // deregister our handler +} + +//static +LLPanelProfileClassified* LLPanelProfileClassified::create() +{ + LLPanelProfileClassified* panel = new LLPanelProfileClassified(); + panel->buildFromFile("panel_profile_classified.xml"); + return panel; +} + +BOOL LLPanelProfileClassified::postBuild() +{ + mScrollContainer = getChild("profile_scroll"); + mInfoPanel = getChild("info_panel"); + mInfoScroll = getChild("info_scroll_content_panel"); + mEditPanel = getChild("edit_panel"); + + mSnapshotCtrl = getChild("classified_snapshot"); + mEditIcon = getChild("edit_icon"); + + //info + mClassifiedNameText = getChild("classified_name"); + mClassifiedDescText = getChild("classified_desc"); + mLocationText = getChild("classified_location"); + mCategoryText = getChild("category"); + mContentTypeText = getChild("content_type"); + mContentTypeM = getChild("content_type_moderate"); + mContentTypeG = getChild("content_type_general"); + mPriceText = getChild("price_for_listing"); + mAutoRenewText = getChild("auto_renew"); + + mMapButton = getChild("show_on_map_btn"); + mTeleportButton = getChild("teleport_btn"); + mEditButton = getChild("edit_btn"); + + //edit + mClassifiedNameEdit = getChild("classified_name_edit"); + mClassifiedDescEdit = getChild("classified_desc_edit"); + mLocationEdit = getChild("classified_location_edit"); + mCategoryCombo = getChild("category_edit"); + mContentTypeCombo = getChild("content_type_edit"); + mAutoRenewEdit = getChild("auto_renew_edit"); + + mSaveButton = getChild("save_changes_btn"); + mSetLocationButton = getChild("set_to_curr_location_btn"); + mCancelButton = getChild("cancel_btn"); + + mUtilityBtnCnt = getChild("util_buttons_lp"); + mPublishBtnsCnt = getChild("publish_layout_panel"); + mCancelBtnCnt = getChild("cancel_btn_lp"); + mSaveBtnCnt = getChild("save_btn_lp"); + + mSnapshotCtrl->setOnSelectCallback(boost::bind(&LLPanelProfileClassified::onTextureSelected, this)); + mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelProfileClassified::onTexturePickerMouseEnter, this)); + mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelProfileClassified::onTexturePickerMouseLeave, this)); + mEditIcon->setVisible(false); + + mMapButton->setCommitCallback(boost::bind(&LLPanelProfileClassified::onMapClick, this)); + mTeleportButton->setCommitCallback(boost::bind(&LLPanelProfileClassified::onTeleportClick, this)); + mEditButton->setCommitCallback(boost::bind(&LLPanelProfileClassified::onEditClick, this)); + mSaveButton->setCommitCallback(boost::bind(&LLPanelProfileClassified::onSaveClick, this)); + mSetLocationButton->setCommitCallback(boost::bind(&LLPanelProfileClassified::onSetLocationClick, this)); + mCancelButton->setCommitCallback(boost::bind(&LLPanelProfileClassified::onCancelClick, this)); + + LLClassifiedInfo::cat_map::iterator iter; + for (iter = LLClassifiedInfo::sCategories.begin(); + iter != LLClassifiedInfo::sCategories.end(); + iter++) + { + mCategoryCombo->add(LLTrans::getString(iter->second)); + } + + mClassifiedNameEdit->setKeystrokeCallback(boost::bind(&LLPanelProfileClassified::onChange, this), NULL); + mClassifiedDescEdit->setKeystrokeCallback(boost::bind(&LLPanelProfileClassified::onChange, this)); + mCategoryCombo->setCommitCallback(boost::bind(&LLPanelProfileClassified::onChange, this)); + mContentTypeCombo->setCommitCallback(boost::bind(&LLPanelProfileClassified::onChange, this)); + mAutoRenewEdit->setCommitCallback(boost::bind(&LLPanelProfileClassified::onChange, this)); + + return TRUE; +} + +void LLPanelProfileClassified::onOpen(const LLSD& key) +{ + mIsNew = key.isUndefined(); + + resetData(); + resetControls(); + scrollToTop(); + + // classified is not created yet + bool is_new = isNew() || isNewWithErrors(); + + if(is_new) + { + LLPanelProfilePropertiesProcessorTab::setAvatarId(gAgent.getID()); + + setPosGlobal(gAgent.getPositionGlobal()); + + LLUUID snapshot_id = LLUUID::null; + std::string desc; + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if(parcel) + { + desc = parcel->getDesc(); + snapshot_id = parcel->getSnapshotID(); + } + + std::string region_name = LLTrans::getString("ClassifiedUpdateAfterPublish"); + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + region_name = region->getName(); + } + + setClassifiedName(makeClassifiedName()); + setDescription(desc); + setSnapshotId(snapshot_id); + setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal())); + // server will set valid parcel id + setParcelId(LLUUID::null); + + mSaveButton->setLabelArg("[LABEL]", getString("publish_label")); + + setEditMode(TRUE); + enableSave(true); + enableEditing(true); + resetDirty(); + setInfoLoaded(false); + } + else + { + LLUUID avatar_id = key["classified_creator_id"]; + if(avatar_id.isNull()) + { + return; + } + LLPanelProfilePropertiesProcessorTab::setAvatarId(avatar_id); + + setClassifiedId(key["classified_id"]); + setClassifiedName(key["classified_name"]); + setFromSearch(key["from_search"]); + mEditOnLoad = key["edit"]; + + LL_INFOS() << "Opening classified [" << getClassifiedName() << "] (" << getClassifiedId() << ")" << LL_ENDL; + + LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(getClassifiedId()); + + gGenericDispatcher.addHandler("classifiedclickthrough", &sClassifiedClickThrough); + + if (gAgent.getRegion()) + { + // While we're at it let's get the stats from the new table if that + // capability exists. + std::string url = gAgent.getRegion()->getCapability("SearchStatRequest"); + if (!url.empty()) + { + LL_INFOS() << "Classified stat request via capability" << LL_ENDL; + LLSD body; + LLUUID classifiedId = getClassifiedId(); + body["classified_id"] = classifiedId; + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, body, + boost::bind(&LLPanelProfileClassified::handleSearchStatResponse, classifiedId, _1)); + } + } + // Update classified click stats. + // *TODO: Should we do this when opening not from search? + if (!fromSearch() ) + { + sendClickMessage("profile"); + } + + setInfoLoaded(false); + } + + + bool is_self = getSelfProfile(); + getChildView("auto_renew_layout_panel")->setVisible(is_self); + getChildView("clickthrough_layout_panel")->setVisible(is_self); + + updateButtons(); +} + +void LLPanelProfileClassified::processProperties(void* data, EAvatarProcessorType type) +{ + if (APT_CLASSIFIED_INFO != type) + { + return; + } + + LLAvatarClassifiedInfo* c_info = static_cast(data); + if(c_info && getClassifiedId() == c_info->classified_id) + { + // see LLPanelProfileClassified::sendUpdate() for notes + if (mIsNewWithErrors) + { + // We just published it + setEditMode(FALSE); + } + mIsNewWithErrors = false; + mIsNew = false; + + setClassifiedName(c_info->name); + setDescription(c_info->description); + setSnapshotId(c_info->snapshot_id); + setParcelId(c_info->parcel_id); + setPosGlobal(c_info->pos_global); + setSimName(c_info->sim_name); + + setClassifiedLocation(createLocationText(c_info->parcel_name, c_info->sim_name, c_info->pos_global)); + + mCategoryText->setValue(LLClassifiedInfo::sCategories[c_info->category]); + // *HACK see LLPanelProfileClassified::sendUpdate() + setCategory(c_info->category - 1); + + bool mature = is_cf_mature(c_info->flags); + setContentType(mature); + + bool auto_renew = is_cf_auto_renew(c_info->flags); + std::string auto_renew_str = auto_renew ? getString("auto_renew_on") : getString("auto_renew_off"); + mAutoRenewText->setValue(auto_renew_str); + mAutoRenewEdit->setValue(auto_renew); + + static LLUIString price_str = getString("l$_price"); + price_str.setArg("[PRICE]", llformat("%d", c_info->price_for_listing)); + mPriceText->setValue(LLSD(price_str)); + + static std::string date_fmt = getString("date_fmt"); + std::string date_str = date_fmt; + LLStringUtil::format(date_str, LLSD().with("datetime", (S32) c_info->creation_date)); + getChild("creation_date")->setValue(date_str); + + resetDirty(); + setInfoLoaded(true); + enableSave(false); + enableEditing(true); + + // for just created classified - in case user opened edit panel before processProperties() callback + mSaveButton->setLabelArg("[LABEL]", getString("save_label")); + + setLoaded(); + updateButtons(); + + if (mEditOnLoad) + { + setEditMode(TRUE); + } + } + +} + +void LLPanelProfileClassified::setEditMode(BOOL edit_mode) +{ + mEditMode = edit_mode; + + mInfoPanel->setVisible(!edit_mode); + mEditPanel->setVisible(edit_mode); + + // snapshot control is common between info and edit, + // enable it only when in edit mode + mSnapshotCtrl->setEnabled(edit_mode); + + scrollToTop(); + updateButtons(); + updateInfoRect(); +} + +void LLPanelProfileClassified::updateButtons() +{ + bool edit_mode = getEditMode(); + mUtilityBtnCnt->setVisible(!edit_mode); + + // cancel button should either delete unpublished + // classified or not be there at all + mCancelBtnCnt->setVisible(edit_mode && !mIsNew); + mPublishBtnsCnt->setVisible(edit_mode); + mSaveBtnCnt->setVisible(edit_mode); + mEditButton->setVisible(!edit_mode && getSelfProfile()); +} + +void LLPanelProfileClassified::updateInfoRect() +{ + if (getEditMode()) + { + // info_scroll_content_panel contains both info and edit panel + // info panel can be very large and scroll bar will carry over. + // Resize info panel to prevent scroll carry over when in edit mode. + mInfoScroll->reshape(mInfoScroll->getRect().getWidth(), DEFAULT_EDIT_CLASSIFIED_SCROLL_HEIGHT, FALSE); + } + else + { + // Adjust text height to make description scrollable. + S32 new_height = mClassifiedDescText->getTextBoundingRect().getHeight(); + LLRect visible_rect = mClassifiedDescText->getVisibleDocumentRect(); + S32 delta_height = new_height - visible_rect.getHeight() + 5; + + LLRect rect = mInfoScroll->getRect(); + mInfoScroll->reshape(rect.getWidth(), rect.getHeight() + delta_height, FALSE); + } +} + +void LLPanelProfileClassified::enableEditing(bool enable) +{ + mEditButton->setEnabled(enable); + mClassifiedNameEdit->setEnabled(enable); + mClassifiedDescEdit->setEnabled(enable); + mSetLocationButton->setEnabled(enable); + mCategoryCombo->setEnabled(enable); + mContentTypeCombo->setEnabled(enable); + mAutoRenewEdit->setEnabled(enable); +} + +void LLPanelProfileClassified::resetControls() +{ + updateButtons(); + + mCategoryCombo->setCurrentByIndex(0); + mContentTypeCombo->setCurrentByIndex(0); + mAutoRenewEdit->setValue(false); + mPriceForListing = MINIMUM_PRICE_FOR_LISTING; +} + +void LLPanelProfileClassified::onEditClick() +{ + setEditMode(TRUE); +} + +void LLPanelProfileClassified::onCancelClick() +{ + if (isNew()) + { + mClassifiedNameEdit->setValue(mClassifiedNameText->getValue()); + mClassifiedDescEdit->setValue(mClassifiedDescText->getValue()); + mLocationEdit->setValue(mLocationText->getValue()); + mCategoryCombo->setCurrentByIndex(0); + mContentTypeCombo->setCurrentByIndex(0); + mAutoRenewEdit->setValue(false); + mPriceForListing = MINIMUM_PRICE_FOR_LISTING; + } + else + { + // Reload data to undo changes to forms + LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(getClassifiedId()); + } + + setInfoLoaded(false); + + setEditMode(FALSE); +} + +void LLPanelProfileClassified::onSaveClick() +{ + mCanClose = false; + + if(!isValidName()) + { + notifyInvalidName(); + return; + } + if(isNew() || isNewWithErrors()) + { + if(gStatusBar->getBalance() < getPriceForListing()) + { + LLNotificationsUtil::add("ClassifiedInsufficientFunds"); + return; + } + + mPublishFloater = LLFloaterReg::findTypedInstance( + "publish_classified", LLSD()); + + if(!mPublishFloater) + { + mPublishFloater = LLFloaterReg::getTypedInstance( + "publish_classified", LLSD()); + + mPublishFloater->setPublishClickedCallback(boost::bind + (&LLPanelProfileClassified::onPublishFloaterPublishClicked, this)); + } + + // set spinner value before it has focus or value wont be set + mPublishFloater->setPrice(getPriceForListing()); + mPublishFloater->openFloater(mPublishFloater->getKey()); + mPublishFloater->center(); + } + else + { + doSave(); + } +} + +/*static*/ +void LLPanelProfileClassified::handleSearchStatResponse(LLUUID classifiedId, LLSD result) +{ + S32 teleport = result["teleport_clicks"].asInteger(); + S32 map = result["map_clicks"].asInteger(); + S32 profile = result["profile_clicks"].asInteger(); + S32 search_teleport = result["search_teleport_clicks"].asInteger(); + S32 search_map = result["search_map_clicks"].asInteger(); + S32 search_profile = result["search_profile_clicks"].asInteger(); + + LLPanelProfileClassified::setClickThrough(classifiedId, + teleport + search_teleport, + map + search_map, + profile + search_profile, + true); +} + +void LLPanelProfileClassified::resetData() +{ + setClassifiedName(LLStringUtil::null); + setDescription(LLStringUtil::null); + setClassifiedLocation(LLStringUtil::null); + setClassifiedId(LLUUID::null); + setSnapshotId(LLUUID::null); + setPosGlobal(LLVector3d::zero); + setParcelId(LLUUID::null); + setSimName(LLStringUtil::null); + setFromSearch(false); + + // reset click stats + mTeleportClicksOld = 0; + mMapClicksOld = 0; + mProfileClicksOld = 0; + mTeleportClicksNew = 0; + mMapClicksNew = 0; + mProfileClicksNew = 0; + + mPriceForListing = MINIMUM_PRICE_FOR_LISTING; + + mCategoryText->setValue(LLStringUtil::null); + mContentTypeText->setValue(LLStringUtil::null); + getChild("click_through_text")->setValue(LLStringUtil::null); + mEditButton->setValue(LLStringUtil::null); + getChild("creation_date")->setValue(LLStringUtil::null); + mContentTypeM->setVisible(FALSE); + mContentTypeG->setVisible(FALSE); +} + +void LLPanelProfileClassified::setClassifiedName(const std::string& name) +{ + mClassifiedNameText->setValue(name); + mClassifiedNameEdit->setValue(name); +} + +std::string LLPanelProfileClassified::getClassifiedName() +{ + return mClassifiedNameEdit->getValue().asString(); +} + +void LLPanelProfileClassified::setDescription(const std::string& desc) +{ + mClassifiedDescText->setValue(desc); + mClassifiedDescEdit->setValue(desc); + + updateInfoRect(); +} + +std::string LLPanelProfileClassified::getDescription() +{ + return mClassifiedDescEdit->getValue().asString(); +} + +void LLPanelProfileClassified::setClassifiedLocation(const std::string& location) +{ + mLocationText->setValue(location); + mLocationEdit->setValue(location); +} + +std::string LLPanelProfileClassified::getClassifiedLocation() +{ + return mLocationText->getValue().asString(); +} + +void LLPanelProfileClassified::setSnapshotId(const LLUUID& id) +{ + mSnapshotCtrl->setValue(id); +} + +LLUUID LLPanelProfileClassified::getSnapshotId() +{ + return mSnapshotCtrl->getValue().asUUID(); +} + +// static +void LLPanelProfileClassified::setClickThrough( + const LLUUID& classified_id, + S32 teleport, + S32 map, + S32 profile, + bool from_new_table) +{ + LL_INFOS() << "Click-through data for classified " << classified_id << " arrived: [" + << teleport << ", " << map << ", " << profile << "] (" + << (from_new_table ? "new" : "old") << ")" << LL_ENDL; + + for (panel_list_t::iterator iter = sAllPanels.begin(); iter != sAllPanels.end(); ++iter) + { + LLPanelProfileClassified* self = *iter; + if (self->getClassifiedId() != classified_id) + { + continue; + } + + // *HACK: Skip LLPanelProfileClassified instances: they don't display clicks data. + // Those instances should not be in the list at all. + if (typeid(*self) != typeid(LLPanelProfileClassified)) + { + continue; + } + + LL_INFOS() << "Updating classified info panel" << LL_ENDL; + + // We need to check to see if the data came from the new stat_table + // or the old classified table. We also need to cache the data from + // the two separate sources so as to display the aggregate totals. + + if (from_new_table) + { + self->mTeleportClicksNew = teleport; + self->mMapClicksNew = map; + self->mProfileClicksNew = profile; + } + else + { + self->mTeleportClicksOld = teleport; + self->mMapClicksOld = map; + self->mProfileClicksOld = profile; + } + + static LLUIString ct_str = self->getString("click_through_text_fmt"); + + ct_str.setArg("[TELEPORT]", llformat("%d", self->mTeleportClicksNew + self->mTeleportClicksOld)); + ct_str.setArg("[MAP]", llformat("%d", self->mMapClicksNew + self->mMapClicksOld)); + ct_str.setArg("[PROFILE]", llformat("%d", self->mProfileClicksNew + self->mProfileClicksOld)); + + self->getChild("click_through_text")->setValue(ct_str.getString()); + // *HACK: remove this when there is enough room for click stats in the info panel + self->getChildView("click_through_text")->setToolTip(ct_str.getString()); + + LL_INFOS() << "teleport: " << llformat("%d", self->mTeleportClicksNew + self->mTeleportClicksOld) + << ", map: " << llformat("%d", self->mMapClicksNew + self->mMapClicksOld) + << ", profile: " << llformat("%d", self->mProfileClicksNew + self->mProfileClicksOld) + << LL_ENDL; + } +} + +// static +std::string LLPanelProfileClassified::createLocationText( + const std::string& original_name, + const std::string& sim_name, + const LLVector3d& pos_global) +{ + std::string location_text; + + location_text.append(original_name); + + if (!sim_name.empty()) + { + if (!location_text.empty()) + location_text.append(", "); + location_text.append(sim_name); + } + + if (!location_text.empty()) + location_text.append(" "); + + if (!pos_global.isNull()) + { + S32 region_x = ll_round((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = ll_round((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = ll_round((F32)pos_global.mdV[VZ]); + location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); + } + + return location_text; +} + +void LLPanelProfileClassified::scrollToTop() +{ + if (mScrollContainer) + { + mScrollContainer->goToTop(); + } +} + +//info +// static +// *TODO: move out of the panel +void LLPanelProfileClassified::sendClickMessage( + const std::string& type, + bool from_search, + const LLUUID& classified_id, + const LLUUID& parcel_id, + const LLVector3d& global_pos, + const std::string& sim_name) +{ + if (gAgent.getRegion()) + { + // You're allowed to click on your own ads to reassure yourself + // that the system is working. + LLSD body; + body["type"] = type; + body["from_search"] = from_search; + body["classified_id"] = classified_id; + body["parcel_id"] = parcel_id; + body["dest_pos_global"] = global_pos.getValue(); + body["region_name"] = sim_name; + + std::string url = gAgent.getRegion()->getCapability("SearchStatTracking"); + LL_INFOS() << "Sending click msg via capability (url=" << url << ")" << LL_ENDL; + LL_INFOS() << "body: [" << body << "]" << LL_ENDL; + LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(url, body, + "SearchStatTracking Click report sent.", "SearchStatTracking Click report NOT sent."); + } +} + +void LLPanelProfileClassified::sendClickMessage(const std::string& type) +{ + sendClickMessage( + type, + fromSearch(), + getClassifiedId(), + getParcelId(), + getPosGlobal(), + getSimName()); +} + +void LLPanelProfileClassified::onMapClick() +{ + sendClickMessage("map"); + LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); + LLFloaterReg::showInstance("world_map", "center"); +} + +void LLPanelProfileClassified::onTeleportClick() +{ + if (!getPosGlobal().isExactlyZero()) + { + sendClickMessage("teleport"); + gAgent.teleportViaLocation(getPosGlobal()); + LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); + } +} + +BOOL LLPanelProfileClassified::isDirty() const +{ + if(mIsNew) + { + return TRUE; + } + + BOOL dirty = false; + dirty |= mSnapshotCtrl->isDirty(); + dirty |= mClassifiedNameEdit->isDirty(); + dirty |= mClassifiedDescEdit->isDirty(); + dirty |= mCategoryCombo->isDirty(); + dirty |= mContentTypeCombo->isDirty(); + dirty |= mAutoRenewEdit->isDirty(); + + return dirty; +} + +void LLPanelProfileClassified::resetDirty() +{ + mSnapshotCtrl->resetDirty(); + mClassifiedNameEdit->resetDirty(); + + // call blockUndo() to really reset dirty(and make isDirty work as intended) + mClassifiedDescEdit->blockUndo(); + mClassifiedDescEdit->resetDirty(); + + mCategoryCombo->resetDirty(); + mContentTypeCombo->resetDirty(); + mAutoRenewEdit->resetDirty(); +} + +bool LLPanelProfileClassified::canClose() +{ + return mCanClose; +} + +U32 LLPanelProfileClassified::getContentType() +{ + return mContentTypeCombo->getCurrentIndex(); +} + +void LLPanelProfileClassified::setContentType(bool mature) +{ + static std::string mature_str = getString("type_mature"); + static std::string pg_str = getString("type_pg"); + mContentTypeText->setValue(mature ? mature_str : pg_str); + mContentTypeM->setVisible(mature); + mContentTypeG->setVisible(!mature); + mContentTypeCombo->setCurrentByIndex(mature ? CB_ITEM_MATURE : CB_ITEM_PG); + mContentTypeCombo->resetDirty(); +} + +bool LLPanelProfileClassified::getAutoRenew() +{ + return mAutoRenewEdit->getValue().asBoolean(); +} + +void LLPanelProfileClassified::sendUpdate() +{ + LLAvatarClassifiedInfo c_data; + + if(getClassifiedId().isNull()) + { + setClassifiedId(LLUUID::generateNewID()); + } + + c_data.agent_id = gAgent.getID(); + c_data.classified_id = getClassifiedId(); + // *HACK + // Categories on server start with 1 while combo-box index starts with 0 + c_data.category = getCategory() + 1; + c_data.name = getClassifiedName(); + c_data.description = getDescription(); + c_data.parcel_id = getParcelId(); + c_data.snapshot_id = getSnapshotId(); + c_data.pos_global = getPosGlobal(); + c_data.flags = getFlags(); + c_data.price_for_listing = getPriceForListing(); + + LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoUpdate(&c_data); + + if(isNew()) + { + // Lets assume there will be some error. + // Successful sendClassifiedInfoUpdate will trigger processProperties and + // let us know there was no error. + mIsNewWithErrors = true; + } +} + +U32 LLPanelProfileClassified::getCategory() +{ + return mCategoryCombo->getCurrentIndex(); +} + +void LLPanelProfileClassified::setCategory(U32 category) +{ + mCategoryCombo->setCurrentByIndex(category); + mCategoryCombo->resetDirty(); +} + +U8 LLPanelProfileClassified::getFlags() +{ + bool auto_renew = mAutoRenewEdit->getValue().asBoolean(); + + bool mature = mContentTypeCombo->getCurrentIndex() == CB_ITEM_MATURE; + + return pack_classified_flags_request(auto_renew, false, mature, false); +} + +void LLPanelProfileClassified::enableSave(bool enable) +{ + mSaveButton->setEnabled(enable); +} + +std::string LLPanelProfileClassified::makeClassifiedName() +{ + std::string name; + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if(parcel) + { + name = parcel->getName(); + } + + if(!name.empty()) + { + return name; + } + + LLViewerRegion* region = gAgent.getRegion(); + if(region) + { + name = region->getName(); + } + + return name; +} + +void LLPanelProfileClassified::onSetLocationClick() +{ + setPosGlobal(gAgent.getPositionGlobal()); + setParcelId(LLUUID::null); + + std::string region_name = LLTrans::getString("ClassifiedUpdateAfterPublish"); + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + region_name = region->getName(); + } + + setClassifiedLocation(createLocationText(getLocationNotice(), region_name, getPosGlobal())); + + // mark classified as dirty + setValue(LLSD()); + + onChange(); +} + +void LLPanelProfileClassified::onChange() +{ + enableSave(isDirty()); +} + +void LLPanelProfileClassified::doSave() +{ + //*TODO: Fix all of this + + mCanClose = true; + sendUpdate(); + updateTabLabel(getClassifiedName()); + resetDirty(); + + if (!canClose()) + { + return; + } + + if (!isNew() && !isNewWithErrors()) + { + setEditMode(FALSE); + return; + } + + updateButtons(); +} + +void LLPanelProfileClassified::onPublishFloaterPublishClicked() +{ + setPriceForListing(mPublishFloater->getPrice()); + + doSave(); +} + +std::string LLPanelProfileClassified::getLocationNotice() +{ + static std::string location_notice = getString("location_notice"); + return location_notice; +} + +bool LLPanelProfileClassified::isValidName() +{ + std::string name = getClassifiedName(); + if (name.empty()) + { + return false; + } + if (!isalnum(name[0])) + { + return false; + } + + return true; +} + +void LLPanelProfileClassified::notifyInvalidName() +{ + std::string name = getClassifiedName(); + if (name.empty()) + { + LLNotificationsUtil::add("BlankClassifiedName"); + } + else if (!isalnum(name[0])) + { + LLNotificationsUtil::add("ClassifiedMustBeAlphanumeric"); + } +} + +void LLPanelProfileClassified::onTexturePickerMouseEnter() +{ + mEditIcon->setVisible(TRUE); +} + +void LLPanelProfileClassified::onTexturePickerMouseLeave() +{ + mEditIcon->setVisible(FALSE); +} + +void LLPanelProfileClassified::onTextureSelected() +{ + setSnapshotId(mSnapshotCtrl->getValue().asUUID()); + onChange(); +} + +void LLPanelProfileClassified::updateTabLabel(const std::string& title) +{ + setLabel(title); + LLTabContainer* parent = dynamic_cast(getParent()); + if (parent) + { + parent->setCurrentTabName(title); + } +} + + +//----------------------------------------------------------------------------- +// LLPublishClassifiedFloater +//----------------------------------------------------------------------------- + +LLPublishClassifiedFloater::LLPublishClassifiedFloater(const LLSD& key) + : LLFloater(key) +{ +} + +LLPublishClassifiedFloater::~LLPublishClassifiedFloater() +{ +} + +BOOL LLPublishClassifiedFloater::postBuild() +{ + LLFloater::postBuild(); + + childSetAction("publish_btn", boost::bind(&LLFloater::closeFloater, this, false)); + childSetAction("cancel_btn", boost::bind(&LLFloater::closeFloater, this, false)); + + return TRUE; +} + +void LLPublishClassifiedFloater::setPrice(S32 price) +{ + getChild("price_for_listing")->setValue(price); +} + +S32 LLPublishClassifiedFloater::getPrice() +{ + return getChild("price_for_listing")->getValue().asInteger(); +} + +void LLPublishClassifiedFloater::setPublishClickedCallback(const commit_signal_t::slot_type& cb) +{ + getChild("publish_btn")->setClickedCallback(cb); +} + +void LLPublishClassifiedFloater::setCancelClickedCallback(const commit_signal_t::slot_type& cb) +{ + getChild("cancel_btn")->setClickedCallback(cb); +} diff --git a/indra/newview/llpanelprofileclassifieds.h b/indra/newview/llpanelprofileclassifieds.h new file mode 100644 index 0000000000..912819e86b --- /dev/null +++ b/indra/newview/llpanelprofileclassifieds.h @@ -0,0 +1,340 @@ +/** + * @file llpanelprofileclassifieds.h + * @brief LLPanelProfileClassifieds and related class implementations + * + * $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_PANELPROFILECLASSIFIEDS_H +#define LL_PANELPROFILECLASSIFIEDS_H + +#include "llavatarpropertiesprocessor.h" +#include "llclassifiedinfo.h" +#include "llfloater.h" +#include "llpanel.h" +#include "llpanelavatar.h" +#include "llrect.h" +#include "lluuid.h" +#include "v3dmath.h" +#include "llcoros.h" +#include "lleventcoro.h" + +class LLCheckBoxCtrl; +class LLLineEditor; +class LLMediaCtrl; +class LLScrollContainer; +class LLTabContainer; +class LLTextEditor; +class LLTextureCtrl; +class LLUICtrl; + + +class LLPublishClassifiedFloater : public LLFloater +{ +public: + LLPublishClassifiedFloater(const LLSD& key); + virtual ~LLPublishClassifiedFloater(); + + BOOL postBuild() override; + + void setPrice(S32 price); + S32 getPrice(); + + void setPublishClickedCallback(const commit_signal_t::slot_type& cb); + void setCancelClickedCallback(const commit_signal_t::slot_type& cb); +}; + + +/** +* Panel for displaying Avatar's picks. +*/ +class LLPanelProfileClassifieds + : public LLPanelProfilePropertiesProcessorTab +{ +public: + LLPanelProfileClassifieds(); + /*virtual*/ ~LLPanelProfileClassifieds(); + + BOOL postBuild() override; + + void onOpen(const LLSD& key) override; + + void selectClassified(const LLUUID& classified_id, bool edit); + + void createClassified(); + + void processProperties(void* data, EAvatarProcessorType type) override; + + void resetData() override; + + void updateButtons(); + + void updateData() override; + + bool hasNewClassifieds(); + bool hasUnsavedChanges() override; + // commits changes to existing classifieds, but does not publish new classified! + void commitUnsavedChanges() override; + +private: + void onClickNewBtn(); + void onClickDelete(); + void callbackDeleteClassified(const LLSD& notification, const LLSD& response); + + bool canAddNewClassified(); + bool canDeleteClassified(); + + LLTabContainer* mTabContainer; + LLUICtrl* mNoItemsLabel; + LLButton* mNewButton; + LLButton* mDeleteButton; + + LLUUID mClassifiedToSelectOnLoad; + bool mClassifiedEditOnLoad; + bool mSheduledClassifiedCreation; +}; + + +class LLPanelProfileClassified + : public LLPanelProfilePropertiesProcessorTab +{ +public: + + static LLPanelProfileClassified* create(); + + LLPanelProfileClassified(); + + /*virtual*/ ~LLPanelProfileClassified(); + + BOOL postBuild() override; + + void onOpen(const LLSD& key) override; + + void processProperties(void* data, EAvatarProcessorType type) override; + + void setSnapshotId(const LLUUID& id); + + LLUUID getSnapshotId(); + + void setClassifiedId(const LLUUID& id) { mClassifiedId = id; } + + LLUUID& getClassifiedId() { return mClassifiedId; } + + void setClassifiedName(const std::string& name); + + std::string getClassifiedName(); + + void setDescription(const std::string& desc); + + std::string getDescription(); + + void setClassifiedLocation(const std::string& location); + + std::string getClassifiedLocation(); + + void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } + + LLVector3d& getPosGlobal() { return mPosGlobal; } + + void setParcelId(const LLUUID& id) { mParcelId = id; } + + LLUUID getParcelId() { return mParcelId; } + + void setSimName(const std::string& sim_name) { mSimName = sim_name; } + + std::string getSimName() { return mSimName; } + + void setFromSearch(bool val) { mFromSearch = val; } + + bool fromSearch() { return mFromSearch; } + + bool getInfoLoaded() { return mInfoLoaded; } + + void setInfoLoaded(bool loaded) { mInfoLoaded = loaded; } + + BOOL isDirty() const override; + + void resetDirty() override; + + bool isNew() { return mIsNew; } + + bool isNewWithErrors() { return mIsNewWithErrors; } + + bool canClose(); + + U32 getCategory(); + + void setCategory(U32 category); + + U32 getContentType(); + + void setContentType(bool mature); + + bool getAutoRenew(); + + S32 getPriceForListing() { return mPriceForListing; } + + void setEditMode(BOOL edit_mode); + bool getEditMode() {return mEditMode;} + + static void setClickThrough( + const LLUUID& classified_id, + S32 teleport, + S32 map, + S32 profile, + bool from_new_table); + + static void sendClickMessage( + const std::string& type, + bool from_search, + const LLUUID& classified_id, + const LLUUID& parcel_id, + const LLVector3d& global_pos, + const std::string& sim_name); + + void doSave(); + +protected: + + void resetData() override; + + void resetControls(); + + void updateButtons(); + void updateInfoRect(); + + static std::string createLocationText( + const std::string& original_name, + const std::string& sim_name, + const LLVector3d& pos_global); + + void sendClickMessage(const std::string& type); + + void scrollToTop(); + + void onEditClick(); + void onCancelClick(); + void onSaveClick(); + void onMapClick(); + void onTeleportClick(); + + void sendUpdate(); + + void enableSave(bool enable); + + void enableEditing(bool enable); + + std::string makeClassifiedName(); + + void setPriceForListing(S32 price) { mPriceForListing = price; } + + U8 getFlags(); + + std::string getLocationNotice(); + + bool isValidName(); + + void notifyInvalidName(); + + void onSetLocationClick(); + void onChange(); + + void onPublishFloaterPublishClicked(); + + void onTexturePickerMouseEnter(); + void onTexturePickerMouseLeave(); + + void onTextureSelected(); + + void updateTabLabel(const std::string& title); + +private: + + LLTextureCtrl* mSnapshotCtrl; + LLUICtrl* mEditIcon; + LLUICtrl* mClassifiedNameText; + LLTextEditor* mClassifiedDescText; + LLLineEditor* mClassifiedNameEdit; + LLTextEditor* mClassifiedDescEdit; + LLUICtrl* mLocationText; + LLUICtrl* mLocationEdit; + LLUICtrl* mCategoryText; + LLComboBox* mCategoryCombo; + LLUICtrl* mContentTypeText; + LLIconCtrl* mContentTypeM; + LLIconCtrl* mContentTypeG; + LLComboBox* mContentTypeCombo; + LLUICtrl* mPriceText; + LLUICtrl* mAutoRenewText; + LLUICtrl* mAutoRenewEdit; + + LLButton* mMapButton; + LLButton* mTeleportButton; + LLButton* mEditButton; + LLButton* mSaveButton; + LLButton* mSetLocationButton; + LLButton* mCancelButton; + + LLPanel* mUtilityBtnCnt; + LLPanel* mPublishBtnsCnt; + LLPanel* mSaveBtnCnt; + LLPanel* mCancelBtnCnt; + + LLScrollContainer* mScrollContainer; + LLView* mInfoPanel; + LLPanel* mInfoScroll; + LLPanel* mEditPanel; + + + LLUUID mClassifiedId; + LLVector3d mPosGlobal; + LLUUID mParcelId; + std::string mSimName; + bool mFromSearch; + bool mInfoLoaded; + bool mEditMode; + + // Needed for stat tracking + S32 mTeleportClicksOld; + S32 mMapClicksOld; + S32 mProfileClicksOld; + S32 mTeleportClicksNew; + S32 mMapClicksNew; + S32 mProfileClicksNew; + + S32 mPriceForListing; + + static void handleSearchStatResponse(LLUUID classifiedId, LLSD result); + + typedef std::list panel_list_t; + static panel_list_t sAllPanels; + + + bool mIsNew; + bool mIsNewWithErrors; + bool mCanClose; + bool mEditOnLoad; + + LLPublishClassifiedFloater* mPublishFloater; +}; + +#endif // LL_PANELPROFILECLASSIFIEDS_H diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp new file mode 100644 index 0000000000..774119f169 --- /dev/null +++ b/indra/newview/llpanelprofilepicks.cpp @@ -0,0 +1,883 @@ +/** + * @file llpanelprofilepicks.cpp + * @brief LLPanelProfilePicks and related class implementations + * + * $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 "llpanelprofilepicks.h" + +#include "llagent.h" +#include "llagentpicksinfo.h" +#include "llavataractions.h" +#include "llavatarpropertiesprocessor.h" +#include "llcommandhandler.h" +#include "lldispatcher.h" +#include "llfloaterreg.h" +#include "llfloaterworldmap.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" +#include "llpanelavatar.h" +#include "llpanelprofile.h" +#include "llparcel.h" +#include "llstartup.h" +#include "lltabcontainer.h" +#include "lltextbox.h" +#include "lltexteditor.h" +#include "lltexturectrl.h" +#include "lltexturectrl.h" +#include "lltrans.h" +#include "llviewergenericmessage.h" // send_generic_message +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" + +static LLPanelInjector t_panel_profile_picks("panel_profile_picks"); +static LLPanelInjector t_panel_profile_pick("panel_profile_pick"); + + +class LLPickHandler : public LLCommandHandler +{ +public: + + // requires trusted browser to trigger + LLPickHandler() : LLCommandHandler("pick", UNTRUSTED_THROTTLE) { } + + bool handle(const LLSD& params, const LLSD& query_map, + LLMediaCtrl* web) + { + if (LLStartUp::getStartupState() < STATE_STARTED) + { + return true; + } + + if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnablePicks")) + { + LLNotificationsUtil::add("NoPicks", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + } + + // handle app/pick/create urls first + if (params.size() == 1 && params[0].asString() == "create") + { + LLAvatarActions::createPick(); + return true; + } + + // then handle the general app/pick/{UUID}/{CMD} urls + if (params.size() < 2) + { + return false; + } + + // get the ID for the pick_id + LLUUID pick_id; + if (!pick_id.set(params[0], FALSE)) + { + return false; + } + + // edit the pick in the side tray. + // need to ask the server for more info first though... + const std::string verb = params[1].asString(); + if (verb == "edit") + { + LLAvatarActions::showPick(gAgent.getID(), pick_id); + return true; + } + else + { + LL_WARNS() << "unknown verb " << verb << LL_ENDL; + return false; + } + } +}; +LLPickHandler gPickHandler; + + +//----------------------------------------------------------------------------- +// LLPanelProfilePicks +//----------------------------------------------------------------------------- + +LLPanelProfilePicks::LLPanelProfilePicks() + : LLPanelProfilePropertiesProcessorTab() + , mPickToSelectOnLoad(LLUUID::null) +{ +} + +LLPanelProfilePicks::~LLPanelProfilePicks() +{ +} + +void LLPanelProfilePicks::onOpen(const LLSD& key) +{ + LLPanelProfilePropertiesProcessorTab::onOpen(key); + + resetData(); + + bool own_profile = getSelfProfile(); + if (own_profile) + { + mNewButton->setVisible(TRUE); + mNewButton->setEnabled(FALSE); + + mDeleteButton->setVisible(TRUE); + mDeleteButton->setEnabled(FALSE); + } + + childSetVisible("buttons_header", own_profile); +} + +void LLPanelProfilePicks::createPick(const LLPickData &data) +{ + if (getIsLoaded()) + { + if (canAddNewPick()) + { + mNoItemsLabel->setVisible(FALSE); + LLPanelProfilePick* pick_panel = LLPanelProfilePick::create(); + pick_panel->setAvatarId(getAvatarId()); + pick_panel->processProperties(&data); + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(pick_panel). + select_tab(true). + label(pick_panel->getPickName())); + updateButtons(); + } + else + { + // This means that something doesn't properly check limits + // before creating a pick + LL_WARNS() << "failed to add pick" << LL_ENDL; + } + } + else + { + mSheduledPickCreation.push_back(data); + } +} + +void LLPanelProfilePicks::selectPick(const LLUUID& pick_id) +{ + if (getIsLoaded()) + { + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfilePick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (pick_panel) + { + if (pick_panel->getPickId() == pick_id) + { + mTabContainer->selectTabPanel(pick_panel); + break; + } + } + } + } + else + { + mPickToSelectOnLoad = pick_id; + } +} + +BOOL LLPanelProfilePicks::postBuild() +{ + mTabContainer = getChild("tab_picks"); + mNoItemsLabel = getChild("picks_panel_text"); + mNewButton = getChild("new_btn"); + mDeleteButton = getChild("delete_btn"); + + mNewButton->setCommitCallback(boost::bind(&LLPanelProfilePicks::onClickNewBtn, this)); + mDeleteButton->setCommitCallback(boost::bind(&LLPanelProfilePicks::onClickDelete, this)); + + return TRUE; +} + +void LLPanelProfilePicks::onClickNewBtn() +{ + mNoItemsLabel->setVisible(FALSE); + LLPanelProfilePick* pick_panel = LLPanelProfilePick::create(); + pick_panel->setAvatarId(getAvatarId()); + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(pick_panel). + select_tab(true). + label(pick_panel->getPickName())); + updateButtons(); +} + +void LLPanelProfilePicks::onClickDelete() +{ + LLPanelProfilePick* pick_panel = dynamic_cast(mTabContainer->getCurrentPanel()); + if (pick_panel) + { + LLUUID pick_id = pick_panel->getPickId(); + LLSD args; + args["PICK"] = pick_panel->getPickName(); + LLSD payload; + payload["pick_id"] = pick_id; + payload["tab_idx"] = mTabContainer->getCurrentPanelIndex(); + LLNotificationsUtil::add("ProfileDeletePick", args, payload, + boost::bind(&LLPanelProfilePicks::callbackDeletePick, this, _1, _2)); + } +} + +void LLPanelProfilePicks::callbackDeletePick(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + if (0 == option) + { + LLUUID pick_id = notification["payload"]["pick_id"].asUUID(); + S32 tab_idx = notification["payload"]["tab_idx"].asInteger(); + + LLPanelProfilePick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (pick_panel && pick_panel->getPickId() == pick_id) + { + mTabContainer->removeTabPanel(pick_panel); + } + + if (pick_id.notNull()) + { + LLAvatarPropertiesProcessor::getInstance()->sendPickDelete(pick_id); + } + + updateButtons(); + } +} + +void LLPanelProfilePicks::processProperties(void* data, EAvatarProcessorType type) +{ + if (APT_PICKS == type) + { + LLAvatarPicks* avatar_picks = static_cast(data); + if (avatar_picks && getAvatarId() == avatar_picks->target_id) + { + processProperties(avatar_picks); + } + } +} + +void LLPanelProfilePicks::processProperties(const LLAvatarPicks* avatar_picks) +{ + LLUUID selected_id = mPickToSelectOnLoad; + bool has_selection = false; + if (mPickToSelectOnLoad.isNull()) + { + if (mTabContainer->getTabCount() > 0) + { + LLPanelProfilePick* active_pick_panel = dynamic_cast(mTabContainer->getCurrentPanel()); + if (active_pick_panel) + { + selected_id = active_pick_panel->getPickId(); + } + } + } + + mTabContainer->deleteAllTabs(); + + LLAvatarPicks::picks_list_t::const_iterator it = avatar_picks->picks_list.begin(); + for (; avatar_picks->picks_list.end() != it; ++it) + { + LLUUID pick_id = it->first; + std::string pick_name = it->second; + + LLPanelProfilePick* pick_panel = LLPanelProfilePick::create(); + + pick_panel->setPickId(pick_id); + pick_panel->setPickName(pick_name); + pick_panel->setAvatarId(getAvatarId()); + + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(pick_panel). + select_tab(selected_id == pick_id). + label(pick_name)); + + if (selected_id == pick_id) + { + has_selection = true; + } + } + + while (!mSheduledPickCreation.empty() && canAddNewPick()) + { + const LLPickData data = + mSheduledPickCreation.back(); + + LLPanelProfilePick* pick_panel = LLPanelProfilePick::create(); + pick_panel->setAvatarId(getAvatarId()); + pick_panel->processProperties(&data); + mTabContainer->addTabPanel( + LLTabContainer::TabPanelParams(). + panel(pick_panel). + select_tab(!has_selection). + label(pick_panel->getPickName())); + + mSheduledPickCreation.pop_back(); + has_selection = true; + } + + // reset 'do on load' values + mPickToSelectOnLoad = LLUUID::null; + mSheduledPickCreation.clear(); + + if (getSelfProfile()) + { + mNoItemsLabel->setValue(LLTrans::getString("NoPicksText")); + } + else + { + mNoItemsLabel->setValue(LLTrans::getString("NoAvatarPicksText")); + } + + bool has_data = mTabContainer->getTabCount() > 0; + mNoItemsLabel->setVisible(!has_data); + if (has_data && !has_selection) + { + mTabContainer->selectFirstTab(); + } + + setLoaded(); + updateButtons(); +} + +void LLPanelProfilePicks::resetData() +{ + resetLoading(); + mTabContainer->deleteAllTabs(); +} + +void LLPanelProfilePicks::updateButtons() +{ + if (getSelfProfile()) + { + mNewButton->setEnabled(canAddNewPick()); + mDeleteButton->setEnabled(canDeletePick()); + } +} + +void LLPanelProfilePicks::apply() +{ + if (getIsLoaded()) + { + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfilePick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (pick_panel) + { + pick_panel->apply(); + } + } + } +} + +void LLPanelProfilePicks::updateData() +{ + // Send picks request only once + LLUUID avatar_id = getAvatarId(); + if (!getStarted() && avatar_id.notNull()) + { + setIsLoading(); + + LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(avatar_id); + } + if (!getIsLoaded()) + { + mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); + mNoItemsLabel->setVisible(TRUE); + } +} + +bool LLPanelProfilePicks::hasUnsavedChanges() +{ + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfilePick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (pick_panel && (pick_panel->isDirty() || pick_panel->isDirty())) + { + return true; + } + } + return false; +} + +void LLPanelProfilePicks::commitUnsavedChanges() +{ + for (S32 tab_idx = 0; tab_idx < mTabContainer->getTabCount(); ++tab_idx) + { + LLPanelProfilePick* pick_panel = dynamic_cast(mTabContainer->getPanelByIndex(tab_idx)); + if (pick_panel) + { + pick_panel->apply(); + } + } +} + +bool LLPanelProfilePicks::canAddNewPick() +{ + return (!LLAgentPicksInfo::getInstance()->isPickLimitReached() && + mTabContainer->getTabCount() < LLAgentPicksInfo::getInstance()->getMaxNumberOfPicks()); +} + +bool LLPanelProfilePicks::canDeletePick() +{ + return (mTabContainer->getTabCount() > 0); +} + + +//----------------------------------------------------------------------------- +// LLPanelProfilePick +//----------------------------------------------------------------------------- + +LLPanelProfilePick::LLPanelProfilePick() + : LLPanelProfilePropertiesProcessorTab() + , LLRemoteParcelInfoObserver() + , mSnapshotCtrl(NULL) + , mPickId(LLUUID::null) + , mParcelId(LLUUID::null) + , mRequestedId(LLUUID::null) + , mLocationChanged(false) + , mNewPick(false) + , mIsEditing(false) +{ +} + +//static +LLPanelProfilePick* LLPanelProfilePick::create() +{ + LLPanelProfilePick* panel = new LLPanelProfilePick(); + panel->buildFromFile("panel_profile_pick.xml"); + return panel; +} + +LLPanelProfilePick::~LLPanelProfilePick() +{ + if (mParcelId.notNull()) + { + LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelId, this); + } +} + +void LLPanelProfilePick::setAvatarId(const LLUUID& avatar_id) +{ + if (avatar_id.isNull()) + { + return; + } + LLPanelProfilePropertiesProcessorTab::setAvatarId(avatar_id); + + // creating new Pick + if (getPickId().isNull() && getSelfProfile()) + { + mNewPick = true; + + setPosGlobal(gAgent.getPositionGlobal()); + + LLUUID parcel_id = LLUUID::null, snapshot_id = LLUUID::null; + std::string pick_name, pick_desc, region_name; + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel) + { + parcel_id = parcel->getID(); + pick_name = parcel->getName(); + pick_desc = parcel->getDesc(); + snapshot_id = parcel->getSnapshotID(); + } + + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + region_name = region->getName(); + } + + setParcelID(parcel_id); + setPickName(pick_name.empty() ? region_name : pick_name); + setPickDesc(pick_desc); + setSnapshotId(snapshot_id); + setPickLocation(createLocationText(getLocationNotice(), pick_name, region_name, getPosGlobal())); + + enableSaveButton(TRUE); + } + else + { + LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest(getAvatarId(), getPickId()); + + enableSaveButton(FALSE); + } + + resetDirty(); + + if (getSelfProfile()) + { + mPickName->setEnabled(TRUE); + mPickDescription->setEnabled(TRUE); + mSetCurrentLocationButton->setVisible(TRUE); + } + else + { + mSnapshotCtrl->setEnabled(FALSE); + } +} + +BOOL LLPanelProfilePick::postBuild() +{ + mPickName = getChild("pick_name"); + mPickDescription = getChild("pick_desc"); + mSaveButton = getChild("save_changes_btn"); + mCreateButton = getChild("create_changes_btn"); + mCancelButton = getChild("cancel_changes_btn"); + mSetCurrentLocationButton = getChild("set_to_curr_location_btn"); + + mSnapshotCtrl = getChild("pick_snapshot"); + mSnapshotCtrl->setCommitCallback(boost::bind(&LLPanelProfilePick::onSnapshotChanged, this)); + + childSetAction("teleport_btn", boost::bind(&LLPanelProfilePick::onClickTeleport, this)); + childSetAction("show_on_map_btn", boost::bind(&LLPanelProfilePick::onClickMap, this)); + + mSaveButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this)); + mCreateButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSave, this)); + mCancelButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickCancel, this)); + mSetCurrentLocationButton->setCommitCallback(boost::bind(&LLPanelProfilePick::onClickSetLocation, this)); + + mPickName->setKeystrokeCallback(boost::bind(&LLPanelProfilePick::onPickChanged, this, _1), NULL); + mPickName->setEnabled(FALSE); + + mPickDescription->setKeystrokeCallback(boost::bind(&LLPanelProfilePick::onPickChanged, this, _1)); + mPickDescription->setFocusReceivedCallback(boost::bind(&LLPanelProfilePick::onDescriptionFocusReceived, this)); + + getChild("pick_location")->setEnabled(FALSE); + + return TRUE; +} + +void LLPanelProfilePick::onDescriptionFocusReceived() +{ + if (!mIsEditing && getSelfProfile()) + { + mIsEditing = true; + mPickDescription->setParseHTML(false); + } +} + +void LLPanelProfilePick::processProperties(void* data, EAvatarProcessorType type) +{ + if (APT_PICK_INFO != type) + { + return; + } + + LLPickData* pick_info = static_cast(data); + if (!pick_info + || pick_info->creator_id != getAvatarId() + || pick_info->pick_id != getPickId()) + { + return; + } + + processProperties(pick_info); +} + +void LLPanelProfilePick::processProperties(const LLPickData* pick_info) +{ + mIsEditing = false; + mPickDescription->setParseHTML(true); + mParcelId = pick_info->parcel_id; + setSnapshotId(pick_info->snapshot_id); + if (!getSelfProfile()) + { + mSnapshotCtrl->setEnabled(FALSE); + } + setPickName(pick_info->name); + setPickDesc(pick_info->desc); + setPosGlobal(pick_info->pos_global); + + // Send remote parcel info request to get parcel name and sim (region) name. + sendParcelInfoRequest(); + + // *NOTE dzaporozhan + // We want to keep listening to APT_PICK_INFO because user may + // edit the Pick and we have to update Pick info panel. + // revomeObserver is called from onClickBack + + setLoaded(); +} + +void LLPanelProfilePick::apply() +{ + if ((mNewPick || getIsLoaded()) && isDirty()) + { + sendUpdate(); + } +} + +void LLPanelProfilePick::setSnapshotId(const LLUUID& id) +{ + mSnapshotCtrl->setImageAssetID(id); + mSnapshotCtrl->setValid(TRUE); +} + +void LLPanelProfilePick::setPickName(const std::string& name) +{ + mPickName->setValue(name); +} + +const std::string LLPanelProfilePick::getPickName() +{ + return mPickName->getValue().asString(); +} + +void LLPanelProfilePick::setPickDesc(const std::string& desc) +{ + mPickDescription->setValue(desc); +} + +void LLPanelProfilePick::setPickLocation(const std::string& location) +{ + getChild("pick_location")->setValue(location); +} + +void LLPanelProfilePick::onClickMap() +{ + LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); + LLFloaterReg::showInstance("world_map", "center"); +} + +void LLPanelProfilePick::onClickTeleport() +{ + if (!getPosGlobal().isExactlyZero()) + { + gAgent.teleportViaLocation(getPosGlobal()); + LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); + } +} + +void LLPanelProfilePick::enableSaveButton(BOOL enable) +{ + childSetVisible("save_changes_lp", enable); + + childSetVisible("save_btn_lp", enable && !mNewPick); + childSetVisible("create_btn_lp", enable && mNewPick); + childSetVisible("cancel_btn_lp", enable && !mNewPick); +} + +void LLPanelProfilePick::onSnapshotChanged() +{ + enableSaveButton(TRUE); +} + +void LLPanelProfilePick::onPickChanged(LLUICtrl* ctrl) +{ + if (ctrl && ctrl == mPickName) + { + updateTabLabel(mPickName->getText()); + } + + enableSaveButton(isDirty()); +} + +void LLPanelProfilePick::resetDirty() +{ + LLPanel::resetDirty(); + + mPickName->resetDirty(); + mPickDescription->resetDirty(); + mSnapshotCtrl->resetDirty(); + mLocationChanged = false; +} + +BOOL LLPanelProfilePick::isDirty() const +{ + if (mNewPick + || LLPanel::isDirty() + || mLocationChanged + || mSnapshotCtrl->isDirty() + || mPickName->isDirty() + || mPickDescription->isDirty()) + { + return TRUE; + } + return FALSE; +} + +void LLPanelProfilePick::onClickSetLocation() +{ + // Save location for later use. + setPosGlobal(gAgent.getPositionGlobal()); + + std::string parcel_name, region_name; + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (parcel) + { + mParcelId = parcel->getID(); + parcel_name = parcel->getName(); + } + + LLViewerRegion* region = gAgent.getRegion(); + if (region) + { + region_name = region->getName(); + } + + setPickLocation(createLocationText(getLocationNotice(), parcel_name, region_name, getPosGlobal())); + + mLocationChanged = true; + enableSaveButton(TRUE); +} + +void LLPanelProfilePick::onClickSave() +{ + sendUpdate(); + + mLocationChanged = false; +} + +void LLPanelProfilePick::onClickCancel() +{ + LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest(getAvatarId(), getPickId()); + mLocationChanged = false; + enableSaveButton(FALSE); +} + +std::string LLPanelProfilePick::getLocationNotice() +{ + static const std::string notice = getString("location_notice"); + return notice; +} + +void LLPanelProfilePick::sendParcelInfoRequest() +{ + if (mParcelId != mRequestedId) + { + if (mRequestedId.notNull()) + { + LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mRequestedId, this); + } + LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this); + LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId); + + mRequestedId = mParcelId; + } +} + +void LLPanelProfilePick::processParcelInfo(const LLParcelData& parcel_data) +{ + setPickLocation(createLocationText(LLStringUtil::null, parcel_data.name, parcel_data.sim_name, getPosGlobal())); + + // We have received parcel info for the requested ID so clear it now. + mRequestedId.setNull(); + + if (mParcelId.notNull()) + { + LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelId, this); + } +} + +void LLPanelProfilePick::sendUpdate() +{ + LLPickData pick_data; + + // If we don't have a pick id yet, we'll need to generate one, + // otherwise we'll keep overwriting pick_id 00000 in the database. + if (getPickId().isNull()) + { + getPickId().generate(); + } + + pick_data.agent_id = gAgentID; + pick_data.session_id = gAgent.getSessionID(); + pick_data.pick_id = getPickId(); + pick_data.creator_id = gAgentID;; + + //legacy var need to be deleted + pick_data.top_pick = FALSE; + pick_data.parcel_id = mParcelId; + pick_data.name = getPickName(); + pick_data.desc = mPickDescription->getValue().asString(); + pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); + pick_data.pos_global = getPosGlobal(); + pick_data.sort_order = 0; + pick_data.enabled = TRUE; + + LLAvatarPropertiesProcessor::getInstance()->sendPickInfoUpdate(&pick_data); + + if(mNewPick) + { + // Assume a successful create pick operation, make new number of picks + // available immediately. Actual number of picks will be requested in + // LLAvatarPropertiesProcessor::sendPickInfoUpdate and updated upon server respond. + LLAgentPicksInfo::getInstance()->incrementNumberOfPicks(); + } +} + +// static +std::string LLPanelProfilePick::createLocationText(const std::string& owner_name, const std::string& original_name, const std::string& sim_name, const LLVector3d& pos_global) +{ + std::string location_text(owner_name); + if (!original_name.empty()) + { + if (!location_text.empty()) + { + location_text.append(", "); + } + location_text.append(original_name); + + } + + if (!sim_name.empty()) + { + if (!location_text.empty()) + { + location_text.append(", "); + } + location_text.append(sim_name); + } + + if (!location_text.empty()) + { + location_text.append(" "); + } + + if (!pos_global.isNull()) + { + S32 region_x = ll_round((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS; + S32 region_y = ll_round((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS; + S32 region_z = ll_round((F32)pos_global.mdV[VZ]); + location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); + } + return location_text; +} + +void LLPanelProfilePick::updateTabLabel(const std::string& title) +{ + setLabel(title); + LLTabContainer* parent = dynamic_cast(getParent()); + if (parent) + { + parent->setCurrentTabName(title); + } +} + diff --git a/indra/newview/llpanelprofilepicks.h b/indra/newview/llpanelprofilepicks.h new file mode 100644 index 0000000000..f84463cc9b --- /dev/null +++ b/indra/newview/llpanelprofilepicks.h @@ -0,0 +1,248 @@ +/** + * @file llpanelprofilepicks.h + * @brief LLPanelProfilePicks and related class definitions + * + * $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_LLPANELPICKS_H +#define LL_LLPANELPICKS_H + +#include "llpanel.h" +#include "lluuid.h" +#include "llavatarpropertiesprocessor.h" +#include "llpanelavatar.h" +#include "llremoteparcelrequest.h" + +class LLTabContainer; +class LLTextureCtrl; +class LLMediaCtrl; +class LLLineEditor; +class LLTextEditor; + + +/** +* Panel for displaying Avatar's picks. +*/ +class LLPanelProfilePicks + : public LLPanelProfilePropertiesProcessorTab +{ +public: + LLPanelProfilePicks(); + /*virtual*/ ~LLPanelProfilePicks(); + + BOOL postBuild() override; + + void onOpen(const LLSD& key) override; + + void createPick(const LLPickData &data); + void selectPick(const LLUUID& pick_id); + + void processProperties(void* data, EAvatarProcessorType type) override; + void processProperties(const LLAvatarPicks* avatar_picks); + + void resetData() override; + + void updateButtons(); + + /** + * Saves changes. + */ + virtual void apply(); + + /** + * Sends update data request to server. + */ + void updateData() override; + + bool hasUnsavedChanges() override; + void commitUnsavedChanges() override; + + friend void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id); + +private: + void onClickNewBtn(); + void onClickDelete(); + void callbackDeletePick(const LLSD& notification, const LLSD& response); + + bool canAddNewPick(); + bool canDeletePick(); + + LLTabContainer* mTabContainer; + LLUICtrl* mNoItemsLabel; + LLButton* mNewButton; + LLButton* mDeleteButton; + + LLUUID mPickToSelectOnLoad; + std::list mSheduledPickCreation; +}; + + +class LLPanelProfilePick + : public LLPanelProfilePropertiesProcessorTab + , public LLRemoteParcelInfoObserver +{ +public: + + // Creates new panel + static LLPanelProfilePick* create(); + + LLPanelProfilePick(); + + /*virtual*/ ~LLPanelProfilePick(); + + BOOL postBuild() override; + + void setAvatarId(const LLUUID& avatar_id) override; + + void setPickId(const LLUUID& id) { mPickId = id; } + virtual LLUUID& getPickId() { return mPickId; } + + virtual void setPickName(const std::string& name); + const std::string getPickName(); + + void processProperties(void* data, EAvatarProcessorType type) override; + void processProperties(const LLPickData* pick_data); + + /** + * Returns true if any of Pick properties was changed by user. + */ + BOOL isDirty() const override; + + /** + * Saves changes. + */ + virtual void apply(); + + void updateTabLabel(const std::string& title); + + //This stuff we got from LLRemoteParcelObserver, in the last one we intentionally do nothing + void processParcelInfo(const LLParcelData& parcel_data) override; + void setParcelID(const LLUUID& parcel_id) override { mParcelId = parcel_id; } + void setErrorStatus(S32 status, const std::string& reason) override {}; + +protected: + + /** + * Sends remote parcel info request to resolve parcel name from its ID. + */ + void sendParcelInfoRequest(); + + /** + * "Location text" is actually the owner name, the original + * name that owner gave the parcel, and the location. + */ + static std::string createLocationText( + const std::string& owner_name, + const std::string& original_name, + const std::string& sim_name, + const LLVector3d& pos_global); + + /** + * Sets snapshot id. + * + * Will mark snapshot control as valid if id is not null. + * Will mark snapshot control as invalid if id is null. If null id is a valid value, + * you have to manually mark snapshot is valid. + */ + virtual void setSnapshotId(const LLUUID& id); + virtual void setPickDesc(const std::string& desc); + virtual void setPickLocation(const std::string& location); + + virtual void setPosGlobal(const LLVector3d& pos) { mPosGlobal = pos; } + virtual LLVector3d& getPosGlobal() { return mPosGlobal; } + + /** + * Callback for "Map" button, opens Map + */ + void onClickMap(); + + /** + * Callback for "Teleport" button, teleports user to Pick location. + */ + void onClickTeleport(); + + /** + * Enables/disables "Save" button + */ + void enableSaveButton(BOOL enable); + + /** + * Called when snapshot image changes. + */ + void onSnapshotChanged(); + + /** + * Callback for Pick snapshot, name and description changed event. + */ + void onPickChanged(LLUICtrl* ctrl); + + /** + * Resets panel and all cantrols to unedited state + */ + void resetDirty() override; + + /** + * Callback for "Set Location" button click + */ + void onClickSetLocation(); + + /** + * Callback for "Save" and "Create" button click + */ + void onClickSave(); + + /** + * Callback for "Save" button click + */ + void onClickCancel(); + + std::string getLocationNotice(); + + /** + * Sends Pick properties to server. + */ + void sendUpdate(); + +protected: + + LLTextureCtrl* mSnapshotCtrl; + LLLineEditor* mPickName; + LLTextEditor* mPickDescription; + LLButton* mSetCurrentLocationButton; + LLButton* mSaveButton; + LLButton* mCreateButton; + LLButton* mCancelButton; + + LLVector3d mPosGlobal; + LLUUID mParcelId; + LLUUID mPickId; + LLUUID mRequestedId; + + bool mLocationChanged; + bool mNewPick; + bool mIsEditing; + + void onDescriptionFocusReceived(); +}; + +#endif // LL_LLPANELPICKS_H diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 5b02609a53..39f4c7485b 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -595,13 +595,6 @@ void LLPanelVolume::refresh() mRootObject = NULL; } - BOOL visible = LLViewerShaderMgr::instance()->getShaderLevel(LLViewerShaderMgr::SHADER_DEFERRED) > 0 ? TRUE : FALSE; - - getChildView("Light FOV")->setVisible( visible); - getChildView("Light Focus")->setVisible( visible); - getChildView("Light Ambiance")->setVisible( visible); - getChildView("light texture control")->setVisible( visible); - bool enable_mesh = false; LLSD sim_features; diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 94d20828ec..9b60d1ae2f 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -38,154 +38,11 @@ #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally #endif -// See EXT-4301. -/** - * class LLAvalineUpdater - observe the list of voice participants in session and check - * presence of Avaline Callers among them. - * - * LLAvalineUpdater is a LLVoiceClientParticipantObserver. It provides two kinds of validation: - * - whether Avaline caller presence among participants; - * - whether watched Avaline caller still exists in voice channel. - * Both validations have callbacks which will notify subscriber if any of event occur. - * - * @see findAvalineCaller() - * @see checkIfAvalineCallersExist() - */ -class LLAvalineUpdater : public LLVoiceClientParticipantObserver -{ -public: - typedef boost::function process_avaline_callback_t; - - LLAvalineUpdater(process_avaline_callback_t found_cb, process_avaline_callback_t removed_cb) - : mAvalineFoundCallback(found_cb) - , mAvalineRemovedCallback(removed_cb) - { - LLVoiceClient::getInstance()->addObserver(this); - } - ~LLAvalineUpdater() - { - if (LLVoiceClient::instanceExists()) - { - LLVoiceClient::getInstance()->removeObserver(this); - } - } - - /** - * Adds UUID of Avaline caller to watch. - * - * @see checkIfAvalineCallersExist(). - */ - void watchAvalineCaller(const LLUUID& avaline_caller_id) - { - mAvalineCallers.insert(avaline_caller_id); - } - - void onParticipantsChanged() - { - uuid_set_t participant_uuids; - LLVoiceClient::getInstance()->getParticipantList(participant_uuids); - - - // check whether Avaline caller exists among voice participants - // and notify Participant List - findAvalineCaller(participant_uuids); - - // check whether watched Avaline callers still present among voice participant - // and remove if absents. - checkIfAvalineCallersExist(participant_uuids); - } - -private: - typedef std::set uuid_set_t; - - /** - * Finds Avaline callers among voice participants and calls mAvalineFoundCallback. - * - * When Avatar is in group call with Avaline caller and then ends call Avaline caller stays - * in Group Chat floater (exists in LLSpeakerMgr). If Avatar starts call with that group again - * Avaline caller is added to voice channel AFTER Avatar is connected to group call. - * But Voice Control Panel (VCP) is filled from session LLSpeakerMgr and there is no information - * if a speaker is Avaline caller. - * - * In this case this speaker is created as avatar and will be recreated when it appears in - * Avatar's Voice session. - * - * @see LLParticipantList::onAvalineCallerFound() - */ - void findAvalineCaller(const uuid_set_t& participant_uuids) - { - uuid_set_t::const_iterator it = participant_uuids.begin(), it_end = participant_uuids.end(); - - for(; it != it_end; ++it) - { - const LLUUID& participant_id = *it; - if (!LLVoiceClient::getInstance()->isParticipantAvatar(participant_id)) - { - LL_DEBUGS("Avaline") << "Avaline caller found among voice participants: " << participant_id << LL_ENDL; - - if (mAvalineFoundCallback) - { - mAvalineFoundCallback(participant_id); - } - } - } - } - - /** - * Finds Avaline callers which are not anymore among voice participants and calls mAvalineRemovedCallback. - * - * The problem is when Avaline caller ends a call it is removed from Voice Client session but - * still exists in LLSpeakerMgr. Server does not send such information. - * This method implements a HUCK to notify subscribers that watched Avaline callers by class - * are not anymore in the call. - * - * @see LLParticipantList::onAvalineCallerRemoved() - */ - void checkIfAvalineCallersExist(const uuid_set_t& participant_uuids) - { - uuid_set_t::iterator it = mAvalineCallers.begin(); - uuid_set_t::const_iterator participants_it_end = participant_uuids.end(); - - while (it != mAvalineCallers.end()) - { - const LLUUID participant_id = *it; - LL_DEBUGS("Avaline") << "Check avaline caller: " << participant_id << LL_ENDL; - bool not_found = participant_uuids.find(participant_id) == participants_it_end; - if (not_found) - { - LL_DEBUGS("Avaline") << "Watched Avaline caller is not found among voice participants: " << participant_id << LL_ENDL; - - // notify Participant List - if (mAvalineRemovedCallback) - { - mAvalineRemovedCallback(participant_id); - } - - // remove from the watch list - mAvalineCallers.erase(it++); - } - else - { - ++it; - } - } - } - - process_avaline_callback_t mAvalineFoundCallback; - process_avaline_callback_t mAvalineRemovedCallback; - - uuid_set_t mAvalineCallers; -}; - LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLFolderViewModelInterface& root_view_model) : LLConversationItemSession(data_source->getSessionID(), root_view_model), mSpeakerMgr(data_source), mValidateSpeakerCallback(NULL) { - - mAvalineUpdater = new LLAvalineUpdater(boost::bind(&LLParticipantList::onAvalineCallerFound, this, _1), - boost::bind(&LLParticipantList::onAvalineCallerRemoved, this, _1)); - mSpeakerAddListener = new SpeakerAddListener(*this); mSpeakerRemoveListener = new SpeakerRemoveListener(*this); mSpeakerClearListener = new SpeakerClearListener(*this); @@ -243,32 +100,6 @@ LLParticipantList::LLParticipantList(LLSpeakerMgr* data_source, LLFolderViewMode LLParticipantList::~LLParticipantList() { - delete mAvalineUpdater; -} - -/* - Seems this method is not necessary after onAvalineCallerRemoved was implemented; - - It does nothing because list item is always created with correct class type for Avaline caller. - For now Avaline Caller is removed from the LLSpeakerMgr List when it is removed from the Voice Client - session. - This happens in two cases: if Avaline Caller ends call itself or if Resident ends group call. - - Probably Avaline caller should be removed from the LLSpeakerMgr list ONLY if it ends call itself. - Asked in EXT-4301. -*/ -void LLParticipantList::onAvalineCallerFound(const LLUUID& participant_id) -{ - removeParticipant(participant_id); - // re-add avaline caller with a correct class instance. - addAvatarIDExceptAgent(participant_id); -} - -void LLParticipantList::onAvalineCallerRemoved(const LLUUID& participant_id) -{ - LL_DEBUGS("Avaline") << "Removing avaline caller from the list: " << participant_id << LL_ENDL; - - mSpeakerMgr->removeAvalineSpeaker(participant_id); } void LLParticipantList::setValidateSpeakerCallback(validate_speaker_callback_t cb) @@ -386,7 +217,6 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id) std::string display_name = LLVoiceClient::getInstance()->getDisplayName(avatar_id); // Create a participant view model instance participant = new LLConversationItemParticipant(display_name.empty() ? LLTrans::getString("AvatarNameWaiting") : display_name, avatar_id, mRootViewModel); - mAvalineUpdater->watchAvalineCaller(avatar_id); } // *TODO : Need to update the online/offline status of the participant diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 3a3ae76604..14c0a63692 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -32,7 +32,6 @@ class LLSpeakerMgr; class LLUICtrl; -class LLAvalineUpdater; class LLParticipantList : public LLConversationItemSession { @@ -133,8 +132,6 @@ protected: }; private: - void onAvalineCallerFound(const LLUUID& participant_id); - void onAvalineCallerRemoved(const LLUUID& participant_id); /** * Adjusts passed participant to work properly. @@ -156,7 +153,6 @@ private: LLPointer mSpeakerMuteListener; validate_speaker_callback_t mValidateSpeakerCallback; - LLAvalineUpdater* mAvalineUpdater; }; #endif // LL_PARTICIPANTLIST_H diff --git a/indra/newview/llpersistentnotificationstorage.cpp b/indra/newview/llpersistentnotificationstorage.cpp index 9bc77393dc..9bf771db8a 100644 --- a/indra/newview/llpersistentnotificationstorage.cpp +++ b/indra/newview/llpersistentnotificationstorage.cpp @@ -163,12 +163,16 @@ void LLPersistentNotificationStorage::loadNotifications() LL_INFOS("LLPersistentNotificationStorage") << "finished loading notifications" << LL_ENDL; } +void LLPersistentNotificationStorage::reset() +{ + std::string file_name = "open_notifications_" + LLGridManager::getInstance()->getGrid() + ".xml"; + setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, file_name)); + setOldFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml")); +} + void LLPersistentNotificationStorage::initialize() { - std::string file_name = "open_notifications_" + LLGridManager::getInstance()->getGrid() + ".xml"; - setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, file_name)); - setOldFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "open_notifications.xml")); - + reset(); LLNotifications::instance().getChannel("Persistent")-> connectChanged(boost::bind(&LLPersistentNotificationStorage::onPersistentChannelChanged, this, _1)); } diff --git a/indra/newview/llpersistentnotificationstorage.h b/indra/newview/llpersistentnotificationstorage.h index 1fb4487286..335d85aaf6 100644 --- a/indra/newview/llpersistentnotificationstorage.h +++ b/indra/newview/llpersistentnotificationstorage.h @@ -52,6 +52,7 @@ public: void saveNotifications(); void loadNotifications(); + void reset(); protected: diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 230def5362..3fd4f51559 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -866,7 +866,10 @@ bool LLPreviewNotecard::loadNotecardText(const std::string& filename) buffer[nread] = '\0'; fclose(file); - mEditor->setText(LLStringExplicit(buffer)); + std::string text = std::string(buffer); + LLStringUtil::replaceTabsWithSpaces(text, LLTextEditor::spacesPerTab()); + + mEditor->setText(text); delete[] buffer; return true; diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index a32dc8beda..d677a996c1 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -601,7 +601,10 @@ bool LLScriptEdCore::loadScriptText(const std::string& filename) buffer[nread] = '\0'; fclose(file); - mEditor->setText(LLStringExplicit(buffer)); + std::string text = std::string(buffer); + LLStringUtil::replaceTabsWithSpaces(text, LLTextEditor::spacesPerTab()); + + mEditor->setText(text); delete[] buffer; return true; diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp index cd7b93aba7..10177c8023 100644 --- a/indra/newview/llpreviewtexture.cpp +++ b/indra/newview/llpreviewtexture.cpp @@ -367,7 +367,10 @@ void LLPreviewTexture::reshape(S32 width, S32 height, BOOL called_from_parent) // add space for dimensions and aspect ratio S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD; - + if (getChild("buttons_panel")->getVisible()) + { + info_height += getChild("buttons_panel")->getRect().getHeight(); + } LLRect client_rect(horiz_pad, getRect().getHeight(), getRect().getWidth() - horiz_pad, 0); client_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD); client_rect.mBottom += PREVIEW_BORDER + CLIENT_RECT_VPAD + info_height ; @@ -412,6 +415,16 @@ void LLPreviewTexture::openToSave() mPreviewToSave = TRUE; } +void LLPreviewTexture::hideCtrlButtons() +{ + getChildView("desc txt")->setVisible(false); + getChildView("desc")->setVisible(false); + getChild("preview_stack")->collapsePanel(getChild("buttons_panel"), true); + getChild("buttons_panel")->setVisible(false); + getChild("combo_aspect_ratio")->setCurrentByIndex(0); //unconstrained + reshape(getRect().getWidth(), getRect().getHeight()); +} + // static void LLPreviewTexture::onFileLoadedForSave(BOOL success, LLViewerFetchedTexture *src_vi, diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h index 9b6a843875..16db51332e 100644 --- a/indra/newview/llpreviewtexture.h +++ b/indra/newview/llpreviewtexture.h @@ -67,6 +67,8 @@ public: static void onSaveAsBtn(void* data); + void hideCtrlButtons(); + /*virtual*/ void setObjectID(const LLUUID& object_id); protected: void init(); diff --git a/indra/newview/llrecentpeople.cpp b/indra/newview/llrecentpeople.cpp index 83b0c4f1bf..0faf6bf889 100644 --- a/indra/newview/llrecentpeople.cpp +++ b/indra/newview/llrecentpeople.cpp @@ -42,14 +42,6 @@ bool LLRecentPeople::add(const LLUUID& id, const LLSD& userdata) if (is_not_group_id) { - // For each avaline call the id of caller is different even if - // the phone number is the same. - // To avoid duplication of avaline list items in the recent list - // of panel People, deleting id's with similar phone number. - const LLUUID& caller_id = getIDByPhoneNumber(userdata); - if (caller_id.notNull()) - mPeople.erase(caller_id); - //[] instead of insert to replace existing id->llsd["date"] with new date value mPeople[id] = userdata; mChangedSignal(); @@ -90,35 +82,6 @@ const LLSD& LLRecentPeople::getData(const LLUUID& id) const return no_data; } -bool LLRecentPeople::isAvalineCaller(const LLUUID& id) const -{ - recent_people_t::const_iterator it = mPeople.find(id); - - if (it != mPeople.end()) - { - const LLSD& user = it->second; - return user["avaline_call"].asBoolean(); - } - - return false; -} - -const LLUUID& LLRecentPeople::getIDByPhoneNumber(const LLSD& userdata) -{ - if (!userdata["avaline_call"].asBoolean()) - return LLUUID::null; - - for (recent_people_t::const_iterator it = mPeople.begin(); it != mPeople.end(); ++it) - { - const LLSD& user_info = it->second; - - if (user_info["call_number"].asString() == userdata["call_number"].asString()) - return it->first; - } - - return LLUUID::null; -} - // virtual bool LLRecentPeople::handleEvent(LLPointer event, const LLSD& userdata) { diff --git a/indra/newview/llrecentpeople.h b/indra/newview/llrecentpeople.h index 18b669ff4f..1b322f2c0a 100644 --- a/indra/newview/llrecentpeople.h +++ b/indra/newview/llrecentpeople.h @@ -62,9 +62,7 @@ public: * @param id avatar to add. * * @param userdata additional information about last interaction party. - * For example when last interaction party is not an avatar - * but an avaline caller, additional info (such as phone - * number, session id and etc.) should be added. + * For example session id can be added. * * @return false if the avatar is in the list already, true otherwise */ @@ -96,13 +94,6 @@ public: */ const LLSD& getData(const LLUUID& id) const; - /** - * Checks whether specific participant is an avaline caller - * - * @param id identifier of specific participant - */ - bool isAvalineCaller(const LLUUID& id) const; - /** * Set callback to be called when the list changed. * @@ -122,8 +113,6 @@ public: private: - const LLUUID& getIDByPhoneNumber(const LLSD& userdata); - typedef std::map recent_people_t; recent_people_t mPeople; signal_t mChangedSignal; diff --git a/indra/newview/llremoteparcelrequest.cpp b/indra/newview/llremoteparcelrequest.cpp index 055ccd5818..f4ace52faa 100644 --- a/indra/newview/llremoteparcelrequest.cpp +++ b/indra/newview/llremoteparcelrequest.cpp @@ -213,7 +213,12 @@ void LLRemoteParcelInfoProcessor::regionParcelInfoCoro(std::string url, if (!status) { - observer->setErrorStatus(status.getStatus(), status.getMessage()); + std::string message = status.getMessage(); + if (message.empty()) + { + message = status.toString(); + } + observer->setErrorStatus(status.getStatus(), message); } else { diff --git a/indra/newview/llsearchableui.h b/indra/newview/llsearchableui.h index e033cae3ab..31f11eb8ef 100644 --- a/indra/newview/llsearchableui.h +++ b/indra/newview/llsearchableui.h @@ -41,9 +41,9 @@ namespace ll struct PanelData; struct TabContainerData; - typedef boost::shared_ptr< SearchableItem > SearchableItemPtr; - typedef boost::shared_ptr< PanelData > PanelDataPtr; - typedef boost::shared_ptr< TabContainerData > TabContainerDataPtr; + typedef std::shared_ptr< SearchableItem > SearchableItemPtr; + typedef std::shared_ptr< PanelData > PanelDataPtr; + typedef std::shared_ptr< TabContainerData > TabContainerDataPtr; typedef std::vector< TabContainerData > tTabContainerDataList; typedef std::vector< SearchableItemPtr > tSearchableItemList; @@ -55,7 +55,7 @@ namespace ll LLView const *mView; ll::ui::SearchableControl const *mCtrl; - std::vector< boost::shared_ptr< SearchableItem > > mChildren; + std::vector< std::shared_ptr< SearchableItem > > mChildren; virtual ~SearchableItem(); @@ -68,8 +68,8 @@ namespace ll LLPanel const *mPanel; std::string mLabel; - std::vector< boost::shared_ptr< SearchableItem > > mChildren; - std::vector< boost::shared_ptr< PanelData > > mChildPanel; + std::vector< std::shared_ptr< SearchableItem > > mChildren; + std::vector< std::shared_ptr< PanelData > > mChildPanel; virtual ~PanelData(); diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 82a165cb35..86f7d2bf25 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -310,14 +310,29 @@ void LLSelectMgr::resetObjectOverrides(LLObjectSelectionHandle selected_handle) { struct f : public LLSelectedNodeFunctor { + f(bool a, LLSelectMgr* p) : mAvatarOverridesPersist(a), mManager(p) {} + bool mAvatarOverridesPersist; + LLSelectMgr* mManager; virtual bool apply(LLSelectNode* node) { + if (mAvatarOverridesPersist) + { + LLViewerObject* object = node->getObject(); + if (object && !object->getParent()) + { + LLVOAvatar* avatar = object->asAvatar(); + if (avatar) + { + mManager->mAvatarOverridesMap.emplace(avatar->getID(), AvatarPositionOverride(node->mLastPositionLocal, node->mLastRotation, object)); + } + } + } node->mLastPositionLocal.setVec(0, 0, 0); node->mLastRotation = LLQuaternion(); node->mLastScale.setVec(0, 0, 0); return true; } - } func; + } func(mAllowSelectAvatar, this); selected_handle->applyToNodes(&func); } @@ -351,6 +366,93 @@ void LLSelectMgr::overrideObjectUpdates() getSelection()->applyToNodes(&func); } +void LLSelectMgr::resetAvatarOverrides() +{ + mAvatarOverridesMap.clear(); +} + +void LLSelectMgr::overrideAvatarUpdates() +{ + if (mAvatarOverridesMap.size() == 0) + { + return; + } + + if (!mAllowSelectAvatar || !gFloaterTools) + { + resetAvatarOverrides(); + return; + } + + if (!gFloaterTools->getVisible() && getSelection()->isEmpty()) + { + // when user switches selection, floater is invisible and selection is empty + LLToolset *toolset = LLToolMgr::getInstance()->getCurrentToolset(); + if (toolset->isShowFloaterTools() + && toolset->isToolSelected(0)) // Pie tool + { + resetAvatarOverrides(); + return; + } + } + + // remove selected avatars from this list, + // but set object overrides to make sure avatar won't snap back + struct f : public LLSelectedNodeFunctor + { + f(LLSelectMgr* p) : mManager(p) {} + LLSelectMgr* mManager; + virtual bool apply(LLSelectNode* selectNode) + { + LLViewerObject* object = selectNode->getObject(); + if (object && !object->getParent()) + { + LLVOAvatar* avatar = object->asAvatar(); + if (avatar) + { + uuid_av_override_map_t::iterator iter = mManager->mAvatarOverridesMap.find(avatar->getID()); + if (iter != mManager->mAvatarOverridesMap.end()) + { + if (selectNode->mLastPositionLocal.isExactlyZero()) + { + selectNode->mLastPositionLocal = iter->second.mLastPositionLocal; + } + if (selectNode->mLastRotation == LLQuaternion()) + { + selectNode->mLastRotation = iter->second.mLastRotation; + } + mManager->mAvatarOverridesMap.erase(iter); + } + } + } + return true; + } + } func(this); + getSelection()->applyToNodes(&func); + + // Override avatar positions + uuid_av_override_map_t::iterator it = mAvatarOverridesMap.begin(); + while (it != mAvatarOverridesMap.end()) + { + if (it->second.mObject->isDead()) + { + it = mAvatarOverridesMap.erase(it); + } + else + { + if (!it->second.mLastPositionLocal.isExactlyZero()) + { + it->second.mObject->setPosition(it->second.mLastPositionLocal); + } + if (it->second.mLastRotation != LLQuaternion()) + { + it->second.mObject->setRotation(it->second.mLastRotation); + } + it++; + } + } +} + //----------------------------------------------------------------------------- // Select just the object, not any other group members. //----------------------------------------------------------------------------- @@ -886,7 +988,7 @@ void LLSelectMgr::addAsFamily(std::vector& objects, BOOL add_to // Can't select yourself if (objectp->mID == gAgentID - && !LLSelectMgr::getInstance()->mAllowSelectAvatar) + && !mAllowSelectAvatar) { continue; } @@ -6281,6 +6383,24 @@ LLSelectNode::LLSelectNode(const LLSelectNode& nodep) LLSelectNode::~LLSelectNode() { + LLSelectMgr *manager = LLSelectMgr::getInstance(); + if (manager->mAllowSelectAvatar + && (!mLastPositionLocal.isExactlyZero() + || mLastRotation != LLQuaternion())) + { + LLViewerObject* object = getObject(); //isDead() check + if (object && !object->getParent()) + { + LLVOAvatar* avatar = object->asAvatar(); + if (avatar) + { + // Avatar was moved and needs to stay that way + manager->mAvatarOverridesMap.emplace(avatar->getID(), LLSelectMgr::AvatarPositionOverride(mLastPositionLocal, mLastRotation, object)); + } + } + } + + delete mPermissions; mPermissions = NULL; } @@ -6788,6 +6908,10 @@ void LLSelectMgr::updateSelectionCenter() const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection // center (tractor beam) + // override any avatar updates received + // Works only if avatar was repositioned + // and edit floater is visible + overrideAvatarUpdates(); //override any object updates received //for selected objects overrideObjectUpdates(); diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index ce0316e610..199141fc32 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -467,6 +467,30 @@ public: void resetObjectOverrides(LLObjectSelectionHandle selected_handle); void overrideObjectUpdates(); + void resetAvatarOverrides(); + void overrideAvatarUpdates(); + + struct AvatarPositionOverride + { + AvatarPositionOverride(); + AvatarPositionOverride(LLVector3 &vec, LLQuaternion &quat, LLViewerObject *obj) : + mLastPositionLocal(vec), + mLastRotation(quat), + mObject(obj) + { + } + LLVector3 mLastPositionLocal; + LLQuaternion mLastRotation; + LLPointer mObject; + }; + + // Avatar overrides should persist even after selection + // was removed as long as edit floater is up + typedef std::map uuid_av_override_map_t; + uuid_av_override_map_t mAvatarOverridesMap; +public: + + // Returns the previous value of mForceSelection BOOL setForceSelection(BOOL force); diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index abb936c3e5..ea671a130e 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -534,7 +534,7 @@ void LLSpeakerMgr::updateSpeakerList() } else if (mSpeakers.size() == 0) { - // For all other session type (ad-hoc, P2P, avaline), we use the initial participants targets list + // For all other session type (ad-hoc, P2P), we use the initial participants targets list for (uuid_vec_t::iterator it = session->mInitialTargetIDs.begin();it!=session->mInitialTargetIDs.end();++it) { // Add buddies if they are on line, add any other avatar. diff --git a/indra/newview/llspeakers.h b/indra/newview/llspeakers.h index d1dbf72fe9..ed795b5155 100644 --- a/indra/newview/llspeakers.h +++ b/indra/newview/llspeakers.h @@ -244,14 +244,6 @@ public: const LLUUID getSessionID(); bool isSpeakerToBeRemoved(const LLUUID& speaker_id); - /** - * Removes avaline speaker. - * - * This is a HACK due to server does not send information that Avaline caller ends call. - * It can be removed when server is updated. See EXT-4301 for details - */ - bool removeAvalineSpeaker(const LLUUID& speaker_id) { return removeSpeaker(speaker_id); } - /** * Initializes mVoiceModerated depend on LLSpeaker::mModeratorMutedVoice of agent's participant. * diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 5beb12057c..dc48eaa823 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -97,6 +97,7 @@ #include "llfloateravatarpicker.h" #include "llcallbacklist.h" #include "llcallingcard.h" +#include "llclassifiedinfo.h" #include "llconsole.h" #include "llcontainerview.h" #include "llconversationlog.h" @@ -124,8 +125,6 @@ #include "llpanellogin.h" #include "llmutelist.h" #include "llavatarpropertiesprocessor.h" -#include "llpanelclassified.h" -#include "llpanelpick.h" #include "llpanelgrouplandmoney.h" #include "llpanelgroupnotices.h" #include "llparcel.h" @@ -194,6 +193,7 @@ #include "llavatariconctrl.h" #include "llvoicechannel.h" #include "llpathfindingmanager.h" +#include "llremoteparcelrequest.h" #include "lllogin.h" #include "llevents.h" @@ -255,6 +255,7 @@ static bool mLoginStatePastUI = false; static bool mBenefitsSuccessfullyInit = false; const F32 STATE_AGENT_WAIT_TIMEOUT = 240; //seconds +const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN = 3; // Give region 3 chances std::unique_ptr LLStartUp::sStateWatcher(new LLEventStream("StartupState")); std::unique_ptr LLStartUp::sListener(new LLStartupListener()); @@ -927,6 +928,12 @@ bool idle_startup() LLPersistentNotificationStorage::initParamSingleton(); LLDoNotDisturbNotificationStorage::initParamSingleton(); } + else + { + // reinitialize paths in case user switched grids or accounts + LLPersistentNotificationStorage::getInstance()->reset(); + LLDoNotDisturbNotificationStorage::getInstance()->reset(); + } // Set PerAccountSettingsFile to the default value. std::string settings_per_account = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount")); @@ -1388,10 +1395,21 @@ bool idle_startup() { LLStartUp::setStartupState( STATE_SEED_CAP_GRANTED ); } + else if (regionp->capabilitiesError()) + { + // Try to connect despite capabilities' error state + LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + } else { U32 num_retries = regionp->getNumSeedCapRetries(); - if (num_retries > 0) + if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN) + { + // Region will keep trying to get capabilities, + // but for now continue as if caps were granted + LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + } + else if (num_retries > 0) { LLStringUtil::format_map_t args; args["[NUMBER]"] = llformat("%d", num_retries + 1); @@ -2389,6 +2407,11 @@ void login_callback(S32 option, void *userdata) void show_release_notes_if_required() { static bool release_notes_shown = false; + // We happen to know that instantiating LLVersionInfo implicitly + // instantiates the LLEventMailDrop named "relnotes", which we (might) use + // below. If viewer release notes stop working, might be because that + // LLEventMailDrop got moved out of LLVersionInfo and hasn't yet been + // instantiated. if (!release_notes_shown && (LLVersionInfo::instance().getChannelAndVersion() != gLastRunVersion) && LLVersionInfo::instance().getViewerMaturity() != LLVersionInfo::TEST_VIEWER // don't show Release Notes for the test builds && gSavedSettings.getBOOL("UpdaterShowReleaseNotes") @@ -2656,7 +2679,6 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFunc("EventInfoReply", LLEventNotifier::processEventInfoReply); msg->setHandlerFunc("PickInfoReply", &LLAvatarPropertiesProcessor::processPickInfoReply); -// msg->setHandlerFunc("ClassifiedInfoReply", LLPanelClassified::processClassifiedInfoReply); msg->setHandlerFunc("ClassifiedInfoReply", LLAvatarPropertiesProcessor::processClassifiedInfoReply); msg->setHandlerFunc("ParcelInfoReply", LLRemoteParcelInfoProcessor::processParcelInfoReply); msg->setHandlerFunc("ScriptDialog", process_script_dialog); diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 51ee5b6157..0ce82a1297 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -48,6 +48,7 @@ #include "llui.h" #include "llviewerinventory.h" #include "llpermissions.h" +#include "llpreviewtexture.h" #include "llsaleinfo.h" #include "llassetstorage.h" #include "lltextbox.h" @@ -1215,6 +1216,7 @@ LLTextureCtrl::LLTextureCtrl(const LLTextureCtrl::Params& p) mNeedsRawImageData( FALSE ), mValid( TRUE ), mShowLoadingPlaceholder( TRUE ), + mOpenTexPreview(false), mImageAssetID(p.image_id), mDefaultImageAssetID(p.default_image_id), mDefaultImageName(p.default_image_name), @@ -1471,12 +1473,31 @@ BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask) if (!handled && mBorder->parentPointInView(x, y)) { - showPicker(FALSE); - //grab textures first... - LLInventoryModelBackgroundFetch::instance().start(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE)); - //...then start full inventory fetch. - LLInventoryModelBackgroundFetch::instance().start(); - handled = TRUE; + if (!mOpenTexPreview) + { + showPicker(FALSE); + //grab textures first... + LLInventoryModelBackgroundFetch::instance().start(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE)); + //...then start full inventory fetch. + LLInventoryModelBackgroundFetch::instance().start(); + handled = TRUE; + } + else + { + if (getImageAssetID().notNull()) + { + LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance("preview_texture", getValue()); + if (preview_texture && !preview_texture->isDependent()) + { + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if (root_floater) + { + root_floater->addDependentFloater(preview_texture); + preview_texture->hideCtrlButtons(); + } + } + } + } } return handled; diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 3769f43737..fbb38c4464 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -169,6 +169,8 @@ public: void setBlankImageAssetID( const LLUUID& id ) { mBlankImageAssetID = id; } const LLUUID& getBlankImageAssetID() const { return mBlankImageAssetID; } + void setOpenTexPreview(bool open_preview) { mOpenTexPreview = open_preview; } + void setCaption(const std::string& caption); void setCanApplyImmediately(BOOL b); @@ -248,6 +250,8 @@ private: BOOL mShowLoadingPlaceholder; std::string mLoadingPlaceholderString; S32 mLabelWidth; + bool mOpenTexPreview; + BOOL mBakeTextureEnabled; }; ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index bc8171b2fc..be54cb2f96 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -739,7 +739,6 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) glh::matrix4f proj = get_current_projection(); glh::matrix4f mod = get_current_modelview(); glViewport(0,0,512,512); - LLVOAvatar::updateFreezeCounter() ; LLVOAvatar::updateImpostors(); diff --git a/indra/newview/llviewerdisplayname.cpp b/indra/newview/llviewerdisplayname.cpp new file mode 100644 index 0000000000..cec08c4f15 --- /dev/null +++ b/indra/newview/llviewerdisplayname.cpp @@ -0,0 +1,226 @@ +/** + * @file llviewerdisplayname.cpp + * @brief Wrapper for display name functionality + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerdisplayname.h" + +// viewer includes +#include "llagent.h" +#include "llfloaterprofile.h" +#include "llfloaterreg.h" +#include "llviewerregion.h" +#include "llvoavatar.h" + +// library includes +#include "llavatarnamecache.h" +#include "llhttpnode.h" +#include "llnotificationsutil.h" +#include "llui.h" // getLanguage() + +namespace LLViewerDisplayName +{ + // Fired when viewer receives server response to display name change + set_name_signal_t sSetDisplayNameSignal; + + // Fired when there is a change in the agent's name + name_changed_signal_t sNameChangedSignal; + + void addNameChangedCallback(const name_changed_signal_t::slot_type& cb) + { + sNameChangedSignal.connect(cb); + } + + void doNothing() { } +} + +void LLViewerDisplayName::set(const std::string& display_name, const set_name_slot_t& slot) +{ + // TODO: simple validation here + + LLViewerRegion* region = gAgent.getRegion(); + llassert(region); + std::string cap_url = region->getCapability("SetDisplayName"); + if (cap_url.empty()) + { + // this server does not support display names, report error + slot(false, "unsupported", LLSD()); + return; + } + + // People API requires both the old and new value to change a variable. + // Our display name will be in cache before the viewer's UI is available + // to request a change, so we can use direct lookup without callback. + LLAvatarName av_name; + if (!LLAvatarNameCache::get( gAgent.getID(), &av_name)) + { + slot(false, "name unavailable", LLSD()); + return; + } + + // People API expects array of [ "old value", "new value" ] + LLSD change_array = LLSD::emptyArray(); + change_array.append(av_name.getDisplayName()); + change_array.append(display_name); + + LL_INFOS() << "Set name POST to " << cap_url << LL_ENDL; + + // Record our caller for when the server sends back a reply + sSetDisplayNameSignal.connect(slot); + + // POST the requested change. The sim will not send a response back to + // this request directly, rather it will send a separate message after it + // communicates with the back-end. + LLSD body; + body["display_name"] = change_array; + LLCoros::instance().launch("LLViewerDisplayName::SetDisplayNameCoro", + boost::bind(&LLViewerDisplayName::setDisplayNameCoro, cap_url, body)); +} + +void LLViewerDisplayName::setDisplayNameCoro(const std::string& cap_url, const LLSD& body) +{ + LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t + httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("SetDisplayNameCoro", httpPolicy)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpHeaders::ptr_t httpHeaders(new LLCore::HttpHeaders); + + // People API can return localized error messages. Indicate our + // language preference via header. + httpHeaders->append(HTTP_OUT_HEADER_ACCEPT_LANGUAGE, LLUI::getLanguage()); + + LLSD result = httpAdapter->postAndSuspend(httpRequest, cap_url, body, httpHeaders); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + LL_WARNS() << "Unable to set display name. Status: " << status.toString() << LL_ENDL; + LLViewerDisplayName::sSetDisplayNameSignal(false, "", LLSD()); + LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); + } +} + +class LLSetDisplayNameReply : public LLHTTPNode +{ + LOG_CLASS(LLSetDisplayNameReply); +public: + /*virtual*/ void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const + { + LLSD body = input["body"]; + + S32 status = body["status"].asInteger(); + bool success = (status == HTTP_OK); + std::string reason = body["reason"].asString(); + LLSD content = body["content"]; + + LL_INFOS() << "status " << status << " reason " << reason << LL_ENDL; + + // If viewer's concept of display name is out-of-date, the set request + // will fail with 409 Conflict. If that happens, fetch up-to-date + // name information. + if (status == HTTP_CONFLICT) + { + LLUUID agent_id = gAgent.getID(); + // Flush stale data + LLAvatarNameCache::getInstance()->erase( agent_id ); + // Queue request for new data: nothing to do on callback though... + // Note: no need to disconnect the callback as it never gets out of scope + LLAvatarNameCache::getInstance()->get(agent_id, boost::bind(&LLViewerDisplayName::doNothing)); + // Kill name tag, as it is wrong + LLVOAvatar::invalidateNameTag( agent_id ); + } + + // inform caller of result + LLViewerDisplayName::sSetDisplayNameSignal(success, reason, content); + LLViewerDisplayName::sSetDisplayNameSignal.disconnect_all_slots(); + } +}; + + +class LLDisplayNameUpdate : public LLHTTPNode +{ + /*virtual*/ void post( + LLHTTPNode::ResponsePtr response, + const LLSD& context, + const LLSD& input) const + { + LLSD body = input["body"]; + LLUUID agent_id = body["agent_id"]; + std::string old_display_name = body["old_display_name"]; + // By convention this record is called "agent" in the People API + LLSD name_data = body["agent"]; + + // Inject the new name data into cache + LLAvatarName av_name; + av_name.fromLLSD( name_data ); + + LL_INFOS() << "name-update now " << LLDate::now() + << " next_update " << LLDate(av_name.mNextUpdate) + << LL_ENDL; + + // Name expiration time may be provided in headers, or we may use a + // default value + // *TODO: get actual headers out of ResponsePtr + //LLSD headers = response->mHeaders; + LLSD headers; + av_name.mExpires = + LLAvatarNameCache::getInstance()->nameExpirationFromHeaders(headers); + + LLAvatarNameCache::getInstance()->insert(agent_id, av_name); + + // force name tag to update + LLVOAvatar::invalidateNameTag(agent_id); + + LLSD args; + args["OLD_NAME"] = old_display_name; + args["SLID"] = av_name.getUserName(); + args["NEW_NAME"] = av_name.getDisplayName(); + LLNotificationsUtil::add("DisplayNameUpdate", args); + if (agent_id == gAgent.getID()) + { + LLViewerDisplayName::sNameChangedSignal(); + } + + LLFloaterProfile* profile_floater = dynamic_cast(LLFloaterReg::findInstance("profile", LLSD().with("id", agent_id))); + if (profile_floater) + { + profile_floater->refreshName(); + } + } +}; + +LLHTTPRegistration + gHTTPRegistrationMessageSetDisplayNameReply( + "/message/SetDisplayNameReply"); + +LLHTTPRegistration + gHTTPRegistrationMessageDisplayNameUpdate( + "/message/DisplayNameUpdate"); diff --git a/indra/newview/llviewerdisplayname.h b/indra/newview/llviewerdisplayname.h new file mode 100644 index 0000000000..337aaa68b6 --- /dev/null +++ b/indra/newview/llviewerdisplayname.h @@ -0,0 +1,55 @@ +/** + * @file llviewerdisplayname.h + * @brief Wrapper for display name functionality + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLVIEWERDISPLAYNAME_H +#define LLVIEWERDISPLAYNAME_H + +#include + +class LLSD; +class LLUUID; + +namespace LLViewerDisplayName +{ + typedef boost::signals2::signal< + void (bool success, const std::string& reason, const LLSD& content)> + set_name_signal_t; + typedef set_name_signal_t::slot_type set_name_slot_t; + + typedef boost::signals2::signal name_changed_signal_t; + typedef name_changed_signal_t::slot_type name_changed_slot_t; + + // Sends an update to the server to change a display name + // and call back when done. May not succeed due to service + // unavailable or name not available. + void set(const std::string& display_name, const set_name_slot_t& slot); + + void setDisplayNameCoro(const std::string& cap_url, const LLSD& body); + + void addNameChangedCallback(const name_changed_signal_t::slot_type& cb); +} + +#endif // LLVIEWERDISPLAYNAME_H diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index f23e4ab9b3..da9f83905a 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -57,11 +57,13 @@ #include "llfloatercamera.h" #include "llfloatercamerapresets.h" #include "llfloaterchatvoicevolume.h" +#include "llfloaterclassified.h" #include "llfloaterconversationlog.h" #include "llfloaterconversationpreview.h" #include "llfloatercreatelandmark.h" #include "llfloaterdeleteprefpreset.h" #include "llfloaterdestinations.h" +#include "llfloaterdisplayname.h" #include "llfloatereditextdaycycle.h" #include "llfloaterenvironmentadjust.h" #include "llfloaterexperienceprofile.h" @@ -113,6 +115,7 @@ #include "llfloaterpreferencesgraphicsadvanced.h" #include "llfloaterpreferenceviewadvanced.h" #include "llfloaterpreviewtrash.h" +#include "llfloaterprofile.h" #include "llfloaterproperties.h" #include "llfloaterregiondebugconsole.h" #include "llfloaterregioninfo.h" @@ -143,7 +146,6 @@ #include "llfloateruipreview.h" #include "llfloatervoiceeffect.h" #include "llfloaterwebcontent.h" -#include "llfloaterwebprofile.h" #include "llfloatervoicevolume.h" #include "llfloaterwhitelistentry.h" #include "llfloaterwindowsize.h" @@ -157,7 +159,7 @@ #include "llmoveview.h" #include "llfloaterimnearbychat.h" #include "llpanelblockedlist.h" -#include "llpanelclassified.h" +#include "llpanelprofileclassifieds.h" #include "llpreviewanim.h" #include "llpreviewgesture.h" #include "llpreviewnotecard.h" @@ -230,6 +232,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("camera_presets", "floater_camera_presets.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("chat_voice", "floater_voice_chat_volume.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("nearby_chat", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterIMNearbyChat::buildFloater); + LLFloaterReg::add("classified", "floater_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("add_landmark", "floater_create_landmark.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -277,6 +280,7 @@ void LLViewerFloaterReg::registerFloaters() LLInspectRemoteObjectUtil::registerFloater(); LLFloaterVoiceVolumeUtil::registerFloater(); LLNotificationsUI::registerFloater(); + LLFloaterDisplayNameUtil::registerFloater(); LLFloaterReg::add("lagmeter", "floater_lagmeter.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("land_holdings", "floater_land_holdings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -323,7 +327,6 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("prefs_translation", "floater_translation_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("prefs_spellchecker", "floater_spellcheck.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("prefs_autoreplace", "floater_autoreplace.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -370,8 +373,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("snapshot", "floater_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("outfit_snapshot", "floater_outfit_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("my_profile", "floater_my_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create); - LLFloaterReg::add("profile", "floater_web_profile.xml", (LLFloaterBuildFunc)&LLFloaterWebProfile::create); + LLFloaterReg::add("profile", "floater_profile.xml",(LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("guidebook", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("big_preview", "floater_big_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 812f804ea9..636909e6f2 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -48,7 +48,7 @@ #include "llmutelist.h" #include "llnotifications.h" #include "llnotificationsutil.h" -#include "llpanelprofile.h" +#include "llavataractions.h" #include "llparcel.h" #include "llpluginclassmedia.h" #include "llurldispatcher.h" @@ -3194,7 +3194,7 @@ bool LLViewerMediaImpl::isForcedUnloaded() const } // If this media's class is not supposed to be shown, unload - if (!shouldShowBasedOnClass()) + if (!shouldShowBasedOnClass() || isObscured()) { return true; } @@ -3879,6 +3879,26 @@ bool LLViewerMediaImpl::shouldShowBasedOnClass() const } } +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isObscured() const +{ + if (getUsedInUI() || isParcelMedia()) return false; + + LLParcel* agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (!agent_parcel) + { + return false; + } + + if (agent_parcel->getObscureMOAP() && !isInAgentParcel()) + { + return true; + } + + return false; +} + ////////////////////////////////////////////////////////////////////////////////////////// // bool LLViewerMediaImpl::isAttachedToAnotherAvatar() const diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 806692929a..b95cfd4c68 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -423,6 +423,7 @@ public: private: bool isAutoPlayable() const; bool shouldShowBasedOnClass() const; + bool isObscured() const; static bool isObjectAttachedToAnotherAvatar(LLVOVolume *obj); static bool isObjectInAgentParcel(LLVOVolume *obj); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 2a8a6e9848..3c64edf2db 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -33,10 +33,11 @@ #include "llviewermenu.h" // linden library includes -#include "llavatarnamecache.h" // IDEVO +#include "llavatarnamecache.h" // IDEVO (I Are Not Men!) +#include "llcombobox.h" +#include "llcoros.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" -#include "llcombobox.h" #include "llinventorypanel.h" #include "llnotifications.h" #include "llnotificationsutil.h" @@ -93,6 +94,7 @@ #include "llmarketplacefunctions.h" #include "llmenuoptionpathfindingrebakenavmesh.h" #include "llmoveview.h" +#include "llnavigationbar.h" #include "llparcel.h" #include "llrootview.h" #include "llsceneview.h" @@ -2407,6 +2409,7 @@ class LLAdvancedForceErrorLlerror : public view_listener_t return true; } }; + class LLAdvancedForceErrorBadMemoryAccess : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -2416,6 +2419,22 @@ class LLAdvancedForceErrorBadMemoryAccess : public view_listener_t } }; +class LLAdvancedForceErrorBadMemoryAccessCoro : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLCoros::instance().launch( + "AdvancedForceErrorBadMemoryAccessCoro", + [](){ + // Wait for one mainloop() iteration, letting the enclosing + // handleEvent() method return. + llcoro::suspend(); + force_error_bad_memory_access(NULL); + }); + return true; + } +}; + class LLAdvancedForceErrorInfiniteLoop : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -2434,6 +2453,22 @@ class LLAdvancedForceErrorSoftwareException : public view_listener_t } }; +class LLAdvancedForceErrorSoftwareExceptionCoro : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLCoros::instance().launch( + "AdvancedForceErrorSoftwareExceptionCoro", + [](){ + // Wait for one mainloop() iteration, letting the enclosing + // handleEvent() method return. + llcoro::suspend(); + force_error_software_exception(NULL); + }); + return true; + } +}; + class LLAdvancedForceErrorDriverCrash : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -3637,6 +3672,11 @@ bool my_profile_visible() return floaterp && floaterp->isInVisibleChain(); } +bool picks_tab_visible() +{ + return my_profile_visible() && LLAvatarActions::isPickTabSelected(gAgentID); +} + bool enable_freeze_eject(const LLSD& avatar_id) { // Use avatar_id if available, otherwise default to right-click avatar @@ -5362,12 +5402,10 @@ class LLToolsEnablePathfindingRebakeRegion : public view_listener_t { bool returnValue = false; - if (LLPathfindingManager::getInstance() != NULL) - { - LLMenuOptionPathfindingRebakeNavmesh *rebakeInstance = LLMenuOptionPathfindingRebakeNavmesh::getInstance(); - returnValue = (rebakeInstance->canRebakeRegion() && - (rebakeInstance->getMode() == LLMenuOptionPathfindingRebakeNavmesh::kRebakeNavMesh_Available)); - } + if (LLNavigationBar::instanceExists()) + { + returnValue = LLNavigationBar::getInstance()->isRebakeNavMeshAvailable(); + } return returnValue; } }; @@ -6330,6 +6368,29 @@ class LLAvatarToggleMyProfile : public view_listener_t } }; +class LLAvatarTogglePicks : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLFloater * instance = LLAvatarActions::getProfileFloater(gAgent.getID()); + if (LLFloater::isMinimized(instance) || (instance && !instance->hasFocus() && !instance->getIsChrome())) + { + instance->setMinimized(FALSE); + instance->setFocus(TRUE); + LLAvatarActions::showPicks(gAgent.getID()); + } + else if (picks_tab_visible()) + { + instance->closeFloater(); + } + else + { + LLAvatarActions::showPicks(gAgent.getID()); + } + return true; + } +}; + class LLAvatarToggleSearch : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -6800,6 +6861,15 @@ class LLShowAgentProfile : public view_listener_t } }; +class LLShowAgentProfilePicks : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + LLAvatarActions::showPicks(gAgent.getID()); + return true; + } +}; + class LLToggleAgentProfile : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -9447,8 +9517,10 @@ void initialize_menus() view_listener_t::addMenu(new LLAdvancedForceErrorBreakpoint(), "Advanced.ForceErrorBreakpoint"); view_listener_t::addMenu(new LLAdvancedForceErrorLlerror(), "Advanced.ForceErrorLlerror"); view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccess(), "Advanced.ForceErrorBadMemoryAccess"); + view_listener_t::addMenu(new LLAdvancedForceErrorBadMemoryAccessCoro(), "Advanced.ForceErrorBadMemoryAccessCoro"); view_listener_t::addMenu(new LLAdvancedForceErrorInfiniteLoop(), "Advanced.ForceErrorInfiniteLoop"); view_listener_t::addMenu(new LLAdvancedForceErrorSoftwareException(), "Advanced.ForceErrorSoftwareException"); + view_listener_t::addMenu(new LLAdvancedForceErrorSoftwareExceptionCoro(), "Advanced.ForceErrorSoftwareExceptionCoro"); view_listener_t::addMenu(new LLAdvancedForceErrorDriverCrash(), "Advanced.ForceErrorDriverCrash"); view_listener_t::addMenu(new LLAdvancedForceErrorCoroutineCrash(), "Advanced.ForceErrorCoroutineCrash"); view_listener_t::addMenu(new LLAdvancedForceErrorThreadCrash(), "Advanced.ForceErrorThreadCrash"); @@ -9523,12 +9595,14 @@ void initialize_menus() enable.add("Avatar.EnableCall", boost::bind(&LLAvatarActions::canCall)); view_listener_t::addMenu(new LLAvatarReportAbuse(), "Avatar.ReportAbuse"); view_listener_t::addMenu(new LLAvatarToggleMyProfile(), "Avatar.ToggleMyProfile"); + view_listener_t::addMenu(new LLAvatarTogglePicks(), "Avatar.TogglePicks"); view_listener_t::addMenu(new LLAvatarToggleSearch(), "Avatar.ToggleSearch"); view_listener_t::addMenu(new LLAvatarResetSkeleton(), "Avatar.ResetSkeleton"); view_listener_t::addMenu(new LLAvatarEnableResetSkeleton(), "Avatar.EnableResetSkeleton"); view_listener_t::addMenu(new LLAvatarResetSkeletonAndAnimations(), "Avatar.ResetSkeletonAndAnimations"); view_listener_t::addMenu(new LLAvatarResetSelfSkeletonAndAnimations(), "Avatar.ResetSelfSkeletonAndAnimations"); enable.add("Avatar.IsMyProfileOpen", boost::bind(&my_profile_visible)); + enable.add("Avatar.IsPicksTabOpen", boost::bind(&picks_tab_visible)); commit.add("Avatar.OpenMarketplace", boost::bind(&LLWeb::loadURLExternal, gSavedSettings.getString("MarketplaceURL"))); @@ -9605,6 +9679,7 @@ void initialize_menus() view_listener_t::addMenu(new LLToggleSpeak(), "ToggleSpeak"); view_listener_t::addMenu(new LLPromptShowURL(), "PromptShowURL"); view_listener_t::addMenu(new LLShowAgentProfile(), "ShowAgentProfile"); + view_listener_t::addMenu(new LLShowAgentProfilePicks(), "ShowAgentProfilePicks"); view_listener_t::addMenu(new LLToggleAgentProfile(), "ToggleAgentProfile"); view_listener_t::addMenu(new LLToggleControl(), "ToggleControl"); view_listener_t::addMenu(new LLToggleShaderControl(), "ToggleShaderControl"); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index be80d0bc0a..5f82f1c44f 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3334,13 +3334,6 @@ void send_agent_update(BOOL force_send, BOOL send_reliable) // trigger a control event. U32 control_flags = gAgent.getControlFlags(); - // Rotation into both directions should cancel out - U32 mask = AGENT_CONTROL_YAW_POS | AGENT_CONTROL_YAW_NEG; - if ((control_flags & mask) == mask) - { - control_flags &= ~mask; - } - MASK key_mask = gKeyboard->currentMask(TRUE); if (key_mask & MASK_ALT || key_mask & MASK_CONTROL) diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index a95636ff23..aad6c14b4d 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -356,6 +356,13 @@ LLViewerObject::~LLViewerObject() mPartSourcep = NULL; } + if (mText) + { + // something recovered LLHUDText when object was already dead + mText->markDead(); + mText = NULL; + } + // Delete memory associated with extra parameters. std::map::iterator iter; for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter) @@ -2457,11 +2464,19 @@ U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys, needs_refresh = needs_refresh || child->mUserSelected; } + static LLCachedControl allow_select_avatar(gSavedSettings, "AllowSelectAvatar", FALSE); if (needs_refresh) { LLSelectMgr::getInstance()->updateSelectionCenter(); dialog_refresh_all(); - } + } + else if (allow_select_avatar && asAvatar()) + { + // Override any avatar position updates received + // Works only if avatar was repositioned using build + // tools and build floater is visible + LLSelectMgr::getInstance()->overrideAvatarUpdates(); + } // Mark update time as approx. now, with the ping delay. diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 55e2386d10..e930b58111 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -571,7 +571,8 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, if(update_cache) { - objectp = regionp->updateCacheEntry(local_id, objectp, update_type); + //update object cache if the object receives a full-update or terse update + objectp = regionp->updateCacheEntry(local_id, objectp); } // This looks like it will break if the local_id of the object doesn't change diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index e69b0347f8..f58eac2ed9 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -1553,6 +1553,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use BOOL region_allow_environment_override = true; S32 parcel_environment_version = 0; BOOL agent_parcel_update = false; // updating previous(existing) agent parcel + U32 extended_flags = 0; //obscure MOAP S32 other_clean_time = 0; @@ -1642,6 +1643,11 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use msg->getBOOLFast(_PREHASH_RegionAllowAccessBlock, _PREHASH_RegionAllowAccessOverride, region_allow_access_override); } + if (msg->getNumberOfBlocks(_PREHASH_ParcelExtendedFlags)) + { + msg->getU32Fast(_PREHASH_ParcelExtendedFlags, _PREHASH_Flags, extended_flags); + } + if (msg->getNumberOfBlocks(_PREHASH_ParcelEnvironmentBlock)) { msg->getS32Fast(_PREHASH_ParcelEnvironmentBlock, _PREHASH_ParcelEnvironmentVersion, parcel_environment_version); @@ -1698,6 +1704,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use parcel->setParcelEnvironmentVersion(cur_parcel_environment_version); parcel->setRegionAllowEnvironmentOverride(region_allow_environment_override); + parcel->setObscureMOAP((bool)extended_flags); + parcel->unpackMessage(msg); if (parcel == parcel_mgr.mAgentParcel) diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index b282a2b90c..fb55c5e816 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -95,8 +95,6 @@ // The server only keeps our pending agent info for 60 seconds. // We want to allow for seed cap retry, but its not useful after that 60 seconds. -// Give it 3 chances, each at 18 seconds to give ourselves a few seconds to connect anyways if we give up. -const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN = 3; // Even though we gave up on login, keep trying for caps after we are logged in: const S32 MAX_CAP_REQUEST_ATTEMPTS = 30; const U32 DEFAULT_MAX_REGION_WIDE_PRIM_COUNT = 15000; @@ -178,7 +176,6 @@ public: mCompositionp(NULL), mEventPoll(NULL), mSeedCapMaxAttempts(MAX_CAP_REQUEST_ATTEMPTS), - mSeedCapMaxAttemptsBeforeLogin(MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN), mSeedCapAttempts(0), mHttpResponderID(0), mLastCameraUpdate(0), @@ -231,7 +228,6 @@ public: LLEventPoll* mEventPoll; S32 mSeedCapMaxAttempts; - S32 mSeedCapMaxAttemptsBeforeLogin; S32 mSeedCapAttempts; S32 mHttpResponderID; @@ -266,14 +262,13 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) return; } - LLWorld *world_inst = LLWorld::getInstance(); // Not a singleton! - if (!world_inst) + if (!LLWorld::instanceExists()) { LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities, but world no longer exists!" << LL_ENDL; return; } - regionp = world_inst->getRegionFromHandle(regionHandle); + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); if (!regionp) //region was removed { LL_WARNS("AppInit", "Capabilities") << "Attempting to get capabilities for region that no longer exists!" << LL_ENDL; @@ -287,19 +282,13 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) if (url.empty()) { LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities, and can not determine url!" << LL_ENDL; + regionp->setCapabilitiesError(); return; // this error condition is not recoverable. } // record that we just entered a new region newRegionEntry(*regionp); - // After a few attempts, continue login. But keep trying to get the caps: - if (impl->mSeedCapAttempts >= impl->mSeedCapMaxAttemptsBeforeLogin && - STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) - { - LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); - } - if (impl->mSeedCapAttempts > impl->mSeedCapMaxAttempts) { // *TODO: Give a user pop-up about this error? @@ -321,7 +310,6 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) regionp = NULL; impl = NULL; - world_inst = NULL; result = httpAdapter->postAndSuspend(httpRequest, url, capabilityNames); if (STATE_WORLD_INIT > LLStartUp::getStartupState()) @@ -332,34 +320,10 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) if (LLApp::isExiting() || gDisconnected) { + LL_DEBUGS("AppInit", "Capabilities") << "Shutting down" << LL_ENDL; return; } - world_inst = LLWorld::getInstance(); - if (!world_inst) - { - LL_WARNS("AppInit", "Capabilities") << "Received capabilities, but world no longer exists!" << LL_ENDL; - return; - } - - regionp = world_inst->getRegionFromHandle(regionHandle); - if (!regionp) //region was removed - { - LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL; - return; // this error condition is not recoverable. - } - - impl = regionp->getRegionImplNC(); - - ++impl->mSeedCapAttempts; - - if (id != impl->mHttpResponderID) // region is no longer referring to this request - { - LL_WARNS("AppInit", "Capabilities") << "Received results for a stale capabilities request!" << LL_ENDL; - // setup for retry. - continue; - } - if (!result.isMap() || result.has("error")) { LL_WARNS("AppInit", "Capabilities") << "Malformed response" << LL_ENDL; @@ -379,6 +343,30 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) // remove the http_result from the llsd result.erase("http_result"); + if (!LLWorld::instanceExists()) + { + LL_WARNS("AppInit", "Capabilities") << "Received capabilities, but world no longer exists!" << LL_ENDL; + return; + } + + regionp = LLWorld::getInstance()->getRegionFromHandle(regionHandle); + if (!regionp) //region was removed + { + LL_WARNS("AppInit", "Capabilities") << "Received capabilities for region that no longer exists!" << LL_ENDL; + return; // this error condition is not recoverable. + } + + impl = regionp->getRegionImplNC(); + + ++(impl->mSeedCapAttempts); + + if (id != impl->mHttpResponderID) // region is no longer referring to this request + { + LL_WARNS("AppInit", "Capabilities") << "Received results for a stale capabilities request!" << LL_ENDL; + // setup for retry. + continue; + } + LLSD::map_const_iterator iter; for (iter = result.beginMap(); iter != result.endMap(); ++iter) { @@ -396,11 +384,6 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCoro(U64 regionHandle) << " region name " << regionp->getName() << LL_ENDL; regionp->setCapabilitiesReceived(true); - if (STATE_SEED_GRANTED_WAIT == LLStartUp::getStartupState()) - { - LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); - } - break; } while (true); @@ -444,6 +427,11 @@ void LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro(U64 regionHandle) if (url.empty()) { LL_WARNS("AppInit", "Capabilities") << "Failed to get seed capabilities, and can not determine url!" << LL_ENDL; + if (regionp->getCapability("Seed").empty()) + { + // initial attempt failed to get this cap as well + regionp->setCapabilitiesError(); + } break; // this error condition is not recoverable. } @@ -645,7 +633,7 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mCacheLoaded(FALSE), mCacheDirty(FALSE), mReleaseNotesRequested(FALSE), - mCapabilitiesReceived(false), + mCapabilitiesState(CAPABILITIES_STATE_INIT), mSimulatorFeaturesReceived(false), mBitsReceived(0.f), mPacketsReceived(0.f), @@ -1820,13 +1808,8 @@ LLViewerObject* LLViewerRegion::addNewObject(LLVOCacheEntry* entry) //update object cache if the object receives a full-update or terse update //update_type == EObjectUpdateType::OUT_TERSE_IMPROVED or EObjectUpdateType::OUT_FULL -LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* objectp, U32 update_type) +LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* objectp) { - if(objectp && update_type != (U32)OUT_TERSE_IMPROVED) - { - return objectp; //no need to access cache - } - LLVOCacheEntry* entry = getCacheEntry(local_id); if (!entry) { @@ -1838,11 +1821,8 @@ LLViewerObject* LLViewerRegion::updateCacheEntry(U32 local_id, LLViewerObject* o objectp = addNewObject(entry); } - //remove from cache if terse update - if(update_type == (U32)OUT_TERSE_IMPROVED) - { - killCacheEntry(entry, true); - } + //remove from cache. + killCacheEntry(entry, true); return objectp; } @@ -2302,6 +2282,11 @@ void LLViewerRegion::requestSimulatorFeatures() std::string coroname = LLCoros::instance().launch("LLViewerRegionImpl::requestSimulatorFeatureCoro", boost::bind(&LLViewerRegionImpl::requestSimulatorFeatureCoro, url, getHandle())); + + // requestSimulatorFeatures can be called from other coros, + // launch() acts like a suspend() + // Make sure we are still good to do + LLCoros::checkStop(); LL_INFOS("AppInit", "SimulatorFeatures") << "Launching " << coroname << " requesting simulator features from " << url << " for region " << getRegionID() << LL_ENDL; } @@ -3002,6 +2987,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("AcceptFriendship"); capabilityNames.append("AcceptGroupInvite"); // ReadOfflineMsgs recieved messages only!!! capabilityNames.append("AgentPreferences"); + capabilityNames.append("AgentProfile"); capabilityNames.append("AgentState"); capabilityNames.append("AttachmentResources"); capabilityNames.append("AvatarPickerSearch"); @@ -3096,6 +3082,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("UpdateScriptTask"); capabilityNames.append("UpdateSettingsAgentInventory"); capabilityNames.append("UpdateSettingsTaskInventory"); + capabilityNames.append("UploadAgentProfileImage"); capabilityNames.append("UploadBakedTexture"); capabilityNames.append("UserInfo"); capabilityNames.append("ViewerAsset"); @@ -3121,6 +3108,12 @@ void LLViewerRegion::setSeedCapability(const std::string& url) std::string coroname = LLCoros::instance().launch("LLEnvironmentRequest::requestBaseCapabilitiesCompleteCoro", boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCompleteCoro, getHandle())); + + // setSeedCapability can be called from other coros, + // launch() acts like a suspend() + // Make sure we are still good to do + LLCoros::checkStop(); + return; } @@ -3134,6 +3127,11 @@ void LLViewerRegion::setSeedCapability(const std::string& url) LLCoros::instance().launch("LLViewerRegionImpl::requestBaseCapabilitiesCoro", boost::bind(&LLViewerRegionImpl::requestBaseCapabilitiesCoro, getHandle())); + // setSeedCapability can be called from other coros, + // launch() acts like a suspend() + // Make sure we are still good to do + LLCoros::checkStop(); + LL_INFOS("AppInit", "Capabilities") << "Launching " << coroname << " requesting seed capabilities from " << url << " for region " << getRegionID() << LL_ENDL; } @@ -3255,12 +3253,17 @@ bool LLViewerRegion::isCapabilityAvailable(const std::string& name) const bool LLViewerRegion::capabilitiesReceived() const { - return mCapabilitiesReceived; + return mCapabilitiesState == CAPABILITIES_STATE_RECEIVED; +} + +bool LLViewerRegion::capabilitiesError() const +{ + return mCapabilitiesState == CAPABILITIES_STATE_ERROR; } void LLViewerRegion::setCapabilitiesReceived(bool received) { - mCapabilitiesReceived = received; + mCapabilitiesState = received ? CAPABILITIES_STATE_RECEIVED : CAPABILITIES_STATE_INIT; // Tell interested parties that we've received capabilities, // so that they can safely use getCapability(). @@ -3275,6 +3278,11 @@ void LLViewerRegion::setCapabilitiesReceived(bool received) } } +void LLViewerRegion::setCapabilitiesError() +{ + mCapabilitiesState = CAPABILITIES_STATE_ERROR; +} + boost::signals2::connection LLViewerRegion::setCapabilitiesReceivedCallback(const caps_received_signal_t::slot_type& cb) { return mCapabilitiesReceivedSignal.connect(cb); diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index fcbf56c81f..d0fa9fea01 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -268,7 +268,9 @@ public: // has region received its final (not seed) capability list? bool capabilitiesReceived() const; + bool capabilitiesError() const; void setCapabilitiesReceived(bool received); + void setCapabilitiesError(); boost::signals2::connection setCapabilitiesReceivedCallback(const caps_received_signal_t::slot_type& cb); static bool isSpecialCapabilityName(const std::string &name); @@ -352,7 +354,7 @@ public: void requestCacheMisses(); void addCacheMissFull(const U32 local_id); //update object cache if the object receives a full-update or terse update - LLViewerObject* updateCacheEntry(U32 local_id, LLViewerObject* objectp, U32 update_type); + LLViewerObject* updateCacheEntry(U32 local_id, LLViewerObject* objectp); void findOrphans(U32 parent_id); void clearCachedVisibleObjects(); void dumpCache(); @@ -527,12 +529,20 @@ private: BOOL mCacheLoaded; BOOL mCacheDirty; BOOL mAlive; // can become false if circuit disconnects - BOOL mCapabilitiesReceived; BOOL mSimulatorFeaturesReceived; BOOL mReleaseNotesRequested; BOOL mDead; //if true, this region is in the process of deleting. BOOL mPaused; //pause processing the objects in the region + typedef enum + { + CAPABILITIES_STATE_INIT = 0, + CAPABILITIES_STATE_ERROR, + CAPABILITIES_STATE_RECEIVED + } eCababilitiesState; + + eCababilitiesState mCapabilitiesState; + typedef std::map > orphan_list_t; orphan_list_t mOrphanMap; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index ffe3baa351..a420de98f5 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -506,6 +506,7 @@ void send_viewer_stats(bool include_preferences) system["ram"] = (S32) gSysMemory.getPhysicalMemoryKB().value(); system["os"] = LLOSInfo::instance().getOSStringSimple(); system["cpu"] = gSysCPU.getCPUString(); + system["cpu_sse"] = gSysCPU.getSSEVersions(); system["address_size"] = ADDRESS_SIZE; system["os_bitness"] = LLOSInfo::instance().getOSBitness(); unsigned char MACAddress[MAC_ADDRESS_BYTES]; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index ed948e506a..2270d86384 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1262,7 +1262,8 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) BOOL LLViewerTextureList::createUploadFile(const std::string& filename, const std::string& out_filename, - const U8 codec) + const U8 codec, + const S32 max_image_dimentions) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; // Load the image @@ -1291,7 +1292,7 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, return FALSE; } // Convert to j2c (JPEG2000) and save the file locally - LLPointer compressedImage = convertToUploadFile(raw_image); + LLPointer compressedImage = convertToUploadFile(raw_image, max_image_dimentions); if (compressedImage.isNull()) { image->setLastError("Couldn't convert the image to jpeg2000."); @@ -1316,10 +1317,10 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, } // note: modifies the argument raw_image!!!! -LLPointer LLViewerTextureList::convertToUploadFile(LLPointer raw_image) +LLPointer LLViewerTextureList::convertToUploadFile(LLPointer raw_image, const S32 max_image_dimentions) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - raw_image->biasedScaleToPowerOfTwo(LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + raw_image->biasedScaleToPowerOfTwo(max_image_dimentions); LLPointer compressedImage = new LLImageJ2C(); if (gSavedSettings.getBOOL("LosslessJ2CUpload") && diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index fead2e52b2..6fb0d3552e 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -92,8 +92,11 @@ class LLViewerTextureList friend class LLLocalBitmap; public: - static BOOL createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec); - static LLPointer convertToUploadFile(LLPointer raw_image); + static BOOL createUploadFile(const std::string& filename, + const std::string& out_filename, + const U8 codec, + const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); + static LLPointer convertToUploadFile(LLPointer raw_image, const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT); static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data ); static void receiveImageHeader(LLMessageSystem *msg, void **user_data); static void receiveImagePacket(LLMessageSystem *msg, void **user_data); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 119859a4ac..5ce46b143a 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -772,6 +772,12 @@ public: ypos += y_inc; addText(xpos, ypos, llformat("%.3f/%.3f MB Mesh Cache Read/Write ", LLMeshRepository::sCacheBytesRead/(1024.f*1024.f), LLMeshRepository::sCacheBytesWritten/(1024.f*1024.f))); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.3f/%.3f MB Mesh Skins/Decompositions Memory", LLMeshRepository::sCacheBytesSkins / (1024.f*1024.f), LLMeshRepository::sCacheBytesDecomps / (1024.f*1024.f))); + ypos += y_inc; + + addText(xpos, ypos, llformat("%.3f MB Mesh Headers Memory", LLMeshRepository::sCacheBytesHeaders / (1024.f*1024.f))); ypos += y_inc; } @@ -1567,9 +1573,11 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) showCursor(); getWindow()->setMouseClipping(FALSE); - // If losing focus while keys are down, reset them. + // If losing focus while keys are down, handle them as + // an 'up' to correctly release states, then reset states if (gKeyboard) { + gKeyboard->resetKeyDownAndHandle(); gKeyboard->resetKeys(); } diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 038cc12c41..b66a6958fe 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1,4 +1,4 @@ -/** +/** * @File llvoavatar.cpp * @brief Implementation of LLVOAvatar class which is a derivation of LLViewerObject * @@ -183,8 +183,6 @@ const F32 MAX_STANDOFF_DISTANCE_CHANGE = 32; // Should probably be 4 or 3, but didn't want to change it while change other logic - SJB const S32 SWITCH_TO_BAKED_DISCARD = 5; -const F32 FOOT_COLLIDE_FUDGE = 0.04f; - const F32 HOVER_EFFECT_MAX_SPEED = 3.f; const F32 HOVER_EFFECT_STRENGTH = 0.f; const F32 UNDERWATER_EFFECT_STRENGTH = 0.1f; @@ -585,7 +583,6 @@ private: //----------------------------------------------------------------------------- // Static Data //----------------------------------------------------------------------------- -S32 LLVOAvatar::sFreezeCounter = 0; U32 LLVOAvatar::sMaxNonImpostors = 12; // Set from RenderAvatarMaxNonImpostors bool LLVOAvatar::sLimitNonImpostors = false; // True unless RenderAvatarMaxNonImpostors is 0 (unlimited) F32 LLVOAvatar::sRenderDistance = 256.f; @@ -610,7 +607,6 @@ S32 LLVOAvatar::sNumVisibleChatBubbles = 0; BOOL LLVOAvatar::sDebugInvisible = FALSE; BOOL LLVOAvatar::sShowAttachmentPoints = FALSE; BOOL LLVOAvatar::sShowAnimationDebug = FALSE; -BOOL LLVOAvatar::sShowFootPlane = FALSE; BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE; F32 LLVOAvatar::sLODFactor = 1.f; F32 LLVOAvatar::sPhysicsLODFactor = 1.f; @@ -779,6 +775,13 @@ std::string LLVOAvatar::avString() const void LLVOAvatar::debugAvatarRezTime(std::string notification_name, std::string comment) { + if (gDisconnected) + { + // If we disconected, these values are likely to be invalid and + // avString() might crash due to a dead sAvatarDictionary + return; + } + LL_INFOS("Avatar") << "REZTIME: [ " << (U32)mDebugExistenceTimer.getElapsedTimeF32() << "sec ]" << avString() @@ -4107,8 +4110,7 @@ void LLVOAvatar::computeUpdatePeriod() && (!isSelf() || visually_muted) && !isUIAvatar() && (sLimitNonImpostors || visually_muted) - && !mNeedsAnimUpdate - && !sFreezeCounter) + && !mNeedsAnimUpdate) { const LLVector4a* ext = mDrawable->getSpatialExtents(); LLVector4a size; @@ -5081,42 +5083,6 @@ U32 LLVOAvatar::renderSkinned() return num_indices; } - // render collision normal - // *NOTE: this is disabled (there is no UI for enabling sShowFootPlane) due - // to DEV-14477. the code is left here to aid in tracking down the cause - // of the crash in the future. -brad - if (sShowFootPlane && mDrawable.notNull()) - { - LLVector3 slaved_pos = mDrawable->getPositionAgent(); - LLVector3 foot_plane_normal(mFootPlane.mV[VX], mFootPlane.mV[VY], mFootPlane.mV[VZ]); - F32 dist_from_plane = (slaved_pos * foot_plane_normal) - mFootPlane.mV[VW]; - LLVector3 collide_point = slaved_pos; - collide_point.mV[VZ] -= foot_plane_normal.mV[VZ] * (dist_from_plane + COLLISION_TOLERANCE - FOOT_COLLIDE_FUDGE); - - gGL.begin(LLRender::LINES); - { - F32 SQUARE_SIZE = 0.2f; - gGL.color4f(1.f, 0.f, 0.f, 1.f); - - gGL.vertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]); - gGL.vertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]); - - gGL.vertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]); - gGL.vertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]); - - gGL.vertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]); - gGL.vertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]); - - gGL.vertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]); - gGL.vertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]); - - gGL.vertex3f(collide_point.mV[VX], collide_point.mV[VY], collide_point.mV[VZ]); - gGL.vertex3f(collide_point.mV[VX] + mFootPlane.mV[VX], collide_point.mV[VY] + mFootPlane.mV[VY], collide_point.mV[VZ] + mFootPlane.mV[VZ]); - - } - gGL.end(); - gGL.flush(); - } //-------------------------------------------------------------------- // render all geometry attached to the skeleton //-------------------------------------------------------------------- @@ -7242,6 +7208,14 @@ LLViewerJoint* LLVOAvatar::getViewerJoint(S32 idx) return dynamic_cast(mMeshLOD[idx]); } +//----------------------------------------------------------------------------- +// hideHair() +//----------------------------------------------------------------------------- +void LLVOAvatar::hideHair() +{ + mMeshLOD[MESH_ID_HAIR]->setVisible(FALSE, TRUE); +} + //----------------------------------------------------------------------------- // hideSkirt() //----------------------------------------------------------------------------- @@ -10247,23 +10221,6 @@ LLHost LLVOAvatar::getObjectHost() const } } -//static -void LLVOAvatar::updateFreezeCounter(S32 counter) -{ - if(counter) - { - sFreezeCounter = counter; - } - else if(sFreezeCounter > 0) - { - sFreezeCounter--; - } - else - { - sFreezeCounter = 0; - } -} - BOOL LLVOAvatar::updateLOD() { if (mDrawable.isNull()) diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 8a47895cf7..e9c3d48a78 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -327,7 +327,6 @@ public: static bool sLimitNonImpostors; // use impostors for far away avatars static F32 sRenderDistance; // distance at which avatars will render. static BOOL sShowAnimationDebug; // show animation debug info - static BOOL sShowFootPlane; // show foot collision plane reported by server static BOOL sShowCollisionVolumes; // show skeletal collision volumes static BOOL sVisibleInFirstPerson; static S32 sNumLODChangesThisFrame; @@ -618,14 +617,6 @@ public: private: BOOL mCulled; - //-------------------------------------------------------------------- - // Freeze counter - //-------------------------------------------------------------------- -public: - static void updateFreezeCounter(S32 counter = 0); -private: - static S32 sFreezeCounter; - //-------------------------------------------------------------------- // Constants //-------------------------------------------------------------------- @@ -809,6 +800,7 @@ public: void parseAppearanceMessage(LLMessageSystem* mesgsys, LLAppearanceMessageContents& msg); void processAvatarAppearance(LLMessageSystem* mesgsys); void applyParsedAppearanceMessage(LLAppearanceMessageContents& contents, bool slam_params); + void hideHair(); void hideSkirt(); void startAppearanceAnimation(); diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index f971554c9d..b0eb8d962c 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -770,8 +770,6 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string mReceivedCall(FALSE) { // make sure URI reflects encoded version of other user's agent id - // *NOTE: in case of Avaline call generated SIP URL will be incorrect. - // But it will be overridden in LLVoiceChannelP2P::setSessionHandle() called when agent accepts call setURI(LLVoiceClient::getInstance()->sipURIFromID(other_user_id)); } @@ -911,8 +909,6 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s else { LL_WARNS("Voice") << "incoming SIP URL is not provided. Channel may not work properly." << LL_ENDL; - // In the case of an incoming AvaLine call, the generated URI will be different from the - // original one. This is because the P2P URI is based on avatar UUID but Avaline is not. // See LLVoiceClient::sessionAddedEvent() setURI(LLVoiceClient::getInstance()->sipURIFromID(mOtherUserID)); } @@ -947,22 +943,5 @@ void LLVoiceChannelP2P::setState(EState state) void LLVoiceChannelP2P::addToTheRecentPeopleList() { - bool avaline_call = LLIMModel::getInstance()->findIMSession(mSessionID)->isAvalineSessionType(); - - if (avaline_call) - { - LLSD call_data; - std::string call_number = LLVoiceChannel::getSessionName(); - - call_data["avaline_call"] = true; - call_data["session_id"] = mSessionID; - call_data["call_number"] = call_number; - call_data["date"] = LLDate::now(); - - LLRecentPeople::instance().add(mOtherUserID, call_data); - } - else - { - LLRecentPeople::instance().add(mOtherUserID); - } + LLRecentPeople::instance().add(mOtherUserID); } diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index e2bd1a39c7..d8b8b8749f 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -200,6 +200,7 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() LLVoiceVersionInfo result; result.serverVersion = std::string(); result.serverType = std::string(); + result.mBuildVersion = std::string(); return result; } } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index cf527a4464..246883b611 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -95,6 +95,7 @@ struct LLVoiceVersionInfo { std::string serverType; std::string serverVersion; + std::string mBuildVersion; }; ////////////////////////////////// diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index 19036a3f77..735bb4ad94 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -690,6 +690,10 @@ void LLVivoxVoiceClient::voiceControlCoro() // surviving longer than LLVivoxVoiceClient voiceControlStateMachine(state); } + catch (const LLCoros::Stop&) + { + LL_DEBUGS("LLVivoxVoiceClient") << "Received a shutdown exception" << LL_ENDL; + } catch (const LLContinueError&) { LOG_UNHANDLED_EXCEPTION("LLVivoxVoiceClient"); @@ -4572,6 +4576,23 @@ void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, st } } +void LLVivoxVoiceClient::voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id) +{ + // We don't generally need to process this. However, one occurence is when we first connect, and so it is the + // earliest opportunity to learn what we're connected to. + if (statusCode) + { + LL_WARNS("Voice") << "VoiceServiceConnectionStateChangedEvent statusCode: " << statusCode << + "statusString: " << statusString << LL_ENDL; + return; + } + if (build_id.empty()) + { + return; + } + mVoiceVersion.mBuildVersion = build_id; +} + void LLVivoxVoiceClient::auxAudioPropertiesEvent(F32 energy) { LL_DEBUGS("VoiceEnergy") << "got energy " << energy << LL_ENDL; @@ -4758,7 +4779,7 @@ void LLVivoxVoiceClient::sessionState::VerifySessions() if ((*it).expired()) { LL_WARNS("Voice") << "Expired session found! removing" << LL_ENDL; - mSession.erase(it++); + it = mSession.erase(it); } else ++it; @@ -6815,7 +6836,7 @@ void LLVivoxVoiceClient::deleteVoiceFont(const LLUUID& id) if (list_iter->second == id) { LL_DEBUGS("VoiceFont") << "Removing " << id << " from the voice font list." << LL_ENDL; - mVoiceFontList.erase(list_iter++); + list_iter = mVoiceFontList.erase(list_iter); mVoiceFontListDirty = true; } else @@ -7554,6 +7575,8 @@ void LLVivoxProtocolParser::EndTag(const char *tag) connectorHandle = string; else if (!stricmp("VersionID", tag)) versionID = string; + else if (!stricmp("Version", tag)) + mBuildID = string; else if (!stricmp("AccountHandle", tag)) accountHandle = string; else if (!stricmp("State", tag)) @@ -7856,7 +7879,8 @@ void LLVivoxProtocolParser::processResponse(std::string tag) // We don't need to process this, but we also shouldn't warn on it, since that confuses people. } else if (!stricmp(eventTypeCstr, "VoiceServiceConnectionStateChangedEvent")) - { // Yet another ignored event + { + LLVivoxVoiceClient::getInstance()->voiceServiceConnectionStateChangedEvent(statusCode, statusString, mBuildID); } else if (!stricmp(eventTypeCstr, "AudioDeviceHotSwapEvent")) { diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index cf30a4e86a..ebc3a62c35 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -465,6 +465,7 @@ protected: void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); + void voiceServiceConnectionStateChangedEvent(int statusCode, std::string &statusString, std::string &build_id); void auxAudioPropertiesEvent(F32 energy); void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); @@ -969,6 +970,7 @@ protected: std::string actionString; std::string connectorHandle; std::string versionID; + std::string mBuildID; std::string accountHandle; std::string sessionHandle; std::string sessionGroupHandle; diff --git a/indra/newview/llwebprofile.cpp b/indra/newview/llwebprofile.cpp index ff899fe895..3cc82621c4 100644 --- a/indra/newview/llwebprofile.cpp +++ b/indra/newview/llwebprofile.cpp @@ -36,7 +36,7 @@ #include "llstring.h" // newview -#include "llpanelprofile.h" // for getProfileURL(). FIXME: move the method to LLAvatarActions +#include "llavataractions.h" // for getProfileURL() #include "llviewermedia.h" // FIXME: don't use LLViewerMedia internals #include "llcorehttputil.h" diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 32c8ce66a0..5c56a1d34f 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -40,6 +40,7 @@ #include "httpoptions.h" #include "httpheaders.h" #include "bufferarray.h" +#include "llversioninfo.h" #include "llviewercontrol.h" // Have to include these last to avoid queue redefinition! @@ -378,6 +379,15 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip, const httpHeaders->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_TEXT_XML); + std::string user_agent = llformat("%s %d.%d.%d (%d)", + LLVersionInfo::instance().getChannel().c_str(), + LLVersionInfo::instance().getMajor(), + LLVersionInfo::instance().getMinor(), + LLVersionInfo::instance().getPatch(), + LLVersionInfo::instance().getBuild()); + + httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + ///* Setting the DNS cache timeout to -1 disables it completely. //This might help with bug #503 */ //httpOpts->setDNSCacheTimeout(-1); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index e8d3c12d39..c53b2167b3 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -886,6 +886,22 @@ name="PanelNotificationListItem" value="0.3 0.3 0.3 .3" /> + + + + + + + + - - @@ -192,6 +190,7 @@ with the same filename but different name + @@ -513,6 +512,19 @@ with the same filename but different name + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/da/panel_me.xml b/indra/newview/skins/default/xui/da/panel_me.xml deleted file mode 100644 index f98ced5f91..0000000000 --- a/indra/newview/skins/default/xui/da/panel_me.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/indra/newview/skins/default/xui/da/panel_region_texture.xml b/indra/newview/skins/default/xui/da/panel_region_texture.xml index 45946fd222..c8a3ad328e 100644 --- a/indra/newview/skins/default/xui/da/panel_region_texture.xml +++ b/indra/newview/skins/default/xui/da/panel_region_texture.xml @@ -7,8 +7,8 @@ ukendt - Terræn teksturer (kræver 512x512, 24 bit .tga filer) - + Terræn teksturer (kræver 1024x1024, 24 bit .tga filer) + 1 (Lav) diff --git a/indra/newview/skins/default/xui/da/panel_side_tray.xml b/indra/newview/skins/default/xui/da/panel_side_tray.xml deleted file mode 100644 index 66c3e69904..0000000000 --- a/indra/newview/skins/default/xui/da/panel_side_tray.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/da/strings.xml b/indra/newview/skins/default/xui/da/strings.xml index 5f1bf73f26..e4f99d14e9 100644 --- a/indra/newview/skins/default/xui/da/strings.xml +++ b/indra/newview/skins/default/xui/da/strings.xml @@ -443,9 +443,6 @@ Prøv venligst om lidt igen. (ingen) - - Avaline opkalder [ORDER] - Ingen fejl diff --git a/indra/newview/skins/default/xui/de/floater_preview_texture.xml b/indra/newview/skins/default/xui/de/floater_preview_texture.xml index eacd11c3e6..b386d0288c 100644 --- a/indra/newview/skins/default/xui/de/floater_preview_texture.xml +++ b/indra/newview/skins/default/xui/de/floater_preview_texture.xml @@ -6,42 +6,23 @@ In Inventar kopieren - - Beschreibung: - - - [WIDTH]px x [HEIGHT]px - - - Vorschau Seitenverhältnis - - - - keines - - - 1:1 - - - 4:3 - - - 10:7 - - - 3:2 - - - 16:10 - - - 16:9 - - - 2:1 - - - + width="85"/> + width="85"/>