diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 0835f9751e..77127856c3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,9 +15,6 @@ jobs: configuration: [ReleaseOS] addrsize: [64] include: - - runner: windows-large - configuration: ReleaseOS - addrsize: 32 - runner: macos-12-xl developer_dir: "/Applications/Xcode_14.0.1.app/Contents/Developer" runs-on: ${{ matrix.runner }} diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000000..17c0ace02f --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,18 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [main, contribute] + tags: [v*] + + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.x + - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 82a9a968b9..35ac41420c 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -15,8 +15,8 @@ jobs: - uses: actions/stale@v6 id: stale with: - stale-pr-message: This pull request is stale because it has been open 60 days with no activity. Remove stale label or comment or it will be closed in 7 days - days-before-stale: 60 + stale-pr-message: This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or it will be closed in 7 days + days-before-stale: 30 days-before-close: 7 exempt-pr-labels: blocked,must,should,keep stale-pr-label: stale diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6d296d7a24..fe7dfbac7a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,8 @@ repos: - - repo: https://bitbucket.org/lindenlab/git-hooks.git - rev: v1.0.0-beta2 + - repo: https://github.com/secondlife/git-hooks.git + rev: v1.0.0 hooks: - id: opensource-license - - id: jira-issue - id: llsd - id: no-trigraphs - id: copyright @@ -11,7 +10,7 @@ repos: files: \.(cpp|c|h|py|glsl|cmake|txt)$ exclude: language.txt - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.5.0 + rev: v4.4.0 hooks: - id: check-xml - id: mixed-line-ending diff --git a/autobuild.xml b/autobuild.xml index 29fee08e41..4dcca3a013 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -2759,9 +2759,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - 9e1b5515ab59b4e9cfeef6626d65d03d + 8b091b1f13348eedadf66d7d81cb6bc1 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/108609/945996/viewer_manager-3.0.577252-darwin64-577252.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/116621/1003286/viewer_manager-3.0.580913-darwin64-580913.tar.bz2 name darwin64 @@ -2771,9 +2771,9 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors archive hash - a3c599595ecc8fb987a5499fca42520a + 647e86470e02509b1cf89829d08dfd46 url - https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/108610/946003/viewer_manager-3.0.577252-windows-577252.tar.bz2 + https://automated-builds-secondlife-com.s3.amazonaws.com/ct2/116623/1003293/viewer_manager-3.0.580913-windows-580913.tar.bz2 name windows @@ -2784,7 +2784,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors source_type hg version - 3.0.577252 + 3.0.580913 vlc-bin diff --git a/doc/contributions.txt b/doc/contributions.txt index c335628391..af8b259c74 100755 --- a/doc/contributions.txt +++ b/doc/contributions.txt @@ -238,6 +238,7 @@ Ansariel Hiller SL-15227 SL-15398 SL-18432 + SL-19140 SL-4126 Aralara Rajal Arare Chantilly @@ -594,6 +595,7 @@ Henri Beauchamp VWR-4157 SL-15175 SL-19110 + SL-19159 herina Bode Hikkoshi Sakai VWR-429 @@ -894,6 +896,7 @@ Kitty Barnett STORM-2149 MAINT-7581 MAINT-7081 + SL-18988 Kolor Fall Komiko Okamoto Korvel Noh @@ -932,6 +935,8 @@ Lexi Frua Lillie Cordeaux Lilly Zenovka Lizzy Macarthur +Logue Takacs + INTL-490 Luban Yiyuan Luc Starsider Luminous Luminos @@ -1188,6 +1193,8 @@ PanteraPolnocy SL-18891 SL-18904 SL-18937 + SL-19207 + SL-19681 Parvati Silverweb Patric Mills VWR-2645 @@ -1413,6 +1420,7 @@ Sovereign Engineer SL-18525 SL-18534 SL-19690 + SL-19336 SpacedOut Frye VWR-34 VWR-45 diff --git a/indra/cmake/00-Common.cmake b/indra/cmake/00-Common.cmake index 58a64a8755..a93c5d06e8 100644 --- a/indra/cmake/00-Common.cmake +++ b/indra/cmake/00-Common.cmake @@ -171,6 +171,10 @@ if (DARWIN) ## Really?? On developer machines too? ##set(ENABLE_SIGNING TRUE) ##set(SIGNING_IDENTITY "Developer ID Application: Linden Research, Inc.") + + # required for clang-15/xcode-15 since our boost package still uses deprecated std::unary_function/binary_function + # see https://developer.apple.com/documentation/xcode-release-notes/xcode-15-release-notes#C++-Standard-Library + add_compile_definitions(_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION) endif (DARWIN) if (LINUX OR DARWIN) diff --git a/indra/llappearance/CMakeLists.txt b/indra/llappearance/CMakeLists.txt index 75c0e276eb..c3be8bc78e 100644 --- a/indra/llappearance/CMakeLists.txt +++ b/indra/llappearance/CMakeLists.txt @@ -21,7 +21,6 @@ set(llappearance_SOURCE_FILES lltexglobalcolor.cpp lltexlayer.cpp lltexlayerparams.cpp - lltexturemanagerbridge.cpp llwearable.cpp llwearabledata.cpp llwearabletype.cpp @@ -44,7 +43,6 @@ set(llappearance_HEADER_FILES lltexglobalcolor.h lltexlayer.h lltexlayerparams.h - lltexturemanagerbridge.h llwearable.h llwearabledata.h llwearabletype.h diff --git a/indra/llappearance/llavatarappearance.cpp b/indra/llappearance/llavatarappearance.cpp index 7946a3e705..18b03c1f89 100644 --- a/indra/llappearance/llavatarappearance.cpp +++ b/indra/llappearance/llavatarappearance.cpp @@ -1050,7 +1050,6 @@ BOOL LLAvatarAppearance::loadSkeletonNode () mRoot->addChild(mMeshLOD[MESH_ID_UPPER_BODY]); mRoot->addChild(mMeshLOD[MESH_ID_LOWER_BODY]); mRoot->addChild(mMeshLOD[MESH_ID_SKIRT]); - mRoot->addChild(mMeshLOD[MESH_ID_HEAD]); LLAvatarJoint *skull = (LLAvatarJoint*)mRoot->findJoint("mSkull"); if (skull) diff --git a/indra/llappearance/llwearable.h b/indra/llappearance/llwearable.h index 875c2932f1..d86db236a3 100644 --- a/indra/llappearance/llwearable.h +++ b/indra/llappearance/llwearable.h @@ -32,7 +32,6 @@ #include "llsaleinfo.h" #include "llwearabletype.h" -class LLMD5; class LLVisualParam; class LLTexGlobalColorInfo; class LLTexGlobalColor; @@ -110,9 +109,6 @@ public: // Something happened that requires the wearable to be updated (e.g. worn/unworn). virtual void setUpdated() const = 0; - // Update the baked texture hash. - virtual void addToBakedTextureHash(LLMD5& hash) const = 0; - typedef std::map visual_param_index_map_t; visual_param_index_map_t mVisualParamIndexMap; diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp index 0eaeedb6ee..9fbbc57c87 100644 --- a/indra/llappearance/llwearabledata.cpp +++ b/indra/llappearance/llwearabledata.cpp @@ -31,7 +31,6 @@ #include "llavatarappearance.h" #include "llavatarappearancedefines.h" #include "lldriverparam.h" -#include "llmd5.h" LLWearableData::LLWearableData() : mAvatarAppearance(NULL) @@ -343,42 +342,3 @@ U32 LLWearableData::getWearableCount(const U32 tex_index) const const LLWearableType::EType wearable_type = LLAvatarAppearance::getDictionary()->getTEWearableType((LLAvatarAppearanceDefines::ETextureIndex)tex_index); return getWearableCount(wearable_type); } - -LLUUID LLWearableData::computeBakedTextureHash(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index, - BOOL generate_valid_hash) // Set to false if you want to upload the baked texture w/o putting it in the cache -{ - LLUUID hash_id; - bool hash_computed = false; - LLMD5 hash; - const LLAvatarAppearanceDictionary::BakedEntry *baked_dict = LLAvatarAppearance::getDictionary()->getBakedTexture(baked_index); - - for (U8 i=0; i < baked_dict->mWearables.size(); i++) - { - const LLWearableType::EType baked_type = baked_dict->mWearables[i]; - const U32 num_wearables = getWearableCount(baked_type); - for (U32 index = 0; index < num_wearables; ++index) - { - const LLWearable* wearable = getWearable(baked_type,index); - if (wearable) - { - wearable->addToBakedTextureHash(hash); - hash_computed = true; - } - } - } - if (hash_computed) - { - hash.update((const unsigned char*)baked_dict->mWearablesHashID.mData, UUID_BYTES); - - if (!generate_valid_hash) - { - invalidateBakedTextureHash(hash); - } - hash.finalize(); - hash.raw_digest(hash_id.mData); - } - - return hash_id; -} - - diff --git a/indra/llappearance/llwearabledata.h b/indra/llappearance/llwearabledata.h index a0c446ea9e..b8e0bf8bd1 100644 --- a/indra/llappearance/llwearabledata.h +++ b/indra/llappearance/llwearabledata.h @@ -85,15 +85,6 @@ protected: private: void pullCrossWearableValues(const LLWearableType::EType type); - //-------------------------------------------------------------------- - // Server Communication - //-------------------------------------------------------------------- -public: - LLUUID computeBakedTextureHash(LLAvatarAppearanceDefines::EBakedTextureIndex baked_index, - BOOL generate_valid_hash = TRUE); -protected: - virtual void invalidateBakedTextureHash(LLMD5& hash) const {} - //-------------------------------------------------------------------- // Member variables //-------------------------------------------------------------------- diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index ddc508455f..a387bb23cd 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -204,7 +204,8 @@ std::string LLAudioEngine::getInternetStreamURL() { if (mStreamingAudioImpl) return mStreamingAudioImpl->getURL(); - else return std::string(); + + return std::string(); } @@ -345,42 +346,43 @@ void LLAudioEngine::idle() } continue; } - else + + // Check to see if the current sound is done playing. + if (!channelp->isPlaying()) { - // Check to see if the current sound is done playing, or looped. - if (!channelp->isPlaying()) + sourcep->mCurrentDatap = sourcep->mQueuedDatap; + sourcep->mQueuedDatap = NULL; + + // Reset the timer so the source doesn't die. + sourcep->mAgeTimer.reset(); + + // Make sure we have the buffer set up if we just decoded the data + if (sourcep->mCurrentDatap) + { + updateBufferForData(sourcep->mCurrentDatap); + } + + // Actually play the associated data. + sourcep->setupChannel(); + channelp->updateBuffer(); + sourcep->getChannel()->play(); + continue; + } + + // Check to see if the current sound is looped. + if (sourcep->isLoop()) + { + // It's a loop, we need to check and see if we're done with it. + if (channelp->mLoopedThisFrame) { sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; - // Reset the timer so the source doesn't die. - sourcep->mAgeTimer.reset(); - - // Make sure we have the buffer set up if we just decoded the data - if (sourcep->mCurrentDatap) - { - updateBufferForData(sourcep->mCurrentDatap); - } - - // Actually play the associated data. + // Actually, should do a time sync so if we're a loop master/slave + // we don't drift away. sourcep->setupChannel(); - channelp->updateBuffer(); sourcep->getChannel()->play(); } - else if (sourcep->isLoop()) - { - // It's a loop, we need to check and see if we're done with it. - if (channelp->mLoopedThisFrame) - { - sourcep->mCurrentDatap = sourcep->mQueuedDatap; - sourcep->mQueuedDatap = NULL; - - // Actually, should do a time sync so if we're a loop master/slave - // we don't drift away. - sourcep->setupChannel(); - sourcep->getChannel()->play(); - } - } } } @@ -396,18 +398,11 @@ void LLAudioEngine::idle() for (source_map::value_type& src_pair : mAllSources) { LLAudioSource *sourcep = src_pair.second; - if (sourcep->isMuted()) + if (sourcep->isMuted() && sourcep->isSyncMaster() && sourcep->getPriority() > max_sm_priority) { - continue; - } - if (sourcep->isSyncMaster()) - { - if (sourcep->getPriority() > max_sm_priority) - { - sync_masterp = sourcep; - master_channelp = sync_masterp->getChannel(); - max_sm_priority = sourcep->getPriority(); - } + sync_masterp = sourcep; + master_channelp = sync_masterp->getChannel(); + max_sm_priority = sourcep->getPriority(); } } @@ -737,7 +732,7 @@ F64 LLAudioEngine::mapWindVecToGain(LLVector3 wind_vec) } return (gain); -} +} F64 LLAudioEngine::mapWindVecToPitch(LLVector3 wind_vec) @@ -964,11 +959,10 @@ void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp) else { LL_DEBUGS("AudioEngine") << "Cleaning up audio sources for "<< asp->getID() <getID() << ")" << LL_ENDL; - if (sourcep == mCurrentSourcep) - { - // Don't reallocate the channel, this will make FMOD goofy. - //LL_INFOS() << "Calling setSource with same source!" << LL_ENDL; + if (sourcep == mCurrentSourcep) + { + // Don't reallocate the channel, this will make FMOD goofy. + //LL_INFOS() << "Calling setSource with same source!" << LL_ENDL; + } + + mCurrentSourcep = sourcep; + + updateBuffer(); + update3DPosition(); } - - mCurrentSourcep = sourcep; - - updateBuffer(); - update3DPosition(); } -} - bool LLAudioChannel::updateBuffer() { diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index 0fe8b3d756..a133898969 100755 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -90,6 +90,7 @@ public: // initialization/startup/shutdown virtual bool init(void *userdata, const std::string &app_title); virtual std::string getDriverName(bool verbose) = 0; + virtual LLStreamingAudioInterface *createDefaultStreamingAudioImpl() const = 0; virtual void shutdown(); // Used by the mechanics of the engine @@ -468,13 +469,13 @@ struct SoundData const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, - const LLVector3d &pos_global = LLVector3d::zero) + const LLVector3d &pos_global = LLVector3d::zero) : + audio_uuid(audio_uuid), + owner_id(owner_id), + gain(gain), + type(type), + pos_global(pos_global) { - this->audio_uuid = audio_uuid; - this->owner_id = owner_id; - this->gain = gain; - this->type = type; - this->pos_global = pos_global; } }; diff --git a/indra/llaudio/llaudioengine_fmodstudio.cpp b/indra/llaudio/llaudioengine_fmodstudio.cpp index ba743020b5..c6313ea289 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.cpp +++ b/indra/llaudio/llaudioengine_fmodstudio.cpp @@ -208,10 +208,6 @@ bool LLAudioEngine_FMODSTUDIO::init(void* userdata, const std::string &app_title } #endif - // set up our favourite FMOD-native streaming audio implementation if none has already been added - if (!getStreamingAudioImpl()) // no existing implementation added - setStreamingAudioImpl(new LLStreamingAudio_FMODSTUDIO(mSystem)); - LL_INFOS("AppInit") << "LLAudioEngine_FMODSTUDIO::init() FMOD Studio initialized correctly" << LL_ENDL; int r_numbuffers, r_samplerate, r_channels; @@ -253,6 +249,13 @@ std::string LLAudioEngine_FMODSTUDIO::getDriverName(bool verbose) } +// create our favourite FMOD-native streaming audio implementation +LLStreamingAudioInterface *LLAudioEngine_FMODSTUDIO::createDefaultStreamingAudioImpl() const +{ + return new LLStreamingAudio_FMODSTUDIO(mSystem); +} + + void LLAudioEngine_FMODSTUDIO::allocateListener(void) { mListenerp = (LLListener *) new LLListener_FMODSTUDIO(mSystem); diff --git a/indra/llaudio/llaudioengine_fmodstudio.h b/indra/llaudio/llaudioengine_fmodstudio.h index d3d6d69685..29e7bc6bf0 100644 --- a/indra/llaudio/llaudioengine_fmodstudio.h +++ b/indra/llaudio/llaudioengine_fmodstudio.h @@ -53,6 +53,7 @@ public: // initialization/startup/shutdown virtual bool init(void *user_data, const std::string &app_title); virtual std::string getDriverName(bool verbose); + virtual LLStreamingAudioInterface* createDefaultStreamingAudioImpl() const; virtual void allocateListener(); virtual void shutdown(); diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp index 117d408b21..5b1b28bf4f 100644 --- a/indra/llcharacter/llbvhloader.cpp +++ b/indra/llcharacter/llbvhloader.cpp @@ -1352,7 +1352,6 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) dp.packS32(joint->mNumRotKeys, "num_rot_keys"); LLQuaternion::Order order = bvhStringToOrder( joint->mOrder ); - S32 outcount = 0; S32 frame = 0; for (Key& key : joint->mKeys) { @@ -1418,7 +1417,6 @@ BOOL LLBVHLoader::serialize(LLDataPacker& dp) dp.packU16(x, "rot_angle_x"); dp.packU16(y, "rot_angle_y"); dp.packU16(z, "rot_angle_z"); - outcount++; frame++; } diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp index 6720f902bd..d95ec159f2 100644 --- a/indra/llcharacter/llkeyframemotion.cpp +++ b/indra/llcharacter/llkeyframemotion.cpp @@ -497,13 +497,20 @@ LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *charact // request asset mAssetStatus = ASSET_FETCHED; - LL_DEBUGS("Animation") << "Requesting data fetch for: " << mID << LL_ENDL; - character_id = new LLUUID(mCharacter->getID()); - gAssetStorage->getAssetData(mID, - LLAssetType::AT_ANIMATION, - onLoadComplete, - (void *)character_id, - FALSE); + if (mID.notNull()) + { + LL_DEBUGS("Animation") << "Requesting data fetch for: " << mID << LL_ENDL; + character_id = new LLUUID(mCharacter->getID()); + gAssetStorage->getAssetData(mID, + LLAssetType::AT_ANIMATION, + onLoadComplete, + (void*)character_id, + FALSE); + } + else + { + LL_INFOS("Animation") << "Attempted to fetch animation " << mName << " with null id for character " << mCharacter->getID() << LL_ENDL; + } return STATUS_HOLD; case ASSET_FETCHED: @@ -1391,6 +1398,8 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo // get number of joint motions //------------------------------------------------------------------------- U32 num_motions = 0; + S32 rotation_dupplicates = 0; + S32 position_dupplicates = 0; if (!dp.unpackU32(num_motions, "num_joints")) { LL_WARNS() << "can't read number of joints" @@ -1621,6 +1630,12 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo rCurve->mKeys[time] = rot_key; } + if (joint_motion->mRotationCurve.mNumKeys > joint_motion->mRotationCurve.mKeys.size()) + { + rotation_dupplicates++; + LL_INFOS() << "Motion: " << asset_id << " had dupplicate rotation keys that were removed" << LL_ENDL; + } + //--------------------------------------------------------------------- // scan position curve header //--------------------------------------------------------------------- @@ -1723,9 +1738,24 @@ BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp, const LLUUID& asset_id, boo } } + if (joint_motion->mPositionCurve.mNumKeys > joint_motion->mPositionCurve.mKeys.size()) + { + position_dupplicates++; + } + joint_motion->mUsage = joint_state->getUsage(); } + if (rotation_dupplicates > 0) + { + LL_INFOS() << "Motion: " << asset_id << " had " << rotation_dupplicates << " dupplicate rotation keys that were removed" << LL_ENDL; + } + + if (position_dupplicates > 0) + { + LL_INFOS() << "Motion: " << asset_id << " had " << position_dupplicates << " dupplicate position keys that were removed" << LL_ENDL; + } + //------------------------------------------------------------------------- // get number of constraints //------------------------------------------------------------------------- @@ -2005,10 +2035,13 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const JointMotion* joint_motionp = mJointMotionList->getJointMotion(i); success &= dp.packString(joint_motionp->mJointName, "joint_name"); success &= dp.packS32(joint_motionp->mPriority, "joint_priority"); - success &= dp.packS32(joint_motionp->mRotationCurve.mNumKeys, "num_rot_keys"); + success &= dp.packS32(joint_motionp->mRotationCurve.mKeys.size(), "num_rot_keys"); - LL_DEBUGS("BVH") << "Joint " << joint_motionp->mJointName << LL_ENDL; - for (RotationCurve::key_map_t::value_type& rot_pair : joint_motionp->mRotationCurve.mKeys) + LL_DEBUGS("BVH") << "Joint " << i + << " name: " << joint_motionp->mJointName + << " Rotation keys: " << joint_motionp->mRotationCurve.mKeys.size() + << " Position keys: " << joint_motionp->mPositionCurve.mKeys.size() << LL_ENDL; + for (RotationCurve::key_map_t::value_type& rot_pair : joint_motionp->mRotationCurve.mKeys) { RotationKey& rot_key = rot_pair.second; U16 time_short = F32_to_U16(rot_key.mTime, 0.f, mJointMotionList->mDuration); @@ -2028,7 +2061,7 @@ BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const LL_DEBUGS("BVH") << " rot: t " << rot_key.mTime << " angles " << rot_angles.mV[VX] <<","<< rot_angles.mV[VY] <<","<< rot_angles.mV[VZ] << LL_ENDL; } - success &= dp.packS32(joint_motionp->mPositionCurve.mNumKeys, "num_pos_keys"); + success &= dp.packS32(joint_motionp->mPositionCurve.mKeys.size(), "num_pos_keys"); for (PositionCurve::key_map_t::value_type& pos_pair : joint_motionp->mPositionCurve.mKeys) { PositionKey& pos_key = pos_pair.second; diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 516813aae1..fa9c6eaa79 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -40,7 +40,6 @@ set(llcommon_SOURCE_FILES lldependencies.cpp lldictionary.cpp llerror.cpp - llerrorthread.cpp llevent.cpp lleventapi.cpp lleventcoro.cpp @@ -153,7 +152,6 @@ set(llcommon_HEADER_FILES llendianswizzle.h llerror.h llerrorcontrol.h - llerrorthread.h llevent.h lleventapi.h lleventcoro.h diff --git a/indra/llcommon/hbxxh.h b/indra/llcommon/hbxxh.h index 236716722a..9c0e9cf172 100644 --- a/indra/llcommon/hbxxh.h +++ b/indra/llcommon/hbxxh.h @@ -96,6 +96,15 @@ public: } } + // Make this class no-copy (it would be possible, with custom copy + // operators, but it is not trivially copyable, because of the mState + // pointer): it does not really make sense to allow copying it anyway, + // since all we care about is the resulting digest (so you should only + // need and care about storing/copying the digest and not a class + // instance). + HBXXH64(const HBXXH64&) noexcept = delete; + HBXXH64& operator=(const HBXXH64&) noexcept = delete; + ~HBXXH64(); void update(const void* buffer, size_t len); @@ -199,6 +208,15 @@ public: } } + // Make this class no-copy (it would be possible, with custom copy + // operators, but it is not trivially copyable, because of the mState + // pointer): it does not really make sense to allow copying it anyway, + // since all we care about is the resulting digest (so you should only + // need and care about storing/copying the digest and not a class + // instance). + HBXXH128(const HBXXH128&) noexcept = delete; + HBXXH128& operator=(const HBXXH128&) noexcept = delete; + ~HBXXH128(); void update(const void* buffer, size_t len); diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index 10b98f49aa..679f79039b 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -345,6 +345,7 @@ const U8 CLICK_ACTION_PLAY = 5; const U8 CLICK_ACTION_OPEN_MEDIA = 6; const U8 CLICK_ACTION_ZOOM = 7; const U8 CLICK_ACTION_DISABLED = 8; +const U8 CLICK_ACTION_IGNORE = 9; // DO NOT CHANGE THE SEQUENCE OF THIS LIST!! diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index d839b19c99..b99166991f 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -39,7 +39,6 @@ #include "llcommon.h" #include "llapr.h" #include "llerrorcontrol.h" -#include "llerrorthread.h" #include "llframetimer.h" #include "lllivefile.h" #include "llmemory.h" @@ -108,12 +107,7 @@ LLAppErrorHandler LLApp::sErrorHandler = NULL; BOOL LLApp::sErrorThreadRunning = FALSE; -LLApp::LLApp() : mThreadErrorp(NULL) -{ - commonCtor(); -} - -void LLApp::commonCtor() +LLApp::LLApp() { // Set our status to running setStatus(APP_STATUS_RUNNING); @@ -143,12 +137,6 @@ void LLApp::commonCtor() mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe"; } -LLApp::LLApp(LLErrorThread *error_thread) : - mThreadErrorp(error_thread) -{ - commonCtor(); -} - LLApp::~LLApp() { @@ -158,13 +146,6 @@ LLApp::~LLApp() mLiveFiles.clear(); setStopped(); - // HACK: wait for the error thread to clean itself - ms_sleep(20); - if (mThreadErrorp) - { - delete mThreadErrorp; - mThreadErrorp = NULL; - } SUBSYSTEM_CLEANUP_DBG(LLCommon); } @@ -393,27 +374,6 @@ void LLApp::setupErrorHandling(bool second_instance) #endif // ! LL_BUGSPLAT #endif // ! LL_WINDOWS - -#ifdef LL_BUGSPLAT - // do not start our own error thread -#else // ! LL_BUGSPLAT - startErrorThread(); -#endif -} - -void LLApp::startErrorThread() -{ - // - // Start the error handling thread, which is responsible for taking action - // when the app goes into the APP_STATUS_ERROR state - // - if(!mThreadErrorp) - { - LL_INFOS() << "Starting error thread" << LL_ENDL; - mThreadErrorp = new LLErrorThread(); - mThreadErrorp->setUserData((void *) this); - mThreadErrorp->start(); - } } void LLApp::setErrorHandler(LLAppErrorHandler handler) @@ -476,7 +436,7 @@ void LLApp::setStatus(EAppStatus status) // static void LLApp::setError() { - // set app status to ERROR so that the LLErrorThread notices + // set app status to ERROR setStatus(APP_STATUS_ERROR); } diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index c65fe21c9c..c832c8b142 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -34,7 +34,6 @@ #include #include // Forward declarations -class LLErrorThread; class LLLiveFile; #if LL_LINUX #include @@ -53,7 +52,6 @@ void clear_signals(); class LL_COMMON_API LLApp { - friend class LLErrorThread; public: typedef enum e_app_status { @@ -67,11 +65,6 @@ public: LLApp(); virtual ~LLApp(); -protected: - LLApp(LLErrorThread* error_thread); - void commonCtor(); -public: - /** * @brief Return the static app instance if one was created. */ @@ -257,14 +250,14 @@ public: void setupErrorHandling(bool mSecondInstance=false); void setErrorHandler(LLAppErrorHandler handler); - static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred. + static void runErrorHandler(); // run shortly after we detect an error //@} - + // the maximum length of the minidump filename returned by getMiniDumpFilename() static const U32 MAX_MINDUMP_PATH_LENGTH = 256; // change the directory where Breakpad minidump files are written to - void setDebugFileNames(const std::string &path); + void setDebugFileNames(const std::string &path); // Return the Google Breakpad minidump filename after a crash. char *getMiniDumpFilename() { return mMinidumpPath; } @@ -310,13 +303,11 @@ protected: void stepFrame(); private: - void startErrorThread(); - // Contains the filename of the minidump file after a crash. char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH]; - std::string mStaticDebugFileName; - std::string mDynamicDebugFileName; + std::string mStaticDebugFileName; + std::string mDynamicDebugFileName; // *NOTE: On Windows, we need a routine to reset the structured // exception handler when some evil driver has taken it over for @@ -324,9 +315,6 @@ private: typedef int(*signal_handler_func)(int signum); static LLAppErrorHandler sErrorHandler; - // Default application threads - LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback - // This is the application level runnable scheduler. LLRunner mRunner; diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp index 93d0a035da..9f23ce5317 100644 --- a/indra/llcommon/llcallbacklist.cpp +++ b/indra/llcommon/llcallbacklist.cpp @@ -55,7 +55,7 @@ void LLCallbackList::addFunction( callback_t func, void *data) // only add one callback per func/data pair // - if (containsFunction(func)) + if (containsFunction(func, data)) { return; } diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h index 5c46f6a796..4e25001fff 100644 --- a/indra/llcommon/lldefs.h +++ b/indra/llcommon/lldefs.h @@ -167,48 +167,34 @@ const U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 + 1 for good luc // // defined for U16, U32, U64, S16, S32, S64, : // llclampb(a) // clamps a to [0 .. 255] -// +// -template -inline auto llmax(T1 d1, T2 d2) +// recursion tail +template +inline auto llmax(T data) { - return (d1 > d2) ? d1 : d2; + return data; } -template -inline auto llmax(T1 d1, T2 d2, T3 d3) +template +inline auto llmax(T0 d0, T1 d1, Ts... rest) { - auto r = llmax(d1,d2); - return llmax(r, d3); + auto maxrest = llmax(d1, rest...); + return (d0 > maxrest)? d0 : maxrest; } -template -inline auto llmax(T1 d1, T2 d2, T3 d3, T4 d4) +// recursion tail +template +inline auto llmin(T data) { - auto r1 = llmax(d1,d2); - auto r2 = llmax(d3,d4); - return llmax(r1, r2); + return data; } -template -inline auto llmin(T1 d1, T2 d2) +template +inline auto llmin(T0 d0, T1 d1, Ts... rest) { - return (d1 < d2) ? d1 : d2; -} - -template -inline auto llmin(T1 d1, T2 d2, T3 d3) -{ - auto r = llmin(d1,d2); - return (r < d3 ? r : d3); -} - -template -inline auto llmin(T1 d1, T2 d2, T3 d3, T4 d4) -{ - auto r1 = llmin(d1,d2); - auto r2 = llmin(d3,d4); - return llmin(r1, r2); + auto minrest = llmin(d1, rest...); + return (d0 < minrest) ? d0 : minrest; } template diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 08eb323c4a..f229b4964a 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -276,7 +276,6 @@ namespace LLError // used to indicate no class info known for logging //LLCallStacks keeps track of call stacks and output the call stacks to log file - //when LLAppViewer::handleViewerCrash() is triggered. // //Note: to be simple, efficient and necessary to keep track of correct call stacks, //LLCallStacks is designed not to be thread-safe. diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp deleted file mode 100644 index f6bc68b5c1..0000000000 --- a/indra/llcommon/llerrorthread.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/** - * @file llerrorthread.cpp - * - * $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$ - */ - -#include "linden_common.h" -#include "llerrorthread.h" - -#include "llapp.h" -#include "lltimer.h" // ms_sleep() - -LLErrorThread::LLErrorThread() - : LLThread("Error"), - mUserDatap(NULL) -{ -} - -LLErrorThread::~LLErrorThread() -{ -} - -void LLErrorThread::setUserData(void* user_data) -{ - mUserDatap = user_data; -} - - -void* LLErrorThread::getUserData() const -{ - return mUserDatap; -} - -#if !LL_WINDOWS -// -// Various signal/error handling functions that can't be put into the class -// -void get_child_status(const int waitpid_status, int &process_status, bool &exited, bool do_logging) -{ - exited = false; - process_status = -1; - // The child process exited. Call its callback, and then clean it up - if (WIFEXITED(waitpid_status)) - { - process_status = WEXITSTATUS(waitpid_status); - exited = true; - if (do_logging) - { - LL_INFOS() << "get_child_status - Child exited cleanly with return of " << process_status << LL_ENDL; - } - return; - } - else if (WIFSIGNALED(waitpid_status)) - { - process_status = WTERMSIG(waitpid_status); - exited = true; - if (do_logging) - { - LL_INFOS() << "get_child_status - Child died because of uncaught signal " << process_status << LL_ENDL; -#ifdef WCOREDUMP - if (WCOREDUMP(waitpid_status)) - { - LL_INFOS() << "get_child_status - Child dumped core" << LL_ENDL; - } - else - { - LL_INFOS() << "get_child_status - Child didn't dump core" << LL_ENDL; - } -#endif - } - return; - } - else if (do_logging) - { - // This is weird. I just dump the waitpid status into the status code, - // not that there's any way of telling what it is... - LL_INFOS() << "get_child_status - Got SIGCHILD but child didn't exit" << LL_ENDL; - process_status = waitpid_status; - } - -} -#endif - -void LLErrorThread::run() -{ - LLApp::sErrorThreadRunning = TRUE; - // This thread sits and waits for the sole purpose - // of waiting for the signal/exception handlers to flag the - // application state as APP_STATUS_ERROR. - LL_INFOS() << "thread_error - Waiting for an error" << LL_ENDL; - - S32 counter = 0; - while (! (LLApp::isError() || LLApp::isStopped())) - { - ms_sleep(10); - counter++; - } - if (LLApp::isError()) - { - // The app is in an error state, run the application's error handler. - //LL_INFOS() << "thread_error - An error has occurred, running error callback!" << LL_ENDL; - // Run the error handling callback - LLApp::runErrorHandler(); - } - else - { - // Everything is okay, a clean exit. - //LL_INFOS() << "thread_error - Application exited cleanly" << LL_ENDL; - } - - //LL_INFOS() << "thread_error - Exiting" << LL_ENDL; - LLApp::sErrorThreadRunning = FALSE; -} - diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index c87c0758fe..259f5bc505 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -204,30 +204,35 @@ public: LLSD packet(LLSDMap("pump", pump)("data", data)); std::ostringstream buffer; - buffer << LLSDNotationStreamer(packet); + // SL-18330: for large data blocks, it's much faster to parse binary + // LLSD than notation LLSD. Use serialize(LLSD_BINARY) rather than + // directly calling LLSDBinaryFormatter because, unlike the latter, + // serialize() prepends the relevant header, needed by a general- + // purpose LLSD parser to distinguish binary from notation. + LLSDSerialize::serialize(packet, buffer, LLSDSerialize::LLSD_BINARY, + LLSDFormatter::OPTIONS_NONE); /*==========================================================================*| // DEBUGGING ONLY: don't copy str() if we can avoid it. std::string strdata(buffer.str()); if (std::size_t(buffer.tellp()) != strdata.length()) { - LL_ERRS("LLLeap") << "tellp() -> " << buffer.tellp() << " != " + LL_ERRS("LLLeap") << "tellp() -> " << static_cast(buffer.tellp()) << " != " << "str().length() -> " << strdata.length() << LL_ENDL; } // DEBUGGING ONLY: reading back is terribly inefficient. std::istringstream readback(strdata); LLSD echo; - LLPointer parser(new LLSDNotationParser()); - S32 parse_status(parser->parse(readback, echo, strdata.length())); - if (parse_status == LLSDParser::PARSE_FAILURE) + bool parse_status(LLSDSerialize::deserialize(echo, readback, strdata.length())); + if (! parse_status) { - LL_ERRS("LLLeap") << "LLSDNotationParser() cannot parse output of " - << "LLSDNotationStreamer()" << LL_ENDL; + LL_ERRS("LLLeap") << "LLSDSerialize::deserialize() cannot parse output of " + << "LLSDSerialize::serialize(LLSD_BINARY)" << LL_ENDL; } if (! llsd_equals(echo, packet)) { - LL_ERRS("LLLeap") << "LLSDNotationParser() produced different LLSD " - << "than passed to LLSDNotationStreamer()" << LL_ENDL; + LL_ERRS("LLLeap") << "LLSDSerialize::deserialize() returned different LLSD " + << "than passed to LLSDSerialize::serialize()" << LL_ENDL; } |*==========================================================================*/ @@ -314,9 +319,17 @@ public: LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got " << childout.size() << ", parsing LLSD" << LL_ENDL; LLSD data; +#if 1 + // specifically require notation LLSD from child LLPointer parser(new LLSDNotationParser()); S32 parse_status(parser->parse(childout.get_istream(), data, mExpect)); if (parse_status == LLSDParser::PARSE_FAILURE) +#else + // SL-18330: accept any valid LLSD serialization format from child + // Unfortunately this runs into trouble we have not yet debugged. + bool parse_status(LLSDSerialize::deserialize(data, childout.get_istream(), mExpect)); + if (! parse_status) +#endif { bad_protocol("unparseable LLSD data"); } diff --git a/indra/llcommon/llmd5.cpp b/indra/llcommon/llmd5.cpp index 9b2a2bab60..0abe817f1d 100644 --- a/indra/llcommon/llmd5.cpp +++ b/indra/llcommon/llmd5.cpp @@ -96,7 +96,7 @@ LLMD5::LLMD5() // operation, processing another message block, and updating the // context. -void LLMD5::update (const uint1 *input, const size_t input_length) { +void LLMD5::update (const uint8_t *input, const size_t input_length) { size_t input_index, buffer_index; size_t buffer_space; // how much space is left in buffer @@ -189,7 +189,7 @@ void LLMD5::finalize (){ unsigned char bits[8]; /* Flawfinder: ignore */ size_t index, padLen; - static uint1 PADDING[64]={ + static uint8_t PADDING[64]={ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -201,8 +201,8 @@ void LLMD5::finalize (){ } // Save number of bits. - // Treat count, a uint64_t, as uint4[2]. - encode (bits, reinterpret_cast(&count), 8); + // Treat count, a uint64_t, as uint32_t[2]. + encode (bits, reinterpret_cast(&count), 8); // Pad out to 56 mod 64. index = size_t((count >> 3) & 0x3f); @@ -412,7 +412,7 @@ Rotation is separate from addition to prevent recomputation. // LLMD5 basic transformation. Transforms state based on block. void LLMD5::transform (const U8 block[64]){ - uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16]; decode (x, block, 64); @@ -496,38 +496,38 @@ void LLMD5::transform (const U8 block[64]){ state[3] += d; // Zeroize sensitive information. - memset ( (uint1 *) x, 0, sizeof(x)); + memset ( (uint8_t *) x, 0, sizeof(x)); } -// Encodes input (UINT4) into output (unsigned char). Assumes len is +// Encodes input (uint32_t) into output (unsigned char). Assumes len is // a multiple of 4. -void LLMD5::encode (uint1 *output, const uint4 *input, const size_t len) { +void LLMD5::encode (uint8_t *output, const uint32_t *input, const size_t len) { size_t i, j; for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (uint1) (input[i] & 0xff); - output[j+1] = (uint1) ((input[i] >> 8) & 0xff); - output[j+2] = (uint1) ((input[i] >> 16) & 0xff); - output[j+3] = (uint1) ((input[i] >> 24) & 0xff); + output[j] = (uint8_t) (input[i] & 0xff); + output[j+1] = (uint8_t) ((input[i] >> 8) & 0xff); + output[j+2] = (uint8_t) ((input[i] >> 16) & 0xff); + output[j+3] = (uint8_t) ((input[i] >> 24) & 0xff); } } -// Decodes input (unsigned char) into output (UINT4). Assumes len is +// Decodes input (unsigned char) into output (uint32_t). Assumes len is // a multiple of 4. -void LLMD5::decode (uint4 *output, const uint1 *input, const size_t len){ +void LLMD5::decode (uint32_t *output, const uint8_t *input, const size_t len){ size_t i, j; for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | - (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); + output[i] = ((uint32_t)input[j]) | (((uint32_t)input[j+1]) << 8) | + (((uint32_t)input[j+2]) << 16) | (((uint32_t)input[j+3]) << 24); } diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h index 8530dc0389..7d6373c20c 100644 --- a/indra/llcommon/llmd5.h +++ b/indra/llcommon/llmd5.h @@ -67,6 +67,8 @@ documentation and/or software. */ +#include // uint32_t et al. + // use for the raw digest output const int MD5RAW_BYTES = 16; @@ -75,18 +77,13 @@ const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null const int MD5HEX_STR_BYTES = 32; // message system fixed size class LL_COMMON_API LLMD5 { -// first, some types: - typedef unsigned int uint4; // assumes integer is 4 words long - typedef unsigned short int uint2; // assumes short integer is 2 words long - typedef unsigned char uint1; // assumes char is 1 word long - // how many bytes to grab at a time when checking files static const int BLOCK_LEN; public: // methods for controlled operation: LLMD5 (); // simple initializer - void update (const uint1 *input, const size_t input_length); + void update (const uint8_t *input, const size_t input_length); void update (std::istream& stream); void update (FILE *file); void update (const std::string& str); @@ -109,19 +106,19 @@ private: // next, the private data: - uint4 state[4]; + uint32_t state[4]; uint64_t count; // number of *bits*, mod 2^64 - uint1 buffer[64]; // input buffer - uint1 digest[16]; - uint1 finalized; + uint8_t buffer[64]; // input buffer + uint8_t digest[16]; + uint8_t finalized; // last, the private methods, mostly static: void init (); // called by all constructors - void transform (const uint1 *buffer); // does the real update work. Note + void transform (const uint8_t *buffer); // does the real update work. Note // that length is implied to be 64. - static void encode (uint1 *dest, const uint4 *src, const size_t length); - static void decode (uint4 *dest, const uint1 *src, const size_t length); + static void encode (uint8_t *dest, const uint32_t *src, const size_t length); + static void decode (uint32_t *dest, const uint8_t *src, const size_t length); }; diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index b7e316da10..3db456ddb3 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -48,6 +48,7 @@ #endif #include "lldate.h" +#include "llmemorystream.h" #include "llsd.h" #include "llstring.h" #include "lluri.h" @@ -64,6 +65,23 @@ const std::string LLSD_NOTATION_HEADER("llsd/notation"); #define windowBits 15 #define ENABLE_ZLIB_GZIP 32 +// If we published this in llsdserialize.h, we could use it in the +// implementation of LLSDOStreamer's operator<<(). +template +void format_using(const LLSD& data, std::ostream& ostr, + LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY) +{ + LLPointer f{ new Formatter }; + f->format(data, ostr, options); +} + +template +S32 parse_using(std::istream& istr, LLSD& data, size_t max_bytes, S32 max_depth=-1) +{ + LLPointer p{ new Parser }; + return p->parse(istr, data, max_bytes, max_depth); +} + /** * LLSDSerialize */ @@ -86,10 +104,10 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize f = new LLSDXMLFormatter; break; - case LLSD_NOTATION: - str << "\n"; - f = new LLSDNotationFormatter; - break; + case LLSD_NOTATION: + str << "\n"; + f = new LLSDNotationFormatter; + break; default: LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL; @@ -104,18 +122,37 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize // static bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes) { - LLPointer p = NULL; char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */ - int i; - int inbuf = 0; - bool legacy_no_header = false; bool fail_if_not_legacy = false; - std::string header; - /* - * Get the first line before anything. - */ - str.get(hdr_buf, MAX_HDR_LEN, '\n'); + /* + * Get the first line before anything. Don't read more than max_bytes: + * this get() overload reads no more than (count-1) bytes into the + * specified buffer. In the usual case when max_bytes exceeds + * sizeof(hdr_buf), get() will read no more than sizeof(hdr_buf)-2. + */ + llssize max_hdr_read = MAX_HDR_LEN; + if (max_bytes != LLSDSerialize::SIZE_UNLIMITED) + { + max_hdr_read = llmin(max_bytes + 1, max_hdr_read); + } + str.get(hdr_buf, max_hdr_read, '\n'); + auto inbuf = str.gcount(); + + // https://en.cppreference.com/w/cpp/io/basic_istream/get + // When the get() above sees the specified delimiter '\n', it stops there + // without pulling it from the stream. If it turns out that the stream + // does NOT contain a header, and the content includes meaningful '\n', + // it's important to pull that into hdr_buf too. + if (inbuf < max_bytes && str.get(hdr_buf[inbuf])) + { + // got the delimiting '\n' + ++inbuf; + // None of the following requires that hdr_buf contain a final '\0' + // byte. We could store one if needed, since even the incremented + // inbuf won't exceed sizeof(hdr_buf)-1, but there's no need. + } + std::string header{ hdr_buf, static_cast(inbuf) }; if (str.fail()) { str.clear(); @@ -123,79 +160,97 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes) } if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */ - { - legacy_no_header = true; - inbuf = (int)str.gcount(); + { // Create a LLSD XML parser, and parse the first chunk read above. + LLSDXMLParser x; + x.parsePart(hdr_buf, inbuf); // Parse the first part that was already read + auto parsed = x.parse(str, sd, max_bytes - inbuf); // Parse the rest of it + // Formally we should probably check (parsed != PARSE_FAILURE && + // parsed > 0), but since PARSE_FAILURE is -1, this suffices. + return (parsed > 0); } - else + + if (fail_if_not_legacy) { - if (fail_if_not_legacy) - goto fail; - /* - * Remove the newline chars - */ - for (i = 0; i < MAX_HDR_LEN; i++) - { - if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' || - hdr_buf[i] == '\n') - { - hdr_buf[i] = 0; - break; - } - } - header = hdr_buf; + LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL; + return false; + } - std::string::size_type start = std::string::npos; - std::string::size_type end = std::string::npos; - start = header.find_first_not_of(" header syntax + auto start = header.find_first_not_of("parsePart(hdr_buf, inbuf); // Parse the first part that was already read - x->parseLines(str, sd); // Parse the rest of it - delete x; - return true; - } - - if (header == LLSD_BINARY_HEADER) + if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER)) { - p = new LLSDBinaryParser; + return (parse_using(str, sd, max_bytes-inbuf) > 0); } - else if (header == LLSD_XML_HEADER) + else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER)) { - p = new LLSDXMLParser; + return (parse_using(str, sd, max_bytes-inbuf) > 0); } - else if (header == LLSD_NOTATION_HEADER) + else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER)) { - p = new LLSDNotationParser; + return (parse_using(str, sd, max_bytes-inbuf) > 0); } - else + else // no header we recognize { - LL_WARNS() << "deserialize request for unknown ELLSD_Serialize" << LL_ENDL; + LLPointer p; + if (inbuf && hdr_buf[0] == '<') + { + // looks like XML + LL_DEBUGS() << "deserialize request with no header, assuming XML" << LL_ENDL; + p = new LLSDXMLParser; + } + else + { + // assume notation + LL_DEBUGS() << "deserialize request with no header, assuming notation" << LL_ENDL; + p = new LLSDNotationParser; + } + // Since we've already read 'inbuf' bytes into 'hdr_buf', prepend that + // data to whatever remains in 'str'. + LLMemoryStreamBuf already(reinterpret_cast(hdr_buf), inbuf); + cat_streambuf prebuff(&already, str.rdbuf()); + std::istream prepend(&prebuff); +#if 1 + return (p->parse(prepend, sd, max_bytes) > 0); +#else + // debugging the reconstituted 'prepend' stream + // allocate a buffer that we hope is big enough for the whole thing + std::vector wholemsg((max_bytes == size_t(SIZE_UNLIMITED))? 1024 : max_bytes); + prepend.read(wholemsg.data(), std::min(max_bytes, wholemsg.size())); + LLMemoryStream replay(reinterpret_cast(wholemsg.data()), prepend.gcount()); + auto success{ p->parse(replay, sd, prepend.gcount()) > 0 }; + { + LL_DEBUGS() << (success? "parsed: $$" : "failed: '") + << std::string(wholemsg.data(), llmin(prepend.gcount(), 100)) << "$$" + << LL_ENDL; + } + return success; +#endif } - - if (p.notNull()) - { - p->parse(str, sd, max_bytes); - return true; - } - -fail: - LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL; - return false; } /** @@ -2193,9 +2248,9 @@ LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size) { U8* result = NULL; - U32 cur_size = 0; + llssize cur_size = 0; z_stream strm; - + constexpr U32 CHUNK = 1024 * 512; static thread_local std::unique_ptr out; @@ -2388,7 +2443,7 @@ U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size return result; } -char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size) +char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size) { const char* deprecated_header = ""; constexpr size_t deprecated_header_size = 17; diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index 2f12c6d1ff..676b7bfd6a 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -873,5 +873,5 @@ LL_COMMON_API std::string zip_llsd(LLSD& data); LL_COMMON_API U8* unzip_llsdNavMesh( bool& valid, size_t& outsize,std::istream& is, S32 size); // returns a pointer to the array or past the array if the deprecated header exists -LL_COMMON_API char* strip_deprecated_header(char* in, U32& cur_size, U32* header_size = nullptr); +LL_COMMON_API char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size = nullptr); #endif // LL_LLSDSERIALIZE_H diff --git a/indra/llcommon/llstreamtools.cpp b/indra/llcommon/llstreamtools.cpp index 1ff15fcf89..bc32b6fd9e 100644 --- a/indra/llcommon/llstreamtools.cpp +++ b/indra/llcommon/llstreamtools.cpp @@ -513,3 +513,29 @@ std::istream& operator>>(std::istream& str, const char *tocheck) } return str; } + +int cat_streambuf::underflow() +{ + if (gptr() == egptr()) + { + // here because our buffer is empty + std::streamsize size = 0; + // Until we've run out of mInputs, try reading the first of them + // into mBuffer. If that fetches some characters, break the loop. + while (! mInputs.empty() + && ! (size = mInputs.front()->sgetn(mBuffer.data(), mBuffer.size()))) + { + // We tried to read mInputs.front() but got zero characters. + // Discard the first streambuf and try the next one. + mInputs.pop_front(); + } + // Either we ran out of mInputs or we succeeded in reading some + // characters, that is, size != 0. Tell base class what we have. + setg(mBuffer.data(), mBuffer.data(), mBuffer.data() + size); + } + // If we fell out of the above loop with mBuffer still empty, return + // eof(), otherwise return the next character. + return (gptr() == egptr()) + ? std::char_traits::eof() + : std::char_traits::to_int_type(*gptr()); +} diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h index 1b04bf91d7..bb7bc20327 100644 --- a/indra/llcommon/llstreamtools.h +++ b/indra/llcommon/llstreamtools.h @@ -27,8 +27,10 @@ #ifndef LL_STREAM_TOOLS_H #define LL_STREAM_TOOLS_H +#include #include #include +#include // unless specifed otherwise these all return input_stream.good() @@ -113,6 +115,27 @@ LL_COMMON_API std::streamsize fullread( LL_COMMON_API std::istream& operator>>(std::istream& str, const char *tocheck); +/** + * cat_streambuf is a std::streambuf subclass that accepts a variadic number + * of std::streambuf* (e.g. some_istream.rdbuf()) and virtually concatenates + * their contents. + */ +// derived from https://stackoverflow.com/a/49441066/5533635 +class cat_streambuf: public std::streambuf +{ +private: + std::deque mInputs; + std::vector mBuffer; + +public: + // only valid for std::streambuf* arguments + template + cat_streambuf(Inputs... inputs): + mInputs{inputs...}, + mBuffer(1024) + {} + + int underflow() override; +}; + #endif - - diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index a8dcc5226a..bb3d667a42 100644 --- a/indra/llcommon/lltracerecording.cpp +++ b/indra/llcommon/lltracerecording.cpp @@ -577,10 +577,12 @@ S32 Recording::getSampleCount( const StatType& stat ) // PeriodicRecording /////////////////////////////////////////////////////////////////////// -PeriodicRecording::PeriodicRecording( S32 num_periods, EPlayState state) +PeriodicRecording::PeriodicRecording( size_t num_periods, EPlayState state) : mAutoResize(num_periods == 0), mCurPeriod(0), mNumRecordedPeriods(0), + // This guarantee that mRecordingPeriods cannot be empty is essential for + // code in several methods. mRecordingPeriods(num_periods ? num_periods : 1) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; @@ -596,18 +598,19 @@ PeriodicRecording::~PeriodicRecording() void PeriodicRecording::nextPeriod() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; if (mAutoResize) { mRecordingPeriods.push_back(Recording()); } Recording& old_recording = getCurRecording(); - mCurPeriod = (mCurPeriod + 1) % mRecordingPeriods.size(); + inci(mCurPeriod); old_recording.splitTo(getCurRecording()); - mNumRecordedPeriods = mRecordingPeriods.empty()? 0 : - llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + 1); + // Since mRecordingPeriods always has at least one entry, we can always + // safely subtract 1 from its size(). + mNumRecordedPeriods = llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + 1); } void PeriodicRecording::appendRecording(Recording& recording) @@ -620,31 +623,29 @@ void PeriodicRecording::appendRecording(Recording& recording) void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; if (other.mRecordingPeriods.empty()) return; getCurRecording().update(); other.getCurRecording().update(); - - const auto other_recording_slots = other.mRecordingPeriods.size(); + const auto other_num_recordings = other.getNumRecordedPeriods(); const auto other_current_recording_index = other.mCurPeriod; - const auto other_oldest_recording_index = (other_current_recording_index + other_recording_slots - other_num_recordings) % other_recording_slots; + const auto other_oldest_recording_index = other.previ(other_current_recording_index, other_num_recordings); // append first recording into our current slot getCurRecording().appendRecording(other.mRecordingPeriods[other_oldest_recording_index]); // from now on, add new recordings for everything after the first - auto other_index = (other_oldest_recording_index + 1) % other_recording_slots; + auto other_index = other.nexti(other_oldest_recording_index); if (mAutoResize) { // push back recordings for everything in the middle - auto other_index = (other_oldest_recording_index + 1) % other_recording_slots; while (other_index != other_current_recording_index) { mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]); - other_index = (other_index + 1) % other_recording_slots; + other.inci(other_index); } // add final recording, if it wasn't already added as the first @@ -653,36 +654,25 @@ void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other ) mRecordingPeriods.push_back(other.mRecordingPeriods[other_current_recording_index]); } - mCurPeriod = mRecordingPeriods.empty()? 0 : mRecordingPeriods.size() - 1; + // mRecordingPeriods is never empty() + mCurPeriod = mRecordingPeriods.size() - 1; mNumRecordedPeriods = mCurPeriod; } else { - S32 num_to_copy = llmin((S32)mRecordingPeriods.size(), (S32)other_num_recordings); - - std::vector::iterator src_it = other.mRecordingPeriods.begin() + other_index ; - std::vector::iterator dest_it = mRecordingPeriods.begin() + mCurPeriod; - + auto num_to_copy = llmin(mRecordingPeriods.size(), other_num_recordings); // already consumed the first recording from other, so start counting at 1 - for(S32 i = 1; i < num_to_copy; i++) + for (size_t n = 1, srci = other_index, dsti = mCurPeriod; + n < num_to_copy; + ++n, other.inci(srci), inci(dsti)) { - *dest_it = *src_it; - - if (++src_it == other.mRecordingPeriods.end()) - { - src_it = other.mRecordingPeriods.begin(); - } - - if (++dest_it == mRecordingPeriods.end()) - { - dest_it = mRecordingPeriods.begin(); - } + mRecordingPeriods[dsti] = other.mRecordingPeriods[srci]; } - + // want argument to % to be positive, otherwise result could be negative and thus out of bounds llassert(num_to_copy >= 1); // advance to last recording period copied, and make that our current period - mCurPeriod = (mCurPeriod + num_to_copy - 1) % mRecordingPeriods.size(); + inci(mCurPeriod, num_to_copy - 1); mNumRecordedPeriods = llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + num_to_copy - 1); } @@ -694,13 +684,11 @@ void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other ) F64Seconds PeriodicRecording::getDuration() const { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; F64Seconds duration; - auto num_periods = mRecordingPeriods.size(); - for (size_t i = 1; i <= num_periods; i++) + for (size_t n = 0; n < mRecordingPeriods.size(); ++n) { - auto index = (mCurPeriod + num_periods - i) % num_periods; - duration += mRecordingPeriods[index].getDuration(); + duration += mRecordingPeriods[nexti(mCurPeriod, n)].getDuration(); } return duration; } @@ -737,16 +725,14 @@ const Recording& PeriodicRecording::getCurRecording() const Recording& PeriodicRecording::getPrevRecording( size_t offset ) { - auto num_periods = mRecordingPeriods.size(); - offset = llclamp(offset, 0, num_periods - 1); - return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods]; + // reuse const implementation, but return non-const reference + return const_cast( + const_cast(this)->getPrevRecording(offset)); } const Recording& PeriodicRecording::getPrevRecording( size_t offset ) const { - auto num_periods = mRecordingPeriods.size(); - offset = llclamp(offset, 0, num_periods - 1); - return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods]; + return mRecordingPeriods[previ(mCurPeriod, offset)]; } void PeriodicRecording::handleStart() @@ -789,14 +775,14 @@ void PeriodicRecording::handleSplitTo(PeriodicRecording& other) getCurRecording().splitTo(other.getCurRecording()); } -F64 PeriodicRecording::getPeriodMin( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodMin( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); bool has_value = false; F64 min_val = std::numeric_limits::max(); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -811,14 +797,14 @@ F64 PeriodicRecording::getPeriodMin( const StatType& stat, siz : NaN; } -F64 PeriodicRecording::getPeriodMax( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodMax( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); bool has_value = false; F64 max_val = std::numeric_limits::min(); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -834,7 +820,7 @@ F64 PeriodicRecording::getPeriodMax( const StatType& stat, siz } // calculates means using aggregates per period -F64 PeriodicRecording::getPeriodMean( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodMean( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); @@ -842,7 +828,7 @@ F64 PeriodicRecording::getPeriodMean( const StatType& stat, si F64 mean = 0; S32 valid_period_count = 0; - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -857,7 +843,7 @@ F64 PeriodicRecording::getPeriodMean( const StatType& stat, si : NaN; } -F64 PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); @@ -866,7 +852,7 @@ F64 PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodMin( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); bool has_value = false; F64 min_val = std::numeric_limits::max(); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -904,14 +890,14 @@ F64 PeriodicRecording::getPeriodMin( const StatType& stat, si : NaN; } -F64 PeriodicRecording::getPeriodMax(const StatType& stat, size_t num_periods /*= S32_MAX*/) +F64 PeriodicRecording::getPeriodMax(const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); bool has_value = false; F64 max_val = std::numeric_limits::min(); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -927,7 +913,7 @@ F64 PeriodicRecording::getPeriodMax(const StatType& stat, siz } -F64 PeriodicRecording::getPeriodMean( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodMean( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); @@ -935,7 +921,7 @@ F64 PeriodicRecording::getPeriodMean( const StatType& stat, s S32 valid_period_count = 0; F64 mean = 0; - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -950,13 +936,13 @@ F64 PeriodicRecording::getPeriodMean( const StatType& stat, s : NaN; } -F64 PeriodicRecording::getPeriodMedian( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodMedian( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); std::vector buf; - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.getDuration() > (F32Seconds)0.f) @@ -976,7 +962,7 @@ F64 PeriodicRecording::getPeriodMedian( const StatType& stat, return F64((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); } -F64 PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64 PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); @@ -985,7 +971,7 @@ F64 PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64Kilobytes PeriodicRecording::getPeriodMin( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); F64Kilobytes min_val(std::numeric_limits::max()); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); min_val = llmin(min_val, recording.getMin(stat)); @@ -1022,13 +1008,13 @@ F64Kilobytes PeriodicRecording::getPeriodMin(const MemStatHandle& stat, size_t n return getPeriodMin(static_cast&>(stat), num_periods); } -F64Kilobytes PeriodicRecording::getPeriodMax(const StatType& stat, size_t num_periods /*= S32_MAX*/) +F64Kilobytes PeriodicRecording::getPeriodMax(const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); F64Kilobytes max_val(0.0); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); max_val = llmax(max_val, recording.getMax(stat)); @@ -1042,14 +1028,14 @@ F64Kilobytes PeriodicRecording::getPeriodMax(const MemStatHandle& stat, size_t n return getPeriodMax(static_cast&>(stat), num_periods); } -F64Kilobytes PeriodicRecording::getPeriodMean( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64Kilobytes PeriodicRecording::getPeriodMean( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); F64Kilobytes mean(0); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); mean += recording.getMean(stat); @@ -1063,7 +1049,7 @@ F64Kilobytes PeriodicRecording::getPeriodMean(const MemStatHandle& stat, size_t return getPeriodMean(static_cast&>(stat), num_periods); } -F64Kilobytes PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= S32_MAX*/ ) +F64Kilobytes PeriodicRecording::getPeriodStandardDeviation( const StatType& stat, size_t num_periods /*= std::numeric_limits::max()*/ ) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); @@ -1072,7 +1058,7 @@ F64Kilobytes PeriodicRecording::getPeriodStandardDeviation( const StatType class LLStopWatchControlsMixinCommon { @@ -330,7 +331,7 @@ namespace LLTrace : public LLStopWatchControlsMixin { public: - PeriodicRecording(S32 num_periods, EPlayState state = STOPPED); + PeriodicRecording(size_t num_periods, EPlayState state = STOPPED); ~PeriodicRecording(); void nextPeriod(); @@ -353,7 +354,7 @@ namespace LLTrace Recording snapshotCurRecording() const; template - auto getSampleCount(const StatType& stat, size_t num_periods = S32_MAX) + auto getSampleCount(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); @@ -373,14 +374,14 @@ namespace LLTrace // catch all for stats that have a defined sum template - typename T::value_t getPeriodMin(const StatType& stat, size_t num_periods = S32_MAX) + typename T::value_t getPeriodMin(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); bool has_value = false; typename T::value_t min_val(std::numeric_limits::max()); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -396,39 +397,39 @@ namespace LLTrace } template - T getPeriodMin(const CountStatHandle& stat, size_t num_periods = S32_MAX) + T getPeriodMin(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return T(getPeriodMin(static_cast&>(stat), num_periods)); } - F64 getPeriodMin(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMin(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - T getPeriodMin(const SampleStatHandle& stat, size_t num_periods = S32_MAX) + T getPeriodMin(const SampleStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return T(getPeriodMin(static_cast&>(stat), num_periods)); } - F64 getPeriodMin(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMin(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - T getPeriodMin(const EventStatHandle& stat, size_t num_periods = S32_MAX) + T getPeriodMin(const EventStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return T(getPeriodMin(static_cast&>(stat), num_periods)); } - F64Kilobytes getPeriodMin(const StatType& stat, size_t num_periods = S32_MAX); - F64Kilobytes getPeriodMin(const MemStatHandle& stat, size_t num_periods = S32_MAX); + F64Kilobytes getPeriodMin(const StatType& stat, size_t num_periods = std::numeric_limits::max()); + F64Kilobytes getPeriodMin(const MemStatHandle& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodMinPerSec(const StatType& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMinPerSec(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); typename RelatedTypes::fractional_t min_val(std::numeric_limits::max()); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); min_val = llmin(min_val, recording.getPerSec(stat)); @@ -437,7 +438,7 @@ namespace LLTrace } template - typename RelatedTypes::fractional_t getPeriodMinPerSec(const CountStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMinPerSec(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodMinPerSec(static_cast&>(stat), num_periods)); @@ -449,14 +450,14 @@ namespace LLTrace // catch all for stats that have a defined sum template - typename T::value_t getPeriodMax(const StatType& stat, size_t num_periods = S32_MAX) + typename T::value_t getPeriodMax(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); bool has_value = false; typename T::value_t max_val(std::numeric_limits::min()); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.hasValue(stat)) @@ -472,39 +473,39 @@ namespace LLTrace } template - T getPeriodMax(const CountStatHandle& stat, size_t num_periods = S32_MAX) + T getPeriodMax(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return T(getPeriodMax(static_cast&>(stat), num_periods)); } - F64 getPeriodMax(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMax(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - T getPeriodMax(const SampleStatHandle& stat, size_t num_periods = S32_MAX) + T getPeriodMax(const SampleStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return T(getPeriodMax(static_cast&>(stat), num_periods)); } - F64 getPeriodMax(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMax(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - T getPeriodMax(const EventStatHandle& stat, size_t num_periods = S32_MAX) + T getPeriodMax(const EventStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return T(getPeriodMax(static_cast&>(stat), num_periods)); } - F64Kilobytes getPeriodMax(const StatType& stat, size_t num_periods = S32_MAX); - F64Kilobytes getPeriodMax(const MemStatHandle& stat, size_t num_periods = S32_MAX); + F64Kilobytes getPeriodMax(const StatType& stat, size_t num_periods = std::numeric_limits::max()); + F64Kilobytes getPeriodMax(const MemStatHandle& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodMaxPerSec(const StatType& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMaxPerSec(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); F64 max_val = std::numeric_limits::min(); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); max_val = llmax(max_val, recording.getPerSec(stat)); @@ -513,7 +514,7 @@ namespace LLTrace } template - typename RelatedTypes::fractional_t getPeriodMaxPerSec(const CountStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMaxPerSec(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodMaxPerSec(static_cast&>(stat), num_periods)); @@ -525,14 +526,14 @@ namespace LLTrace // catch all for stats that have a defined sum template - typename RelatedTypes::fractional_t getPeriodMean(const StatType& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMean(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); typename RelatedTypes::fractional_t mean(0); - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.getDuration() > (F32Seconds)0.f) @@ -546,39 +547,39 @@ namespace LLTrace } template - typename RelatedTypes::fractional_t getPeriodMean(const CountStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMean(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodMean(static_cast&>(stat), num_periods)); } - F64 getPeriodMean(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMean(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodMean(const SampleStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMean(const SampleStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodMean(static_cast&>(stat), num_periods)); } - F64 getPeriodMean(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMean(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodMean(const EventStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMean(const EventStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodMean(static_cast&>(stat), num_periods)); } - F64Kilobytes getPeriodMean(const StatType& stat, size_t num_periods = S32_MAX); - F64Kilobytes getPeriodMean(const MemStatHandle& stat, size_t num_periods = S32_MAX); + F64Kilobytes getPeriodMean(const StatType& stat, size_t num_periods = std::numeric_limits::max()); + F64Kilobytes getPeriodMean(const MemStatHandle& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodMeanPerSec(const StatType& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMeanPerSec(const StatType& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; num_periods = llmin(num_periods, getNumRecordedPeriods()); typename RelatedTypes::fractional_t mean = 0; - for (S32 i = 1; i <= num_periods; i++) + for (size_t i = 1; i <= num_periods; i++) { Recording& recording = getPrevRecording(i); if (recording.getDuration() > (F32Seconds)0.f) @@ -593,64 +594,64 @@ namespace LLTrace } template - typename RelatedTypes::fractional_t getPeriodMeanPerSec(const CountStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodMeanPerSec(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodMeanPerSec(static_cast&>(stat), num_periods)); } - F64 getPeriodMedian( const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodMedian( const StatType& stat, size_t num_periods = std::numeric_limits::max()); - template - typename RelatedTypes::fractional_t getPeriodMedianPerSec(const StatType& stat, size_t num_periods = S32_MAX) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + template + typename RelatedTypes::fractional_t getPeriodMedianPerSec(const StatType& stat, size_t num_periods = std::numeric_limits::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - std::vector ::fractional_t> buf; - for (S32 i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.getDuration() > (F32Seconds)0.f) - { - buf.push_back(recording.getPerSec(stat)); - } - } - std::sort(buf.begin(), buf.end()); + std::vector ::fractional_t> buf; + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.getDuration() > (F32Seconds)0.f) + { + buf.push_back(recording.getPerSec(stat)); + } + } + std::sort(buf.begin(), buf.end()); - return typename RelatedTypes::fractional_t((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); - } + return typename RelatedTypes::fractional_t((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); + } - template - typename RelatedTypes::fractional_t getPeriodMedianPerSec(const CountStatHandle& stat, size_t num_periods = S32_MAX) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes::fractional_t(getPeriodMedianPerSec(static_cast&>(stat), num_periods)); - } + template + typename RelatedTypes::fractional_t getPeriodMedianPerSec(const CountStatHandle& stat, size_t num_periods = std::numeric_limits::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes::fractional_t(getPeriodMedianPerSec(static_cast&>(stat), num_periods)); + } // // PERIODIC STANDARD DEVIATION // - F64 getPeriodStandardDeviation(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodStandardDeviation(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodStandardDeviation(const SampleStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodStandardDeviation(const SampleStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodStandardDeviation(static_cast&>(stat), num_periods)); } - F64 getPeriodStandardDeviation(const StatType& stat, size_t num_periods = S32_MAX); + F64 getPeriodStandardDeviation(const StatType& stat, size_t num_periods = std::numeric_limits::max()); template - typename RelatedTypes::fractional_t getPeriodStandardDeviation(const EventStatHandle& stat, size_t num_periods = S32_MAX) + typename RelatedTypes::fractional_t getPeriodStandardDeviation(const EventStatHandle& stat, size_t num_periods = std::numeric_limits::max()) { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; return typename RelatedTypes::fractional_t(getPeriodStandardDeviation(static_cast&>(stat), num_periods)); } - F64Kilobytes getPeriodStandardDeviation(const StatType& stat, size_t num_periods = S32_MAX); - F64Kilobytes getPeriodStandardDeviation(const MemStatHandle& stat, size_t num_periods = S32_MAX); + F64Kilobytes getPeriodStandardDeviation(const StatType& stat, size_t num_periods = std::numeric_limits::max()); + F64Kilobytes getPeriodStandardDeviation(const MemStatHandle& stat, size_t num_periods = std::numeric_limits::max()); private: // implementation for LLStopWatchControlsMixin @@ -659,6 +660,35 @@ namespace LLTrace /*virtual*/ void handleReset(); /*virtual*/ void handleSplitTo(PeriodicRecording& other); + // helper methods for wraparound ring-buffer arithmetic + inline + size_t wrapi(size_t i) const + { + return i % mRecordingPeriods.size(); + } + + inline + size_t nexti(size_t i, size_t offset=1) const + { + return wrapi(i + offset); + } + + inline + size_t previ(size_t i, size_t offset=1) const + { + auto num_periods = mRecordingPeriods.size(); + // constrain offset + offset = llclamp(offset, 0, num_periods - 1); + // add size() so expression can't go (unsigned) "negative" + return wrapi(i + num_periods - offset); + } + + inline + void inci(size_t& i, size_t offset=1) const + { + i = nexti(i, offset); + } + private: std::vector mRecordingPeriods; const bool mAutoResize; diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index 7105c4502d..200add404f 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -885,7 +885,7 @@ U32 LLUUID::getRandomSeed() seed[7] = (unsigned char)(pid); getSystemTime((uuid_time_t*)(&seed[8])); - U64 seed64 = HBXXH64((const void*)seed, 16).digest(); + U64 seed64 = HBXXH64::digest((const void*)seed, 16); return U32(seed64) ^ U32(seed64 >> 32); } diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index c139c4eb4e..80597fa186 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -116,6 +116,14 @@ public: U16 getCRC16() const; U32 getCRC32() const; + // Returns a 64 bits digest of the UUID, by XORing its two 64 bits long + // words. HB + inline U64 getDigest64() const + { + U64* tmp = (U64*)mData; + return tmp[0] ^ tmp[1]; + } + static BOOL validate(const std::string& in_string); // Validate that the UUID string is legal. static const LLUUID null; @@ -165,36 +173,22 @@ public: LLAssetID makeAssetID(const LLUUID& session) const; }; -// Generate a hash of an LLUUID object using the boost hash templates. -template <> -struct boost::hash -{ - typedef LLUUID argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& s) const - { - result_type seed(0); - - for (S32 i = 0; i < UUID_BYTES; ++i) - { - boost::hash_combine(seed, s.mData[i]); - } - - return seed; - } -}; - -// Adapt boost hash to std hash +// std::hash implementation for LLUUID namespace std { - template<> struct hash - { - std::size_t operator()(LLUUID const& s) const noexcept - { - return boost::hash()(s); - } - }; + template<> struct hash + { + inline size_t operator()(const LLUUID& id) const noexcept + { + return (size_t)id.getDigest64(); + } + }; } -#endif +// For use with boost containers. +inline size_t hash_value(const LLUUID& id) noexcept +{ + return (size_t)id.getDigest64(); +} +#endif // LL_LLUUID_H diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h index da8512169c..0b43d7ad4b 100644 --- a/indra/llcommon/stdtypes.h +++ b/indra/llcommon/stdtypes.h @@ -41,7 +41,7 @@ typedef unsigned int U32; // to express an index that might go negative // (ssize_t is provided by SOME compilers, don't collide) -typedef typename std::make_signed::type llssize; +typedef typename std::make_signed::type llssize; #if LL_WINDOWS // https://docs.microsoft.com/en-us/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index 7ee36a9ea6..3ae48a2532 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -109,7 +109,12 @@ namespace tut "import os\n" "import sys\n" "\n" - "from llbase import llsd\n" + "try:\n" + // new freestanding llsd package + " import llsd\n" + "except ImportError:\n" + // older llbase.llsd module + " from llbase import llsd\n" "\n" "class ProtocolError(Exception):\n" " def __init__(self, msg, data):\n" @@ -120,26 +125,26 @@ namespace tut " pass\n" "\n" "def get():\n" - " hdr = ''\n" - " while ':' not in hdr and len(hdr) < 20:\n" - " hdr += sys.stdin.read(1)\n" + " hdr = []\n" + " while b':' not in hdr and len(hdr) < 20:\n" + " hdr.append(sys.stdin.buffer.read(1))\n" " if not hdr:\n" " sys.exit(0)\n" - " if not hdr.endswith(':'):\n" + " if not hdr[-1] == b':':\n" " raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n" " try:\n" - " length = int(hdr[:-1])\n" + " length = int(b''.join(hdr[:-1]))\n" " except ValueError:\n" " raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n" " parts = []\n" " received = 0\n" " while received < length:\n" - " parts.append(sys.stdin.read(length - received))\n" + " parts.append(sys.stdin.buffer.read(length - received))\n" " received += len(parts[-1])\n" - " data = ''.join(parts)\n" + " data = b''.join(parts)\n" " assert len(data) == length\n" " try:\n" - " return llsd.parse(data.encode())\n" + " return llsd.parse(data)\n" // Seems the old indra.base.llsd module didn't properly // convert IndexError (from running off end of string) to // LLSDParseError. @@ -179,11 +184,11 @@ namespace tut " return _reply\n" "\n" "def put(req):\n" - " sys.stdout.write(':'.join((str(len(req)), req)))\n" + " sys.stdout.buffer.write(b'%d:%b' % (len(req), req))\n" " sys.stdout.flush()\n" "\n" "def send(pump, data):\n" - " put(llsd.format_notation(dict(pump=pump, data=data)).decode())\n" + " put(llsd.format_notation(dict(pump=pump, data=data)))\n" "\n" "def request(pump, data):\n" " # we expect 'data' is a dict\n" diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index 5dbcf4c9b8..acb2953b5b 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -46,20 +46,24 @@ typedef U32 uint32_t; #include "boost/range.hpp" #include "boost/foreach.hpp" -#include "boost/function.hpp" #include "boost/bind.hpp" #include "boost/phoenix/bind/bind_function.hpp" #include "boost/phoenix/core/argument.hpp" using namespace boost::phoenix; -#include "../llsd.h" -#include "../llsdserialize.h" +#include "llsd.h" +#include "llsdserialize.h" #include "llsdutil.h" -#include "../llformat.h" +#include "llformat.h" +#include "llmemorystream.h" #include "../test/lltut.h" #include "../test/namedtempfile.h" #include "stringize.h" +#include + +typedef std::function FormatterFunction; +typedef std::function ParserFunction; std::vector string_to_vector(const std::string& str) { @@ -112,7 +116,7 @@ namespace tut mSD = LLUUID::null; expected = "\n"; xml_test("null uuid", expected); - + mSD = LLUUID("c96f9b1e-f589-4100-9774-d98643ce0bed"); expected = "c96f9b1e-f589-4100-9774-d98643ce0bed\n"; xml_test("uuid", expected); @@ -136,7 +140,7 @@ namespace tut expected = "aGVsbG8=\n"; xml_test("binary", expected); } - + template<> template<> void sd_xml_object::test<2>() { @@ -225,7 +229,7 @@ namespace tut expected = "bazfoobar\n"; xml_test("2 element map", expected); } - + template<> template<> void sd_xml_object::test<6>() { @@ -241,7 +245,7 @@ namespace tut expected = "Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==\n"; xml_test("binary", expected); } - + class TestLLSDSerializeData { public: @@ -250,9 +254,34 @@ namespace tut void doRoundTripTests(const std::string&); void checkRoundTrip(const std::string&, const LLSD& v); - - LLPointer mFormatter; - LLPointer mParser; + + void setFormatterParser(LLPointer formatter, LLPointer parser) + { + mFormatter = [formatter](const LLSD& data, std::ostream& str) + { + formatter->format(data, str); + }; + // this lambda must be mutable since otherwise the bound 'parser' + // is assumed to point to a const LLSDParser + mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) mutable + { + // reset() call is needed since test code re-uses parser object + parser->reset(); + return (parser->parse(istr, data, max_bytes) > 0); + }; + } + + void setParser(bool (*parser)(LLSD&, std::istream&, llssize)) + { + // why does LLSDSerialize::deserialize() reverse the parse() params?? + mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) + { + return parser(data, istr, max_bytes); + }; + } + + FormatterFunction mFormatter; + ParserFunction mParser; }; TestLLSDSerializeData::TestLLSDSerializeData() @@ -265,12 +294,11 @@ namespace tut void TestLLSDSerializeData::checkRoundTrip(const std::string& msg, const LLSD& v) { - std::stringstream stream; - mFormatter->format(v, stream); + std::stringstream stream; + mFormatter(v, stream); //LL_INFOS() << "checkRoundTrip: length " << stream.str().length() << LL_ENDL; LLSD w; - mParser->reset(); // reset() call is needed since test code re-uses mParser - mParser->parse(stream, w, stream.str().size()); + mParser(stream, w, stream.str().size()); try { @@ -299,52 +327,52 @@ namespace tut fillmap(root[key], width, depth - 1); } } - + void TestLLSDSerializeData::doRoundTripTests(const std::string& msg) { LLSD v; checkRoundTrip(msg + " undefined", v); - + v = true; checkRoundTrip(msg + " true bool", v); - + v = false; checkRoundTrip(msg + " false bool", v); - + v = 1; checkRoundTrip(msg + " positive int", v); - + v = 0; checkRoundTrip(msg + " zero int", v); - + v = -1; checkRoundTrip(msg + " negative int", v); - + v = 1234.5f; checkRoundTrip(msg + " positive float", v); - + v = 0.0f; checkRoundTrip(msg + " zero float", v); - + v = -1234.5f; checkRoundTrip(msg + " negative float", v); - + // FIXME: need a NaN test - + v = LLUUID::null; checkRoundTrip(msg + " null uuid", v); - + LLUUID newUUID; newUUID.generate(); v = newUUID; checkRoundTrip(msg + " new uuid", v); - + v = ""; checkRoundTrip(msg + " empty string", v); - + v = "some string"; checkRoundTrip(msg + " non-empty string", v); - + v = "Second Life is a 3-D virtual world entirely built and owned by its residents. " "Since opening to the public in 2003, it has grown explosively and today is " @@ -372,7 +400,7 @@ namespace tut for (U32 block = 0x000000; block <= 0x10ffff; block += block_size) { std::ostringstream out; - + for (U32 c = block; c < block + block_size; ++c) { if (c <= 0x000001f @@ -386,7 +414,7 @@ namespace tut if (0x00fdd0 <= c && c <= 0x00fdef) { continue; } if ((c & 0x00fffe) == 0x00fffe) { continue; } // see Unicode standard, section 15.8 - + if (c <= 0x00007f) { out << (char)(c & 0x7f); @@ -410,55 +438,55 @@ namespace tut out << (char)(0x80 | ((c >> 0) & 0x3f)); } } - + v = out.str(); std::ostringstream blockmsg; blockmsg << msg << " unicode string block 0x" << std::hex << block; checkRoundTrip(blockmsg.str(), v); } - + LLDate epoch; v = epoch; checkRoundTrip(msg + " epoch date", v); - + LLDate aDay("2002-12-07T05:07:15.00Z"); v = aDay; checkRoundTrip(msg + " date", v); - + LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); v = path; checkRoundTrip(msg + " url", v); - + const char source[] = "it must be a blue moon again"; std::vector data; // note, includes terminating '\0' copy(&source[0], &source[sizeof(source)], back_inserter(data)); - + v = data; checkRoundTrip(msg + " binary", v); - + v = LLSD::emptyMap(); checkRoundTrip(msg + " empty map", v); - + v = LLSD::emptyMap(); v["name"] = "luke"; //v.insert("name", "luke"); v["age"] = 3; //v.insert("age", 3); checkRoundTrip(msg + " map", v); - + v.clear(); v["a"]["1"] = true; v["b"]["0"] = false; checkRoundTrip(msg + " nested maps", v); - + v = LLSD::emptyArray(); checkRoundTrip(msg + " empty array", v); - + v = LLSD::emptyArray(); v.append("ali"); v.append(28); checkRoundTrip(msg + " array", v); - + v.clear(); v[0][0] = true; v[1][0] = false; @@ -468,7 +496,7 @@ namespace tut fillmap(v, 10, 3); // 10^6 maps checkRoundTrip(msg + " many nested maps", v); } - + typedef tut::test_group TestLLSDSerializeGroup; typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject; TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization"); @@ -476,35 +504,106 @@ namespace tut template<> template<> void TestLLSDSerializeObject::test<1>() { - mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY); - mParser = new LLSDNotationParser(); + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY), + new LLSDNotationParser()); doRoundTripTests("pretty binary notation serialization"); } template<> template<> void TestLLSDSerializeObject::test<2>() { - mFormatter = new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE); - mParser = new LLSDNotationParser(); + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDNotationParser()); doRoundTripTests("raw binary notation serialization"); } template<> template<> void TestLLSDSerializeObject::test<3>() { - mFormatter = new LLSDXMLFormatter(); - mParser = new LLSDXMLParser(); + setFormatterParser(new LLSDXMLFormatter(), new LLSDXMLParser()); doRoundTripTests("xml serialization"); } template<> template<> void TestLLSDSerializeObject::test<4>() { - mFormatter = new LLSDBinaryFormatter(); - mParser = new LLSDBinaryParser(); + setFormatterParser(new LLSDBinaryFormatter(), new LLSDBinaryParser()); doRoundTripTests("binary serialization"); } + template<> template<> + void TestLLSDSerializeObject::test<5>() + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_BINARY); + }; + setParser(LLSDSerialize::deserialize); + doRoundTripTests("serialize(LLSD_BINARY)"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<6>() + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_XML); + }; + setParser(LLSDSerialize::deserialize); + doRoundTripTests("serialize(LLSD_XML)"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<7>() + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_NOTATION); + }; + setParser(LLSDSerialize::deserialize); + // In this test, serialize(LLSD_NOTATION) emits a header recognized by + // deserialize(). + doRoundTripTests("serialize(LLSD_NOTATION)"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<8>() + { + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDNotationParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDNotationFormatter does not + // emit an llsd/notation header. + doRoundTripTests("LLSDNotationFormatter -> deserialize"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<9>() + { + setFormatterParser(new LLSDXMLFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDXMLParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDXMLFormatter does not + // emit an LLSD/XML header. + doRoundTripTests("LLSDXMLFormatter -> deserialize"); + }; + +/*==========================================================================*| + // We do not expect this test to succeed. Without a header, neither + // notation LLSD nor binary LLSD reliably start with a distinct character, + // the way XML LLSD starts with '<'. By convention, we default to notation + // rather than binary. + template<> template<> + void TestLLSDSerializeObject::test<10>() + { + setFormatterParser(new LLSDBinaryFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDBinaryParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDBinaryFormatter does not + // emit an LLSD/Binary header. + doRoundTripTests("LLSDBinaryFormatter -> deserialize"); + }; +|*==========================================================================*/ /** * @class TestLLSDParsing @@ -555,7 +654,7 @@ namespace tut public: TestLLSDXMLParsing() {} }; - + typedef tut::test_group TestLLSDXMLParsingGroup; typedef TestLLSDXMLParsingGroup::object TestLLSDXMLParsingObject; TestLLSDXMLParsingGroup gTestLLSDXMLParsingGroup("llsd XML parsing"); @@ -586,8 +685,8 @@ namespace tut LLSD(), LLSDParser::PARSE_FAILURE); } - - + + template<> template<> void TestLLSDXMLParsingObject::test<2>() { @@ -596,7 +695,7 @@ namespace tut v["amy"] = 23; v["bob"] = LLSD(); v["cam"] = 1.23; - + ensureParse( "unknown data type", "" @@ -607,16 +706,16 @@ namespace tut v, v.size() + 1); } - + template<> template<> void TestLLSDXMLParsingObject::test<3>() { // test handling of nested bad data - + LLSD v; v["amy"] = 23; v["cam"] = 1.23; - + ensureParse( "map with html", "" @@ -626,7 +725,7 @@ namespace tut "", v, v.size() + 1); - + v.clear(); v["amy"] = 23; v["cam"] = 1.23; @@ -639,7 +738,7 @@ namespace tut "", v, v.size() + 1); - + v.clear(); v["amy"] = 23; v["bob"] = LLSD::emptyMap(); @@ -661,7 +760,7 @@ namespace tut v[0] = 23; v[1] = LLSD(); v[2] = 1.23; - + ensureParse( "array value of html", "" @@ -671,7 +770,7 @@ namespace tut "", v, v.size() + 1); - + v.clear(); v[0] = 23; v[1] = LLSD::emptyMap(); @@ -1225,7 +1324,7 @@ namespace tut vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; LLSD value = vec; - + vec.resize(11); vec[0] = 'b'; // for binary vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; @@ -1694,85 +1793,83 @@ namespace tut ensureBinaryAndXML("map", test); } - struct TestPythonCompatible + // helper for TestPythonCompatible + static std::string import_llsd("import os.path\n" + "import sys\n" + "try:\n" + // new freestanding llsd package + " import llsd\n" + "except ImportError:\n" + // older llbase.llsd module + " from llbase import llsd\n"); + + // helper for TestPythonCompatible + template + void python(const std::string& desc, const CONTENT& script, int expect=0) { - TestPythonCompatible(): - // Note the peculiar insertion of __FILE__ into this string. Since - // this script is being written into a platform-dependent temp - // directory, we can't locate indra/lib/python relative to - // Python's __file__. Use __FILE__ instead, navigating relative - // to this C++ source file. Use Python raw-string syntax so - // Windows pathname backslashes won't mislead Python's string - // scanner. - import_llsd("import os.path\n" - "import sys\n" - "from llbase import llsd\n") - {} - ~TestPythonCompatible() {} + auto PYTHON(LLStringUtil::getenv("PYTHON")); + ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty()); - std::string import_llsd; - - template - void python(const std::string& desc, const CONTENT& script, int expect=0) - { - auto PYTHON(LLStringUtil::getenv("PYTHON")); - ensure("Set $PYTHON to the Python interpreter", !PYTHON.empty()); - - NamedTempFile scriptfile("py", script); + NamedTempFile scriptfile("py", script); #if LL_WINDOWS - std::string q("\""); - std::string qPYTHON(q + PYTHON + q); - std::string qscript(q + scriptfile.getName() + q); - int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), NULL); - if (rc == -1) - { - char buffer[256]; - strerror_s(buffer, errno); // C++ can infer the buffer size! :-O - ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false); - } - else - { - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect); - } + std::string q("\""); + std::string qPYTHON(q + PYTHON + q); + std::string qscript(q + scriptfile.getName() + q); + int rc = _spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(), NULL); + if (rc == -1) + { + char buffer[256]; + strerror_s(buffer, errno); // C++ can infer the buffer size! :-O + ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false); + } + else + { + ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect); + } #else // LL_DARWIN, LL_LINUX - LLProcess::Params params; - params.executable = PYTHON; - params.args.add(scriptfile.getName()); - LLProcessPtr py(LLProcess::create(params)); - ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py)); - // Implementing timeout would mean messing with alarm() and - // catching SIGALRM... later maybe... - int status(0); - if (waitpid(py->getProcessID(), &status, 0) == -1) + LLProcess::Params params; + params.executable = PYTHON; + params.args.add(scriptfile.getName()); + LLProcessPtr py(LLProcess::create(params)); + ensure(STRINGIZE("Couldn't launch " << desc << " script"), bool(py)); + // Implementing timeout would mean messing with alarm() and + // catching SIGALRM... later maybe... + int status(0); + if (waitpid(py->getProcessID(), &status, 0) == -1) + { + int waitpid_errno(errno); + ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: " + "waitpid() errno " << waitpid_errno), + waitpid_errno, ECHILD); + } + else + { + if (WIFEXITED(status)) { - int waitpid_errno(errno); - ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: " - "waitpid() errno " << waitpid_errno), - waitpid_errno, ECHILD); + int rc(WEXITSTATUS(status)); + ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), + rc, expect); + } + else if (WIFSIGNALED(status)) + { + ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)), + false); } else { - if (WIFEXITED(status)) - { - int rc(WEXITSTATUS(status)); - ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), - rc, expect); - } - else if (WIFSIGNALED(status)) - { - ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)), - false); - } - else - { - ensure(STRINGIZE(desc << " script produced impossible status " << status), - false); - } + ensure(STRINGIZE(desc << " script produced impossible status " << status), + false); } -#endif } +#endif + } + + struct TestPythonCompatible + { + TestPythonCompatible() {} + ~TestPythonCompatible() {} }; typedef tut::test_group TestPythonCompatibleGroup; @@ -1798,25 +1895,33 @@ namespace tut "print('Running on', sys.platform)\n"); } - // helper for test<3> - static void writeLLSDArray(std::ostream& out, const LLSD& array) + // helper for test<3> - test<7> + static void writeLLSDArray(const FormatterFunction& serialize, + std::ostream& out, const LLSD& array) { - BOOST_FOREACH(LLSD item, llsd::inArray(array)) + for (const LLSD& item : llsd::inArray(array)) { - LLSDSerialize::toNotation(item, out); - // It's important to separate with newlines because Python's llsd - // module doesn't support parsing from a file stream, only from a - // string, so we have to know how much of the file to read into a - // string. - out << '\n'; + // It's important to delimit the entries in this file somehow + // because, although Python's llsd.parse() can accept a file + // stream, the XML parser expects EOF after a single outer element + // -- it doesn't just stop. So we must extract a sequence of bytes + // strings from the file. But since one of the serialization + // formats we want to test is binary, we can't pick any single + // byte value as a delimiter! Use a binary integer length prefix + // instead. + std::ostringstream buffer; + serialize(item, buffer); + auto buffstr{ buffer.str() }; + int bufflen{ static_cast(buffstr.length()) }; + out.write(reinterpret_cast(&bufflen), sizeof(bufflen)); + out.write(buffstr.c_str(), buffstr.length()); } } - template<> template<> - void TestPythonCompatibleObject::test<3>() + // helper for test<3> - test<7> + static void toPythonUsing(const std::string& desc, + const FormatterFunction& serialize) { - set_test_name("verify sequence to Python"); - LLSD cdata(llsd::array(17, 3.14, "This string\n" "has several\n" @@ -1836,7 +1941,7 @@ namespace tut " except StopIteration:\n" " pass\n" " else:\n" - " assert False, 'Too many data items'\n"; + " raise AssertionError('Too many data items')\n"; // Create an llsdXXXXXX file containing 'data' serialized to // notation. @@ -1845,32 +1950,128 @@ namespace tut // takes a callable. To this callable it passes the // std::ostream with which it's writing the // NamedTempFile. - boost::bind(writeLLSDArray, _1, cdata)); + [serialize, cdata] + (std::ostream& out) + { writeLLSDArray(serialize, out, cdata); }); - python("read C++ notation", + python("read C++ " + desc, placeholders::arg1 << import_llsd << - "def parse_each(iterable):\n" - " for item in iterable:\n" - " yield llsd.parse(item)\n" << - pydata << + "from functools import partial\n" + "import io\n" + "import struct\n" + "lenformat = struct.Struct('i')\n" + "def parse_each(inf):\n" + " for rawlen in iter(partial(inf.read, lenformat.size), b''):\n" + " len = lenformat.unpack(rawlen)[0]\n" + // Since llsd.parse() has no max_bytes argument, instead of + // passing the input stream directly to parse(), read the item + // into a distinct bytes object and parse that. + " data = inf.read(len)\n" + " try:\n" + " frombytes = llsd.parse(data)\n" + " except llsd.LLSDParseError as err:\n" + " print(f'*** {err}')\n" + " print(f'Bad content:\\n{data!r}')\n" + " raise\n" + // Also try parsing from a distinct stream. + " stream = io.BytesIO(data)\n" + " fromstream = llsd.parse(stream)\n" + " assert frombytes == fromstream\n" + " yield frombytes\n" + << pydata << // Don't forget raw-string syntax for Windows pathnames. "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n"); } + template<> template<> + void TestPythonCompatibleObject::test<3>() + { + set_test_name("to Python using LLSDSerialize::serialize(LLSD_XML)"); + toPythonUsing("LLSD_XML", + [](const LLSD& sd, std::ostream& out) + { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_XML); }); + } + template<> template<> void TestPythonCompatibleObject::test<4>() { - set_test_name("verify sequence from Python"); + set_test_name("to Python using LLSDSerialize::serialize(LLSD_NOTATION)"); + toPythonUsing("LLSD_NOTATION", + [](const LLSD& sd, std::ostream& out) + { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_NOTATION); }); + } + template<> template<> + void TestPythonCompatibleObject::test<5>() + { + set_test_name("to Python using LLSDSerialize::serialize(LLSD_BINARY)"); + toPythonUsing("LLSD_BINARY", + [](const LLSD& sd, std::ostream& out) + { LLSDSerialize::serialize(sd, out, LLSDSerialize::LLSD_BINARY); }); + } + + template<> template<> + void TestPythonCompatibleObject::test<6>() + { + set_test_name("to Python using LLSDSerialize::toXML()"); + toPythonUsing("toXML()", LLSDSerialize::toXML); + } + + template<> template<> + void TestPythonCompatibleObject::test<7>() + { + set_test_name("to Python using LLSDSerialize::toNotation()"); + toPythonUsing("toNotation()", LLSDSerialize::toNotation); + } + +/*==========================================================================*| + template<> template<> + void TestPythonCompatibleObject::test<8>() + { + set_test_name("to Python using LLSDSerialize::toBinary()"); + // We don't expect this to work because, without a header, + // llsd.parse() will assume notation rather than binary. + toPythonUsing("toBinary()", LLSDSerialize::toBinary); + } +|*==========================================================================*/ + + // helper for test<8> - test<12> + bool itemFromStream(std::istream& istr, LLSD& item, const ParserFunction& parse) + { + // reset the output value for debugging clarity + item.clear(); + // We use an int length prefix as a foolproof delimiter even for + // binary serialized streams. + int length{ 0 }; + istr.read(reinterpret_cast(&length), sizeof(length)); +// return parse(istr, item, length); + // Sadly, as of 2022-12-01 it seems we can't really trust our LLSD + // parsers to honor max_bytes: this test works better when we read + // each item into its own distinct LLMemoryStream, instead of passing + // the original istr with a max_bytes constraint. + std::vector buffer(length); + istr.read(reinterpret_cast(buffer.data()), length); + LLMemoryStream stream(buffer.data(), length); + return parse(stream, item, length); + } + + // helper for test<8> - test<12> + void fromPythonUsing(const std::string& pyformatter, + const ParserFunction& parse= + [](std::istream& istr, LLSD& data, llssize max_bytes) + { return LLSDSerialize::deserialize(data, istr, max_bytes); }) + { // Create an empty data file. This is just a placeholder for our // script to write into. Create it to establish a unique name that // we know. NamedTempFile file("llsd", ""); - python("write Python notation", + python("Python " + pyformatter, placeholders::arg1 << import_llsd << + "import struct\n" + "lenformat = struct.Struct('i')\n" "DATA = [\n" " 17,\n" " 3.14,\n" @@ -1881,34 +2082,87 @@ namespace tut "]\n" // Don't forget raw-string syntax for Windows pathnames. // N.B. Using 'print' implicitly adds newlines. - "with open(r'" << file.getName() << "', 'w') as f:\n" + "with open(r'" << file.getName() << "', 'wb') as f:\n" " for item in DATA:\n" - " print(llsd.format_notation(item).decode(), file=f)\n"); + " serialized = llsd." << pyformatter << "(item)\n" + " f.write(lenformat.pack(len(serialized)))\n" + " f.write(serialized)\n"); std::ifstream inf(file.getName().c_str()); LLSD item; - // Notice that we're not doing anything special to parse out the - // newlines: LLSDSerialize::fromNotation ignores them. While it would - // seem they're not strictly necessary, going in this direction, we - // want to ensure that notation-separated-by-newlines works in both - // directions -- since in practice, a given file might be read by - // either language. - ensure_equals("Failed to read LLSD::Integer from Python", - LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED), - 1); - ensure_equals(item.asInteger(), 17); - ensure_equals("Failed to read LLSD::Real from Python", - LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED), - 1); - ensure_approximately_equals("Bad LLSD::Real value from Python", - item.asReal(), 3.14, 7); // 7 bits ~= 0.01 - ensure_equals("Failed to read LLSD::String from Python", - LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED), - 1); - ensure_equals(item.asString(), - "This string\n" - "has several\n" - "lines."); - + try + { + ensure("Failed to read LLSD::Integer from Python", + itemFromStream(inf, item, parse)); + ensure_equals(item.asInteger(), 17); + ensure("Failed to read LLSD::Real from Python", + itemFromStream(inf, item, parse)); + ensure_approximately_equals("Bad LLSD::Real value from Python", + item.asReal(), 3.14, 7); // 7 bits ~= 0.01 + ensure("Failed to read LLSD::String from Python", + itemFromStream(inf, item, parse)); + ensure_equals(item.asString(), + "This string\n" + "has several\n" + "lines."); + } + catch (const tut::failure& err) + { + std::cout << "for " << err.what() << ", item = " << item << std::endl; + throw; + } } + + template<> template<> + void TestPythonCompatibleObject::test<8>() + { + set_test_name("from Python XML using LLSDSerialize::deserialize()"); + fromPythonUsing("format_xml"); + } + + template<> template<> + void TestPythonCompatibleObject::test<9>() + { + set_test_name("from Python notation using LLSDSerialize::deserialize()"); + fromPythonUsing("format_notation"); + } + + template<> template<> + void TestPythonCompatibleObject::test<10>() + { + set_test_name("from Python binary using LLSDSerialize::deserialize()"); + fromPythonUsing("format_binary"); + } + + template<> template<> + void TestPythonCompatibleObject::test<11>() + { + set_test_name("from Python XML using fromXML()"); + // fromXML()'s optional 3rd param isn't max_bytes, it's emit_errors + fromPythonUsing("format_xml", + [](std::istream& istr, LLSD& data, llssize) + { return LLSDSerialize::fromXML(data, istr) > 0; }); + } + + template<> template<> + void TestPythonCompatibleObject::test<12>() + { + set_test_name("from Python notation using fromNotation()"); + fromPythonUsing("format_notation", + [](std::istream& istr, LLSD& data, llssize max_bytes) + { return LLSDSerialize::fromNotation(data, istr, max_bytes) > 0; }); + } + +/*==========================================================================*| + template<> template<> + void TestPythonCompatibleObject::test<13>() + { + set_test_name("from Python binary using fromBinary()"); + // We don't expect this to work because format_binary() emits a + // header, but fromBinary() won't recognize a header. + fromPythonUsing("format_binary", + [](std::istream& istr, LLSD& data, llssize max_bytes) + { return LLSDSerialize::fromBinary(data, istr, max_bytes) > 0; }); + } +|*==========================================================================*/ } diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py index 185e8e25c6..b9992538ba 100755 --- a/indra/llcorehttp/tests/test_llcorehttp_peer.py +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -38,7 +38,7 @@ from io import StringIO from http.server import HTTPServer, BaseHTTPRequestHandler -from llbase import llsd +import llsd # we're in llcorehttp/tests ; testrunner.py is found in llmessage/tests sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp index 81261f0767..5adf1fa0e6 100644 --- a/indra/llinventory/llinventory.cpp +++ b/indra/llinventory/llinventory.cpp @@ -40,9 +40,12 @@ ///---------------------------------------------------------------------------- /// Exported functions ///---------------------------------------------------------------------------- +// FIXME D567 - what's the point of these, especially if we don't even use them consistently? static const std::string INV_ITEM_ID_LABEL("item_id"); static const std::string INV_FOLDER_ID_LABEL("cat_id"); static const std::string INV_PARENT_ID_LABEL("parent_id"); +static const std::string INV_THUMBNAIL_LABEL("thumbnail"); +static const std::string INV_THUMBNAIL_ID_LABEL("thumbnail_id"); static const std::string INV_ASSET_TYPE_LABEL("type"); static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type"); static const std::string INV_INVENTORY_TYPE_LABEL("inv_type"); @@ -99,6 +102,7 @@ void LLInventoryObject::copyObject(const LLInventoryObject* other) mParentUUID = other->mParentUUID; mType = other->mType; mName = other->mName; + mThumbnailUUID = other->mThumbnailUUID; } const LLUUID& LLInventoryObject::getUUID() const @@ -111,6 +115,11 @@ const LLUUID& LLInventoryObject::getParentUUID() const return mParentUUID; } +const LLUUID& LLInventoryObject::getThumbnailUUID() const +{ + return mThumbnailUUID; +} + const std::string& LLInventoryObject::getName() const { return mName; @@ -160,6 +169,11 @@ void LLInventoryObject::setParent(const LLUUID& new_parent) mParentUUID = new_parent; } +void LLInventoryObject::setThumbnailUUID(const LLUUID& thumbnail_uuid) +{ + mThumbnailUUID = thumbnail_uuid; +} + void LLInventoryObject::setType(LLAssetType::EType type) { mType = type; @@ -201,6 +215,26 @@ BOOL LLInventoryObject::importLegacyStream(std::istream& input_stream) { mType = LLAssetType::lookup(valuestr); } + else if (0 == strcmp("metadata", keyword)) + { + LLSD metadata(valuestr); + if (metadata.has("thumbnail")) + { + const LLSD& thumbnail = metadata["thumbnail"]; + if (thumbnail.has("asset_id")) + { + setThumbnailUUID(thumbnail["asset_id"].asUUID()); + } + else + { + setThumbnailUUID(LLUUID::null); + } + } + else + { + setThumbnailUUID(LLUUID::null); + } + } else if(0 == strcmp("name", keyword)) { //strcpy(valuestr, buffer + strlen(keyword) + 3); @@ -336,6 +370,7 @@ void LLInventoryItem::copyItem(const LLInventoryItem* other) copyObject(other); mPermissions = other->mPermissions; mAssetUUID = other->mAssetUUID; + mThumbnailUUID = other->mThumbnailUUID; mDescription = other->mDescription; mSaleInfo = other->mSaleInfo; mInventoryType = other->mInventoryType; @@ -400,6 +435,7 @@ U32 LLInventoryItem::getCRC32() const //LL_DEBUGS() << "8 crc: " << std::hex << crc << std::dec << LL_ENDL; crc += (U32)mCreationDate; //LL_DEBUGS() << "9 crc: " << std::hex << crc << std::dec << LL_ENDL; + crc += mThumbnailUUID.getCRC32(); return crc; } @@ -655,6 +691,26 @@ BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream) { mType = LLAssetType::lookup(valuestr); } + else if (0 == strcmp("metadata", keyword)) + { + LLSD metadata(valuestr); + if (metadata.has("thumbnail")) + { + const LLSD& thumbnail = metadata["thumbnail"]; + if (thumbnail.has("asset_id")) + { + setThumbnailUUID(thumbnail["asset_id"].asUUID()); + } + else + { + setThumbnailUUID(LLUUID::null); + } + } + else + { + setThumbnailUUID(LLUUID::null); + } + } else if(0 == strcmp("inv_type", keyword)) { mInventoryType = LLInventoryType::lookup(std::string(valuestr)); @@ -744,6 +800,13 @@ BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL inclu output_stream << "\t\tparent_id\t" << uuid_str << "\n"; mPermissions.exportLegacyStream(output_stream); + if (mThumbnailUUID.notNull()) + { + LLSD metadata; + metadata["thumbnail"] = LLSD().with("asset_id", mThumbnailUUID); + output_stream << "\t\tmetadata\t" << metadata << "|\n"; + } + // Check for permissions to see the asset id, and if so write it // out as an asset id. Otherwise, apply our cheesy encryption. if(include_asset_key) @@ -797,6 +860,11 @@ void LLInventoryItem::asLLSD( LLSD& sd ) const sd[INV_PARENT_ID_LABEL] = mParentUUID; sd[INV_PERMISSIONS_LABEL] = ll_create_sd_from_permissions(mPermissions); + if (mThumbnailUUID.notNull()) + { + sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } + U32 mask = mPermissions.getMaskBase(); if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) || (mAssetUUID.isNull())) @@ -848,6 +916,35 @@ bool LLInventoryItem::fromLLSD(const LLSD& sd, bool is_new) { mParentUUID = sd[w]; } + mThumbnailUUID.setNull(); + w = INV_THUMBNAIL_LABEL; + if (sd.has(w)) + { + const LLSD &thumbnail_map = sd[w]; + w = INV_ASSET_ID_LABEL; + if (thumbnail_map.has(w)) + { + mThumbnailUUID = thumbnail_map[w]; + } + /* Example: + asset_id + acc0ec86 - 17f2 - 4b92 - ab41 - 6718b1f755f7 + perms + 8 + service + 3 + version + 1 + */ + } + else + { + w = INV_THUMBNAIL_ID_LABEL; + if (sd.has(w)) + { + mThumbnailUUID = sd[w].asUUID(); + } + } w = INV_PERMISSIONS_LABEL; if (sd.has(w)) { @@ -972,135 +1069,6 @@ fail: } -// Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML() -// because I can't find any non-test code references to it. 2009-05-04 JC - -S32 LLInventoryItem::packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override) const -{ - // Figure out which permissions to use. - LLPermissions perm; - if (perm_override) - { - // Use the permissions override. - perm = *perm_override; - } - else - { - // Use the current permissions. - perm = getPermissions(); - } - - // describe the inventory item - char* buffer = (char*) bin_bucket; - std::string creator_id_str; - - perm.getCreator().toString(creator_id_str); - std::string owner_id_str; - perm.getOwner().toString(owner_id_str); - std::string last_owner_id_str; - perm.getLastOwner().toString(last_owner_id_str); - std::string group_id_str; - perm.getGroup().toString(group_id_str); - std::string asset_id_str; - getAssetUUID().toString(asset_id_str); - S32 size = sprintf(buffer, /* Flawfinder: ignore */ - "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x", - getType(), - getInventoryType(), - getName().c_str(), - creator_id_str.c_str(), - owner_id_str.c_str(), - last_owner_id_str.c_str(), - group_id_str.c_str(), - perm.getMaskBase(), - perm.getMaskOwner(), - perm.getMaskGroup(), - perm.getMaskEveryone(), - perm.getMaskNextOwner(), - asset_id_str.c_str(), - getDescription().c_str(), - getSaleInfo().getSaleType(), - getSaleInfo().getSalePrice(), - getFlags()) + 1; - - return size; -} - -void LLInventoryItem::unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size) -{ - // Early exit on an empty binary bucket. - if (bin_bucket_size <= 1) return; - - if (NULL == bin_bucket) - { - LL_ERRS() << "unpackBinaryBucket failed. bin_bucket is NULL." << LL_ENDL; - return; - } - - // Convert the bin_bucket into a string. - std::vector item_buffer(bin_bucket_size+1); - memcpy(&item_buffer[0], bin_bucket, bin_bucket_size); /* Flawfinder: ignore */ - item_buffer[bin_bucket_size] = '\0'; - std::string str(&item_buffer[0]); - - LL_DEBUGS() << "item buffer: " << str << LL_ENDL; - - // Tokenize the string. - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|", "", boost::keep_empty_tokens); - tokenizer tokens(str, sep); - tokenizer::iterator iter = tokens.begin(); - - // Extract all values. - LLUUID item_id; - item_id.generate(); - setUUID(item_id); - - LLAssetType::EType type; - type = (LLAssetType::EType)(atoi((*(iter++)).c_str())); - setType( type ); - - LLInventoryType::EType inv_type; - inv_type = (LLInventoryType::EType)(atoi((*(iter++)).c_str())); - setInventoryType( inv_type ); - - std::string name((*(iter++)).c_str()); - rename( name ); - - LLUUID creator_id((*(iter++)).c_str()); - LLUUID owner_id((*(iter++)).c_str()); - LLUUID last_owner_id((*(iter++)).c_str()); - LLUUID group_id((*(iter++)).c_str()); - PermissionMask mask_base = strtoul((*(iter++)).c_str(), NULL, 16); - PermissionMask mask_owner = strtoul((*(iter++)).c_str(), NULL, 16); - PermissionMask mask_group = strtoul((*(iter++)).c_str(), NULL, 16); - PermissionMask mask_every = strtoul((*(iter++)).c_str(), NULL, 16); - PermissionMask mask_next = strtoul((*(iter++)).c_str(), NULL, 16); - LLPermissions perm; - perm.init(creator_id, owner_id, last_owner_id, group_id); - perm.initMasks(mask_base, mask_owner, mask_group, mask_every, mask_next); - setPermissions(perm); - //LL_DEBUGS() << "perm: " << perm << LL_ENDL; - - LLUUID asset_id((*(iter++)).c_str()); - setAssetUUID(asset_id); - - std::string desc((*(iter++)).c_str()); - setDescription(desc); - - LLSaleInfo::EForSale sale_type; - sale_type = (LLSaleInfo::EForSale)(atoi((*(iter++)).c_str())); - S32 price = atoi((*(iter++)).c_str()); - LLSaleInfo sale_info(sale_type, price); - setSaleInfo(sale_info); - - U32 flags = strtoul((*(iter++)).c_str(), NULL, 16); - setFlags(flags); - - time_t now = time(NULL); - setCreationDate(now); -} - ///---------------------------------------------------------------------------- /// Class LLInventoryCategory ///---------------------------------------------------------------------------- @@ -1150,11 +1118,32 @@ void LLInventoryCategory::setPreferredType(LLFolderType::EType type) LLSD LLInventoryCategory::asLLSD() const { LLSD sd = LLSD(); - sd["item_id"] = mUUID; - sd["parent_id"] = mParentUUID; + sd[INV_ITEM_ID_LABEL] = mUUID; + sd[INV_PARENT_ID_LABEL] = mParentUUID; S8 type = static_cast(mPreferredType); - sd["type"] = type; - sd["name"] = mName; + sd[INV_ASSET_TYPE_LABEL] = type; + sd[INV_NAME_LABEL] = mName; + + if (mThumbnailUUID.notNull()) + { + sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } + + return sd; +} + +LLSD LLInventoryCategory::asAISCreateCatLLSD() const +{ + LLSD sd = LLSD(); + sd[INV_FOLDER_ID_LABEL_WS] = mUUID; + sd[INV_PARENT_ID_LABEL] = mParentUUID; + S8 type = static_cast(mPreferredType); + sd[INV_ASSET_TYPE_LABEL_WS] = type; + sd[INV_NAME_LABEL] = mName; + if (mThumbnailUUID.notNull()) + { + sd[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } return sd; } @@ -1184,6 +1173,25 @@ bool LLInventoryCategory::fromLLSD(const LLSD& sd) { mParentUUID = sd[w]; } + mThumbnailUUID.setNull(); + w = INV_THUMBNAIL_LABEL; + if (sd.has(w)) + { + const LLSD &thumbnail_map = sd[w]; + w = INV_ASSET_ID_LABEL; + if (thumbnail_map.has(w)) + { + mThumbnailUUID = thumbnail_map[w]; + } + } + else + { + w = INV_THUMBNAIL_ID_LABEL; + if (sd.has(w)) + { + mThumbnailUUID = sd[w]; + } + } w = INV_ASSET_TYPE_LABEL; if (sd.has(w)) { @@ -1275,6 +1283,26 @@ BOOL LLInventoryCategory::importLegacyStream(std::istream& input_stream) LLStringUtil::replaceNonstandardASCII(mName, ' '); LLStringUtil::replaceChar(mName, '|', ' '); } + else if (0 == strcmp("metadata", keyword)) + { + LLSD metadata(valuestr); + if (metadata.has("thumbnail")) + { + const LLSD& thumbnail = metadata["thumbnail"]; + if (thumbnail.has("asset_id")) + { + setThumbnailUUID(thumbnail["asset_id"].asUUID()); + } + else + { + setThumbnailUUID(LLUUID::null); + } + } + else + { + setThumbnailUUID(LLUUID::null); + } + } else { LL_WARNS() << "unknown keyword '" << keyword @@ -1295,6 +1323,12 @@ BOOL LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, BOOL) output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n"; output_stream << "\t\tpref_type\t" << LLFolderType::lookup(mPreferredType) << "\n"; output_stream << "\t\tname\t" << mName.c_str() << "|\n"; + if (mThumbnailUUID.notNull()) + { + LLSD metadata; + metadata["thumbnail"] = LLSD().with("asset_id", mThumbnailUUID); + output_stream << "\t\tmetadata\t" << metadata << "|\n"; + } output_stream << "\t}\n"; return TRUE; } @@ -1308,6 +1342,11 @@ LLSD LLInventoryCategory::exportLLSD() const cat_data[INV_PREFERRED_TYPE_LABEL] = LLFolderType::lookup(mPreferredType); cat_data[INV_NAME_LABEL] = mName; + if (mThumbnailUUID.notNull()) + { + cat_data[INV_THUMBNAIL_LABEL] = LLSD().with(INV_ASSET_ID_LABEL, mThumbnailUUID); + } + return cat_data; } @@ -1329,6 +1368,16 @@ bool LLInventoryCategory::importLLSD(const LLSD& cat_data) { setPreferredType(LLFolderType::lookup(cat_data[INV_PREFERRED_TYPE_LABEL].asString())); } + if (cat_data.has(INV_THUMBNAIL_LABEL)) + { + LLUUID thumbnail_uuid; + const LLSD &thumbnail_data = cat_data[INV_THUMBNAIL_LABEL]; + if (thumbnail_data.has(INV_ASSET_ID_LABEL)) + { + thumbnail_uuid = thumbnail_data[INV_ASSET_ID_LABEL].asUUID(); + } + setThumbnailUUID(thumbnail_uuid); + } if (cat_data.has(INV_NAME_LABEL)) { mName = cat_data[INV_NAME_LABEL].asString(); diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h index 7d9f9704f1..6d4535af27 100644 --- a/indra/llinventory/llinventory.h +++ b/indra/llinventory/llinventory.h @@ -70,6 +70,7 @@ public: virtual const LLUUID& getUUID() const; // inventoryID that this item points to virtual const LLUUID& getLinkedUUID() const; // inventoryID that this item points to, else this item's inventoryID const LLUUID& getParentUUID() const; + virtual const LLUUID& getThumbnailUUID() const; virtual const std::string& getName() const; virtual LLAssetType::EType getType() const; LLAssetType::EType getActualType() const; // bypasses indirection for linked items @@ -84,6 +85,7 @@ public: void setUUID(const LLUUID& new_uuid); virtual void rename(const std::string& new_name); void setParent(const LLUUID& new_parent); + virtual void setThumbnailUUID(const LLUUID& thumbnail_uuid); void setType(LLAssetType::EType type); virtual void setCreationDate(time_t creation_date_utc); // only stored for items @@ -108,6 +110,7 @@ public: protected: LLUUID mUUID; LLUUID mParentUUID; // Parent category. Root categories have LLUUID::NULL. + LLUUID mThumbnailUUID; LLAssetType::EType mType; std::string mName; time_t mCreationDate; // seconds from 1/1/1970, UTC @@ -203,9 +206,6 @@ public: // Helper Functions //-------------------------------------------------------------------- public: - // Pack all information needed to reconstruct this item into the given binary bucket. - S32 packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override = NULL) const; - void unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size); LLSD asLLSD() const; void asLLSD( LLSD& sd ) const; bool fromLLSD(const LLSD& sd, bool is_new = true); @@ -253,6 +253,7 @@ public: LLFolderType::EType getPreferredType() const; void setPreferredType(LLFolderType::EType type); LLSD asLLSD() const; + LLSD asAISCreateCatLLSD() const; bool fromLLSD(const LLSD& sd); //-------------------------------------------------------------------- diff --git a/indra/llinventory/llsettingssky.cpp b/indra/llinventory/llsettingssky.cpp index 8338245897..fedbed2990 100644 --- a/indra/llinventory/llsettingssky.cpp +++ b/indra/llinventory/llsettingssky.cpp @@ -32,6 +32,7 @@ #include "llfasttimer.h" #include "v3colorutil.h" + //========================================================================= namespace { @@ -133,7 +134,9 @@ const std::string LLSettingsSky::SETTING_SKY_ICE_LEVEL("ice_level"); const std::string LLSettingsSky::SETTING_REFLECTION_PROBE_AMBIANCE("reflection_probe_ambiance"); -const LLUUID LLSettingsSky::DEFAULT_ASSET_ID("3ae23978-ac82-bcf3-a9cb-ba6e52dcb9ad"); +const LLUUID LLSettingsSky::DEFAULT_ASSET_ID("651510b8-5f4d-8991-1592-e7eeab2a5a06"); + +F32 LLSettingsSky::sAutoAdjustProbeAmbiance = 1.f; static const LLUUID DEFAULT_SUN_ID("32bfbcea-24b1-fb9d-1ef9-48a28a63730f"); // dataserver static const LLUUID DEFAULT_MOON_ID("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver @@ -1438,7 +1441,7 @@ F32 LLSettingsSky::getReflectionProbeAmbiance(bool auto_adjust) const { if (auto_adjust && canAutoAdjust()) { - return 1.f; + return sAutoAdjustProbeAmbiance; } return mSettings[SETTING_REFLECTION_PROBE_AMBIANCE].asReal(); @@ -1446,6 +1449,7 @@ F32 LLSettingsSky::getReflectionProbeAmbiance(bool auto_adjust) const F32 LLSettingsSky::getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool auto_adjust) const { +#if 0 // feed cloud shadow back into reflection probe ambiance to mimic pre-reflection-probe behavior // without brightening dark/interior spaces F32 probe_ambiance = getReflectionProbeAmbiance(auto_adjust); @@ -1456,6 +1460,9 @@ F32 LLSettingsSky::getTotalReflectionProbeAmbiance(F32 cloud_shadow_scale, bool } return probe_ambiance; +#else + return getReflectionProbeAmbiance(auto_adjust); +#endif } F32 LLSettingsSky::getSkyBottomRadius() const diff --git a/indra/llinventory/llsettingssky.h b/indra/llinventory/llsettingssky.h index f55e9f0631..7ba7a9ba06 100644 --- a/indra/llinventory/llsettingssky.h +++ b/indra/llinventory/llsettingssky.h @@ -103,6 +103,8 @@ public: static const LLUUID DEFAULT_ASSET_ID; + static F32 sAutoAdjustProbeAmbiance; + typedef PTR_NAMESPACE::shared_ptr ptr_t; //--------------------------------------------------------------------- diff --git a/indra/llinventory/tests/inventorymisc_test.cpp b/indra/llinventory/tests/inventorymisc_test.cpp index e8b063bffe..039fa938dd 100644 --- a/indra/llinventory/tests/inventorymisc_test.cpp +++ b/indra/llinventory/tests/inventorymisc_test.cpp @@ -400,27 +400,7 @@ namespace tut // Deleted LLInventoryItem::exportFileXML() and LLInventoryItem::importXML() // because I can't find any non-test code references to it. 2009-05-04 JC } - - template<> template<> - void inventory_object::test<10>() - { - LLPointer src1 = create_random_inventory_item(); - U8* bin_bucket = new U8[300]; - S32 bin_bucket_size = src1->packBinaryBucket(bin_bucket, NULL); - LLPointer src2 = new LLInventoryItem(); - src2->unpackBinaryBucket(bin_bucket, bin_bucket_size); - - ensure_equals("1.sale price::getSalePrice() failed price", src1->getSaleInfo().getSalePrice(), src2->getSaleInfo().getSalePrice()); - ensure_equals("2.sale type::getSaleType() failed type", src1->getSaleInfo().getSaleType(), src2->getSaleInfo().getSaleType()); - ensure_equals("3.type::getType() failed", src1->getType(), src2->getType()); - ensure_equals("4.inventory type::getInventoryType() failed type", src1->getInventoryType(), src2->getInventoryType()); - ensure_equals("5.name::getName() failed", src1->getName(), src2->getName()); - ensure_equals("6.description::getDescription() failed", src1->getDescription(), src2->getDescription()); - ensure_equals("7.flags::getFlags() failed", src1->getFlags(), src2->getFlags()); - - } - template<> template<> void inventory_object::test<11>() { diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h index 27eaa614c9..c4d04f5d02 100644 --- a/indra/llmath/llcamera.h +++ b/indra/llmath/llcamera.h @@ -39,7 +39,7 @@ const F32 DEFAULT_NEAR_PLANE = 0.25f; const F32 DEFAULT_FAR_PLANE = 64.f; // far reaches across two horizontal, not diagonal, regions const F32 MAX_ASPECT_RATIO = 50.0f; -const F32 MAX_NEAR_PLANE = 10.f; +const F32 MAX_NEAR_PLANE = 1023.9f; // Clamp the near plane just before the skybox ends const F32 MAX_FAR_PLANE = 100000.0f; //1000000.0f; // Max allowed. Not good Z precision though. const F32 MAX_FAR_CLIP = 512.0f; diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 667108320a..a5f047d207 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -5626,29 +5626,29 @@ bool LLVolumeFace::cacheOptimize(bool gen_tangents) mWeights[dst_idx].loadua(data.w[src_idx].mV); } } + + // put back in normalized coordinate frame + LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); + LLVector4a scale; + scale.load3(mNormalizedScale.mV); + scale.getF32ptr()[3] = 1.f; + + for (int i = 0; i < mNumVertices; ++i) + { + mPositions[i].mul(inv_scale); + mNormals[i].mul(scale); + mNormals[i].normalize3(); + F32 w = mTangents[i].getF32ptr()[3]; + mTangents[i].mul(scale); + mTangents[i].normalize3(); + mTangents[i].getF32ptr()[3] = w; + } } else { // blew past the max vertex size limit, use legacy tangent generation which never adds verts createTangents(); } - - // put back in normalized coordinate frame - LLVector4a inv_scale(1.f/mNormalizedScale.mV[0], 1.f / mNormalizedScale.mV[1], 1.f / mNormalizedScale.mV[2]); - LLVector4a scale; - scale.load3(mNormalizedScale.mV); - scale.getF32ptr()[3] = 1.f; - - for (int i = 0; i < mNumVertices; ++i) - { - mPositions[i].mul(inv_scale); - mNormals[i].mul(scale); - mNormals[i].normalize3(); - F32 w = mTangents[i].getF32ptr()[3]; - mTangents[i].mul(scale); - mTangents[i].normalize3(); - mTangents[i].getF32ptr()[3] = w; - } } // cache optimize index buffer diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp index 89cdb1c6b9..9399504529 100644 --- a/indra/llmath/llvolumemgr.cpp +++ b/indra/llmath/llvolumemgr.cpp @@ -89,7 +89,7 @@ BOOL LLVolumeMgr::cleanup() // Note however that LLVolumeLODGroup that contains the volume // also holds a LLPointer so the volume will only go away after // anything holding the volume and the LODGroup are destroyed -LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 detail) +LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 lod) { LLVolumeLODGroup* volgroupp; if (mDataMutex) @@ -109,7 +109,7 @@ LLVolume* LLVolumeMgr::refVolume(const LLVolumeParams &volume_params, const S32 { mDataMutex->unlock(); } - return volgroupp->refLOD(detail); + return volgroupp->refLOD(lod); } // virtual @@ -287,18 +287,18 @@ bool LLVolumeLODGroup::cleanupRefs() return res; } -LLVolume* LLVolumeLODGroup::refLOD(const S32 detail) +LLVolume* LLVolumeLODGroup::refLOD(const S32 lod) { - llassert(detail >=0 && detail < NUM_LODS); - mAccessCount[detail]++; + llassert(lod >=0 && lod < NUM_LODS); + mAccessCount[lod]++; mRefs++; - if (mVolumeLODs[detail].isNull()) + if (mVolumeLODs[lod].isNull()) { - mVolumeLODs[detail] = new LLVolume(mVolumeParams, mDetailScales[detail]); + mVolumeLODs[lod] = new LLVolume(mVolumeParams, mDetailScales[lod]); } - mLODRefs[detail]++; - return mVolumeLODs[detail]; + mLODRefs[lod]++; + return mVolumeLODs[lod]; } BOOL LLVolumeLODGroup::derefLOD(LLVolume *volumep) diff --git a/indra/llmessage/CMakeLists.txt b/indra/llmessage/CMakeLists.txt index 4786956e85..e44309476b 100644 --- a/indra/llmessage/CMakeLists.txt +++ b/indra/llmessage/CMakeLists.txt @@ -30,6 +30,7 @@ set(llmessage_SOURCE_FILES lldispatcher.cpp llexperiencecache.cpp llfiltersd2xmlrpc.cpp + llgenericstreamingmessage.cpp llhost.cpp llhttpnode.cpp llhttpsdhandler.cpp @@ -114,6 +115,7 @@ set(llmessage_HEADER_FILES llextendedstatus.h llfiltersd2xmlrpc.h llfollowcamparams.h + llgenericstreamingmessage.h llhost.h llhttpnode.h llhttpnodeadapter.h diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 66bd85f4e6..a2e85cf6c2 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -552,7 +552,9 @@ std::string LLCacheName::buildUsername(const std::string& full_name) // if the input wasn't a correctly formatted legacy name, just return it // cleaned up from a potential terminal "Resident" - return cleanFullName(full_name); + std::string clean_name = cleanFullName(full_name); + LLStringUtil::toLower(clean_name); + return clean_name; } //static diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp index 8baa2e328b..a9a292958f 100644 --- a/indra/llmessage/llcircuit.cpp +++ b/indra/llmessage/llcircuit.cpp @@ -271,7 +271,6 @@ void LLCircuitData::ackReliablePacket(TPACKETID packet_num) S32 LLCircuitData::resendUnackedPackets(const F64Seconds now) { - S32 resent_packets = 0; LLReliablePacket *packetp; @@ -375,7 +374,6 @@ S32 LLCircuitData::resendUnackedPackets(const F64Seconds now) // Don't remove it yet, it still gets to try to resend at least once. ++iter; } - resent_packets++; } else { diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp index d310cefd1e..ebbaea9b12 100644 --- a/indra/llmessage/llcoproceduremanager.cpp +++ b/indra/llmessage/llcoproceduremanager.cpp @@ -364,7 +364,26 @@ LLUUID LLCoprocedurePool::enqueueCoprocedure(const std::string &name, LLCoproced { LLUUID id(LLUUID::generateNewID()); - LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " << mPending << LL_ENDL; + if (mPoolName == "AIS") + { + // Fetch is going to be spammy. + LL_DEBUGS("CoProcMgr", "Inventory") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName + << "\" at " + << mPending << LL_ENDL; + + if (mPending >= (LLCoprocedureManager::DEFAULT_QUEUE_SIZE - 1)) + { + // If it's all used up (not supposed to happen, + // fetched should cap it), we are going to crash + LL_WARNS("CoProcMgr", "Inventory") << "About to run out of queue space for Coprocedure(" << name + << ") enqueuing with id=" << id.asString() << " Already pending:" << mPending << LL_ENDL; + } + } + else + { + LL_INFOS("CoProcMgr") << "Coprocedure(" << name << ") enqueuing with id=" << id.asString() << " in pool \"" << mPoolName << "\" at " + << mPending << LL_ENDL; + } auto pushed = mPendingCoprocs->try_push(boost::make_shared(name, id, proc)); if (pushed == boost::fibers::channel_op_status::success) { diff --git a/indra/llmessage/llcorehttputil.cpp b/indra/llmessage/llcorehttputil.cpp index 02f273b204..460740cebc 100644 --- a/indra/llmessage/llcorehttputil.cpp +++ b/indra/llmessage/llcorehttputil.cpp @@ -1329,6 +1329,48 @@ void HttpCoroutineAdapter::trivialPostCoro(std::string url, LLCore::HttpRequest: } +/*static*/ +void HttpCoroutineAdapter::callbackHttpDel(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, + completionCallback_t failure) +{ + LLCoros::instance().launch("HttpCoroutineAdapter::genericDelCoro", + boost::bind(&HttpCoroutineAdapter::trivialDelCoro, url, policyId, success, failure)); +} + +/*static*/ +void HttpCoroutineAdapter::trivialDelCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, + completionCallback_t failure) +{ + LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("genericDelCoro", policyId)); + LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); + LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); + + httpOpts->setWantHeaders(true); + + LL_INFOS("HttpCoroutineAdapter", "genericDelCoro") << "Generic DEL for " << url << LL_ENDL; + + LLSD result = httpAdapter->deleteAndSuspend(httpRequest, url, httpOpts); + + LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + + if (!status) + { + if (failure) + { + failure(httpResults); + } + } + else + { + if (success) + { + success(result); + } + } +} + + } // end namespace LLCoreHttpUtil diff --git a/indra/llmessage/llcorehttputil.h b/indra/llmessage/llcorehttputil.h index 430dc417ac..6d0d68cf24 100644 --- a/indra/llmessage/llcorehttputil.h +++ b/indra/llmessage/llcorehttputil.h @@ -598,6 +598,9 @@ public: callbackHttpPost(url, LLCore::HttpRequest::DEFAULT_POLICY_ID, postData, success, failure); } + static void callbackHttpDel(const std::string &url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success = NULL, + completionCallback_t failure = NULL); + /// Generic Get and post routines for HTTP via coroutines. /// These static methods do all required setup for the GET or POST operation. /// When the operation completes successfully they will put the success message in the log at INFO level, @@ -659,6 +662,7 @@ private: static void trivialGetCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure); static void trivialPostCoro(std::string url, LLCore::HttpRequest::policy_t policyId, LLSD postData, completionCallback_t success, completionCallback_t failure); + static void trivialDelCoro(std::string url, LLCore::HttpRequest::policy_t policyId, completionCallback_t success, completionCallback_t failure); void checkDefaultHeaders(LLCore::HttpHeaders::ptr_t &headers); diff --git a/indra/llmessage/llgenericstreamingmessage.cpp b/indra/llmessage/llgenericstreamingmessage.cpp new file mode 100644 index 0000000000..8627675c54 --- /dev/null +++ b/indra/llmessage/llgenericstreamingmessage.cpp @@ -0,0 +1,72 @@ +/** + * @file llgenericstreamingmessage.cpp + * @brief Generic Streaming Message helpers. Shared between viewer and simulator. + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "linden_common.h" + +#include "llgenericstreamingmessage.h" + +#include "message.h" + +void LLGenericStreamingMessage::send(LLMessageSystem* msg) +{ +#if 0 // viewer cannot send GenericStreamingMessage + msg->newMessageFast(_PREHASH_GenericStreamingMessage); + + if (mData.size() < 1024 * 7) + { // disable warning about big messages unless we're sending a REALLY big message + msg->tempDisableWarnAboutBigMessage(); + } + else + { + LL_WARNS("Messaging") << "Attempted to send too large GenericStreamingMessage, dropping." << LL_ENDL; + return; + } + + msg->nextBlockFast(_PREHASH_MethodData); + msg->addU16Fast(_PREHASH_Method, mMethod); + msg->nextBlockFast(_PREHASH_DataBlock); + msg->addStringFast(_PREHASH_Data, mData.c_str()); +#endif +} + +void LLGenericStreamingMessage::unpack(LLMessageSystem* msg) +{ + U16* m = (U16*)&mMethod; // squirrely pass enum as U16 by reference + msg->getU16Fast(_PREHASH_MethodData, _PREHASH_Method, *m); + + constexpr int MAX_SIZE = 7 * 1024; + + char buffer[MAX_SIZE]; + + // NOTE: don't use getStringFast to avoid 1200 byte truncation + U32 size = msg->getSizeFast(_PREHASH_DataBlock, _PREHASH_Data); + msg->getBinaryDataFast(_PREHASH_DataBlock, _PREHASH_Data, buffer, size, 0, MAX_SIZE); + + mData.assign(buffer, size); +} + + + diff --git a/indra/llmessage/llgenericstreamingmessage.h b/indra/llmessage/llgenericstreamingmessage.h new file mode 100644 index 0000000000..9ac9719ea1 --- /dev/null +++ b/indra/llmessage/llgenericstreamingmessage.h @@ -0,0 +1,50 @@ +/** + * @file llgenericstreamingmessage.h + * @brief Generic Streaming Message helpers. Shared between viewer and simulator. + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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$ + */ + +#pragma once + +#include +#include "stdtypes.h" + +class LLMessageSystem; + +class LLGenericStreamingMessage +{ +public: + enum Method : U16 + { + METHOD_GLTF_MATERIAL_OVERRIDE = 0x4175, + METHOD_UNKNOWN = 0xFFFF, + }; + + void send(LLMessageSystem* msg); + void unpack(LLMessageSystem* msg); + + Method mMethod = METHOD_UNKNOWN; + std::string mData; +}; + + diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h index 8548ed51e7..464bf05250 100644 --- a/indra/llmessage/llregionflags.h +++ b/indra/llmessage/llregionflags.h @@ -171,8 +171,9 @@ const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1U << 9; const U32 ESTATE_ACCESS_NO_REPLY = 1U << 10; const U32 ESTATE_ACCESS_FAILED_BAN_ESTATE_MANAGER = 1U << 11; -const S32 ESTATE_MAX_MANAGERS = 15; -const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access, banned +const S32 ESTATE_MAX_MANAGERS = 20; +const S32 ESTATE_MAX_ACCESS_IDS = 500; // max for access +const S32 ESTATE_MAX_BANNED_IDS = 750; // max for banned const S32 ESTATE_MAX_GROUP_IDS = (S32) ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET; // 'Sim Wide Delete' flags diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 19146c64f4..31acc65642 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -3402,6 +3402,7 @@ typedef std::map BuilderMap; void LLMessageSystem::newMessageFast(const char *name) { + //LL_DEBUGS("Messaging") << "creating new message: " << name << LL_ENDL; LLMessageConfig::Flavor message_flavor = LLMessageConfig::getMessageFlavor(name); LLMessageConfig::Flavor server_flavor = diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 57ea954054..4dccacb889 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -1367,6 +1367,7 @@ char const* const _PREHASH_MuteType = LLMessageStringTable::getInstance()->getSt char const* const _PREHASH_IMViaEMail = LLMessageStringTable::getInstance()->getString("IMViaEMail"); char const* const _PREHASH_RentPrice = LLMessageStringTable::getInstance()->getString("RentPrice"); char const* const _PREHASH_GenericMessage = LLMessageStringTable::getInstance()->getString("GenericMessage"); +char const* const _PREHASH_GenericStreamingMessage = LLMessageStringTable::getInstance()->getString("GenericStreamingMessage"); char const* const _PREHASH_ChildAgentAlive = LLMessageStringTable::getInstance()->getString("ChildAgentAlive"); char const* const _PREHASH_AssetType = LLMessageStringTable::getInstance()->getString("AssetType"); char const* const _PREHASH_SpawnPointBlock = LLMessageStringTable::getInstance()->getString("SpawnPointBlock"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 572dadd408..a393bbabb2 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -1368,6 +1368,7 @@ extern char const* const _PREHASH_MuteType; extern char const* const _PREHASH_IMViaEMail; extern char const* const _PREHASH_RentPrice; extern char const* const _PREHASH_GenericMessage; +extern char const* const _PREHASH_GenericStreamingMessage; extern char const* const _PREHASH_ChildAgentAlive; extern char const* const _PREHASH_AssetType; extern char const* const _PREHASH_SpawnPointBlock; diff --git a/indra/llmessage/tests/test_llsdmessage_peer.py b/indra/llmessage/tests/test_llsdmessage_peer.py index 5ba0749e31..8e9b6c09e7 100755 --- a/indra/llmessage/tests/test_llsdmessage_peer.py +++ b/indra/llmessage/tests/test_llsdmessage_peer.py @@ -33,8 +33,8 @@ import os import sys from http.server import HTTPServer, BaseHTTPRequestHandler -from llbase.fastest_elementtree import parse as xml_parse -from llbase import llsd +from llsd.fastest_elementtree import parse as xml_parse +import llsd from testrunner import freeport, run, debug, VERBOSE import time diff --git a/indra/llprimitive/CMakeLists.txt b/indra/llprimitive/CMakeLists.txt index 76d261ab3e..2bd1edaacc 100644 --- a/indra/llprimitive/CMakeLists.txt +++ b/indra/llprimitive/CMakeLists.txt @@ -34,6 +34,7 @@ set(llprimitive_HEADER_FILES lldaeloader.h llgltfloader.h llgltfmaterial.h + llgltfmaterial_templates.h legacy_object_types.h llmaterial.h llmaterialid.h diff --git a/indra/llprimitive/llgltfmaterial.cpp b/indra/llprimitive/llgltfmaterial.cpp index c16803d39d..f42c11ee21 100644 --- a/indra/llprimitive/llgltfmaterial.cpp +++ b/indra/llprimitive/llgltfmaterial.cpp @@ -24,24 +24,28 @@ * $/LicenseInfo$ */ + #include "linden_common.h" #include "llgltfmaterial.h" +#include "llsdserialize.h" + // NOTE -- this should be the one and only place tiny_gltf.h is included #include "tinygltf/tiny_gltf.h" +#include "llgltfmaterial_templates.h" const char* const LLGLTFMaterial::ASSET_VERSION = "1.1"; const char* const LLGLTFMaterial::ASSET_TYPE = "GLTF 2.0"; const std::array LLGLTFMaterial::ACCEPTED_ASSET_VERSIONS = { "1.0", "1.1" }; -const char* const GLTF_FILE_EXTENSION_TRANSFORM = "KHR_texture_transform"; -const char* const GLTF_FILE_EXTENSION_TRANSFORM_SCALE = "scale"; -const char* const GLTF_FILE_EXTENSION_TRANSFORM_OFFSET = "offset"; -const char* const GLTF_FILE_EXTENSION_TRANSFORM_ROTATION = "rotation"; +const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM = "KHR_texture_transform"; +const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_SCALE = "scale"; +const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_OFFSET = "offset"; +const char* const LLGLTFMaterial::GLTF_FILE_EXTENSION_TRANSFORM_ROTATION = "rotation"; // special UUID that indicates a null UUID in override data -static const LLUUID GLTF_OVERRIDE_NULL_UUID = LLUUID("ffffffff-ffff-ffff-ffff-ffffffffffff"); +const LLUUID LLGLTFMaterial::GLTF_OVERRIDE_NULL_UUID = LLUUID("ffffffff-ffff-ffff-ffff-ffffffffffff"); void LLGLTFMaterial::TextureTransform::getPacked(F32 (&packed)[8]) const { @@ -67,14 +71,14 @@ LLGLTFMaterial::LLGLTFMaterial(const LLGLTFMaterial& rhs) LLGLTFMaterial& LLGLTFMaterial::operator=(const LLGLTFMaterial& rhs) { - //have to do a manual operator= because of LLRefCount + //have to do a manual operator= because of LLRefCount mTextureId = rhs.mTextureId; mTextureTransform = rhs.mTextureTransform; mBaseColor = rhs.mBaseColor; mEmissiveColor = rhs.mEmissiveColor; - + mMetallicFactor = rhs.mMetallicFactor; mRoughnessFactor = rhs.mRoughnessFactor; mAlphaCutoff = rhs.mAlphaCutoff; @@ -96,7 +100,7 @@ bool LLGLTFMaterial::operator==(const LLGLTFMaterial& rhs) const mBaseColor == rhs.mBaseColor && mEmissiveColor == rhs.mEmissiveColor && - + mMetallicFactor == rhs.mMetallicFactor && mRoughnessFactor == rhs.mRoughnessFactor && mAlphaCutoff == rhs.mAlphaCutoff && @@ -121,6 +125,7 @@ bool LLGLTFMaterial::fromJSON(const std::string& json, std::string& warn_msg, st return true; } + return false; } @@ -189,7 +194,8 @@ void LLGLTFMaterial::setFromModel(const tinygltf::Model& model, S32 mat_index) } } -LLVector2 vec2_from_json(const tinygltf::Value::Object& object, const char* key, const LLVector2& default_value) +// static +LLVector2 LLGLTFMaterial::vec2FromJson(const tinygltf::Value::Object& object, const char* key, const LLVector2& default_value) { const auto it = object.find(key); if (it == object.end()) @@ -214,7 +220,8 @@ LLVector2 vec2_from_json(const tinygltf::Value::Object& object, const char* key, return value; } -F32 float_from_json(const tinygltf::Value::Object& object, const char* key, const F32 default_value) +// static +F32 LLGLTFMaterial::floatFromJson(const tinygltf::Value::Object& object, const char* key, const F32 default_value) { const auto it = object.find(key); if (it == object.end()) @@ -229,52 +236,6 @@ F32 float_from_json(const tinygltf::Value::Object& object, const char* key, cons return (F32)real_json.GetNumberAsDouble(); } -template -std::string gltf_get_texture_image(const tinygltf::Model& model, const T& texture_info) -{ - const S32 texture_idx = texture_info.index; - if (texture_idx < 0 || texture_idx >= model.textures.size()) - { - return ""; - } - const tinygltf::Texture& texture = model.textures[texture_idx]; - - // Ignore texture.sampler for now - - const S32 image_idx = texture.source; - if (image_idx < 0 || image_idx >= model.images.size()) - { - return ""; - } - const tinygltf::Image& image = model.images[image_idx]; - - return image.uri; -} - -// *NOTE: Use template here as workaround for the different similar texture info classes -template -void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id) -{ - LL_PROFILE_ZONE_SCOPED; - const std::string uri = gltf_get_texture_image(model, texture_info); - mTextureId[texture_info_id].set(uri); - - const tinygltf::Value::Object& extensions_object = texture_info.extensions; - const auto transform_it = extensions_object.find(GLTF_FILE_EXTENSION_TRANSFORM); - if (transform_it != extensions_object.end()) - { - const tinygltf::Value& transform_json = std::get<1>(*transform_it); - if (transform_json.IsObject()) - { - const tinygltf::Value::Object& transform_object = transform_json.Get(); - TextureTransform& transform = mTextureTransform[texture_info_id]; - transform.mOffset = vec2_from_json(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_OFFSET, getDefaultTextureOffset()); - transform.mScale = vec2_from_json(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_SCALE, getDefaultTextureScale()); - transform.mRotation = float_from_json(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_ROTATION, getDefaultTextureRotation()); - } - } -} - void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const { LL_PROFILE_ZONE_SCOPED; @@ -301,7 +262,7 @@ void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const material_out.alphaMode = getAlphaMode(); material_out.alphaCutoff = mAlphaCutoff; - + mBaseColor.write(material_out.pbrMetallicRoughness.baseColorFactor); if (mEmissiveColor != LLGLTFMaterial::getDefaultEmissiveColor()) @@ -319,7 +280,7 @@ void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const tinygltf::Value::Object extras; bool write_extras = false; if (mOverrideAlphaMode && mAlphaMode == getDefaultAlphaMode()) - { + { extras["override_alpha_mode"] = tinygltf::Value(mOverrideAlphaMode); write_extras = true; } @@ -338,57 +299,6 @@ void LLGLTFMaterial::writeToModel(tinygltf::Model& model, S32 mat_index) const model.asset.version = "2.0"; } -template -void gltf_allocate_texture_image(tinygltf::Model& model, T& texture_info, const std::string& uri) -{ - const S32 image_idx = model.images.size(); - model.images.emplace_back(); - model.images[image_idx].uri = uri; - - // The texture, not to be confused with the texture info - const S32 texture_idx = model.textures.size(); - model.textures.emplace_back(); - tinygltf::Texture& texture = model.textures[texture_idx]; - texture.source = image_idx; - - texture_info.index = texture_idx; -} - -template -void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write) const -{ - LL_PROFILE_ZONE_SCOPED; - const LLUUID& texture_id = mTextureId[texture_info_id]; - const TextureTransform& transform = mTextureTransform[texture_info_id]; - const bool is_blank_transform = transform == sDefault.mTextureTransform[0]; - // Check if this material matches all the fallback values, and if so, then - // skip including it to reduce material size - if (!force_write && texture_id.isNull() && is_blank_transform) - { - return; - } - - // tinygltf will discard this texture info if there is no valid texture, - // causing potential loss of information for overrides, so ensure one is - // defined. -Cosmic,2023-01-30 - gltf_allocate_texture_image(model, texture_info, texture_id.asString()); - - if (!is_blank_transform) - { - tinygltf::Value::Object transform_map; - transform_map[GLTF_FILE_EXTENSION_TRANSFORM_OFFSET] = tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(transform.mOffset.mV[VX]), - tinygltf::Value(transform.mOffset.mV[VY]) - })); - transform_map[GLTF_FILE_EXTENSION_TRANSFORM_SCALE] = tinygltf::Value(tinygltf::Value::Array({ - tinygltf::Value(transform.mScale.mV[VX]), - tinygltf::Value(transform.mScale.mV[VY]) - })); - transform_map[GLTF_FILE_EXTENSION_TRANSFORM_ROTATION] = tinygltf::Value(transform.mRotation); - texture_info.extensions[GLTF_FILE_EXTENSION_TRANSFORM] = tinygltf::Value(transform_map); - } -} - void LLGLTFMaterial::sanitizeAssetMaterial() { mTextureTransform = sDefault.mTextureTransform; @@ -402,19 +312,19 @@ bool LLGLTFMaterial::setBaseMaterial() return *this != old_override; } -bool LLGLTFMaterial::isClearedForBaseMaterial() -{ - LLGLTFMaterial cleared_override = sDefault; - cleared_override.setBaseMaterial(*this); - return *this == cleared_override; -} - // For material overrides only. Copies transforms from the old override. void LLGLTFMaterial::setBaseMaterial(const LLGLTFMaterial& old_override_mat) { mTextureTransform = old_override_mat.mTextureTransform; } +bool LLGLTFMaterial::isClearedForBaseMaterial() const +{ + LLGLTFMaterial cleared_override = sDefault; + cleared_override.setBaseMaterial(*this); + return *this == cleared_override; +} + // static void LLGLTFMaterial::hackOverrideUUID(LLUUID& id) @@ -515,7 +425,7 @@ void LLGLTFMaterial::setAlphaMode(const std::string& mode, bool for_override) { m = ALPHA_MODE_BLEND; } - + setAlphaMode(m, for_override); } @@ -693,6 +603,159 @@ void LLGLTFMaterial::applyOverride(const LLGLTFMaterial& override_mat) } } +void LLGLTFMaterial::getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data) +{ + LL_PROFILE_ZONE_SCOPED; + llassert(data.isUndefined()); + + // make every effort to shave bytes here + + for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) + { + LLUUID& texture_id = mTextureId[i]; + const LLUUID& override_texture_id = override_mat.mTextureId[i]; + if (override_texture_id.notNull() && override_texture_id != texture_id) + { + data["tex"][i] = LLSD::UUID(override_texture_id); + } + } + + if (override_mat.mBaseColor != getDefaultBaseColor()) + { + data["bc"] = override_mat.mBaseColor.getValue(); + } + + if (override_mat.mEmissiveColor != getDefaultEmissiveColor()) + { + data["ec"] = override_mat.mEmissiveColor.getValue(); + } + + if (override_mat.mMetallicFactor != getDefaultMetallicFactor()) + { + data["mf"] = override_mat.mMetallicFactor; + } + + if (override_mat.mRoughnessFactor != getDefaultRoughnessFactor()) + { + data["rf"] = override_mat.mRoughnessFactor; + } + + if (override_mat.mAlphaMode != getDefaultAlphaMode() || override_mat.mOverrideAlphaMode) + { + data["am"] = override_mat.mAlphaMode; + } + + if (override_mat.mAlphaCutoff != getDefaultAlphaCutoff()) + { + data["ac"] = override_mat.mAlphaCutoff; + } + + if (override_mat.mDoubleSided != getDefaultDoubleSided() || override_mat.mOverrideDoubleSided) + { + data["ds"] = override_mat.mDoubleSided; + } + + for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) + { + if (override_mat.mTextureTransform[i].mOffset != getDefaultTextureOffset()) + { + data["ti"][i]["o"] = override_mat.mTextureTransform[i].mOffset.getValue(); + } + + if (override_mat.mTextureTransform[i].mScale != getDefaultTextureScale()) + { + data["ti"][i]["s"] = override_mat.mTextureTransform[i].mScale.getValue(); + } + + if (override_mat.mTextureTransform[i].mRotation != getDefaultTextureRotation()) + { + data["ti"][i]["r"] = override_mat.mTextureTransform[i].mRotation; + } + } +} + + +void LLGLTFMaterial::applyOverrideLLSD(const LLSD& data) +{ + const LLSD& tex = data["tex"]; + + if (tex.isArray()) + { + for (int i = 0; i < tex.size(); ++i) + { + mTextureId[i] = tex[i].asUUID(); + } + } + + const LLSD& bc = data["bc"]; + if (bc.isDefined()) + { + mBaseColor.setValue(bc); + } + + const LLSD& ec = data["ec"]; + if (ec.isDefined()) + { + mEmissiveColor.setValue(ec); + } + + const LLSD& mf = data["mf"]; + if (mf.isReal()) + { + mMetallicFactor = mf.asReal(); + } + + const LLSD& rf = data["rf"]; + if (rf.isReal()) + { + mRoughnessFactor = rf.asReal(); + } + + const LLSD& am = data["am"]; + if (am.isInteger()) + { + mAlphaMode = (AlphaMode) am.asInteger(); + } + + const LLSD& ac = data["ac"]; + if (ac.isReal()) + { + mAlphaCutoff = ac.asReal(); + } + + const LLSD& ds = data["ds"]; + if (ds.isBoolean()) + { + mDoubleSided = ds.asBoolean(); + mOverrideDoubleSided = true; + } + + const LLSD& ti = data["ti"]; + if (ti.isArray()) + { + for (int i = 0; i < GLTF_TEXTURE_INFO_COUNT; ++i) + { + const LLSD& o = ti[i]["o"]; + if (o.isDefined()) + { + mTextureTransform[i].mOffset.setValue(o); + } + + const LLSD& s = ti[i]["s"]; + if (s.isDefined()) + { + mTextureTransform[i].mScale.setValue(s); + } + + const LLSD& r = ti[i]["r"]; + if (r.isReal()) + { + mTextureTransform[i].mRotation = r.asReal(); + } + } + } +} + LLUUID LLGLTFMaterial::getHash() const { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; diff --git a/indra/llprimitive/llgltfmaterial.h b/indra/llprimitive/llgltfmaterial.h index ad7784f6d1..a078a530a4 100644 --- a/indra/llprimitive/llgltfmaterial.h +++ b/indra/llprimitive/llgltfmaterial.h @@ -35,10 +35,13 @@ #include "hbxxh.h" #include +#include namespace tinygltf { class Model; + struct TextureInfo; + class Value; } class LLTextureEntry; @@ -52,6 +55,9 @@ public: static const char* const ASSET_VERSION; static const char* const ASSET_TYPE; + // Max allowed size of a GLTF material asset or override, when serialized + // as a minified JSON string + static constexpr size_t MAX_ASSET_LENGTH = 2048; static const std::array ACCEPTED_ASSET_VERSIONS; static bool isAcceptedVersion(const std::string& version) { return std::find(ACCEPTED_ASSET_VERSIONS.cbegin(), ACCEPTED_ASSET_VERSIONS.cend(), version) != ACCEPTED_ASSET_VERSIONS.cend(); } @@ -64,6 +70,7 @@ public: void getPacked(F32 (&packed)[8]) const; bool operator==(const TextureTransform& other) const; + bool operator!=(const TextureTransform& other) const { return !(*this == other); } }; enum AlphaMode @@ -96,8 +103,13 @@ public: GLTF_TEXTURE_INFO_COUNT }; - std::array mTextureId; + static const char* const GLTF_FILE_EXTENSION_TRANSFORM; + static const char* const GLTF_FILE_EXTENSION_TRANSFORM_SCALE; + static const char* const GLTF_FILE_EXTENSION_TRANSFORM_OFFSET; + static const char* const GLTF_FILE_EXTENSION_TRANSFORM_ROTATION; + static const LLUUID GLTF_OVERRIDE_NULL_UUID; + std::array mTextureId; std::array mTextureTransform; // NOTE: initialize values to defaults according to the GLTF spec @@ -137,7 +149,7 @@ public: void setAlphaMode(S32 mode, bool for_override = false); void setDoubleSided(bool double_sided, bool for_override = false); - //NOTE: texture offsets only exist in overrides, so "for_override" is not needed + // *NOTE: texture offsets only exist in overrides, so "for_override" is not needed void setTextureOffset(TextureInfo texture_info, const LLVector2& offset); void setTextureScale(TextureInfo texture_info, const LLVector2& scale); @@ -155,7 +167,6 @@ public: static LLVector2 getDefaultTextureScale(); static F32 getDefaultTextureRotation(); - static void hackOverrideUUID(LLUUID& id); static void applyOverrideUUID(LLUUID& dst_id, const LLUUID& override_id); @@ -164,7 +175,7 @@ public: void setAlphaMode(const std::string& mode, bool for_override = false); const char* getAlphaMode() const; - + // set the contents of this LLGLTFMaterial from the given json // returns true if successful // if unsuccessful, the contents of this LLGLTFMaterial should be left unchanged and false is returned @@ -185,6 +196,14 @@ public: void writeToModel(tinygltf::Model& model, S32 mat_index) const; void applyOverride(const LLGLTFMaterial& override_mat); + + // apply the given LLSD override data + void applyOverrideLLSD(const LLSD& data); + + // Get the given override on this LLGLTFMaterial as LLSD + // override_mat -- the override source data + // data -- output LLSD object (should be passed in empty) + void getOverrideLLSD(const LLGLTFMaterial& override_mat, LLSD& data); // For base materials only (i.e. assets). Clears transforms to // default since they're not supported in assets yet. @@ -193,21 +212,29 @@ public: // For material overrides only. Clears most properties to // default/fallthrough, but preserves the transforms. bool setBaseMaterial(); + void setBaseMaterial(const LLGLTFMaterial& old_override_mat); // True if setBaseMaterial() was just called - bool isClearedForBaseMaterial(); + bool isClearedForBaseMaterial() const; // For local materials, they have to keep track of where // they are assigned to for full updates virtual void addTextureEntry(LLTextureEntry* te) {}; virtual void removeTextureEntry(LLTextureEntry* te) {}; -private: +protected: + static LLVector2 vec2FromJson(const std::map& object, const char* key, const LLVector2& default_value); + static F32 floatFromJson(const std::map& object, const char* key, const F32 default_value); + + template + static void allocateTextureImage(tinygltf::Model& model, T& texture_info, const std::string& uri); + template void setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id); + template + static void setFromTexture(const tinygltf::Model& model, const T& texture_info, LLUUID& texture_id, TextureTransform& transform); template void writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write = false) const; - - void setBaseMaterial(const LLGLTFMaterial& old_override_mat); + template + static void writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write = false); }; - diff --git a/indra/llprimitive/llgltfmaterial_templates.h b/indra/llprimitive/llgltfmaterial_templates.h new file mode 100644 index 0000000000..f607dfe967 --- /dev/null +++ b/indra/llprimitive/llgltfmaterial_templates.h @@ -0,0 +1,142 @@ +/** + * @file llgltfmaterial_templates.h + * @brief Material template definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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$ + */ + +#pragma once + +#include "llgltfmaterial.h" + +// Use templates here as workaround for the different similar texture info classes in tinygltf +// Includer must first include tiny_gltf.h with the desired flags + +template +std::string gltf_get_texture_image(const tinygltf::Model& model, const T& texture_info) +{ + const S32 texture_idx = texture_info.index; + if (texture_idx < 0 || texture_idx >= model.textures.size()) + { + return ""; + } + const tinygltf::Texture& texture = model.textures[texture_idx]; + + // Ignore texture.sampler for now + + const S32 image_idx = texture.source; + if (image_idx < 0 || image_idx >= model.images.size()) + { + return ""; + } + const tinygltf::Image& image = model.images[image_idx]; + + return image.uri; +} + +template +void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, TextureInfo texture_info_id) +{ + setFromTexture(model, texture_info, mTextureId[texture_info_id], mTextureTransform[texture_info_id]); + const std::string uri = gltf_get_texture_image(model, texture_info); +} + +// static +template +void LLGLTFMaterial::setFromTexture(const tinygltf::Model& model, const T& texture_info, LLUUID& texture_id, TextureTransform& transform) +{ + LL_PROFILE_ZONE_SCOPED; + const std::string uri = gltf_get_texture_image(model, texture_info); + texture_id.set(uri); + + const tinygltf::Value::Object& extensions_object = texture_info.extensions; + const auto transform_it = extensions_object.find(GLTF_FILE_EXTENSION_TRANSFORM); + if (transform_it != extensions_object.end()) + { + const tinygltf::Value& transform_json = std::get<1>(*transform_it); + if (transform_json.IsObject()) + { + const tinygltf::Value::Object& transform_object = transform_json.Get(); + transform.mOffset = vec2FromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_OFFSET, getDefaultTextureOffset()); + transform.mScale = vec2FromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_SCALE, getDefaultTextureScale()); + transform.mRotation = floatFromJson(transform_object, GLTF_FILE_EXTENSION_TRANSFORM_ROTATION, getDefaultTextureRotation()); + } + } +} + +// static +template +void LLGLTFMaterial::allocateTextureImage(tinygltf::Model& model, T& texture_info, const std::string& uri) +{ + const S32 image_idx = model.images.size(); + model.images.emplace_back(); + model.images[image_idx].uri = uri; + + // The texture, not to be confused with the texture info + const S32 texture_idx = model.textures.size(); + model.textures.emplace_back(); + tinygltf::Texture& texture = model.textures[texture_idx]; + texture.source = image_idx; + + texture_info.index = texture_idx; +} + +// static +template +void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, TextureInfo texture_info_id, bool force_write) const +{ + writeToTexture(model, texture_info, mTextureId[texture_info_id], mTextureTransform[texture_info_id], force_write); +} + +// static +template +void LLGLTFMaterial::writeToTexture(tinygltf::Model& model, T& texture_info, const LLUUID& texture_id, const TextureTransform& transform, bool force_write) +{ + LL_PROFILE_ZONE_SCOPED; + const bool is_blank_transform = transform == sDefault.mTextureTransform[0]; + // Check if this material matches all the fallback values, and if so, then + // skip including it to reduce material size + if (!force_write && texture_id.isNull() && is_blank_transform) + { + return; + } + + // tinygltf will discard this texture info if there is no valid texture, + // causing potential loss of information for overrides, so ensure one is + // defined. -Cosmic,2023-01-30 + allocateTextureImage(model, texture_info, texture_id.asString()); + + if (!is_blank_transform) + { + tinygltf::Value::Object transform_map; + transform_map[GLTF_FILE_EXTENSION_TRANSFORM_OFFSET] = tinygltf::Value(tinygltf::Value::Array({ + tinygltf::Value(transform.mOffset.mV[VX]), + tinygltf::Value(transform.mOffset.mV[VY]) + })); + transform_map[GLTF_FILE_EXTENSION_TRANSFORM_SCALE] = tinygltf::Value(tinygltf::Value::Array({ + tinygltf::Value(transform.mScale.mV[VX]), + tinygltf::Value(transform.mScale.mV[VY]) + })); + transform_map[GLTF_FILE_EXTENSION_TRANSFORM_ROTATION] = tinygltf::Value(transform.mRotation); + texture_info.extensions[GLTF_FILE_EXTENSION_TRANSFORM] = tinygltf::Value(transform_map); + } +} diff --git a/indra/llprimitive/llmaterial.cpp b/indra/llprimitive/llmaterial.cpp index 37525e4a3d..0d146de949 100644 --- a/indra/llprimitive/llmaterial.cpp +++ b/indra/llprimitive/llmaterial.cpp @@ -332,17 +332,6 @@ void LLMaterial::setAlphaMaskCutoff(U8 cutoff) mAlphaMaskCutoff = cutoff; } -LLUUID LLMaterial::getMaterialID() const -{ - // TODO - not null - return LLUUID::null; -} - -void LLMaterial::setMaterialID(const LLUUID &material_id) -{ - // TODO - set -} - LLSD LLMaterial::asLLSD() const { LLSD material_data; @@ -438,18 +427,18 @@ bool LLMaterial::operator != (const LLMaterial& rhs) const } -U32 LLMaterial::getShaderMask(U32 alpha_mode) +U32 LLMaterial::getShaderMask(U32 alpha_mode, BOOL is_alpha) { //NEVER incorporate this value into the message system -- this function will vary depending on viewer implementation - U32 ret = 0; - //two least significant bits are "diffuse alpha mode" - if (alpha_mode != DIFFUSE_ALPHA_MODE_DEFAULT) + //two least significant bits are "diffuse alpha mode" + U32 ret = alpha_mode; + if (ret == DIFFUSE_ALPHA_MODE_DEFAULT) { - ret = alpha_mode; - } - else - { - ret = getDiffuseAlphaMode(); + ret = getDiffuseAlphaMode(); + if (ret == DIFFUSE_ALPHA_MODE_BLEND && !is_alpha) + { + ret = DIFFUSE_ALPHA_MODE_NONE; + } } llassert(ret < SHADER_COUNT); diff --git a/indra/llprimitive/llmaterial.h b/indra/llprimitive/llmaterial.h index d715671ae1..81f41ddc51 100644 --- a/indra/llprimitive/llmaterial.h +++ b/indra/llprimitive/llmaterial.h @@ -115,8 +115,6 @@ public: void setDiffuseAlphaMode(U8 alpha_mode); U8 getAlphaMaskCutoff() const; void setAlphaMaskCutoff(U8 cutoff); - LLUUID getMaterialID() const; - void setMaterialID(LLUUID const & material_id); bool isNull() const; static const LLMaterial null; @@ -124,7 +122,7 @@ public: bool operator == (const LLMaterial& rhs) const; bool operator != (const LLMaterial& rhs) const; - U32 getShaderMask(U32 alpha_mode = DIFFUSE_ALPHA_MODE_DEFAULT); + U32 getShaderMask(U32 alpha_mode, BOOL is_alpha); LLUUID getHash() const; protected: diff --git a/indra/llprimitive/llmaterialid.h b/indra/llprimitive/llmaterialid.h index e6165dfc91..5eb463b0fd 100644 --- a/indra/llprimitive/llmaterialid.h +++ b/indra/llprimitive/llmaterialid.h @@ -67,6 +67,14 @@ public: static const LLMaterialID null; + // Returns a 64 bits digest of the material Id, by XORing its two 64 bits + // long words. HB + inline U64 getDigest64() const + { + U64* tmp = (U64*)mID; + return tmp[0] ^ tmp[1]; + } + private: void parseFromBinary(const LLSD::Binary& pMaterialID); void copyFromOtherMaterialID(const LLMaterialID& pOtherMaterialID); @@ -75,5 +83,23 @@ private: U8 mID[MATERIAL_ID_SIZE]; } ; +// std::hash implementation for LLMaterialID +namespace std +{ + template<> struct hash + { + inline size_t operator()(const LLMaterialID& id) const noexcept + { + return (size_t)id.getDigest64(); + } + }; +} + +// For use with boost containers. +inline size_t hash_value(const LLMaterialID& id) noexcept +{ + return (size_t)id.getDigest64(); +} + #endif // LL_LLMATERIALID_H diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp index 5dfce4ae16..350d84ae6c 100644 --- a/indra/llprimitive/llprimitive.cpp +++ b/indra/llprimitive/llprimitive.cpp @@ -85,6 +85,8 @@ const F32 LIGHT_MAX_CUTOFF = 180.f; const F32 REFLECTION_PROBE_MIN_AMBIANCE = 0.f; const F32 REFLECTION_PROBE_MAX_AMBIANCE = 100.f; const F32 REFLECTION_PROBE_DEFAULT_AMBIANCE = 0.f; +// *NOTE: Clip distances are clamped in LLCamera::setNear. The max clip +// distance is currently limited by the skybox const F32 REFLECTION_PROBE_MIN_CLIP_DISTANCE = 0.f; const F32 REFLECTION_PROBE_MAX_CLIP_DISTANCE = 1024.f; const F32 REFLECTION_PROBE_DEFAULT_CLIP_DISTANCE = 0.f; diff --git a/indra/llrender/CMakeLists.txt b/indra/llrender/CMakeLists.txt index 9454943dc5..a95daed6cd 100644 --- a/indra/llrender/CMakeLists.txt +++ b/indra/llrender/CMakeLists.txt @@ -29,6 +29,7 @@ set(llrender_SOURCE_FILES llrendertarget.cpp llshadermgr.cpp lltexture.cpp + lltexturemanagerbridge.cpp lluiimage.cpp llvertexbuffer.cpp llglcommonfunc.cpp @@ -58,6 +59,7 @@ set(llrender_HEADER_FILES llrendersphere.h llshadermgr.h lltexture.h + lltexturemanagerbridge.h lluiimage.h lluiimage.inl llvertexbuffer.h diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 1bf061bc8d..15fddbc99f 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -494,7 +494,7 @@ F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max return getWidthF32(wtext.c_str(), begin_offset, max_chars); } -F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars) const +F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars, bool no_padding) const { const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; @@ -517,12 +517,15 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars F32 advance = mFontFreetype->getXAdvance(fgi); - // for the last character we want to measure the greater of its width and xadvance values - // so keep track of the difference between these values for the each character we measure - // so we can fix things up at the end - width_padding = llmax( 0.f, // always use positive padding amount - width_padding - advance, // previous padding left over after advance of current character - (F32)(fgi->mWidth + fgi->mXBearing) - advance); // difference between width of this character and advance to next character + if (!no_padding) + { + // for the last character we want to measure the greater of its width and xadvance values + // so keep track of the difference between these values for the each character we measure + // so we can fix things up at the end + width_padding = llmax(0.f, // always use positive padding amount + width_padding - advance, // previous padding left over after advance of current character + (F32)(fgi->mWidth + fgi->mXBearing) - advance); // difference between width of this character and advance to next character + } cur_x += advance; llwchar next_char = wchars[i+1]; @@ -539,8 +542,11 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars cur_x = (F32)ll_round(cur_x); } - // add in extra pixels for last character's width past its xadvance - cur_x += width_padding; + if (!no_padding) + { + // add in extra pixels for last character's width past its xadvance + cur_x += width_padding; + } return cur_x / sScaleX; } @@ -1017,6 +1023,20 @@ LLFontGL* LLFontGL::getFontSansSerifSmall() return fontp; } +//static +LLFontGL* LLFontGL::getFontSansSerifSmallBold() +{ + static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",BOLD)); + return fontp; +} + +//static +LLFontGL* LLFontGL::getFontSansSerifSmallItalic() +{ + static LLFontGL* fontp = getFont(LLFontDescriptor("SansSerif","Small",ITALIC)); + return fontp; +} + //static LLFontGL* LLFontGL::getFontSansSerif() { diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 3b58a37d33..9bbf9ce0c4 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -138,7 +138,7 @@ public: F32 getWidthF32(const std::string& utf8text) const; F32 getWidthF32(const llwchar* wchars) const; F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const; - F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars) const; + F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars, bool no_padding = false) const; // The following are called often, frequently with large buffers, so do not use a string interface @@ -189,6 +189,8 @@ public: static LLFontGL* getFontMonospace(); static LLFontGL* getFontSansSerifSmall(); + static LLFontGL* getFontSansSerifSmallBold(); + static LLFontGL* getFontSansSerifSmallItalic(); static LLFontGL* getFontSansSerif(); static LLFontGL* getFontSansSerifBig(); static LLFontGL* getFontSansSerifHuge(); diff --git a/indra/llrender/llgl.cpp b/indra/llrender/llgl.cpp index ae408dae4f..4c7c8e6f5c 100644 --- a/indra/llrender/llgl.cpp +++ b/indra/llrender/llgl.cpp @@ -2890,9 +2890,7 @@ void LLGLSyncFence::wait() if (mSync) { while (glClientWaitSync(mSync, 0, FENCE_WAIT_TIME_NANOSECONDS) == GL_TIMEOUT_EXPIRED) - { //track the number of times we've waited here - static S32 waits = 0; - waits++; + { } } } diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index 3e3a3095e3..c6fd824c4e 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -58,7 +58,7 @@ U32 wpo2(U32 i); // texture memory accounting (for OS X) static LLMutex sTexMemMutex; -static std::unordered_map sTextureAllocs; +static std::unordered_map sTextureAllocs; static U64 sTextureBytes = 0; // track a texture alloc on the currently bound texture. @@ -67,7 +67,7 @@ static void alloc_tex_image(U32 width, U32 height, U32 pixformat) { U32 texUnit = gGL.getCurrentTexUnitIndex(); U32 texName = gGL.getTexUnit(texUnit)->getCurrTexture(); - S32 size = LLImageGL::dataFormatBytes(pixformat, width, height); + U64 size = LLImageGL::dataFormatBytes(pixformat, width, height); llassert(size >= 0); @@ -296,7 +296,7 @@ S32 LLImageGL::dataFormatBits(S32 dataformat) } //static -S32 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height) +S64 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height) { switch (dataformat) { @@ -312,8 +312,8 @@ S32 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height) default: break; } - S32 bytes ((width*height*dataFormatBits(dataformat)+7)>>3); - S32 aligned = (bytes+3)&~3; + S64 bytes (((S64)width * (S64)height * (S64)dataFormatBits(dataformat)+7)>>3); + S64 aligned = (bytes+3)&~3; return aligned; } @@ -518,7 +518,7 @@ void LLImageGL::init(BOOL usemipmaps) // so that it is obvious by visual inspection if we forgot to // init a field. - mTextureMemory = (S32Bytes)0; + mTextureMemory = S64Bytes(0); mLastBindTime = 0.f; mPickMask = NULL; @@ -905,9 +905,16 @@ BOOL LLImageGL::setImage(const U8* data_in, BOOL data_hasmips /* = FALSE */, S32 stop_glerror(); if (prev_mip_data) - delete[] prev_mip_data; + { + if (prev_mip_data != cur_mip_data) + delete[] prev_mip_data; + prev_mip_data = nullptr; + } if (cur_mip_data) + { delete[] cur_mip_data; + cur_mip_data = nullptr; + } mGLTextureCreated = false; return FALSE; @@ -1737,7 +1744,7 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ } - mTextureMemory = (S32Bytes)getMipBytes(mCurrentDiscardLevel); + mTextureMemory = (S64Bytes)getMipBytes(mCurrentDiscardLevel); mTexelsInGLTexture = getWidth() * getHeight(); // mark this as bound at this point, so we don't throw it out immediately @@ -1931,9 +1938,9 @@ void LLImageGL::destroyGLTexture() if (mTexName != 0) { - if(mTextureMemory != S32Bytes(0)) + if(mTextureMemory != S64Bytes(0)) { - mTextureMemory = (S32Bytes)0; + mTextureMemory = (S64Bytes)0; } LLImageGL::deleteTextures(1, &mTexName); @@ -2029,7 +2036,7 @@ S32 LLImageGL::getWidth(S32 discard_level) const return width; } -S32 LLImageGL::getBytes(S32 discard_level) const +S64 LLImageGL::getBytes(S32 discard_level) const { if (discard_level < 0) { @@ -2042,7 +2049,7 @@ S32 LLImageGL::getBytes(S32 discard_level) const return dataFormatBytes(mFormatPrimary, w, h); } -S32 LLImageGL::getMipBytes(S32 discard_level) const +S64 LLImageGL::getMipBytes(S32 discard_level) const { if (discard_level < 0) { @@ -2050,7 +2057,7 @@ S32 LLImageGL::getMipBytes(S32 discard_level) const } S32 w = mWidth>>discard_level; S32 h = mHeight>>discard_level; - S32 res = dataFormatBytes(mFormatPrimary, w, h); + S64 res = dataFormatBytes(mFormatPrimary, w, h); if (mUseMipMaps) { while (w > 1 && h > 1) diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 243aeaea25..a9a6b93cb3 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -65,7 +65,7 @@ public: // Size calculation static S32 dataFormatBits(S32 dataformat); - static S32 dataFormatBytes(S32 dataformat, S32 width, S32 height); + static S64 dataFormatBytes(S32 dataformat, S32 width, S32 height); static S32 dataFormatComponents(S32 dataformat); BOOL updateBindStats() const ; @@ -145,8 +145,8 @@ public: S32 getWidth(S32 discard_level = -1) const; S32 getHeight(S32 discard_level = -1) const; U8 getComponents() const { return mComponents; } - S32 getBytes(S32 discard_level = -1) const; - S32 getMipBytes(S32 discard_level = -1) const; + S64 getBytes(S32 discard_level = -1) const; + S64 getMipBytes(S32 discard_level = -1) const; BOOL getBoundRecently() const; BOOL isJustBound() const; BOOL getHasExplicitFormat() const { return mHasExplicitFormat; } @@ -208,7 +208,7 @@ public: public: // Various GL/Rendering options - S32Bytes mTextureMemory; + S64Bytes mTextureMemory; mutable F32 mLastBindTime; // last time this was bound, by discard level private: diff --git a/indra/llrender/llrender.cpp b/indra/llrender/llrender.cpp index 8430d13093..ee66122774 100644 --- a/indra/llrender/llrender.cpp +++ b/indra/llrender/llrender.cpp @@ -975,8 +975,8 @@ void LLRender::syncLightState() shader->uniform3fv(LLShaderMgr::LIGHT_AMBIENT, 1, mAmbientLightColor.mV); shader->uniform1i(LLShaderMgr::SUN_UP_FACTOR, sun_primary[0] ? 1 : 0); //shader->uniform3fv(LLShaderMgr::AMBIENT, 1, mAmbientLightColor.mV); - shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, diffuse[0].mV); - shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, 1, diffuse_b[0].mV); + //shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, diffuse[0].mV); + //shader->uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, 1, diffuse_b[0].mV); } } diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 91412add44..d1ff85c85b 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1275,6 +1275,8 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("sunlight_color"); mReservedUniforms.push_back("ambient_color"); mReservedUniforms.push_back("sky_hdr_scale"); + mReservedUniforms.push_back("sky_sunlight_scale"); + mReservedUniforms.push_back("sky_ambient_scale"); mReservedUniforms.push_back("blue_horizon"); mReservedUniforms.push_back("blue_density"); mReservedUniforms.push_back("haze_horizon"); @@ -1308,8 +1310,9 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("warmthAmount"); mReservedUniforms.push_back("glowStrength"); mReservedUniforms.push_back("glowDelta"); + mReservedUniforms.push_back("glowNoiseMap"); - llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_DELTA+1); + llassert(mReservedUniforms.size() == LLShaderMgr::GLOW_NOISE_MAP+1); mReservedUniforms.push_back("minimum_alpha"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 151e94093d..5c99a6b42d 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -103,6 +103,8 @@ public: SUNLIGHT_COLOR, // "sunlight_color" AMBIENT, // "ambient_color" SKY_HDR_SCALE, // "sky_hdr_scale" + SKY_SUNLIGHT_SCALE, // "sky_sunlight_scale" + SKY_AMBIENT_SCALE, // "sky_ambient_scale" BLUE_HORIZON, // "blue_horizon" BLUE_DENSITY, // "blue_density" HAZE_HORIZON, // "haze_horizon" @@ -131,6 +133,7 @@ public: GLOW_WARMTH_AMOUNT, // "warmthAmount" GLOW_STRENGTH, // "glowStrength" GLOW_DELTA, // "glowDelta" + GLOW_NOISE_MAP, // "glowNoiseMap" MINIMUM_ALPHA, // "minimum_alpha" EMISSIVE_BRIGHTNESS, // "emissive_brightness" diff --git a/indra/llappearance/lltexturemanagerbridge.cpp b/indra/llrender/lltexturemanagerbridge.cpp similarity index 100% rename from indra/llappearance/lltexturemanagerbridge.cpp rename to indra/llrender/lltexturemanagerbridge.cpp diff --git a/indra/llappearance/lltexturemanagerbridge.h b/indra/llrender/lltexturemanagerbridge.h similarity index 97% rename from indra/llappearance/lltexturemanagerbridge.h rename to indra/llrender/lltexturemanagerbridge.h index 101704b162..f61433ea4d 100644 --- a/indra/llappearance/lltexturemanagerbridge.h +++ b/indra/llrender/lltexturemanagerbridge.h @@ -27,7 +27,6 @@ #ifndef LL_TEXTUREMANAGERBRIDGE_H #define LL_TEXTUREMANAGERBRIDGE_H -#include "llavatarappearancedefines.h" #include "llpointer.h" #include "llgltexture.h" diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index b5dda23828..de27636c33 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -703,7 +703,7 @@ bool LLVertexBuffer::validateRange(U32 start, U32 end, U32 count, U32 indices_of for (U32 i = start; i <= end; ++i) { - if (!v->isFinite3()) + if (!v[i].isFinite3()) { LL_ERRS() << "Non-finite vertex position data detected." << LL_ENDL; } diff --git a/indra/llui/llaccordionctrl.cpp b/indra/llui/llaccordionctrl.cpp index 809d72208f..0a82bed896 100644 --- a/indra/llui/llaccordionctrl.cpp +++ b/indra/llui/llaccordionctrl.cpp @@ -60,7 +60,7 @@ LLAccordionCtrl::LLAccordionCtrl(const Params& params):LLPanel(params) initNoTabsWidget(params.no_matched_tabs_text); mSingleExpansion = params.single_expansion; - if(mFitParent && !mSingleExpansion) + if (mFitParent && !mSingleExpansion) { LL_INFOS() << "fit_parent works best when combined with single_expansion" << LL_ENDL; } @@ -102,14 +102,13 @@ void LLAccordionCtrl::draw() LLPanel::draw(); } - //--------------------------------------------------------------------------------- BOOL LLAccordionCtrl::postBuild() { - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); LLRect scroll_rect; - scroll_rect.setOriginAndSize( + scroll_rect.setOriginAndSize( getRect().getWidth() - scrollbar_size, 1, scrollbar_size, @@ -126,39 +125,42 @@ BOOL LLAccordionCtrl::postBuild() sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); sbparams.change_callback(boost::bind(&LLAccordionCtrl::onScrollPosChangeCallback, this, _1, _2)); - mScrollbar = LLUICtrlFactory::create (sbparams); - LLView::addChild( mScrollbar ); - mScrollbar->setVisible( false ); + mScrollbar = LLUICtrlFactory::create(sbparams); + LLView::addChild(mScrollbar); + mScrollbar->setVisible(FALSE); mScrollbar->setFollowsRight(); mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); //if it was created from xml... std::vector accordion_tabs; - for(child_list_const_iter_t it = getChildList()->begin(); + for (child_list_const_iter_t it = getChildList()->begin(); getChildList()->end() != it; ++it) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(*it); - if(accordion_tab == NULL) + if (accordion_tab == NULL) continue; - if(std::find(mAccordionTabs.begin(),mAccordionTabs.end(),accordion_tab) == mAccordionTabs.end()) + if (std::find(mAccordionTabs.begin(), mAccordionTabs.end(), accordion_tab) == mAccordionTabs.end()) { accordion_tabs.push_back(accordion_tab); } } - for(std::vector::reverse_iterator it = accordion_tabs.rbegin();it!=accordion_tabs.rend();++it) - addCollapsibleCtrl(*it); - - arrange (); - - if(mSingleExpansion) + for (std::vector::reverse_iterator it = accordion_tabs.rbegin(); + it < accordion_tabs.rend(); ++it) { - if(!mAccordionTabs[0]->getDisplayChildren()) + addCollapsibleCtrl(*it); + } + + arrange(); + + if (mSingleExpansion) + { + if (!mAccordionTabs[0]->getDisplayChildren()) mAccordionTabs[0]->setDisplayChildren(true); - for(size_t i=1;igetDisplayChildren()) + if (mAccordionTabs[i]->getDisplayChildren()) mAccordionTabs[i]->setDisplayChildren(false); } } @@ -205,23 +207,22 @@ BOOL LLAccordionCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) //--------------------------------------------------------------------------------- void LLAccordionCtrl::shiftAccordionTabs(S16 panel_num, S32 delta) { - for(size_t i = panel_num; i < mAccordionTabs.size(); i++ ) + for (size_t i = panel_num; i < mAccordionTabs.size(); ++i) { ctrlShiftVertical(mAccordionTabs[i],delta); } } - //--------------------------------------------------------------------------------- void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num) { - if(mSingleExpansion) + if (mSingleExpansion) { - for(size_t i=0;igetDisplayChildren()) + if (mAccordionTabs[i]->getDisplayChildren()) mAccordionTabs[i]->setDisplayChildren(false); } @@ -232,64 +233,63 @@ void LLAccordionCtrl::onCollapseCtrlCloseOpen(S16 panel_num) void LLAccordionCtrl::show_hide_scrollbar(S32 width, S32 height) { calcRecuiredHeight(); - if(getRecuiredHeight() > height ) - showScrollbar(width,height); + if (getRecuiredHeight() > height) + showScrollbar(width, height); else - hideScrollbar(width,height); + hideScrollbar(width, height); } -void LLAccordionCtrl::showScrollbar(S32 width, S32 height) +void LLAccordionCtrl::showScrollbar(S32 width, S32 height) { bool was_visible = mScrollbar->getVisible(); - mScrollbar->setVisible(true); + mScrollbar->setVisible(TRUE); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); ctrlSetLeftTopAndSize(mScrollbar - ,width-scrollbar_size - PARENT_BORDER_MARGIN/2 - ,height-PARENT_BORDER_MARGIN - ,scrollbar_size - ,height-2*PARENT_BORDER_MARGIN); + , width - scrollbar_size - PARENT_BORDER_MARGIN / 2 + , height - PARENT_BORDER_MARGIN + , scrollbar_size + , height - PARENT_BORDER_MARGIN * 2); mScrollbar->setPageSize(height); - mScrollbar->setDocParams(mInnerRect.getHeight(),mScrollbar->getDocPos()); + mScrollbar->setDocParams(mInnerRect.getHeight(), mScrollbar->getDocPos()); - if(was_visible) + if (was_visible) { S32 scroll_pos = llmin(mScrollbar->getDocPos(), getRecuiredHeight() - height - 1); mScrollbar->setDocPos(scroll_pos); } } -void LLAccordionCtrl::hideScrollbar( S32 width, S32 height ) +void LLAccordionCtrl::hideScrollbar(S32 width, S32 height) { - if(mScrollbar->getVisible() == false) + if (mScrollbar->getVisible() == FALSE) return; - mScrollbar->setVisible(false); + mScrollbar->setVisible(FALSE); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); S32 panel_width = width - 2*BORDER_MARGIN; - //reshape all accordeons and shift all draggers - for(size_t i=0;igetRect(); - ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_rect.mTop,panel_width,panel_rect.getHeight()); + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_rect.mTop, panel_width, panel_rect.getHeight()); } mScrollbar->setDocPos(0); - if(mAccordionTabs.size()>0) + if (!mAccordionTabs.empty()) { - S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_top = height - BORDER_MARGIN; // Top coordinate of the first panel S32 diff = panel_top - mAccordionTabs[0]->getRect().mTop; - shiftAccordionTabs(0,diff); + shiftAccordionTabs(0, diff); } } - //--------------------------------------------------------------------------------- S32 LLAccordionCtrl::calcRecuiredHeight() { @@ -305,7 +305,7 @@ S32 LLAccordionCtrl::calcRecuiredHeight() } } - mInnerRect.setLeftTopAndSize(0,rec_height + BORDER_MARGIN*2,getRect().getWidth(),rec_height + BORDER_MARGIN); + mInnerRect.setLeftTopAndSize(0, rec_height + BORDER_MARGIN * 2, getRect().getWidth(), rec_height + BORDER_MARGIN); return mInnerRect.getHeight(); } @@ -313,7 +313,7 @@ S32 LLAccordionCtrl::calcRecuiredHeight() //--------------------------------------------------------------------------------- void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) { - if(!panel) + if (!panel) return; LLRect panel_rect = panel->getRect(); panel_rect.setLeftTopAndSize( left, top, width, height); @@ -321,9 +321,9 @@ void LLAccordionCtrl::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S3 panel->setRect(panel_rect); } -void LLAccordionCtrl::ctrlShiftVertical(LLView* panel,S32 delta) +void LLAccordionCtrl::ctrlShiftVertical(LLView* panel, S32 delta) { - if(!panel) + if (!panel) return; panel->translate(0,delta); } @@ -333,9 +333,9 @@ void LLAccordionCtrl::ctrlShiftVertical(LLView* panel,S32 delta) void LLAccordionCtrl::addCollapsibleCtrl(LLView* view) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(view); - if(!accordion_tab) + if (!accordion_tab) return; - if(std::find(beginChild(), endChild(), accordion_tab) == endChild()) + if (std::find(beginChild(), endChild(), accordion_tab) == endChild()) addChild(accordion_tab); mAccordionTabs.push_back(accordion_tab); @@ -369,7 +369,7 @@ void LLAccordionCtrl::removeCollapsibleCtrl(LLView* view) } } -void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params) +void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params) { LLTextBox::Params tp = tb_params; tp.rect(getLocalRect()); @@ -377,39 +377,39 @@ void LLAccordionCtrl::initNoTabsWidget(const LLTextBox::Params& tb_params) mNoVisibleTabsHelpText = LLUICtrlFactory::create(tp, this); } -void LLAccordionCtrl::updateNoTabsHelpTextVisibility() +void LLAccordionCtrl::updateNoTabsHelpTextVisibility() { bool visible_exists = false; std::vector::const_iterator it = mAccordionTabs.begin(); const std::vector::const_iterator it_end = mAccordionTabs.end(); - for (; it != it_end; ++it) + while (it < it_end) { - if ((*it)->getVisible()) + if ((*(it++))->getVisible()) { visible_exists = true; break; } } - mNoVisibleTabsHelpText->setVisible(!visible_exists); + mNoVisibleTabsHelpText->setVisible(visible_exists ? FALSE : TRUE); } -void LLAccordionCtrl::arrangeSinge() +void LLAccordionCtrl::arrangeSingle() { - S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter - S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel - S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel + S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter + S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_width = getRect().getWidth() - 4; S32 panel_height; S32 collapsed_height = 0; - for(size_t i=0;i(mAccordionTabs[i]); - if(accordion_tab->getVisible() == false) //skip hidden accordion tabs + if (accordion_tab->getVisible() == FALSE) // Skip hidden accordion tabs continue; - if(!accordion_tab->isExpanded() ) + if (!accordion_tab->isExpanded() ) { collapsed_height+=mAccordionTabs[i]->getRect().getHeight(); } @@ -417,28 +417,28 @@ void LLAccordionCtrl::arrangeSinge() S32 expanded_height = getRect().getHeight() - BORDER_MARGIN - collapsed_height; - for(size_t i=0;i(mAccordionTabs[i]); - if(accordion_tab->getVisible() == false) //skip hidden accordion tabs + if (accordion_tab->getVisible() == FALSE) // Skip hidden accordion tabs continue; - if(!accordion_tab->isExpanded() ) + if (!accordion_tab->isExpanded() ) { panel_height = accordion_tab->getRect().getHeight(); } else { - if(mFitParent) + if (mFitParent) { panel_height = expanded_height; } else { - if(accordion_tab->getAccordionView()) + if (accordion_tab->getAccordionView()) { panel_height = accordion_tab->getAccordionView()->getRect().getHeight() + - accordion_tab->getHeaderHeight() + 2*BORDER_MARGIN; + accordion_tab->getHeaderHeight() + BORDER_MARGIN * 2; } else { @@ -451,67 +451,67 @@ void LLAccordionCtrl::arrangeSinge() panel_height = llmax(panel_height, accordion_tab->getHeaderHeight()); ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); - panel_top-=mAccordionTabs[i]->getRect().getHeight(); + panel_top -= mAccordionTabs[i]->getRect().getHeight(); } show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); updateLayout(getRect().getWidth(), getRect().getHeight()); } -void LLAccordionCtrl::arrangeMultiple() +void LLAccordionCtrl::arrangeMultiple() { - S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter - S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel - S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel + S32 panel_left = BORDER_MARGIN; // Margin from left side of Splitter + S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_width = getRect().getWidth() - 4; //Calculate params - for(size_t i = 0; i < mAccordionTabs.size(); i++ ) + for (size_t i = 0; i < mAccordionTabs.size(); i++ ) { LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[i]); - if(accordion_tab->getVisible() == false) //skip hidden accordion tabs + if (accordion_tab->getVisible() == FALSE) // Skip hidden accordion tabs continue; - if(!accordion_tab->isExpanded() ) + if (!accordion_tab->isExpanded() ) { ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, accordion_tab->getRect().getHeight()); - panel_top-=mAccordionTabs[i]->getRect().getHeight(); + panel_top -= mAccordionTabs[i]->getRect().getHeight(); } else { S32 panel_height = accordion_tab->getRect().getHeight(); - if(mFitParent) + if (mFitParent) { - // all expanded tabs will have equal height + // All expanded tabs will have equal height panel_height = calcExpandedTabHeight(i, panel_top); ctrlSetLeftTopAndSize(accordion_tab, panel_left, panel_top, panel_width, panel_height); - // try to make accordion tab fit accordion view height. + // Try to make accordion tab fit accordion view height. // Accordion View should implement getRequiredRect() and provide valid height S32 optimal_height = accordion_tab->getAccordionView()->getRequiredRect().getHeight(); optimal_height += accordion_tab->getHeaderHeight() + 2 * BORDER_MARGIN; - if(optimal_height < panel_height) + if (optimal_height < panel_height) { panel_height = optimal_height; } // minimum tab height is equal to header height - if(mAccordionTabs[i]->getHeaderHeight() > panel_height) + if (mAccordionTabs[i]->getHeaderHeight() > panel_height) { panel_height = mAccordionTabs[i]->getHeaderHeight(); } } ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_left, panel_top, panel_width, panel_height); - panel_top-=panel_height; + panel_top -= panel_height; } } - show_hide_scrollbar(getRect().getWidth(),getRect().getHeight()); + show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); - updateLayout(getRect().getWidth(),getRect().getHeight()); + updateLayout(getRect().getWidth(), getRect().getHeight()); } @@ -519,70 +519,67 @@ void LLAccordionCtrl::arrange() { updateNoTabsHelpTextVisibility(); - if( mAccordionTabs.size() == 0) + if (mAccordionTabs.empty()) { - //We do not arrange if we do not have what should be arranged + // Nothing to arrange return; } - - if(mAccordionTabs.size() == 1) + if (mAccordionTabs.size() == 1) { - S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel - S32 panel_width = getRect().getWidth() - 4; // Top coordinate of the first panel + S32 panel_top = getRect().getHeight() - BORDER_MARGIN; // Top coordinate of the first panel + S32 panel_width = getRect().getWidth() - 4; LLAccordionCtrlTab* accordion_tab = dynamic_cast(mAccordionTabs[0]); LLRect panel_rect = accordion_tab->getRect(); - S32 panel_height = getRect().getHeight() - 2*BORDER_MARGIN; - + S32 panel_height = getRect().getHeight() - BORDER_MARGIN * 2; if (accordion_tab->getFitParent()) panel_height = accordion_tab->getRect().getHeight(); - ctrlSetLeftTopAndSize(accordion_tab,panel_rect.mLeft,panel_top,panel_width,panel_height); - - show_hide_scrollbar(getRect().getWidth(),getRect().getHeight()); - return; + ctrlSetLeftTopAndSize(accordion_tab, panel_rect.mLeft, panel_top, panel_width, panel_height); + + show_hide_scrollbar(getRect().getWidth(), getRect().getHeight()); + return; } - if(mSingleExpansion) - arrangeSinge (); + if (mSingleExpansion) + arrangeSingle(); else - arrangeMultiple (); + arrangeMultiple(); } //--------------------------------------------------------------------------------- -BOOL LLAccordionCtrl::handleScrollWheel ( S32 x, S32 y, S32 clicks ) +BOOL LLAccordionCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if(LLPanel::handleScrollWheel(x,y,clicks)) + if (LLPanel::handleScrollWheel(x, y, clicks)) return TRUE; - if( mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) ) + if (mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) return TRUE; - return false; - + return FALSE; } -BOOL LLAccordionCtrl::handleKeyHere (KEY key, MASK mask) +BOOL LLAccordionCtrl::handleKeyHere(KEY key, MASK mask) { - if( mScrollbar->getVisible() && mScrollbar->handleKeyHere( key,mask ) ) + if (mScrollbar->getVisible() && mScrollbar->handleKeyHere(key, mask)) return TRUE; - return LLPanel::handleKeyHere(key,mask); + return LLPanel::handleKeyHere(key, mask); } -BOOL LLAccordionCtrl::handleDragAndDrop (S32 x, S32 y, MASK mask, - BOOL drop, - EDragAndDropType cargo_type, - void* cargo_data, - EAcceptance* accept, - std::string& tooltip_msg) +BOOL LLAccordionCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, + BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) { // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; BOOL handled = autoScroll(x, y); - if( !handled ) + if (!handled) { handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg) != NULL; @@ -590,14 +587,14 @@ BOOL LLAccordionCtrl::handleDragAndDrop (S32 x, S32 y, MASK mask, return TRUE; } -BOOL LLAccordionCtrl::autoScroll (S32 x, S32 y) +BOOL LLAccordionCtrl::autoScroll(S32 x, S32 y) { static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); bool scrolling = false; - if( mScrollbar->getVisible() ) + if (mScrollbar->getVisible()) { - LLRect rect_local( 0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0 ); + LLRect rect_local(0, getRect().getHeight(), getRect().getWidth() - scrollbar_size, 0); LLRect screen_local_extents; // clip rect against root view @@ -610,51 +607,52 @@ BOOL LLAccordionCtrl::autoScroll (S32 x, S32 y) LLRect bottom_scroll_rect = screen_local_extents; bottom_scroll_rect.mTop = rect_local.mBottom + auto_scroll_region_height; - if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax()) ) + if (bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() < mScrollbar->getDocPosMax())) { - mScrollbar->setDocPos( mScrollbar->getDocPos() + auto_scroll_speed ); + mScrollbar->setDocPos(mScrollbar->getDocPos() + auto_scroll_speed); mAutoScrolling = true; scrolling = true; } LLRect top_scroll_rect = screen_local_extents; top_scroll_rect.mBottom = rect_local.mTop - auto_scroll_region_height; - if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar->getDocPos() > 0) ) + if (top_scroll_rect.pointInRect(x, y) && (mScrollbar->getDocPos() > 0)) { - mScrollbar->setDocPos( mScrollbar->getDocPos() - auto_scroll_speed ); + mScrollbar->setDocPos(mScrollbar->getDocPos() - auto_scroll_speed); mAutoScrolling = true; scrolling = true; } } - return scrolling; + + return scrolling ? TRUE : FALSE; } -void LLAccordionCtrl::updateLayout (S32 width, S32 height) +void LLAccordionCtrl::updateLayout(S32 width, S32 height) { S32 panel_top = height - BORDER_MARGIN ; - if(mScrollbar->getVisible()) - panel_top+=mScrollbar->getDocPos(); + if (mScrollbar->getVisible()) + panel_top += mScrollbar->getDocPos(); - S32 panel_width = width - 2*BORDER_MARGIN; + S32 panel_width = width - BORDER_MARGIN * 2; static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - if(mScrollbar->getVisible()) - panel_width-=scrollbar_size; + if (mScrollbar->getVisible()) + panel_width -= scrollbar_size; - //set sizes for first panels and dragbars - for(size_t i=0;igetVisible()) + if (!mAccordionTabs[i]->getVisible()) continue; LLRect panel_rect = mAccordionTabs[i]->getRect(); - ctrlSetLeftTopAndSize(mAccordionTabs[i],panel_rect.mLeft,panel_top,panel_width,panel_rect.getHeight()); - panel_top-=panel_rect.getHeight(); + ctrlSetLeftTopAndSize(mAccordionTabs[i], panel_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); + panel_top -= panel_rect.getHeight(); } } -void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*) +void LLAccordionCtrl::onScrollPosChangeCallback(S32, LLScrollbar*) { - updateLayout(getRect().getWidth(),getRect().getHeight()); + updateLayout(getRect().getWidth(), getRect().getHeight()); } // virtual @@ -687,42 +685,43 @@ void LLAccordionCtrl::onUpdateScrollToChild(const LLUICtrl *cntrl) LLUICtrl::onUpdateScrollToChild(cntrl); } -void LLAccordionCtrl::onOpen (const LLSD& key) +void LLAccordionCtrl::onOpen(const LLSD& key) { - for(size_t i=0;i(mAccordionTabs[i]); LLPanel* panel = dynamic_cast(accordion_tab->getAccordionView()); - if(panel!=NULL) + if (panel != NULL) { panel->onOpen(key); } } } + S32 LLAccordionCtrl::notifyParent(const LLSD& info) { - if(info.has("action")) + if (info.has("action")) { std::string str_action = info["action"]; - if(str_action == "size_changes") + if (str_action == "size_changes") { // arrange(); return 1; } - else if(str_action == "select_next") + if (str_action == "select_next") { - for(size_t i=0;i(mAccordionTabs[i]); - if(accordion_tab->hasFocus()) + if (accordion_tab->hasFocus()) { - while(++igetVisible()) + if (mAccordionTabs[i]->getVisible()) break; } - if(i(mAccordionTabs[i]); accordion_tab->notify(LLSD().with("action","select_first")); @@ -733,17 +732,17 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) } return 0; } - else if(str_action == "select_prev") + if (str_action == "select_prev") { - for(size_t i=0;i(mAccordionTabs[i]); - if(accordion_tab->hasFocus() && i>0) + if (accordion_tab->hasFocus() && i > 0) { bool prev_visible_tab_found = false; - while(i>0) + while (i > 0) { - if(mAccordionTabs[--i]->getVisible()) + if (mAccordionTabs[--i]->getVisible()) { prev_visible_tab_found = true; break; @@ -761,12 +760,12 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) } return 0; } - else if(str_action == "select_current") + if (str_action == "select_current") { - for(size_t i=0;ihasFocus()) + if (mAccordionTabs[i]->hasFocus()) { if (mAccordionTabs[i] != mSelectedTab) { @@ -783,7 +782,7 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) } return 0; } - else if(str_action == "deselect_current") + if (str_action == "deselect_current") { // Reset selection to the currently selected tab. if (mSelectedTab) @@ -802,9 +801,9 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) screenRectToLocal(screen_rc, &local_rc); // Translate to parent coordinatess to check if we are in visible rectangle - local_rc.translate( getRect().mLeft, getRect().mBottom ); + local_rc.translate(getRect().mLeft, getRect().mBottom); - if ( !getRect().contains (local_rc) ) + if (!getRect().contains (local_rc)) { // Back to local coords and calculate position for scroller S32 bottom = mScrollbar->getDocPos() - local_rc.mBottom + getRect().mBottom; @@ -814,7 +813,7 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) bottom, // min vertical scroll top); // max vertical scroll - mScrollbar->setDocPos( scroll_pos ); + mScrollbar->setDocPos(scroll_pos); } return 1; } @@ -834,15 +833,16 @@ S32 LLAccordionCtrl::notifyParent(const LLSD& info) } return LLPanel::notifyParent(info); } -void LLAccordionCtrl::reset () + +void LLAccordionCtrl::reset() { - if(mScrollbar) + if (mScrollbar) mScrollbar->setDocPos(0); } void LLAccordionCtrl::expandDefaultTab() { - if (mAccordionTabs.size() > 0) + if (!mAccordionTabs.empty()) { LLAccordionCtrlTab* tab = mAccordionTabs.front(); @@ -877,7 +877,7 @@ void LLAccordionCtrl::sort() arrange(); } -void LLAccordionCtrl::setFilterSubString(const std::string& filter_string) +void LLAccordionCtrl::setFilterSubString(const std::string& filter_string) { LLStringUtil::format_map_t args; args["[SEARCH_TERM]"] = LLURI::escape(filter_string); @@ -907,7 +907,7 @@ const LLAccordionCtrlTab* LLAccordionCtrl::getExpandedTab() const S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 available_height /* = 0 */) { - if(tab_index < 0) + if (tab_index < 0) { return available_height; } @@ -915,9 +915,9 @@ S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 availabl S32 collapsed_tabs_height = 0; S32 num_expanded = 0; - for(size_t n = tab_index; n < mAccordionTabs.size(); ++n) + for (size_t n = tab_index; n < mAccordionTabs.size(); ++n) { - if(!mAccordionTabs[n]->isExpanded()) + if (!mAccordionTabs[n]->isExpanded()) { collapsed_tabs_height += mAccordionTabs[n]->getHeaderHeight(); } @@ -927,7 +927,7 @@ S32 LLAccordionCtrl::calcExpandedTabHeight(S32 tab_index /* = 0 */, S32 availabl } } - if(0 == num_expanded) + if (0 == num_expanded) { return available_height; } diff --git a/indra/llui/llaccordionctrl.h b/indra/llui/llaccordionctrl.h index 2828254472..6a1989afba 100644 --- a/indra/llui/llaccordionctrl.h +++ b/indra/llui/llaccordionctrl.h @@ -144,7 +144,7 @@ private: void initNoTabsWidget(const LLTextBox::Params& tb_params); void updateNoTabsHelpTextVisibility(); - void arrangeSinge(); + void arrangeSingle(); void arrangeMultiple(); // Calc Splitter's height that is necessary to display all child content diff --git a/indra/llui/llaccordionctrltab.cpp b/indra/llui/llaccordionctrltab.cpp index 04485c6262..20da568746 100644 --- a/indra/llui/llaccordionctrltab.cpp +++ b/indra/llui/llaccordionctrltab.cpp @@ -69,13 +69,13 @@ public: virtual BOOL postBuild(); std::string getTitle(); - void setTitle(const std::string& title, const std::string& hl); + void setTitle(const std::string& title, const std::string& hl); - void setTitleFontStyle(std::string style); + void setTitleFontStyle(std::string style); - void setTitleColor(LLUIColor); + void setTitleColor(LLUIColor); - void setSelected(bool is_selected) { mIsSelected = is_selected; } + void setSelected(bool is_selected) { mIsSelected = is_selected; } virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); @@ -85,8 +85,8 @@ public: void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); -private: +private: LLTextBox* mHeaderTextbox; // Overlay images (arrows) @@ -102,7 +102,7 @@ private: LLPointer mImageHeaderFocused; // style saved when applying it in setTitleFontStyle - LLStyle::Params mStyleParams; + LLStyle::Params mStyleParams; LLUIColor mHeaderBGColor; @@ -157,19 +157,17 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::postBuild() std::string LLAccordionCtrlTab::LLAccordionCtrlTabHeader::getTitle() { - if(mHeaderTextbox) + if (mHeaderTextbox) { return mHeaderTextbox->getText(); } - else - { - return LLStringUtil::null; - } + + return LLStringUtil::null; } void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitle(const std::string& title, const std::string& hl) { - if(mHeaderTextbox) + if (mHeaderTextbox) { LLTextUtil::textboxSetHighlightedVal( mHeaderTextbox, @@ -192,7 +190,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleFontStyle(std::string void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::setTitleColor(LLUIColor color) { - if(mHeaderTextbox) + if (mHeaderTextbox) { mHeaderTextbox->setColor(color); } @@ -204,11 +202,11 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() S32 height = getRect().getHeight(); F32 alpha = getCurrentTransparency(); - gl_rect_2d(0,0,width - 1 ,height - 1,mHeaderBGColor.get() % alpha,true); + gl_rect_2d(0, 0, width - 1, height - 1, mHeaderBGColor.get() % alpha, TRUE); LLAccordionCtrlTab* parent = dynamic_cast(getParent()); - bool collapsible = (parent && parent->getCollapsible()); - bool expanded = (parent && parent->getDisplayChildren()); + bool collapsible = parent && parent->getCollapsible(); + bool expanded = parent && parent->getDisplayChildren(); // Handle overlay images, if needed // Only show green "focus" background image if the accordion is open, @@ -218,23 +216,22 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() /*&& !(collapsible && !expanded)*/ // WHY?? ) { - mImageHeaderFocused->draw(0,0,width,height); + mImageHeaderFocused->draw(0, 0, width, height); } else { - mImageHeader->draw(0,0,width,height); + mImageHeader->draw(0, 0, width, height); } - if(mNeedsHighlight) + if (mNeedsHighlight) { - mImageHeaderOver->draw(0,0,width,height); + mImageHeaderOver->draw(0, 0, width, height); } - - if(collapsible) + if (collapsible) { LLPointer overlay_image; - if(expanded) + if (expanded) { overlay_image = mImageExpanded; } @@ -242,8 +239,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::draw() { overlay_image = mImageCollapsed; } - overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET, - (height - overlay_image->getHeight()) / 2); + overlay_image->draw(HEADER_IMAGE_LEFT_OFFSET, (height - overlay_image->getHeight()) / 2); } LLUICtrl::draw(); @@ -253,7 +249,7 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::reshape(S32 width, S32 height { S32 header_height = mHeaderTextbox->getTextPixelHeight(); - LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET,(height+header_height)/2 ,width,(height-header_height)/2); + LLRect textboxRect(HEADER_TEXT_LEFT_OFFSET, (height + header_height) / 2, width, (height - header_height) / 2); mHeaderTextbox->reshape(textboxRect.getWidth(), textboxRect.getHeight()); mHeaderTextbox->setRect(textboxRect); @@ -272,20 +268,24 @@ void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseEnter(S32 x, S32 y, MA LLUICtrl::onMouseEnter(x, y, mask); mNeedsHighlight = true; } + void LLAccordionCtrlTab::LLAccordionCtrlTabHeader::onMouseLeave(S32 x, S32 y, MASK mask) { LLUICtrl::onMouseLeave(x, y, mask); mNeedsHighlight = false; mAutoOpenTimer.stop(); } + BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - if ( ( key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE) + if ((key == KEY_LEFT || key == KEY_RIGHT) && mask == MASK_NONE) { return getParent()->handleKey(key, mask, called_from_parent); } + return LLUICtrl::handleKey(key, mask, called_from_parent); } + BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -295,7 +295,7 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 { LLAccordionCtrlTab* parent = dynamic_cast(getParent()); - if ( parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose() ) + if (parent && !parent->getDisplayChildren() && parent->getCollapsible() && parent->canOpenClose()) { if (mAutoOpenTimer.getStarted()) { @@ -307,12 +307,15 @@ BOOL LLAccordionCtrlTab::LLAccordionCtrlTabHeader::handleDragAndDrop(S32 x, S32 } } else + { mAutoOpenTimer.start(); + } } return LLUICtrl::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg); } + LLAccordionCtrlTab::Params::Params() : title("title") ,display_children("expanded", true) @@ -384,41 +387,39 @@ LLAccordionCtrlTab::~LLAccordionCtrlTab() { } - void LLAccordionCtrlTab::setDisplayChildren(bool display) { mDisplayChildren = display; LLRect rect = getRect(); - rect.mBottom = rect.mTop - (getDisplayChildren() ? - mExpandedHeight : HEADER_HEIGHT); + rect.mBottom = rect.mTop - (getDisplayChildren() ? mExpandedHeight : HEADER_HEIGHT); setRect(rect); - if(mContainerPanel) + if (mContainerPanel) + { mContainerPanel->setVisible(getDisplayChildren()); + } - if(mDisplayChildren) + if (mDisplayChildren) { adjustContainerPanel(); } else { - if(mScrollbar) - mScrollbar->setVisible(false); + if (mScrollbar) + mScrollbar->setVisible(FALSE); } - } void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent /* = TRUE */) { LLRect headerRect; - headerRect.setLeftTopAndSize( - 0,height,width,HEADER_HEIGHT); + headerRect.setLeftTopAndSize(0, height, width, HEADER_HEIGHT); mHeader->setRect(headerRect); mHeader->reshape(headerRect.getWidth(), headerRect.getHeight()); - if(!mDisplayChildren) + if (!mDisplayChildren) return; LLRect childRect; @@ -426,7 +427,7 @@ void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent childRect.setLeftTopAndSize( getPaddingLeft(), height - getHeaderHeight() - getPaddingTop(), - width - getPaddingLeft() - getPaddingRight(), + width - getPaddingLeft() - getPaddingRight(), height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); adjustContainerPanel(childRect); @@ -434,7 +435,7 @@ void LLAccordionCtrlTab::reshape(S32 width, S32 height, BOOL called_from_parent void LLAccordionCtrlTab::changeOpenClose(bool is_open) { - if(is_open) + if (is_open) mExpandedHeight = getRect().getHeight(); setDisplayChildren(!is_open); @@ -483,14 +484,14 @@ void LLAccordionCtrlTab::onUpdateScrollToChild(const LLUICtrl *cntrl) BOOL LLAccordionCtrlTab::handleMouseDown(S32 x, S32 y, MASK mask) { - if(mCollapsible && mHeaderVisible && mCanOpenClose) + if (mCollapsible && mHeaderVisible && mCanOpenClose) { - if(y >= (getRect().getHeight() - HEADER_HEIGHT) ) + if (y >= (getRect().getHeight() - HEADER_HEIGHT)) { mHeader->setFocus(true); changeOpenClose(getDisplayChildren()); - //reset stored state + // Reset stored state mWasStateStored = false; return TRUE; } @@ -510,7 +511,7 @@ boost::signals2::connection LLAccordionCtrlTab::setDropDownStateChangedCallback( bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group) { - if(DD_HEADER_NAME != child->getName()) + if (DD_HEADER_NAME != child->getName()) { reshape(child->getRect().getWidth() , child->getRect().getHeight() + HEADER_HEIGHT ); mExpandedHeight = getRect().getHeight(); @@ -518,12 +519,12 @@ bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group) bool res = LLUICtrl::addChild(child, tab_group); - if(DD_HEADER_NAME != child->getName()) + if (DD_HEADER_NAME != child->getName()) { - if(!mCollapsible) + if (!mCollapsible) setDisplayChildren(true); else - setDisplayChildren(getDisplayChildren()); + setDisplayChildren(getDisplayChildren()); } if (!mContainerPanel) @@ -534,7 +535,7 @@ bool LLAccordionCtrlTab::addChild(LLView* child, S32 tab_group) void LLAccordionCtrlTab::setAccordionView(LLView* panel) { - addChild(panel,0); + addChild(panel, 0); } std::string LLAccordionCtrlTab::getTitle() const @@ -543,10 +544,8 @@ std::string LLAccordionCtrlTab::getTitle() const { return mHeader->getTitle(); } - else - { - return LLStringUtil::null; - } + + return LLStringUtil::null; } void LLAccordionCtrlTab::setTitle(const std::string& title, const std::string& hl) @@ -579,6 +578,7 @@ boost::signals2::connection LLAccordionCtrlTab::setFocusReceivedCallback(const f { return mHeader->setFocusReceivedCallback(cb); } + return boost::signals2::connection(); } @@ -588,6 +588,7 @@ boost::signals2::connection LLAccordionCtrlTab::setFocusLostCallback(const focus { return mHeader->setFocusLostCallback(cb); } + return boost::signals2::connection(); } @@ -601,59 +602,65 @@ void LLAccordionCtrlTab::setSelected(bool is_selected) LLView* LLAccordionCtrlTab::findContainerView() { - for(child_list_const_iter_t it = getChildList()->begin(); - getChildList()->end() != it; ++it) + child_list_const_iter_t it = getChildList()->begin(), it_end = getChildList()->end(); + while (it != it_end) { - LLView* child = *it; - if(DD_HEADER_NAME == child->getName()) - continue; - if(!child->getVisible()) - continue; - return child; + LLView* child = *(it++); + if (DD_HEADER_NAME != child->getName() && child->getVisible()) + return child; } + return NULL; } void LLAccordionCtrlTab::selectOnFocusReceived() { if (getParent()) // A parent may not be set if tabs are added dynamically. + { getParent()->notifyParent(LLSD().with("action", "select_current")); + } } void LLAccordionCtrlTab::deselectOnFocusLost() { - if(getParent()) // A parent may not be set if tabs are added dynamically. + if (getParent()) // A parent may not be set if tabs are added dynamically. { getParent()->notifyParent(LLSD().with("action", "deselect_current")); } - } S32 LLAccordionCtrlTab::getHeaderHeight() { - return mHeaderVisible?HEADER_HEIGHT:0; + return mHeaderVisible ? HEADER_HEIGHT : 0; } -void LLAccordionCtrlTab::setHeaderVisible(bool value) +void LLAccordionCtrlTab::setHeaderVisible(bool value) { - if(mHeaderVisible == value) + if (mHeaderVisible == value) return; + mHeaderVisible = value; - if(mHeader) - mHeader->setVisible(value); + + if (mHeader) + { + mHeader->setVisible(value ? TRUE : FALSE); + } + reshape(getRect().getWidth(), getRect().getHeight(), FALSE); }; //virtual BOOL LLAccordionCtrlTab::postBuild() { - if(mHeader) + if (mHeader) + { mHeader->setVisible(mHeaderVisible); - - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); + } + + static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); LLRect scroll_rect; - scroll_rect.setOriginAndSize( + scroll_rect.setOriginAndSize( getRect().getWidth() - scrollbar_size, 1, scrollbar_size, @@ -661,7 +668,7 @@ BOOL LLAccordionCtrlTab::postBuild() mContainerPanel = findContainerView(); - if(!mFitPanel) + if (!mFitPanel) { LLScrollbar::Params sbparams; sbparams.name("scrollable vertical"); @@ -674,9 +681,8 @@ BOOL LLAccordionCtrlTab::postBuild() sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); sbparams.change_callback(boost::bind(&LLAccordionCtrlTab::onScrollPosChangeCallback, this, _1, _2)); - - mScrollbar = LLUICtrlFactory::create (sbparams); - LLView::addChild( mScrollbar ); + mScrollbar = LLUICtrlFactory::create(sbparams); + LLView::addChild(mScrollbar); mScrollbar->setFollowsRight(); mScrollbar->setFollowsTop(); mScrollbar->setFollowsBottom(); @@ -684,44 +690,48 @@ BOOL LLAccordionCtrlTab::postBuild() mScrollbar->setVisible(false); } - if(mContainerPanel) + if (mContainerPanel) + { mContainerPanel->setVisible(mDisplayChildren); + } return LLUICtrl::postBuild(); } -bool LLAccordionCtrlTab::notifyChildren (const LLSD& info) + +bool LLAccordionCtrlTab::notifyChildren (const LLSD& info) { - if(info.has("action")) + if (info.has("action")) { std::string str_action = info["action"]; - if(str_action == "store_state") + if (str_action == "store_state") { storeOpenCloseState(); return true; } - if(str_action == "restore_state") + + if (str_action == "restore_state") { restoreOpenCloseState(); return true; } } + return LLUICtrl::notifyChildren(info); } S32 LLAccordionCtrlTab::notifyParent(const LLSD& info) { - if(info.has("action")) + if (info.has("action")) { std::string str_action = info["action"]; - if(str_action == "size_changes") + if (str_action == "size_changes") { - // S32 height = info["height"]; - height = llmax(height,10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom(); + height = llmax(height, 10) + HEADER_HEIGHT + getPaddingTop() + getPaddingBottom(); mExpandedHeight = height; - if(isExpanded() && !mSkipChangesOnNotifyParent) + if (isExpanded() && !mSkipChangesOnNotifyParent) { LLRect panel_rect = getRect(); panel_rect.setLeftTopAndSize( panel_rect.mLeft, panel_rect.mTop, panel_rect.getWidth(), height); @@ -729,12 +739,13 @@ S32 LLAccordionCtrlTab::notifyParent(const LLSD& info) setRect(panel_rect); } - //LLAccordionCtrl should rearrange accordion tab if one of accordion change its size + // LLAccordionCtrl should rearrange accordion tab if one of accordions changed its size if (getParent()) // A parent may not be set if tabs are added dynamically. getParent()->notifyParent(info); return 1; } - else if(str_action == "select_prev") + + if (str_action == "select_prev") { showAndFocusHeader(); return 1; @@ -772,78 +783,85 @@ S32 LLAccordionCtrlTab::notifyParent(const LLSD& info) S32 LLAccordionCtrlTab::notify(const LLSD& info) { - if(info.has("action")) + if (info.has("action")) { std::string str_action = info["action"]; - if(str_action == "select_first") + if (str_action == "select_first") { showAndFocusHeader(); return 1; } - else if( str_action == "select_last" ) + + if (str_action == "select_last") { - if(getDisplayChildren() == false) + if (!getDisplayChildren()) { showAndFocusHeader(); } else { LLView* view = getAccordionView(); - if(view) - view->notify(LLSD().with("action","select_last")); + if (view) + { + view->notify(LLSD().with("action", "select_last")); + } } } } + return 0; } BOOL LLAccordionCtrlTab::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - if( !mHeader->hasFocus() ) + if (!mHeader->hasFocus()) return LLUICtrl::handleKey(key, mask, called_from_parent); - if ( (key == KEY_RETURN )&& mask == MASK_NONE) + if ((key == KEY_RETURN) && mask == MASK_NONE) { changeOpenClose(getDisplayChildren()); return TRUE; } - if ( (key == KEY_ADD || key == KEY_RIGHT)&& mask == MASK_NONE) + if ((key == KEY_ADD || key == KEY_RIGHT) && mask == MASK_NONE) { - if(getDisplayChildren() == false) - { - changeOpenClose(getDisplayChildren()); - return TRUE; - } - } - if ( (key == KEY_SUBTRACT || key == KEY_LEFT)&& mask == MASK_NONE) - { - if(getDisplayChildren() == true) + if (!getDisplayChildren()) { changeOpenClose(getDisplayChildren()); return TRUE; } } - if ( key == KEY_DOWN && mask == MASK_NONE) + if ((key == KEY_SUBTRACT || key == KEY_LEFT) && mask == MASK_NONE) { - //if collapsed go to the next accordion - if(getDisplayChildren() == false) - //we processing notifyParent so let call parent directly - getParent()->notifyParent(LLSD().with("action","select_next")); + if (getDisplayChildren()) + { + changeOpenClose(getDisplayChildren()); + return TRUE; + } + } + + if (key == KEY_DOWN && mask == MASK_NONE) + { + // if collapsed go to the next accordion + if (!getDisplayChildren()) + { + // we're processing notifyParent so let call parent directly + getParent()->notifyParent(LLSD().with("action", "select_next")); + } else { - getAccordionView()->notify(LLSD().with("action","select_first")); + getAccordionView()->notify(LLSD().with("action", "select_first")); } return TRUE; } - if ( key == KEY_UP && mask == MASK_NONE) + if (key == KEY_UP && mask == MASK_NONE) { - //go to the previous accordion + // go to the previous accordion - //we processing notifyParent so let call parent directly - getParent()->notifyParent(LLSD().with("action","select_prev")); + // we're processing notifyParent so let call parent directly + getParent()->notifyParent(LLSD().with("action", "select_prev")); return TRUE; } @@ -869,28 +887,29 @@ void LLAccordionCtrlTab::showAndFocusHeader() // accordion tab (assuming that the parent is an LLAccordionCtrl) the calls chain // is shortened and messages from inside the collapsed tabs are avoided. // See STORM-536. - getParent()->notifyParent(LLSD().with("scrollToShowRect",screen_rc.getValue())); + getParent()->notifyParent(LLSD().with("scrollToShowRect", screen_rc.getValue())); } -void LLAccordionCtrlTab::storeOpenCloseState() + +void LLAccordionCtrlTab::storeOpenCloseState() { - if(mWasStateStored) + if (mWasStateStored) return; mStoredOpenCloseState = getDisplayChildren(); mWasStateStored = true; } -void LLAccordionCtrlTab::restoreOpenCloseState() +void LLAccordionCtrlTab::restoreOpenCloseState() { - if(!mWasStateStored) + if (!mWasStateStored) return; - if(getDisplayChildren() != mStoredOpenCloseState) + if (getDisplayChildren() != mStoredOpenCloseState) { changeOpenClose(getDisplayChildren()); } mWasStateStored = false; } -void LLAccordionCtrlTab::adjustContainerPanel () +void LLAccordionCtrlTab::adjustContainerPanel() { S32 width = getRect().getWidth(); S32 height = getRect().getHeight(); @@ -907,83 +926,83 @@ void LLAccordionCtrlTab::adjustContainerPanel () void LLAccordionCtrlTab::adjustContainerPanel(const LLRect& child_rect) { - if(!mContainerPanel) + if (!mContainerPanel) return; - if(!mFitPanel) + if (!mFitPanel) { show_hide_scrollbar(child_rect); updateLayout(child_rect); } else { - mContainerPanel->reshape(child_rect.getWidth(),child_rect.getHeight()); + mContainerPanel->reshape(child_rect.getWidth(), child_rect.getHeight()); mContainerPanel->setRect(child_rect); } } S32 LLAccordionCtrlTab::getChildViewHeight() { - if(!mContainerPanel) + if (!mContainerPanel) return 0; return mContainerPanel->getRect().getHeight(); } void LLAccordionCtrlTab::show_hide_scrollbar(const LLRect& child_rect) { - if(getChildViewHeight() > child_rect.getHeight() ) + if (getChildViewHeight() > child_rect.getHeight()) showScrollbar(child_rect); else hideScrollbar(child_rect); } + void LLAccordionCtrlTab::showScrollbar(const LLRect& child_rect) { - if(!mContainerPanel || !mScrollbar) + if (!mContainerPanel || !mScrollbar) return; bool was_visible = mScrollbar->getVisible(); mScrollbar->setVisible(true); static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - { - ctrlSetLeftTopAndSize(mScrollbar,child_rect.getWidth()-scrollbar_size, - child_rect.getHeight()-PARENT_BORDER_MARGIN, - scrollbar_size, - child_rect.getHeight()-2*PARENT_BORDER_MARGIN); - } + ctrlSetLeftTopAndSize(mScrollbar, + child_rect.getWidth() - scrollbar_size, + child_rect.getHeight() - PARENT_BORDER_MARGIN, + scrollbar_size, + child_rect.getHeight() - PARENT_BORDER_MARGIN * 2); LLRect orig_rect = mContainerPanel->getRect(); mScrollbar->setPageSize(child_rect.getHeight()); - mScrollbar->setDocParams(orig_rect.getHeight(),mScrollbar->getDocPos()); + mScrollbar->setDocParams(orig_rect.getHeight(), mScrollbar->getDocPos()); - if(was_visible) + if (was_visible) { S32 scroll_pos = llmin(mScrollbar->getDocPos(), orig_rect.getHeight() - child_rect.getHeight() - 1); mScrollbar->setDocPos(scroll_pos); } - else//shrink child panel + else // Shrink child panel { updateLayout(child_rect); } - } -void LLAccordionCtrlTab::hideScrollbar( const LLRect& child_rect ) +void LLAccordionCtrlTab::hideScrollbar(const LLRect& child_rect) { - if(!mContainerPanel || !mScrollbar) + if (!mContainerPanel || !mScrollbar) return; - if(mScrollbar->getVisible() == false) + if (mScrollbar->getVisible() == FALSE) return; - mScrollbar->setVisible(false); + + mScrollbar->setVisible(FALSE); mScrollbar->setDocPos(0); //shrink child panel updateLayout(child_rect); } -void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*) +void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*) { LLRect child_rect; @@ -999,21 +1018,20 @@ void LLAccordionCtrlTab::onScrollPosChangeCallback(S32, LLScrollbar*) updateLayout(child_rect); } -void LLAccordionCtrlTab::drawChild(const LLRect& root_rect,LLView* child) +void LLAccordionCtrlTab::drawChild(const LLRect& root_rect, LLView* child) { if (child && child->getVisible() && child->getRect().isValid()) { LLRect screen_rect; - localRectToScreen(child->getRect(),&screen_rect); - - if ( root_rect.overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect)) + localRectToScreen(child->getRect(), &screen_rect); + + if (root_rect.overlaps(screen_rect) && sDirtyRect.overlaps(screen_rect)) { gGL.matrixMode(LLRender::MM_MODELVIEW); LLUI::pushMatrix(); { LLUI::translate((F32)child->getRect().mLeft, (F32)child->getRect().mBottom); child->draw(); - } LLUI::popMatrix(); } @@ -1022,64 +1040,67 @@ void LLAccordionCtrlTab::drawChild(const LLRect& root_rect,LLView* child) void LLAccordionCtrlTab::draw() { - if(mFitPanel) + if (mFitPanel) + { LLUICtrl::draw(); + } else { - LLRect root_rect = getRootView()->getRect(); - drawChild(root_rect,mHeader); - drawChild(root_rect,mScrollbar ); - { - LLRect child_rect; + LLRect root_rect(getRootView()->getRect()); + drawChild(root_rect, mHeader); + drawChild(root_rect, mScrollbar); - S32 width = getRect().getWidth(); - S32 height = getRect().getHeight(); + LLRect child_rect; - child_rect.setLeftTopAndSize( - getPaddingLeft(), - height - getHeaderHeight() - getPaddingTop(), - width - getPaddingLeft() - getPaddingRight(), - height - getHeaderHeight() - getPaddingTop() - getPaddingBottom() ); + S32 width = getRect().getWidth(); + S32 height = getRect().getHeight(); - LLLocalClipRect clip(child_rect); - drawChild(root_rect,mContainerPanel); - } + child_rect.setLeftTopAndSize( + getPaddingLeft(), + height - getHeaderHeight() - getPaddingTop(), + width - getPaddingLeft() - getPaddingRight(), + height - getHeaderHeight() - getPaddingTop() - getPaddingBottom()); + + LLLocalClipRect clip(child_rect); + drawChild(root_rect,mContainerPanel); } } -void LLAccordionCtrlTab::updateLayout ( const LLRect& child_rect ) +void LLAccordionCtrlTab::updateLayout(const LLRect& child_rect) { LLView* child = getAccordionView(); - if(!mContainerPanel) + if (!mContainerPanel) return; S32 panel_top = child_rect.getHeight(); S32 panel_width = child_rect.getWidth(); - static LLUICachedControl scrollbar_size ("UIScrollbarSize", 0); - if(mScrollbar && mScrollbar->getVisible() != false) + static LLUICachedControl scrollbar_size("UIScrollbarSize", 0); + if (mScrollbar && mScrollbar->getVisible()) { - panel_top+=mScrollbar->getDocPos(); - panel_width-=scrollbar_size; + panel_top += mScrollbar->getDocPos(); + panel_width -= scrollbar_size; } - //set sizes for first panels and dragbars + // Set sizes for first panels and dragbars LLRect panel_rect = child->getRect(); - ctrlSetLeftTopAndSize(mContainerPanel,child_rect.mLeft,panel_top,panel_width,panel_rect.getHeight()); + ctrlSetLeftTopAndSize(mContainerPanel, child_rect.mLeft, panel_top, panel_width, panel_rect.getHeight()); } + void LLAccordionCtrlTab::ctrlSetLeftTopAndSize(LLView* panel, S32 left, S32 top, S32 width, S32 height) { - if(!panel) + if (!panel) return; LLRect panel_rect = panel->getRect(); - panel_rect.setLeftTopAndSize( left, top, width, height); + panel_rect.setLeftTopAndSize(left, top, width, height); panel->reshape( width, height, 1); panel->setRect(panel_rect); } + BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) { //header may be not the first child but we need to process it first - if(y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT/2) ) + if (y >= (getRect().getHeight() - HEADER_HEIGHT - HEADER_HEIGHT / 2)) { //inside tab header //fix for EXT-6619 @@ -1088,16 +1109,18 @@ BOOL LLAccordionCtrlTab::handleToolTip(S32 x, S32 y, MASK mask) } return LLUICtrl::handleToolTip(x, y, mask); } -BOOL LLAccordionCtrlTab::handleScrollWheel ( S32 x, S32 y, S32 clicks ) + +BOOL LLAccordionCtrlTab::handleScrollWheel(S32 x, S32 y, S32 clicks) { - if( LLUICtrl::handleScrollWheel(x,y,clicks)) + if (LLUICtrl::handleScrollWheel(x, y, clicks)) { return TRUE; } - if( mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel( 0, 0, clicks ) ) + + if (mScrollbar && mScrollbar->getVisible() && mScrollbar->handleScrollWheel(0, 0, clicks)) { return TRUE; } + return FALSE; } - diff --git a/indra/llui/llaccordionctrltab.h b/indra/llui/llaccordionctrltab.h index 2c72e8c036..896a34cac4 100644 --- a/indra/llui/llaccordionctrltab.h +++ b/indra/llui/llaccordionctrltab.h @@ -104,7 +104,7 @@ public: virtual void setDisplayChildren(bool display); // Returns expand/collapse state - virtual bool getDisplayChildren() const {return mDisplayChildren;}; + virtual bool getDisplayChildren() const { return mDisplayChildren; }; //set LLAccordionCtrlTab panel void setAccordionView(LLView* panel); diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 8028f397f3..49d275997a 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -203,7 +203,8 @@ LLButton::LLButton(const LLButton::Params& p) } // Hack to make sure there is space for at least one character - if (getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) + if (getRect().mRight >= 0 && getRect().getWidth() > 0 && + getRect().getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(std::string(" "))) { // Use old defaults mLeftHPad = llbutton_orig_h_pad; @@ -942,11 +943,8 @@ void LLButton::draw() break; } - S32 y_offset = 2 + (getRect().getHeight() - 20)/2; - if (pressed && mDisplayPressedState) { - y_offset--; x++; } diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp index 48bf5bb80f..220f5ee825 100644 --- a/indra/llui/lldraghandle.cpp +++ b/indra/llui/lldraghandle.cpp @@ -68,6 +68,7 @@ LLDragHandle::LLDragHandle(const LLDragHandle::Params& p) LLDragHandle::~LLDragHandle() { + gFocusMgr.removeKeyboardFocusWithoutCallback(this); removeChild(mTitleBox); delete mTitleBox; } diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index b13e7389cc..460bd0945b 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -1198,7 +1198,7 @@ void LLFlatListView::onFocusReceived() { if (size()) { - mSelectedItemsBorder->setVisible(TRUE); + mSelectedItemsBorder->setVisible(TRUE); } gEditMenuHandler = this; } @@ -1207,7 +1207,7 @@ void LLFlatListView::onFocusLost() { mSelectedItemsBorder->setVisible(FALSE); // Route menu back to the default - if( gEditMenuHandler == this ) + if (gEditMenuHandler == this) { gEditMenuHandler = NULL; } @@ -1216,16 +1216,16 @@ void LLFlatListView::onFocusLost() //virtual S32 LLFlatListView::notify(const LLSD& info) { - if(info.has("action")) + if (info.has("action")) { std::string str_action = info["action"]; - if(str_action == "select_first") + if (str_action == "select_first") { setFocus(true); selectFirstItem(); return 1; } - else if(str_action == "select_last") + else if (str_action == "select_last") { setFocus(true); selectLastItem(); @@ -1238,6 +1238,7 @@ S32 LLFlatListView::notify(const LLSD& info) notifyParentItemsRectChanged(); return 1; } + return 0; } @@ -1249,10 +1250,8 @@ void LLFlatListView::detachItems(std::vector& detached_items) detached_items.clear(); // Go through items and detach valid items, remove them from items panel // and add to detached_items. - for (pairs_iterator_t - iter = mItemPairs.begin(), - iter_end = mItemPairs.end(); - iter != iter_end; ++iter) + pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); + while (iter != iter_end) { LLPanel* pItem = (*iter)->first; if (1 == pItem->notify(action)) @@ -1261,6 +1260,7 @@ void LLFlatListView::detachItems(std::vector& detached_items) mItemsPanel->removeChild(pItem); detached_items.push_back(pItem); } + iter++; } if (!detached_items.empty()) { @@ -1268,13 +1268,12 @@ void LLFlatListView::detachItems(std::vector& detached_items) if (detached_items.size() == mItemPairs.size()) { // This way will be faster if all items were disconnected - for (pairs_iterator_t - iter = mItemPairs.begin(), - iter_end = mItemPairs.end(); - iter != iter_end; ++iter) + pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); + while (iter != iter_end) { (*iter)->first = NULL; delete *iter; + iter++; } mItemPairs.clear(); // Also set items panel height to zero. @@ -1287,16 +1286,14 @@ void LLFlatListView::detachItems(std::vector& detached_items) } else { - for (std::vector::const_iterator - detached_iter = detached_items.begin(), - detached_iter_end = detached_items.end(); - detached_iter != detached_iter_end; ++detached_iter) + std::vector::const_iterator + detached_iter = detached_items.begin(), + detached_iter_end = detached_items.end(); + while (detached_iter < detached_iter_end) { LLPanel* pDetachedItem = *detached_iter; - for (pairs_iterator_t - iter = mItemPairs.begin(), - iter_end = mItemPairs.end(); - iter != iter_end; ++iter) + pairs_iterator_t iter = mItemPairs.begin(), iter_end = mItemPairs.end(); + while (iter != iter_end) { item_pair_t* item_pair = *iter; if (item_pair->first == pDetachedItem) @@ -1306,7 +1303,9 @@ void LLFlatListView::detachItems(std::vector& detached_items) delete item_pair; break; } + iter++; } + detached_iter++; } rearrangeItems(); } @@ -1322,7 +1321,6 @@ LLFlatListViewEx::Params::Params() : no_items_msg("no_items_msg") , no_filtered_items_msg("no_filtered_items_msg") { - } LLFlatListViewEx::LLFlatListViewEx(const Params& p) @@ -1332,7 +1330,6 @@ LLFlatListViewEx::LLFlatListViewEx(const Params& p) , mForceShowingUnmatchedItems(false) , mHasMatchedItems(false) { - } void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string) @@ -1352,7 +1349,6 @@ void LLFlatListViewEx::updateNoItemsMessage(const std::string& filter_string) // list does not contain any items at all setNoItemsCommentText(mNoItemsMsg); } - } bool LLFlatListViewEx::getForceShowingUnmatchedItems() @@ -1411,12 +1407,10 @@ void LLFlatListViewEx::filterItems() getItems(items); mHasMatchedItems = false; - for (item_panel_list_t::iterator - iter = items.begin(), - iter_end = items.end(); - iter != iter_end; ++iter) + item_panel_list_t::iterator iter = items.begin(), iter_end = items.end(); + while (iter < iter_end) { - LLPanel* pItem = (*iter); + LLPanel* pItem = *(iter++); updateItemVisibility(pItem, action); } diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index 62c311f522..650ae9ae75 100644 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -189,7 +189,9 @@ LLFolderView::LLFolderView(const Params& p) mStatusTextBox(NULL), mShowItemLinkOverlays(p.show_item_link_overlays), mViewModel(p.view_model), - mGroupedItemModel(p.grouped_item_model) + mGroupedItemModel(p.grouped_item_model), + mForceArrange(false), + mSingleFolderMode(false) { LLPanel* panel = p.parent_panel; mParentPanel = panel->getHandle(); @@ -609,6 +611,7 @@ void LLFolderView::clearSelection() } mSelectedItems.clear(); + mNeedsScroll = false; } std::set LLFolderView::getSelectionList() const @@ -665,7 +668,7 @@ void LLFolderView::draw() } else if (mShowEmptyMessage) { - mStatusTextBox->setValue(getFolderViewModel()->getStatusText()); + mStatusTextBox->setValue(getFolderViewModel()->getStatusText(mItems.empty() && mFolders.empty())); mStatusTextBox->setVisible( TRUE ); // firstly reshape message textbox with current size. This is necessary to @@ -693,12 +696,16 @@ void LLFolderView::draw() } } - if (mRenameItem && mRenamer && mRenamer->getVisible() && !getVisibleRect().overlaps(mRenamer->getRect())) - { - // renamer is not connected to the item we are renaming in any form so manage it manually - // TODO: consider stopping on any scroll action instead of when out of visible area - finishRenamingItem(); - } + if (mRenameItem + && mRenamer + && mRenamer->getVisible() + && !getVisibleRect().overlaps(mRenamer->getRect())) + { + // renamer is not connected to the item we are renaming in any form so manage it manually + // TODO: consider stopping on any scroll action instead of when out of visible area + LL_DEBUGS("Inventory") << "Renamer out of bounds, hiding" << LL_ENDL; + finishRenamingItem(); + } // skip over LLFolderViewFolder::draw since we don't want the folder icon, label, // and arrow for the root folder @@ -832,9 +839,12 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) mAutoOpenItems.push(item); item->setOpen(TRUE); + if(!item->isSingleFolderMode()) + { LLRect content_rect = (mScrollContainer ? mScrollContainer->getContentWindowRect() : LLRect()); LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); scrollToShowItem(item, constraint_rect); + } } void LLFolderView::closeAutoOpenedFolders() @@ -1038,6 +1048,8 @@ void LLFolderView::paste() // public rename functionality - can only start the process void LLFolderView::startRenamingSelectedItem( void ) { + LL_DEBUGS("Inventory") << "Starting inventory renamer" << LL_ENDL; + // make sure selection is visible scrollToShowSelection(); @@ -1273,6 +1285,11 @@ BOOL LLFolderView::handleKeyHere( KEY key, MASK mask ) if(mSelectedItems.size()) { LLFolderViewItem* last_selected = getCurSelectedItem(); + if(last_selected && last_selected->isSingleFolderMode()) + { + handled = FALSE; + break; + } LLFolderViewItem* parent_folder = last_selected->getParentFolder(); if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder()) { @@ -1458,9 +1475,19 @@ BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask ) mCallbackRegistrar->popScope(); } } + + BOOL item_clicked = FALSE; + for (selected_items_t::iterator item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it) + { + item_clicked |= (*item_it)->getRect().pointInRect(x, y); + } + if(!item_clicked && mSingleFolderMode) + { + clearSelection(); + } bool hide_folder_menu = mSuppressFolderMenu && isFolderSelected(); - if (menu && (handled - && ( count > 0 && (hasVisibleChildren()) )) && // show menu only if selected items are visible + if (menu && (mSingleFolderMode || (handled + && ( count > 0 && (hasVisibleChildren()) ))) && // show menu only if selected items are visible !hide_folder_menu) { if (mCallbackRegistrar) @@ -1532,6 +1559,22 @@ BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask ) return LLView::handleHover( x, y, mask ); } +LLFolderViewItem* LLFolderView::getHoveredItem() const +{ + return dynamic_cast(mHoveredItem.get()); +} + +void LLFolderView::setHoveredItem(LLFolderViewItem* itemp) +{ + if (mHoveredItem.get() != itemp) + { + if (itemp) + mHoveredItem = itemp->getHandle(); + else + mHoveredItem.markDead(); + } +} + BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, @@ -1716,7 +1759,7 @@ void LLFolderView::update() mNeedsAutoSelect = FALSE; } - BOOL is_visible = isInVisibleChain(); + BOOL is_visible = isInVisibleChain() || mForceArrange; //Puts folders/items in proper positions // arrange() takes the model filter flag into account and call sort() if necessary (CHUI-849) @@ -1817,13 +1860,28 @@ void LLFolderView::update() } } - if (mSignalSelectCallback) - { - //RN: we use keyboard focus as a proxy for user-explicit actions - BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); - mSelectSignal(mSelectedItems, take_keyboard_focus); - } - mSignalSelectCallback = FALSE; + if (mSelectedItems.size()) + { + LLFolderViewItem* item = mSelectedItems.back(); + // If the goal is to show renamer, don't callback untill + // item is visible or is no longer being scrolled to. + // Otherwise renamer will be instantly closed + // Todo: consider moving renamer out of selection callback + if (!mNeedsAutoRename || !mNeedsScroll || item->getVisible()) + { + if (mSignalSelectCallback) + { + //RN: we use keyboard focus as a proxy for user-explicit actions + BOOL take_keyboard_focus = (mSignalSelectCallback == SIGNAL_KEYBOARD_FOCUS); + mSelectSignal(mSelectedItems, take_keyboard_focus); + } + mSignalSelectCallback = FALSE; + } + } + else + { + mSignalSelectCallback = FALSE; + } } void LLFolderView::dumpSelectionInformation() @@ -1886,6 +1944,11 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu) flags = multi_select_flag; } + if(mSingleFolderMode && (mSelectedItems.size() == 0)) + { + buildContextMenu(*menu, flags); + } + // This adds a check for restrictions based on the entire // selection set - for example, any one wearable may not push you // over the limit, but all wearables together still might. @@ -2042,7 +2105,7 @@ LLFolderViewItem* LLFolderView::getNextUnselectedItem() return new_selection; } -S32 LLFolderView::getItemHeight() +S32 LLFolderView::getItemHeight() const { if(!hasVisibleChildren()) { diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index 7dfa04828a..5f8a173889 100644 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -127,6 +127,9 @@ public: bool getAllowMultiSelect() { return mAllowMultiSelect; } bool getAllowDrag() { return mAllowDrag; } + void setSingleFolderMode(bool is_single_mode) { mSingleFolderMode = is_single_mode; } + bool isSingleFolderMode() { return mSingleFolderMode; } + // Close all folders in the view void closeAllFolders(); void openTopLevelFolders(); @@ -136,7 +139,7 @@ public: // Find width and height of this object and its children. Also // makes sure that this view and its children are the right size. virtual S32 arrange( S32* width, S32* height ); - virtual S32 getItemHeight(); + virtual S32 getItemHeight() const; void arrangeAll() { mArrangeGeneration++; } S32 getArrangeGeneration() { return mArrangeGeneration; } @@ -144,6 +147,10 @@ public: // applies filters to control visibility of items virtual void filter( LLFolderViewFilter& filter); + void clearHoveredItem() { setHoveredItem(nullptr); } + LLFolderViewItem* getHoveredItem() const; + void setHoveredItem(LLFolderViewItem* itemp); + // Get the last selected item virtual LLFolderViewItem* getCurSelectedItem( void ); selected_items_t& getSelectedItems( void ); @@ -210,6 +217,7 @@ public: virtual void draw(); virtual void deleteAllChildren(); + void stopAutoScollining() {mNeedsScroll = false;} void scrollToShowSelection(); void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } @@ -237,11 +245,15 @@ public: void setCallbackRegistrar(LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* registrar) { mCallbackRegistrar = registrar; } void setEnableRegistrar(LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* registrar) { mEnableRegistrar = registrar; } + void setForceArrange(bool force) { mForceArrange = force; } + LLPanel* getParentPanel() { return mParentPanel.get(); } // DEBUG only void dumpSelectionInformation(); virtual S32 notify(const LLSD& info) ; + + void setShowEmptyMessage(bool show_msg) { mShowEmptyMessage = show_msg; } bool useLabelSuffix() { return mUseLabelSuffix; } virtual void updateMenu(); @@ -275,6 +287,7 @@ protected: LLHandle mPopupMenuHandle; std::string mMenuFileName; + LLHandle mHoveredItem; selected_items_t mSelectedItems; bool mKeyboardSelection, mAllowMultiSelect, @@ -291,7 +304,8 @@ protected: mShowItemLinkOverlays, mShowSelectionContext, mShowSingleSelection, - mSuppressFolderMenu; + mSuppressFolderMenu, + mSingleFolderMode; // Renaming variables and methods LLFolderViewItem* mRenameItem; // The item currently being renamed @@ -330,6 +344,8 @@ protected: LLUICtrl::CommitCallbackRegistry::ScopedRegistrar* mCallbackRegistrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar* mEnableRegistrar; + + bool mForceArrange; public: static F32 sAutoOpenTime; diff --git a/indra/llui/llfolderviewitem.cpp b/indra/llui/llfolderviewitem.cpp index eba93beed9..0dc66bf37a 100644 --- a/indra/llui/llfolderviewitem.cpp +++ b/indra/llui/llfolderviewitem.cpp @@ -32,6 +32,7 @@ #include "llfolderview.h" #include "llfolderviewmodel.h" #include "llpanel.h" +#include "llcallbacklist.h" #include "llcriticaldamp.h" #include "llclipboard.h" #include "llfocusmgr.h" // gFocusMgr @@ -113,6 +114,8 @@ LLFolderViewItem::Params::Params() icon_width("icon_width", 0), text_pad("text_pad", 0), text_pad_right("text_pad_right", 0), + single_folder_mode("single_folder_mode", false), + double_click_override("double_click_override", false), arrow_size("arrow_size", 0), max_folder_item_overlap("max_folder_item_overlap", 0) { } @@ -151,7 +154,9 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) mTextPad(p.text_pad), mTextPadRight(p.text_pad_right), mArrowSize(p.arrow_size), - mMaxFolderItemOverlap(p.max_folder_item_overlap) + mSingleFolderMode(p.single_folder_mode), + mMaxFolderItemOverlap(p.max_folder_item_overlap), + mDoubleClickOverride(p.double_click_override) { if (!sColorSetInitialized) { @@ -162,7 +167,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE); sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE); - sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); + sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); sColorSetInitialized = true; } @@ -177,6 +182,7 @@ LLFolderViewItem::LLFolderViewItem(const LLFolderViewItem::Params& p) LLFolderViewItem::~LLFolderViewItem() { mViewModelItem = NULL; + gFocusMgr.removeKeyboardFocusWithoutCallback(this); } BOOL LLFolderViewItem::postBuild() @@ -395,7 +401,7 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height ) // it is purely visual, so it is fine to do at our laisure refreshSuffix(); } - mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(mLabelStyle)->getWidth(mLabelSuffix) + mLabelPaddingRight; + mLabelWidth = getLabelXPos() + getLabelFontForStyle(mLabelStyle)->getWidth(mLabel) + getLabelFontForStyle(LLFontGL::NORMAL)->getWidth(mLabelSuffix) + mLabelPaddingRight; mLabelWidthDirty = false; } @@ -412,7 +418,7 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height ) return *height; } -S32 LLFolderViewItem::getItemHeight() +S32 LLFolderViewItem::getItemHeight() const { return mItemHeight; } @@ -624,11 +630,14 @@ BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask ) getWindow()->setCursor(UI_CURSOR_NOLOCKED); } + root->clearHoveredItem(); return TRUE; } else { - getRoot()->setShowSelectionContext(FALSE); + LLFolderView* pRoot = getRoot(); + pRoot->setHoveredItem(this); + pRoot->setShowSelectionContext(FALSE); getWindow()->setCursor(UI_CURSOR_ARROW); // let parent handle this then... return FALSE; @@ -683,6 +692,13 @@ BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask ) void LLFolderViewItem::onMouseLeave(S32 x, S32 y, MASK mask) { mIsMouseOverTitle = false; + + // NOTE: LLViewerWindow::updateUI() calls "enter" before "leave"; if the mouse moved to another item, we can't just outright clear it + LLFolderView* pRoot = getRoot(); + if (this == pRoot->getHoveredItem()) + { + pRoot->clearHoveredItem(); + } } BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, @@ -889,7 +905,10 @@ void LLFolderViewItem::draw() getViewModelItem()->update(); - drawOpenFolderArrow(default_params, sFgColor); + if(!mSingleFolderMode) + { + drawOpenFolderArrow(default_params, sFgColor); + } drawHighlight(show_context, filled, sHighlightBgColor, sFlashBgColor, sFocusOutlineColor, sMouseOverColor); @@ -925,16 +944,43 @@ void LLFolderViewItem::draw() F32 text_left = (F32)getLabelXPos(); std::string combined_string = mLabel + mLabelSuffix; + const LLFontGL* suffix_font = getLabelFontForStyle(LLFontGL::NORMAL); + S32 filter_offset = mViewModelItem->getFilterStringOffset(); if (filter_string_length > 0) { - S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; + S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); + S32 top = getRect().getHeight() - TOP_PAD; + if(mLabelSuffix.empty() || (font == suffix_font)) + { + S32 left = ll_round(text_left) + font->getWidth(combined_string, 0, mViewModelItem->getFilterStringOffset()) - 2; S32 right = left + font->getWidth(combined_string, mViewModelItem->getFilterStringOffset(), filter_string_length) + 2; - S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); - S32 top = getRect().getHeight() - TOP_PAD; LLUIImage* box_image = default_params.selection_image; LLRect box_rect(left, top, right, bottom); box_image->draw(box_rect, sFilterBGColor); + } + else + { + S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); + if(label_filter_length > 0) + { + S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, llmin(filter_offset, (S32)mLabel.size())) - 2; + S32 right = left + font->getWidthF32(mLabel, filter_offset, label_filter_length) + 2; + LLUIImage* box_image = default_params.selection_image; + LLRect box_rect(left, top, right, bottom); + box_image->draw(box_rect, sFilterBGColor); + } + S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; + if(suffix_filter_length > 0) + { + S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); + S32 left = ll_round(text_left) + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset) - 2; + S32 right = left + suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length) + 2; + LLUIImage* box_image = default_params.selection_image; + LLRect box_rect(left, top, right, bottom); + box_image->draw(box_rect, sFilterBGColor); + } + } } LLColor4 color = (mIsSelected && filled) ? mFontHighlightColor : mFontColor; @@ -951,7 +997,7 @@ void LLFolderViewItem::draw() // if (!mLabelSuffix.empty()) { - font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, + suffix_font->renderUTF8( mLabelSuffix, 0, right_x, y, isFadeItem() ? color : (LLColor4)sSuffixColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE ); } @@ -961,12 +1007,35 @@ void LLFolderViewItem::draw() // if (filter_string_length > 0) { - S32 filter_offset = mViewModelItem->getFilterStringOffset(); - F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length); - F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; - font->renderUTF8( combined_string, filter_offset, match_string_left, yy, + if(mLabelSuffix.empty() || (font == suffix_font)) + { + F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, filter_offset + filter_string_length) - font->getWidthF32(combined_string, filter_offset, filter_string_length); + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + font->renderUTF8( combined_string, filter_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, filter_string_length, S32_MAX, &right_x, FALSE ); + } + else + { + S32 label_filter_length = llmin((S32)mLabel.size() - filter_offset, (S32)filter_string_length); + if(label_filter_length > 0) + { + F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, filter_offset + label_filter_length) - font->getWidthF32(mLabel, filter_offset, label_filter_length); + F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + font->renderUTF8( mLabel, filter_offset, match_string_left, yy, + sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, label_filter_length, S32_MAX, &right_x, FALSE ); + } + + S32 suffix_filter_length = label_filter_length > 0 ? filter_string_length - label_filter_length : filter_string_length; + if(suffix_filter_length > 0) + { + S32 suffix_offset = llmax(0, filter_offset - (S32)mLabel.size()); + F32 match_string_left = text_left + font->getWidthF32(mLabel, 0, mLabel.size()) + suffix_font->getWidthF32(mLabelSuffix, 0, suffix_offset + suffix_filter_length) - suffix_font->getWidthF32(mLabelSuffix, suffix_offset, suffix_filter_length); + F32 yy = (F32)getRect().getHeight() - suffix_font->getLineHeight() - (F32)mTextPad - (F32)TOP_PAD; + suffix_font->renderUTF8( mLabelSuffix, suffix_offset, match_string_left, yy, sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, suffix_filter_length, S32_MAX, &right_x, FALSE ); + } + } + } //Gilbert Linden 9-20-2012: Although this should be legal, removing it because it causes the mLabelSuffix rendering to @@ -1277,7 +1346,7 @@ BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL openitem child_selected = TRUE; } } - if(openitem && child_selected) + if(openitem && child_selected && !mSingleFolderMode) { setOpenArrangeRecursively(TRUE); } @@ -1702,6 +1771,11 @@ BOOL LLFolderViewFolder::isRemovable() return TRUE; } +void LLFolderViewFolder::destroyRoot() +{ + delete this; +} + // this is an internal method used for adding items to folders. void LLFolderViewFolder::addItem(LLFolderViewItem* item) { @@ -1770,7 +1844,19 @@ void LLFolderViewFolder::toggleOpen() // Force a folder open or closed void LLFolderViewFolder::setOpen(BOOL openitem) { - setOpenArrangeRecursively(openitem); + if(mSingleFolderMode) + { + // navigateToFolder can destroy this view + // delay it in case setOpen was called from click or key processing + doOnIdleOneTime([this]() + { + getViewModelItem()->navigateToFolder(); + }); + } + else + { + setOpenArrangeRecursively(openitem); + } } void LLFolderViewFolder::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse) @@ -1973,7 +2059,8 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) } if( !handled ) { - if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) + if((mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) + && !mSingleFolderMode) { toggleOpen(); handled = TRUE; @@ -1991,12 +2078,45 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) { BOOL handled = FALSE; + if(mSingleFolderMode) + { + static LLUICachedControl double_click_new_window("SingleModeDoubleClickOpenWindow", false); + if (double_click_new_window) + { + getViewModelItem()->navigateToFolder(true); + } + else + { + // navigating is going to destroy views and change children + // delay it untill handleDoubleClick processing is complete + doOnIdleOneTime([this]() + { + getViewModelItem()->navigateToFolder(false); + }); + } + return TRUE; + } + if( isOpen() ) { handled = childrenHandleDoubleClick( x, y, mask ) != NULL; } if( !handled ) { + if(mDoubleClickOverride) + { + static LLUICachedControl double_click_action("MultiModeDoubleClickFolder", false); + if (double_click_action == 1) + { + getViewModelItem()->navigateToFolder(true); + return TRUE; + } + if (double_click_action == 2) + { + getViewModelItem()->navigateToFolder(false, true); + return TRUE; + } + } if(mIndentation < x && x < mIndentation + (isCollapsed() ? 0 : mArrowSize) + mTextPad) { // don't select when user double-clicks plus sign diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index a5157266c5..5c2a1ecff0 100644 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -72,6 +72,8 @@ public: text_pad_right, arrow_size, max_folder_item_overlap; + Optional single_folder_mode, + double_click_override; Params(); }; @@ -121,6 +123,8 @@ protected: mIsMouseOverTitle, mAllowWear, mAllowDrop, + mSingleFolderMode, + mDoubleClickOverride, mSelectPending, mIsItemCut; @@ -174,7 +178,7 @@ public: // Finds width and height of this object and it's children. Also // makes sure that this view and it's children are the right size. virtual S32 arrange( S32* width, S32* height ); - virtual S32 getItemHeight(); + virtual S32 getItemHeight() const; virtual S32 getLabelXPos(); S32 getIconPad(); S32 getTextPad(); @@ -213,9 +217,9 @@ public: void setIsCurSelection(BOOL select) { mIsCurSelection = select; } - BOOL getIsCurSelection() { return mIsCurSelection; } + BOOL getIsCurSelection() const { return mIsCurSelection; } - BOOL hasVisibleChildren() { return mHasVisibleChildren; } + BOOL hasVisibleChildren() const { return mHasVisibleChildren; } // true if object can't have children virtual bool isFolderComplete() { return true; } @@ -264,7 +268,7 @@ public: virtual LLFolderView* getRoot(); virtual const LLFolderView* getRoot() const; BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor ); - S32 getIndentation() { return mIndentation; } + S32 getIndentation() const { return mIndentation; } virtual BOOL passedFilter(S32 filter_generation = -1); virtual BOOL isPotentiallyVisible(S32 filter_generation = -1); @@ -277,6 +281,8 @@ public: // Does not need filter update virtual void refreshSuffix(); + bool isSingleFolderMode() { return mSingleFolderMode; } + // LLView functionality virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask ); virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); @@ -387,6 +393,7 @@ public: // destroys this folder, and all children virtual void destroyView(); + void destroyRoot(); // whether known children are fully loaded (arrange sets to true) virtual bool isFolderComplete() { return mIsFolderComplete; } diff --git a/indra/llui/llfolderviewmodel.cpp b/indra/llui/llfolderviewmodel.cpp index 93122503d1..f217b743a0 100644 --- a/indra/llui/llfolderviewmodel.cpp +++ b/indra/llui/llfolderviewmodel.cpp @@ -34,7 +34,7 @@ bool LLFolderViewModelCommon::needsSort(LLFolderViewModelItem* item) return item->getSortVersion() < mTargetSortVersion; } -std::string LLFolderViewModelCommon::getStatusText() +std::string LLFolderViewModelCommon::getStatusText(bool is_empty_folder) { if (!contentsReady() || mFolderView->getViewModelItem()->getLastFilterGeneration() < getFilter().getCurrentGeneration()) { @@ -42,7 +42,7 @@ std::string LLFolderViewModelCommon::getStatusText() } else { - return getFilter().getEmptyLookupMessage(); + return getFilter().getEmptyLookupMessage(is_empty_folder); } } diff --git a/indra/llui/llfolderviewmodel.h b/indra/llui/llfolderviewmodel.h index c5e027d314..551a60e097 100644 --- a/indra/llui/llfolderviewmodel.h +++ b/indra/llui/llfolderviewmodel.h @@ -69,7 +69,7 @@ public: virtual bool checkFolder(const LLFolderViewModelItem* folder) const = 0; virtual void setEmptyLookupMessage(const std::string& message) = 0; - virtual std::string getEmptyLookupMessage() const = 0; + virtual std::string getEmptyLookupMessage(bool is_empty_folder = false) const = 0; virtual bool showAllResults() const = 0; @@ -125,7 +125,7 @@ public: virtual void setFolderView(LLFolderView* folder_view) = 0; virtual LLFolderViewFilter& getFilter() = 0; virtual const LLFolderViewFilter& getFilter() const = 0; - virtual std::string getStatusText() = 0; + virtual std::string getStatusText(bool is_empty_folder = false) = 0; virtual bool startDrag(std::vector& items) = 0; }; @@ -159,6 +159,8 @@ public: virtual void openItem( void ) = 0; virtual void closeItem( void ) = 0; virtual void selectItem(void) = 0; + + virtual void navigateToFolder(bool new_window = false, bool change_mode = false) = 0; virtual BOOL isItemWearable() const { return FALSE; } @@ -392,7 +394,7 @@ public: // sort everything mTargetSortVersion++; } - virtual std::string getStatusText(); + virtual std::string getStatusText(bool is_empty_folder = false); virtual void filter(); void setFolderView(LLFolderView* folder_view) { mFolderView = folder_view;} diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index e01aba402e..2791377a5e 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -37,6 +37,8 @@ #include "lluiimage.h" #include "llwindow.h" +#include "llgltexture.h" + static LLDefaultChildRegistry::Register r("icon"); LLIconCtrl::Params::Params() @@ -94,6 +96,22 @@ BOOL LLIconCtrl::handleHover(S32 x, S32 y, MASK mask) return LLUICtrl::handleHover(x, y, mask); } +void LLIconCtrl::onVisibilityChange(BOOL new_visibility) +{ + LLUICtrl::onVisibilityChange(new_visibility); + if (mPriority == LLGLTexture::BOOST_ICON) + { + if (new_visibility) + { + loadImage(getValue(), mPriority); + } + else + { + mImagep = nullptr; + } + } +} + // virtual // value might be a string or a UUID void LLIconCtrl::setValue(const LLSD& value) @@ -110,6 +128,14 @@ void LLIconCtrl::setValue(const LLSD& value, S32 priority) tvalue = LLSD(LLUUID(value.asString())); } LLUICtrl::setValue(tvalue); + + loadImage(tvalue, priority); +} + +void LLIconCtrl::loadImage(const LLSD& tvalue, S32 priority) +{ + if(mPriority == LLGLTexture::BOOST_ICON && !getVisible()) return; + if (tvalue.isUUID()) { mImagep = LLUI::getUIImageByID(tvalue.asUUID(), priority); diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index 9c3b517bca..e983d63a01 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -39,7 +39,9 @@ class LLUICtrlFactory; // Classes // -// +// Class for diplaying named UI textures +// Do not use for displaying textures from network, +// UI textures are stored permanently! class LLIconCtrl : public LLUICtrl { @@ -72,6 +74,7 @@ public: virtual BOOL handleHover(S32 x, S32 y, MASK mask); // lluictrl overrides + void onVisibilityChange(BOOL new_visibility); virtual void setValue(const LLSD& value ); std::string getImageName() const; @@ -95,6 +98,8 @@ protected: bool mInteractable; private: + void loadImage(const LLSD& value, S32 priority); + LLUIColor mColor; LLPointer mImagep; }; diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 77938edf27..7e4e828a88 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -89,6 +89,8 @@ LLLayoutPanel::~LLLayoutPanel() // probably not necessary, but... delete mResizeBar; mResizeBar = NULL; + + gFocusMgr.removeKeyboardFocusWithoutCallback(this); } F32 LLLayoutPanel::getAutoResizeFactor() const @@ -214,7 +216,8 @@ LLLayoutStack::Params::Params() drag_handle_first_indent("drag_handle_first_indent", 0), drag_handle_second_indent("drag_handle_second_indent", 0), drag_handle_thickness("drag_handle_thickness", 5), - drag_handle_shift("drag_handle_shift", 2) + drag_handle_shift("drag_handle_shift", 2), + drag_handle_color("drag_handle_color", LLUIColorTable::instance().getColor("ResizebarBody")) { addSynonym(border_size, "drag_handle_gap"); } @@ -234,7 +237,8 @@ LLLayoutStack::LLLayoutStack(const LLLayoutStack::Params& p) mDragHandleFirstIndent(p.drag_handle_first_indent), mDragHandleSecondIndent(p.drag_handle_second_indent), mDragHandleThickness(p.drag_handle_thickness), - mDragHandleShift(p.drag_handle_shift) + mDragHandleShift(p.drag_handle_shift), + mDragHandleColor(p.drag_handle_color()) { } @@ -282,6 +286,17 @@ void LLLayoutStack::draw() } } +void LLLayoutStack::deleteAllChildren() +{ + mPanels.clear(); + LLView::deleteAllChildren(); + + // Not really needed since nothing is left to + // display, but for the sake of consistency + updateFractionalSizes(); + mNeedsLayout = true; +} + void LLLayoutStack::removeChild(LLView* view) { LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast(view)); @@ -289,12 +304,14 @@ void LLLayoutStack::removeChild(LLView* view) if (embedded_panelp) { mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); - delete embedded_panelp; + LLView::removeChild(view); updateFractionalSizes(); mNeedsLayout = true; } - - LLView::removeChild(view); + else + { + LLView::removeChild(view); + } } BOOL LLLayoutStack::postBuild() @@ -508,6 +525,15 @@ void LLLayoutStack::updateLayout() mNeedsLayout = continue_animating; } // end LLLayoutStack::updateLayout +void LLLayoutStack::setPanelSpacing(S32 val) +{ + if (mPanelSpacing != val) + { + mPanelSpacing = val; + mNeedsLayout = true; + } +} + LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const { if (!panelp) return NULL; @@ -561,7 +587,7 @@ void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) resize_bar_bg_panel_p.follows.flags = FOLLOWS_ALL; resize_bar_bg_panel_p.tab_stop = false; resize_bar_bg_panel_p.background_visible = true; - resize_bar_bg_panel_p.bg_alpha_color = LLUIColorTable::instance().getColor("ResizebarBody"); + resize_bar_bg_panel_p.bg_alpha_color = mDragHandleColor; resize_bar_bg_panel_p.has_border = true; resize_bar_bg_panel_p.border.border_thickness = 1; resize_bar_bg_panel_p.border.highlight_light_color = LLUIColorTable::instance().getColor("ResizebarBorderLight"); diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h index f772dbc6b4..000b919ae7 100644 --- a/indra/llui/lllayoutstack.h +++ b/indra/llui/lllayoutstack.h @@ -59,6 +59,8 @@ public: Optional drag_handle_thickness; Optional drag_handle_shift; + Optional drag_handle_color; + Params(); }; @@ -67,6 +69,7 @@ public: virtual ~LLLayoutStack(); /*virtual*/ void draw(); + /*virtual*/ void deleteAllChildren(); /*virtual*/ void removeChild(LLView*); /*virtual*/ BOOL postBuild(); /*virtual*/ bool addChild(LLView* child, S32 tab_group = 0); @@ -88,6 +91,7 @@ public: void updateLayout(); S32 getPanelSpacing() const { return mPanelSpacing; } + void setPanelSpacing(S32 val); static void updateClass(); @@ -127,6 +131,7 @@ private: S32 mDragHandleSecondIndent; S32 mDragHandleThickness; S32 mDragHandleShift; + LLUIColor mDragHandleColor; }; // end class LLLayoutStack diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index 5cb840fd61..cebca70b59 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -487,9 +487,6 @@ void LLMenuItemGL::draw( void ) // let disabled items be highlighted, just don't draw them as such if( getEnabled() && getHighlight() && !mBriefItem) { - int debug_count = 0; - if (dynamic_cast(this)) - debug_count++; gGL.color4fv( mHighlightBackground.get().mV ); gl_rect_2d( 0, getRect().getHeight(), getRect().getWidth(), 0 ); @@ -576,13 +573,13 @@ void LLMenuItemGL::onVisibilityChange(BOOL new_visibility) // // This class represents a separator. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LLMenuItemSeparatorGL::Params::Params() -{ -} - LLMenuItemSeparatorGL::LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p) : LLMenuItemGL( p ) { + if (p.on_visible.isProvided()) + { + mVisibleSignal.connect(initEnableCallback(p.on_visible)); + } } //virtual @@ -599,6 +596,15 @@ void LLMenuItemSeparatorGL::draw( void ) gl_line_2d( PAD, y, getRect().getWidth() - PAD, y ); } +void LLMenuItemSeparatorGL::buildDrawLabel( void ) +{ + if (mVisibleSignal.num_slots() > 0) + { + bool visible = mVisibleSignal(this, LLSD()); + setVisible(visible); + } +} + BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask) { LLMenuGL* parent_menu = getMenu(); @@ -1882,6 +1888,13 @@ bool LLMenuGL::addContextChild(LLView* view, S32 tab_group) return false; } + +void LLMenuGL::deleteAllChildren() +{ + mItems.clear(); + LLUICtrl::deleteAllChildren(); +} + void LLMenuGL::removeChild( LLView* ctrl) { // previously a dynamic_cast with if statement to check validity diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index f84c4d41eb..87e3f18ebc 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -234,7 +234,9 @@ class LLMenuItemSeparatorGL : public LLMenuItemGL public: struct Params : public LLInitParam::Block { - Params(); + Optional on_visible; + Params() : on_visible("on_visible") + {} }; LLMenuItemSeparatorGL(const LLMenuItemSeparatorGL::Params& p = LLMenuItemSeparatorGL::Params()); @@ -243,7 +245,12 @@ public: /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + virtual void buildDrawLabel(); + /*virtual*/ U32 getNominalHeight( void ) const; + +private: + enable_signal_t mVisibleSignal; }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -442,6 +449,7 @@ public: /*virtual*/ void drawBackground(LLMenuItemGL* itemp, F32 alpha); /*virtual*/ void setVisible(BOOL visible); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); + /*virtual*/ void deleteAllChildren(); /*virtual*/ void removeChild( LLView* ctrl); /*virtual*/ BOOL postBuild(); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index b8f47ef6ba..8018365d3e 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -127,6 +127,7 @@ public: virtual void clearCtrls(); // overridden in LLPanelObject and LLPanelVolume // Border controls + const LLViewBorder* getBorder() const { return mBorder; } void addBorder( LLViewBorder::Params p); void addBorder(); void removeBorder(); diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp index 24794305ac..13ef0fdb7f 100644 --- a/indra/llui/llresizehandle.cpp +++ b/indra/llui/llresizehandle.cpp @@ -70,6 +70,11 @@ LLResizeHandle::LLResizeHandle(const LLResizeHandle::Params& p) } } +LLResizeHandle::~LLResizeHandle() +{ + gFocusMgr.removeKeyboardFocusWithoutCallback(this); +} + BOOL LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask) { diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h index 7541b9e6c0..ae20ecaa77 100644 --- a/indra/llui/llresizehandle.h +++ b/indra/llui/llresizehandle.h @@ -45,6 +45,7 @@ public: Params(); }; + ~LLResizeHandle(); protected: LLResizeHandle(const LLResizeHandle::Params&); friend class LLUICtrlFactory; diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index fde6de4921..735e2d529e 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -188,12 +188,12 @@ void LLScrollbar::setPageSize( S32 page_size ) } } -BOOL LLScrollbar::isAtBeginning() +bool LLScrollbar::isAtBeginning() const { return mDocPos == 0; } -BOOL LLScrollbar::isAtEnd() +bool LLScrollbar::isAtEnd() const { return mDocPos == getDocPosMax(); } @@ -591,7 +591,12 @@ void LLScrollbar::setValue(const LLSD& value) BOOL LLScrollbar::handleKeyHere(KEY key, MASK mask) { - BOOL handled = FALSE; + if (getDocPosMax() == 0 && !getVisible()) + { + return FALSE; + } + + BOOL handled = FALSE; switch( key ) { diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 5f2f490d81..9be9d22db8 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -105,8 +105,8 @@ public: bool setDocPos( S32 pos, BOOL update_thumb = TRUE ); S32 getDocPos() const { return mDocPos; } - BOOL isAtBeginning(); - BOOL isAtEnd(); + bool isAtBeginning() const; + bool isAtEnd() const; // Setting both at once. void setDocParams( S32 size, S32 pos ); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 3db38bbfac..ad32f7186c 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -105,8 +105,8 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) mBorder = LLUICtrlFactory::create (params); LLView::addChild( mBorder ); - mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 ); - mInnerRect.stretch( -getBorderWidth() ); + mInnerRect = getLocalRect(); + mInnerRect.stretch( -getBorderWidth() ); LLRect vertical_scroll_rect = mInnerRect; vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - scrollbar_size; @@ -124,8 +124,9 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) mScrollbar[VERTICAL] = LLUICtrlFactory::create (sbparams); LLView::addChild( mScrollbar[VERTICAL] ); - LLRect horizontal_scroll_rect = mInnerRect; - horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size; + LLRect horizontal_scroll_rect; + horizontal_scroll_rect.mTop = scrollbar_size; + horizontal_scroll_rect.mRight = mInnerRect.getWidth(); sbparams.name("scrollable horizontal"); sbparams.rect(horizontal_scroll_rect); sbparams.orientation(LLScrollbar::HORIZONTAL); @@ -134,7 +135,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) sbparams.page_size(mInnerRect.getWidth()); sbparams.step_size(VERTICAL_MULTIPLE); sbparams.visible(false); - sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT); + sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM); sbparams.change_callback(p.scroll_callback); mScrollbar[HORIZONTAL] = LLUICtrlFactory::create (sbparams); LLView::addChild( mScrollbar[HORIZONTAL] ); diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index c14099dbd5..dacea2a987 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -98,8 +98,10 @@ public: void pageDown(S32 overlap = 0); void goToTop(); void goToBottom(); - bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); } - bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); } + bool isAtTop() const { return mScrollbar[VERTICAL]->isAtBeginning(); } + bool isAtBottom() const { return mScrollbar[VERTICAL]->isAtEnd(); } + S32 getDocPosVertical() const { return mScrollbar[VERTICAL]->getDocPos(); } + S32 getDocPosHorizontal() const { return mScrollbar[HORIZONTAL]->getDocPos(); } S32 getBorderWidth() const; // LLView functionality diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 8c841540a5..76b9e448a1 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -605,6 +605,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) LLButton* tab_button = getTab(index)->mButton; gFocusMgr.setMouseCapture(this); tab_button->setFocus(TRUE); + mMouseDownTimer.start(); } } if (handled) { @@ -653,7 +654,11 @@ BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask ) handled = LLPanel::handleHover(x, y, mask); } - commitHoveredButton(x, y); + F32 drag_delay = 0.25f; // filter out clicks from dragging + if (mMouseDownTimer.getElapsedTimeF32() > drag_delay) + { + commitHoveredButton(x, y); + } return handled; } @@ -699,6 +704,7 @@ BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask ) } commitHoveredButton(x, y); + mMouseDownTimer.stop(); LLPanel* cur_panel = getCurrentPanel(); if (hasMouseCapture()) { @@ -1002,7 +1008,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) } else { - //Scip tab button space if they are invisible(EXT - 576) + // Skip tab button space if tabs are invisible (EXT-576) tab_panel_top = getRect().getHeight(); tab_panel_bottom = LLPANEL_BORDER_WIDTH; } @@ -1017,9 +1023,9 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) } else { - tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH, + tab_panel_rect = LLRect(LLPANEL_BORDER_WIDTH * 3, tab_panel_top, - getRect().getWidth()-LLPANEL_BORDER_WIDTH, + getRect().getWidth() - LLPANEL_BORDER_WIDTH * 2, tab_panel_bottom ); } child->setFollowsAll(); @@ -1106,7 +1112,7 @@ void LLTabContainer::addTabPanel(const TabPanelParams& panel) p.follows.flags = p.follows.flags() | FOLLOWS_TOP; } else - { + { p.name("htab_"+std::string(child->getName())); p.visible(false); p.image_unselected(tab_img); diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 8f8cedb1b9..aa4a08c4ff 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -320,6 +320,7 @@ private: LLUIColor mTabsFlashingColor; S32 mTabIconCtrlPad; bool mUseTabEllipses; + LLFrameTimer mMouseDownTimer; }; #endif // LL_TABCONTAINER_H diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 82a3c01c6d..8732a7ce45 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -361,7 +361,7 @@ void LLTextBase::onValueChange(S32 start, S32 end) { } -std::vector LLTextBase::getSelctionRects() +std::vector LLTextBase::getSelectionRects() { // Nor supposed to be called without selection llassert(hasSelection()); @@ -458,7 +458,7 @@ void LLTextBase::drawSelectionBackground() // Draw selection even if we don't have keyboard focus for search/replace if (hasSelection() && !mLineInfoList.empty()) { - std::vector selection_rects = getSelctionRects(); + std::vector selection_rects = getSelectionRects(); // Draw the selection box (we're using a box instead of reversing the colors on the selected text). gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); @@ -1356,9 +1356,9 @@ void LLTextBase::draw() drawCursor(); } - mDocumentView->setVisible(FALSE); + mDocumentView->setVisibleDirect(FALSE); LLUICtrl::draw(); - mDocumentView->setVisible(TRUE); + mDocumentView->setVisibleDirect(TRUE); } @@ -3464,7 +3464,7 @@ bool LLNormalTextSegment::getDimensionsF32(S32 first_char, S32 num_chars, F32& w height = mFontHeight; const LLWString &text = getWText(); // if last character is a newline, then return true, forcing line break - width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars); + width = mStyle->getFont()->getWidthF32(text.c_str(), mStart + first_char, num_chars, true); } return false; } diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index e3cf56a5ee..3611ab0499 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -638,7 +638,7 @@ protected: return mLabel.getString() + getToolTip(); } - std::vector getSelctionRects(); + std::vector getSelectionRects(); protected: // text segmentation and flow diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index c567451973..521dabf9d4 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -171,7 +171,8 @@ void LLTextBox::reshapeToFitText(BOOL called_from_parent) S32 width = getTextPixelWidth(); S32 height = getTextPixelHeight(); - reshape( width + 2 * mHPad, height + 2 * mVPad, called_from_parent ); + //consider investigating reflow() to find missing width pixel (see SL-17045 changes) + reshape( width + 2 * mHPad + 1, height + 2 * mVPad, called_from_parent ); } diff --git a/indra/llui/lltooltip.cpp b/indra/llui/lltooltip.cpp index 2f56a8b1d0..a6552d4ff1 100644 --- a/indra/llui/lltooltip.cpp +++ b/indra/llui/lltooltip.cpp @@ -163,6 +163,7 @@ LLToolTip::LLToolTip(const LLToolTip::Params& p) : LLPanel(p), mHasClickCallback(p.click_callback.isProvided()), mPadding(p.padding), + mMaxWidth(p.max_width), mTextBox(NULL), mInfoButton(NULL), mPlayMediaButton(NULL), @@ -272,7 +273,7 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p) // do this *after* we've had our size set in LLPanel::initFromParams(); const S32 REALLY_LARGE_HEIGHT = 10000; - mTextBox->reshape(p.max_width, REALLY_LARGE_HEIGHT); + mTextBox->reshape(mMaxWidth, REALLY_LARGE_HEIGHT); if (p.styled_message.isProvided()) { @@ -288,16 +289,19 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p) mTextBox->setText(p.message()); } - S32 text_width = llmin(p.max_width(), mTextBox->getTextPixelWidth() + 1); + updateTextBox(); + snapToChildren(); +} + +void LLToolTip::updateTextBox() +{ + S32 text_width = llmin(mMaxWidth, mTextBox->getTextPixelWidth() + 1); S32 text_height = mTextBox->getTextPixelHeight(); mTextBox->reshape(text_width, text_height); - if (mInfoButton) - { - LLRect text_rect = mTextBox->getRect(); - LLRect icon_rect = mInfoButton->getRect(); - mTextBox->translate(0, icon_rect.getCenterY() - text_rect.getCenterY()); - } - +} + +void LLToolTip::snapToChildren() +{ // reshape tooltip panel to fit text box LLRect tooltip_rect = calcBoundingRect(); tooltip_rect.mTop += mPadding; @@ -305,7 +309,14 @@ void LLToolTip::initFromParams(const LLToolTip::Params& p) tooltip_rect.mBottom = 0; tooltip_rect.mLeft = 0; - mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding)); + if (mInfoButton) + { + mTextBox->reshape(mTextBox->getRect().getWidth(), llmax(mTextBox->getRect().getHeight(), tooltip_rect.getHeight() - 2 * mPadding)); + + LLRect text_rect = mTextBox->getRect(); + LLRect icon_rect = mInfoButton->getRect(); + mInfoButton->translate(0, text_rect.getCenterY() - icon_rect.getCenterY()); + } setShape(tooltip_rect); } @@ -428,7 +439,10 @@ void LLToolTipMgr::createToolTip(const LLToolTip::Params& params) } tooltip_params.rect = LLRect (0, 1, 1, 0); - mToolTip = LLUICtrlFactory::create (tooltip_params); + if (tooltip_params.create_callback.isProvided()) + mToolTip = tooltip_params.create_callback()(tooltip_params); + else + mToolTip = LLUICtrlFactory::create (tooltip_params); gToolTipView->addChild(mToolTip); diff --git a/indra/llui/lltooltip.h b/indra/llui/lltooltip.h index 0b1fbe5367..86943625ff 100644 --- a/indra/llui/lltooltip.h +++ b/indra/llui/lltooltip.h @@ -68,6 +68,7 @@ public: struct Params : public LLInitParam::Block { typedef boost::function click_callback_t; + typedef boost::function create_callback_t; Optional message; Multiple styled_message; @@ -84,6 +85,8 @@ public: Optional time_based_media, web_based_media, media_playing; + Optional create_callback; + Optional create_params; Optional click_callback, click_playmedia_callback, click_homepage_callback; @@ -103,11 +106,15 @@ public: bool hasClickCallback(); LLToolTip(const Params& p); - void initFromParams(const LLToolTip::Params& params); + virtual void initFromParams(const LLToolTip::Params& params); void getToolTipMessage(std::string & message); -private: +protected: + void updateTextBox(); + void snapToChildren(); + +protected: class LLTextBox* mTextBox; class LLButton* mInfoButton; class LLButton* mPlayMediaButton; @@ -117,6 +124,7 @@ private: LLFrameTimer mVisibleTimer; bool mHasClickCallback; S32 mPadding; // pixels + S32 mMaxWidth; }; // used for the inspector tooltips which need different background images etc. diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 244f0c6f00..096336045c 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -200,7 +200,6 @@ LLUIColor LLUIColorTable::getColor(const std::string& name, const LLColor4& defa void LLUIColorTable::setColor(const std::string& name, const LLColor4& color) { setColor(name, color, mUserSetColors); - setColor(name, color, mLoadedColors); } bool LLUIColorTable::loadFromSettings() @@ -229,6 +228,11 @@ void LLUIColorTable::saveUserSettings() const it != mUserSetColors.end(); ++it) { + // Compare user color value with the default value, skip if equal + string_color_map_t::const_iterator itd = mLoadedColors.find(it->first); + if(itd != mLoadedColors.end() && itd->second == it->second) + continue; + ColorEntryParams color_entry; color_entry.name = it->first; color_entry.color.value = it->second; diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 2196ba201b..21afcae7c3 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -531,6 +531,15 @@ void LLUICtrl::setControlVariable(LLControlVariable* control) } } +void LLUICtrl::removeControlVariable() +{ + if (mControlVariable) + { + mControlConnection.disconnect(); + mControlVariable = NULL; + } +} + //virtual void LLUICtrl::setControlName(const std::string& control_name, LLView *context) { diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 67dd24341c..be1c7dd0b6 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -175,6 +175,7 @@ public: bool setControlValue(const LLSD& value); void setControlVariable(LLControlVariable* control); virtual void setControlName(const std::string& control, LLView *context = NULL); + void removeControlVariable(); LLControlVariable* getControlVariable() { return mControlVariable; } diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 9ba71913d0..da7868d804 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -311,7 +311,13 @@ bool LLView::addChild(LLView* child, S32 tab_group) } child->mParentView = this; - updateBoundingRect(); + if (getVisible() && child->getVisible()) + { + // if child isn't visible it won't affect bounding rect + // if current view is not visible it will be recalculated + // on visibility change + updateBoundingRect(); + } mLastTabGroup = tab_group; return true; } @@ -576,9 +582,12 @@ void LLView::deleteAllChildren() while (!mChildList.empty()) { - LLView* viewp = mChildList.front(); - delete viewp; // will remove the child from mChildList + LLView* viewp = mChildList.front(); + viewp->mParentView = NULL; + delete viewp; + mChildList.pop_front(); } + updateBoundingRect(); } void LLView::setAllChildrenEnabled(BOOL b) @@ -877,6 +886,17 @@ LLView* LLView::childFromPoint(S32 x, S32 y, bool recur) return 0; } +F32 LLView::getTooltipTimeout() +{ + static LLCachedControl tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f); + static LLCachedControl tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f); + // allow "scrubbing" over ui by showing next tooltip immediately + // if previous one was still visible + return (F32)(LLToolTipMgr::instance().toolTipVisible() + ? tooltip_fast_delay + : tooltip_delay); +} + BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -886,14 +906,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) std::string tooltip = getToolTip(); if (!tooltip.empty()) { - static LLCachedControl tooltip_fast_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipFastDelay", 0.1f); - static LLCachedControl tooltip_delay(*LLUI::getInstance()->mSettingGroups["config"], "ToolTipDelay", 0.7f); static LLCachedControl allow_ui_tooltips(*LLUI::getInstance()->mSettingGroups["config"], "BasicUITooltips", true); - // allow "scrubbing" over ui by showing next tooltip immediately - // if previous one was still visible - F32 timeout = LLToolTipMgr::instance().toolTipVisible() - ? tooltip_fast_delay - : tooltip_delay; // Even if we don't show tooltips, consume the event, nothing below should show tooltip if (allow_ui_tooltips) @@ -901,7 +914,7 @@ BOOL LLView::handleToolTip(S32 x, S32 y, MASK mask) LLToolTipMgr::instance().show(LLToolTip::Params() .message(tooltip) .sticky_rect(calcScreenRect()) - .delay_time(timeout)); + .delay_time(getTooltipTimeout())); } handled = TRUE; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index bec45df78a..7360fd0fb9 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -243,6 +243,7 @@ public: ECursorType getHoverCursor() { return mHoverCursor; } + static F32 getTooltipTimeout(); virtual const std::string getToolTip() const { return mToolTipMsg.getString(); } void sendChildToFront(LLView* child); @@ -287,6 +288,7 @@ public: void setAllChildrenEnabled(BOOL b); virtual void setVisible(BOOL visible); + void setVisibleDirect(BOOL visible) { mVisible = visible; } const BOOL& getVisible() const { return mVisible; } virtual void setEnabled(BOOL enabled); BOOL getEnabled() const { return mEnabled; } diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index b1408d894f..f435d46584 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -118,7 +118,8 @@ public: // Sets cursor, may set to arrow+hourglass virtual void setCursor(ECursorType cursor) { mNextCursor = cursor; }; - virtual ECursorType getCursor() const; + virtual ECursorType getCursor() const; + virtual ECursorType getNextCursor() const { return mNextCursor; }; virtual void updateCursor() = 0; virtual void captureMouse() = 0; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 1f91cbbaa8..42ec4ee29d 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -352,6 +352,11 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool void run() override; + void glReady() + { + mGLReady = true; + } + // initialzie DXGI adapter (for querying available VRAM) void initDX(); @@ -410,6 +415,9 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool HWND mWindowHandle = NULL; HDC mhDC = 0; + // *HACK: Attempt to prevent startup crashes by deferring memory accounting + // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 + bool mGLReady = false; // best guess at available video memory in MB std::atomic mAvailableVRAM; @@ -1720,6 +1728,13 @@ const S32 max_format = (S32)num_formats - 1; // ok to post quit messages now mPostQuit = TRUE; + // *HACK: Attempt to prevent startup crashes by deferring memory accounting + // until after some graphics setup. See SL-20177. -Cosmic,2023-09-18 + mWindowThread->post([=]() + { + mWindowThread->glReady(); + }); + if (auto_show) { show(); @@ -4692,6 +4707,8 @@ void debugEnumerateGraphicsAdapters() void LLWindowWin32::LLWindowWin32Thread::initDX() { + if (!mGLReady) { return; } + if (mDXGIAdapter == NULL) { debugEnumerateGraphicsAdapters(); @@ -4726,6 +4743,8 @@ void LLWindowWin32::LLWindowWin32Thread::initDX() void LLWindowWin32::LLWindowWin32Thread::initD3D() { + if (!mGLReady) { return; } + if (mDXGIAdapter == NULL && mD3DDevice == NULL && mWindowHandle != 0) { mD3D = Direct3DCreate9(D3D_SDK_VERSION); @@ -4752,6 +4771,8 @@ void LLWindowWin32::LLWindowWin32Thread::initD3D() void LLWindowWin32::LLWindowWin32Thread::updateVRAMUsage() { LL_PROFILE_ZONE_SCOPED; + if (!mGLReady) { return; } + if (mDXGIAdapter != nullptr) { // NOTE: what lies below is hand wavy math based on compatibility testing and observation against a variety of hardware @@ -4825,8 +4846,6 @@ void LLWindowWin32::LLWindowWin32Thread::run() sWindowThreadId = std::this_thread::get_id(); LogChange logger("Window"); - initDX(); - //as good a place as any to up the MM timer resolution (see ms_sleep) //attempt to set timer resolution to 1ms TIMECAPS tc; @@ -4839,9 +4858,12 @@ void LLWindowWin32::LLWindowWin32Thread::run() { LL_PROFILE_ZONE_SCOPED_CATEGORY_WIN32; + // lazily call initD3D inside this loop to catch when mGLReady has been set to true + initDX(); + if (mWindowHandle != 0) { - // lazily call initD3D inside this loop to catch when mWindowHandle has been set + // lazily call initD3D inside this loop to catch when mWindowHandle has been set, and mGLReady has been set to true // *TODO: Shutdown if this fails when mWindowHandle exists initD3D(); diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp index ead8634df4..2960ecf829 100644 --- a/indra/llxml/llcontrol.cpp +++ b/indra/llxml/llcontrol.cpp @@ -774,7 +774,6 @@ U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require return 0; } - U32 item = 0; U32 validitems = 0; S32 version; @@ -808,8 +807,6 @@ U32 LLControlGroup::loadFromFileLegacy(const std::string& filename, BOOL require } // Got an item. Load it up. - item++; - // If not declared, assume it's a string if (!declared) { diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index c6d82ea260..8eef7cd9d4 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -195,6 +195,7 @@ set(viewer_SOURCE_FILES llfloaterbuyland.cpp llfloatercamera.cpp llfloatercamerapresets.cpp + llfloaterchangeitemthumbnail.cpp llfloaterchatvoicevolume.cpp llfloaterclassified.cpp llfloatercolorpicker.cpp @@ -231,6 +232,7 @@ set(viewer_SOURCE_FILES llfloaterimsession.cpp llfloaterimcontainer.cpp llfloaterinspect.cpp + llfloaterinventorysettings.cpp llfloaterjoystick.cpp llfloaterlagmeter.cpp llfloaterland.cpp @@ -246,12 +248,12 @@ set(viewer_SOURCE_FILES llfloatermyscripts.cpp llfloatermyenvironment.cpp llfloaternamedesc.cpp + llfloaternewfeaturenotification.cpp llfloaternotificationsconsole.cpp llfloaternotificationstabbed.cpp - llfloateroutfitphotopreview.cpp llfloaterobjectweights.cpp llfloateropenobject.cpp - llfloatersimpleoutfitsnapshot.cpp + llfloatersimplesnapshot.cpp llfloaterpathfindingcharacters.cpp llfloaterpathfindingconsole.cpp llfloaterpathfindinglinksets.cpp @@ -266,7 +268,6 @@ set(viewer_SOURCE_FILES llfloaterpreferenceviewadvanced.cpp llfloaterpreviewtrash.cpp llfloaterprofiletexture.cpp - llfloaterproperties.cpp llfloaterregiondebugconsole.cpp llfloaterregioninfo.cpp llfloaterreporter.cpp @@ -338,10 +339,13 @@ set(viewer_SOURCE_FILES llinspectgroup.cpp llinspectobject.cpp llinspectremoteobject.cpp + llinspecttexture.cpp llinspecttoast.cpp llinventorybridge.cpp llinventoryfilter.cpp llinventoryfunctions.cpp + llinventorygallery.cpp + llinventorygallerymenu.cpp llinventoryicon.cpp llinventoryitemslist.cpp llinventorylistitem.cpp @@ -571,6 +575,7 @@ set(viewer_SOURCE_FILES lltextureinfodetails.cpp lltexturestats.cpp lltextureview.cpp + llthumbnailctrl.cpp lltinygltfhelper.cpp lltoast.cpp lltoastalertpanel.cpp @@ -840,6 +845,7 @@ set(viewer_HEADER_FILES llfloaterbuycurrencyhtml.h llfloaterbuyland.h llfloatercamerapresets.h + llfloaterchangeitemthumbnail.h llfloatercamera.h llfloaterchatvoicevolume.h llfloaterclassified.h @@ -880,6 +886,7 @@ set(viewer_HEADER_FILES llfloaterimsession.h llfloaterimcontainer.h llfloaterinspect.h + llfloaterinventorysettings.h llfloaterjoystick.h llfloaterlagmeter.h llfloaterland.h @@ -895,12 +902,12 @@ set(viewer_HEADER_FILES llfloatermyscripts.h llfloatermyenvironment.h llfloaternamedesc.h + llfloaternewfeaturenotification.h llfloaternotificationsconsole.h llfloaternotificationstabbed.h - llfloateroutfitphotopreview.h llfloaterobjectweights.h llfloateropenobject.h - llfloatersimpleoutfitsnapshot.h + llfloatersimplesnapshot.h llfloaterpathfindingcharacters.h llfloaterpathfindingconsole.h llfloaterpathfindinglinksets.h @@ -915,7 +922,6 @@ set(viewer_HEADER_FILES llfloaterpreferenceviewadvanced.h llfloaterpreviewtrash.h llfloaterprofiletexture.h - llfloaterproperties.h llfloaterregiondebugconsole.h llfloaterregioninfo.h llfloaterreporter.h @@ -985,10 +991,13 @@ set(viewer_HEADER_FILES llinspectgroup.h llinspectobject.h llinspectremoteobject.h + llinspecttexture.h llinspecttoast.h llinventorybridge.h llinventoryfilter.h llinventoryfunctions.h + llinventorygallery.h + llinventorygallerymenu.h llinventoryicon.h llinventoryitemslist.h llinventorylistitem.h @@ -1209,6 +1218,7 @@ set(viewer_HEADER_FILES lltextureinfodetails.h lltexturestats.h lltextureview.h + llthumbnailctrl.h lltinygltfhelper.h lltoast.h lltoastalertpanel.h diff --git a/indra/newview/app_settings/cmd_line.xml b/indra/newview/app_settings/cmd_line.xml index e16a5c7e76..340334aee8 100644 --- a/indra/newview/app_settings/cmd_line.xml +++ b/indra/newview/app_settings/cmd_line.xml @@ -209,6 +209,12 @@ NoAudio + nofmod + + map-to + UseMediaPluginsForStreamingAudio + + noninteractive desc diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini index 9699eda96b..4e177ab518 100644 --- a/indra/newview/app_settings/keywords.ini +++ b/indra/newview/app_settings/keywords.ini @@ -274,6 +274,7 @@ INVENTORY_NOTECARD Passed to task inventory library functions to reference notec INVENTORY_BODYPART Passed to task inventory library functions to reference body parts INVENTORY_ANIMATION Passed to task inventory library functions to reference animations INVENTORY_GESTURE Passed to task inventory library functions to reference gestures +INVENTORY_MATERIAL Passed to task inventory library functions to reference materials INVENTORY_ALL Passed to task inventory library functions to reference all inventory items INVENTORY_NONE Returned by llGetInventoryType when no item is found diff --git a/indra/newview/app_settings/keywords_lsl_default.xml b/indra/newview/app_settings/keywords_lsl_default.xml index d641883d5a..893b017367 100644 --- a/indra/newview/app_settings/keywords_lsl_default.xml +++ b/indra/newview/app_settings/keywords_lsl_default.xml @@ -1963,6 +1963,15 @@ tooltip + INVENTORY_MATERIAL + + type + integer + value + 57 + tooltip + + JSON_APPEND type diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index 2a26cb9a43..482012cdd6 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -73,7 +73,6 @@ Avatar Voice --> - Capabilities diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 85dfe688a7..9a1da5311c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -250,7 +250,7 @@ TextureLivePreview Comment - Preview selections in texture picker immediately + Preview selections in texture picker or material picker immediately Persist 1 Type @@ -5012,17 +5012,6 @@ Boolean Value 0 - - InventoryInboxToggleState - - Comment - Stores the open/closed state of inventory Received items panel - Persist - 1 - Type - Boolean - Value - 0 InventoryLinking @@ -5519,6 +5508,17 @@ Value 0 + LastUIFeatureVersion + + Comment + UI Feature Version number for tracking feature notification between viewer builds + Persist + 1 + Type + LLSD + Value + + LastFindPanel Comment @@ -7088,6 +7088,17 @@ Value 0 + UseMediaPluginsForStreamingAudio + + Comment + Use media plugins (VLC) for streaming audio. + Persist + 1 + Type + Boolean + Value + 0 + NoHardwareProbe Comment @@ -9035,16 +9046,16 @@ Value 0 - RenderLocalLights + RenderLocalLightCount Comment - Whether or not to render local lights. + Number of local lights to render. Persist 1 Type - Boolean + S32 Value - 1 + 256 RenderShadowSplitExponent @@ -9340,6 +9351,17 @@ Value 0 + RenderPostProcessingHDR + + Comment + Enable HDR for post processing buffer + Persist + 1 + Type + Boolean + Value + 0 + RenderMaxOpenGLVersion Comment @@ -9626,7 +9648,7 @@ Type F32 Value - -0.001 + -0.0002 RenderSpotShadowOffset @@ -10155,6 +10177,17 @@ Value 9 + RenderGlowHDR + + Comment + Enable HDR for glow map + Persist + 1 + Type + Boolean + Value + 0 + RenderGlowStrength Comment @@ -10203,6 +10236,17 @@ Value 1.3 + RenderGlowNoise + + Comment + Enables glow noise (dithering). Reduces banding from glow in certain cases. + Persist + 1 + Type + Boolean + Value + 1 + DisableAllRenderTypes Comment @@ -10610,7 +10654,7 @@ Type F32 Value - 0.5 + 1.0 RenderSkyAutoAdjustHDRScale @@ -10623,6 +10667,73 @@ Value 2.0 + RendeSkyAutoAdjustBlueHorizonScale + + Comment + Blue Horizon Scale value to use when auto-adjusting legacy skies + Persist + 1 + Type + F32 + Value + 1.0 + + RendeSkyAutoAdjustBlueDensityScale + + Comment + Blue Horizon Scale value to use when auto-adjusting legacy skies + Persist + 1 + Type + F32 + Value + 1.0 + + RenderSkyAutoAdjustSunColorScale + + Comment + Sun color scalar when auto-adjusting legacy skies + Persist + 1 + Type + F32 + Value + 1.0 + + RenderSkyAutoAdjustProbeAmbiance + + Comment + Probe ambiance value when auto-adjusting legacy skies + Persist + 1 + Type + F32 + Value + 0.001 + + RenderSkySunlightScale + + Comment + Sunlight scale fudge factor for matching with pre-PBR viewer + Persist + 1 + Type + F32 + Value + 1.0 + + RenderSkyAmbientScale + + Comment + Ambient scale fudge factor for matching with pre-PBR viewer + Persist + 1 + Type + F32 + Value + 0.7 + + RenderReflectionProbeMaxLocalLightAmbiance Comment @@ -11531,6 +11642,17 @@ Value 1 + SelectReflectionProbes + + Comment + Select reflection probes + Persist + 0 + Type + Boolean + Value + 0 + SelectOwnedOnly Comment @@ -11666,11 +11788,11 @@ ShowBanLines Comment - Show in-world ban/access borders + Show in-world ban/access borders, 0 - do not show, 1 - show on collision, 2 - show on proximity Persist 1 Type - Boolean + S32 Value 1 @@ -12866,39 +12988,6 @@ Value 0 - AmbientDisable - - Comment - If TRUE, ambient light has no effect - Persist - 0 - Type - Boolean - Value - 0 - - SunlightDisable - - Comment - If TRUE, sunlight has no effect - Persist - 0 - Type - Boolean - Value - 0 - - LocalLightDisable - - Comment - If TRUE, local lights have no effect - Persist - 0 - Type - Boolean - Value - 0 - TextureDiscardLevel Comment @@ -13171,13 +13260,13 @@ TranslationService Comment - Translation API to use. (google|bing) + Translation API to use. (google|azure) Persist 1 Type String Value - bing + azure GoogleTranslateAPIKey @@ -13193,7 +13282,7 @@ BingTranslateAPIKey Comment - Bing AppID to use with the Microsoft Translator API + (Deprecated) Bing AppID to use with the Microsoft Translator API Persist 1 Type @@ -13201,6 +13290,28 @@ Value + AzureTranslateAPIKey + + Comment + Azure Translation service data to use with the MS Azure Translator API + Persist + 1 + Type + LLSD + Value + + + DeepLTranslateAPIKey + + Comment + DeepL Translation service data to use with the DeepL Translator API + Persist + 1 + Type + LLSD + Value + + TutorialURL Comment @@ -15536,6 +15647,17 @@ Value 1 + BatchSizeAIS3 + + Comment + Amount of folder ais packs into category subset request + Persist + 1 + Type + S32 + Value + 20 + PoolSizeAIS Comment @@ -15543,7 +15665,7 @@ Type U32 Value - 1 + 20 PoolSizeUpload @@ -17250,17 +17372,6 @@ Value 0 - 360CaptureUseInterestListCap - - Comment - Flag if set, uses the new InterestList cap to ask the simulator for full content - Persist - 1 - Type - Boolean - Value - 1 - 360CaptureJPEGEncodeQuality Comment @@ -17382,5 +17493,71 @@ Value 0 + FindOriginalOpenWindow + + Comment + Sets the action for 'Find original' and 'Show in Inventory' (0 - shows item in main Inventory, 1 - opens a new single-folder window) + Persist + 1 + Type + Boolean + Value + 0 + + StatsReportMaxDuration + + Comment + Maximum seconds for viewer stats file data, prevents huge file + Persist + 1 + Type + F32 + Value + 300 + + StatsReportFileInterval + + Comment + Interval to save viewer stats file data + Persist + 1 + Type + F32 + Value + 0.2 + + StatsReportSkipZeroDataSaves + + Comment + In viewer stats data file, skip saving entry if there is no data + Persist + 0 + Type + Boolean + Value + 0 + +MultiModeDoubleClickFolder + + Comment + Sets the action for Double-click on folder in multi-folder view (0 - expands and collapses folder, 1 - opens a new window, 2 – stays in current floater but switches to SFV) + Persist + 1 + Type + U32 + Value + 0 + +SingleModeDoubleClickOpenWindow + + Comment + Sets the action for Double-click on folder in single-folder view (0 - stays in current window, 1 - opens a new window) + Persist + 1 + Type + Boolean + Value + 0 + diff --git a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl index c8eaba6418..f9ebf33b4a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/deferredUtil.glsl @@ -382,8 +382,7 @@ vec3 pbrIbl(vec3 diffuseColor, vec3 irradiance, // irradiance map sample float ao, // ambient occlusion factor float nv, // normal dot view vector - float perceptualRough, - out vec3 specContrib) + float perceptualRough) { // retrieve a scale and bias to F0. See [1], Figure 3 vec2 brdf = BRDF(clamp(nv, 0, 1), 1.0-perceptualRough); @@ -393,23 +392,9 @@ vec3 pbrIbl(vec3 diffuseColor, vec3 diffuse = diffuseLight * diffuseColor; vec3 specular = specularLight * (specularColor * brdf.x + brdf.y); - specContrib = specular * ao; - return (diffuse + specular) * ao; } -vec3 pbrIbl(vec3 diffuseColor, - vec3 specularColor, - vec3 radiance, // radiance map sample - vec3 irradiance, // irradiance map sample - float ao, // ambient occlusion factor - float nv, // normal dot view vector - float perceptualRough) -{ - vec3 specContrib; - return pbrIbl(diffuseColor, specularColor, radiance, irradiance, ao, nv, perceptualRough, specContrib); -} - // Encapsulate the various inputs used by the various functions in the shading equation // We store values in this struct to simplify the integration of alternative implementations @@ -475,8 +460,7 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 n, // normal vec3 v, // surface point to camera - vec3 l, //surface point to light - out vec3 specContrib) //specular contribution (exposed to alpha shaders to calculate "glare") + vec3 l) //surface point to light { // make sure specular highlights from punctual lights don't fall off of polished surfaces perceptualRoughness = max(perceptualRoughness, 8.0/255.0); @@ -524,28 +508,13 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, // Calculation of analytical lighting contribution vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs); - specContrib = F * G * D / (4.0 * NdotL * NdotV); + vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV); // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law) vec3 color = NdotL * (diffuseContrib + specContrib); - specContrib *= NdotL; - specContrib = max(specContrib, vec3(0)); - return clamp(color, vec3(0), vec3(10)); } -vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, - float perceptualRoughness, - float metallic, - vec3 n, // normal - vec3 v, // surface point to camera - vec3 l) //surface point to light -{ - vec3 specContrib; - - return pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n, v, l, specContrib); -} - void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor, inout vec3 specularColor) { vec3 f0 = vec3(0.04); @@ -554,30 +523,21 @@ void calcDiffuseSpecular(vec3 baseColor, float metallic, inout vec3 diffuseColor specularColor = mix(f0, baseColor, metallic); } -vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 v, vec3 norm, float perceptualRoughness, vec3 light_dir, vec3 sunlit, float scol, vec3 radiance, vec3 irradiance, vec3 colorEmissive, float ao, vec3 additive, vec3 atten, out vec3 specContrib) +vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 v, vec3 norm, float perceptualRoughness, vec3 light_dir, vec3 sunlit, float scol, vec3 radiance, vec3 irradiance, vec3 colorEmissive, float ao, vec3 additive, vec3 atten) { vec3 color = vec3(0); float NdotV = clamp(abs(dot(norm, v)), 0.001, 1.0); - vec3 ibl_spec; - color += pbrIbl(diffuseColor, specularColor, radiance, irradiance, ao, NdotV, perceptualRoughness, ibl_spec); + color += pbrIbl(diffuseColor, specularColor, radiance, irradiance, ao, NdotV, perceptualRoughness); - color += pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, norm, v, normalize(light_dir), specContrib) * sunlit * 3.0 * scol; //magic number to balance with legacy materials - specContrib *= sunlit * 2.75 * scol; - specContrib += ibl_spec; + color += pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, norm, v, normalize(light_dir)) * sunlit * 3.0 * scol; //magic number to balance with legacy materials color += colorEmissive; return color; } -vec3 pbrBaseLight(vec3 diffuseColor, vec3 specularColor, float metallic, vec3 v, vec3 norm, float perceptualRoughness, vec3 light_dir, vec3 sunlit, float scol, vec3 radiance, vec3 irradiance, vec3 colorEmissive, float ao, vec3 additive, vec3 atten) -{ - vec3 specContrib; - return pbrBaseLight(diffuseColor, specularColor, metallic, v, norm, perceptualRoughness, light_dir, sunlit, scol, radiance, irradiance, colorEmissive, ao, additive, atten, specContrib); -} - uniform vec4 waterPlane; uniform float waterSign; diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl index b752307d13..5d58cc91cd 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl @@ -40,8 +40,6 @@ vec4 applyWaterFogView(vec3 pos, vec4 color); #endif vec3 srgb_to_linear(vec3 cs); -vec3 legacy_adjust_fullbright(vec3 c); -vec3 legacy_adjust(vec3 c); vec3 linear_to_srgb(vec3 cl); vec3 atmosFragLighting(vec3 light, vec3 additive, vec3 atten); void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, out vec3 atten); @@ -98,9 +96,7 @@ void main() #endif #ifndef IS_HUD - color.rgb = legacy_adjust(color.rgb); color.rgb = srgb_to_linear(color.rgb); - color.rgb = legacy_adjust_fullbright(color.rgb); color.rgb = atmosFragLighting(color.rgb, additive, atten); #endif diff --git a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl index 53e4f02314..64e6bc9da2 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postDeferredGammaCorrect.glsl @@ -105,7 +105,8 @@ vec3 toneMap(vec3 color) color *= exposure * exp_scale; - color = toneMapACES_Hill(color); + // mix ACES and Linear here as a compromise to avoid over-darkening legacy content + color = mix(toneMapACES_Hill(color), color, 0.333); #endif return color; diff --git a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl index 7a5e14566b..b5437d43d2 100644 --- a/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl +++ b/indra/newview/app_settings/shaders/class1/effects/glowExtractF.glsl @@ -28,6 +28,10 @@ out vec4 frag_color; uniform sampler2D diffuseMap; +#if HAS_NOISE +uniform sampler2D glowNoiseMap; +uniform vec2 screen_res; +#endif uniform float minLuminance; uniform float maxExtractAlpha; uniform vec3 lumWeights; @@ -44,7 +48,16 @@ void main() float lum = smoothstep(minLuminance, minLuminance+1.0, dot(col.rgb, lumWeights ) ); float warmth = smoothstep(minLuminance, minLuminance+1.0, max(col.r * warmthWeights.r, max(col.g * warmthWeights.g, col.b * warmthWeights.b)) ); - frag_color.rgb = col.rgb; +#if HAS_NOISE + float TRUE_NOISE_RES = 128; // See mTrueNoiseMap + // *NOTE: Usually this is vary_fragcoord not vary_texcoord0, but glow extraction is in screen space + vec3 glow_noise = texture(glowNoiseMap, vary_texcoord0.xy * (screen_res / TRUE_NOISE_RES)).xyz; + // Dithering. Reduces banding effects in the reduced precision glow buffer. + float NOISE_DEPTH = 64.0; + col.rgb += glow_noise / NOISE_DEPTH; + col.rgb = max(col.rgb, vec3(0)); +#endif + frag_color.rgb = col.rgb; frag_color.a = max(col.a, mix(lum, warmth, warmthAmount) * maxExtractAlpha); } diff --git a/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl b/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl index 31b02377da..e3fd10447e 100644 --- a/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl +++ b/indra/newview/app_settings/shaders/class1/environment/srgbF.glsl @@ -113,31 +113,3 @@ vec3 inv_toneMapACES_Hill(vec3 color) return color; } -// adjust legacy colors to back out tonemapping and exposure -// NOTE: obsolete now that setting probe ambiance to zero removes tonemapping and exposure, but keeping for a minute -// while that change goes through testing - davep 6/1/2023 -#define LEGACY_ADJUST 0 - -vec3 legacy_adjust(vec3 c) -{ -#if LEGACY_ADJUST - vec3 desat = rgb2hsv(c.rgb); - desat.g *= 1.0-(1.0-desat.b)*0.5; - desat.b += (1.0-desat.b)*0.1f; - desat.rgb = hsv2rgb(desat); - return desat; -#else - return c; -#endif -} - -vec3 legacy_adjust_fullbright(vec3 c) -{ -#if LEGACY_ADJUST - float exp_scale = clamp(texture(exposureMap, vec2(0.5, 0.5)).r, 0.01, 10.0); - return c / exp_scale * 1.34; //magic 1.34 arrived at by binary search for a value that reproduces midpoint grey consistenty -#else - return c; -#endif -} - diff --git a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl index 437fa0a6d5..a1da4b1f9a 100644 --- a/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl +++ b/indra/newview/app_settings/shaders/class1/windlight/atmosphericsFuncs.glsl @@ -39,12 +39,12 @@ uniform float max_y; uniform vec3 glow; uniform float scene_light_strength; uniform float sun_moon_glow_factor; -uniform float sky_hdr_scale; +uniform float sky_sunlight_scale; +uniform float sky_ambient_scale; float getAmbientClamp() { return 1.0f; } vec3 srgb_to_linear(vec3 col); -vec3 legacy_adjust(vec3 col); // return colors in sRGB space void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, @@ -148,12 +148,9 @@ void calcAtmosphericVarsLinear(vec3 inPositionEye, vec3 norm, vec3 light_dir, ou // multiply to get similar colors as when the "scaleSoftClip" implementation was doubling color values // (allows for mixing of light sources other than sunlight e.g. reflection probes) - sunlit *= 1.5; - amblit *= 0.5; + sunlit *= sky_sunlight_scale; + amblit *= sky_ambient_scale; - // override amblit with ambient_color if sky probe ambiance is not zero - amblit = mix(amblit, ambient_color, clamp(sky_hdr_scale-1.0, 0.0, 1.0)); - amblit = srgb_to_linear(amblit); amblit *= ambientLighting(norm, light_dir); } diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index da5f997429..b63f3b60f9 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -71,7 +71,6 @@ vec4 applyWaterFogViewLinear(vec3 pos, vec4 color, vec3 sunlit); vec3 srgb_to_linear(vec3 c); vec3 linear_to_srgb(vec3 c); -vec3 legacy_adjust(vec3 c); vec2 encode_normal (vec3 n); vec3 atmosFragLightingLinear(vec3 light, vec3 additive, vec3 atten); @@ -85,7 +84,7 @@ float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); float getAmbientClamp(); void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent); + vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); vec3 calcPointLightOrSpotLight(vec3 light_col, vec3 diffuse, vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float fa, float is_pointlight, float ambiance) { @@ -234,7 +233,6 @@ void main() } diffuse_srgb.rgb *= vertex_color.rgb; - diffuse_srgb.rgb = legacy_adjust(diffuse_srgb.rgb); diffuse_linear.rgb = srgb_to_linear(diffuse_srgb.rgb); #endif // USE_VERTEX_COLOR @@ -251,7 +249,7 @@ void main() vec3 irradiance; vec3 glossenv; vec3 legacyenv; - sampleReflectionProbesLegacy(irradiance, glossenv, legacyenv, frag, pos.xyz, norm.xyz, 0.0, 0.0, true); + sampleReflectionProbesLegacy(irradiance, glossenv, legacyenv, frag, pos.xyz, norm.xyz, 0.0, 0.0, true, amblit_linear); float da = dot(norm.xyz, light_dir.xyz); @@ -266,7 +264,7 @@ void main() vec3 sun_contrib = min(final_da, shadow) * sunlit_linear; - color.rgb = max(amblit, irradiance); + color.rgb = irradiance; color.rgb += sun_contrib; @@ -291,9 +289,7 @@ void main() LIGHT_LOOP(7) // sum local light contrib in linear colorspace -#if !defined(LOCAL_LIGHT_KILL) color.rgb += light.rgb; -#endif // !defined(LOCAL_LIGHT_KILL) #endif // #else // FOR_IMPOSTOR diff --git a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl index e8db856b1f..35d752be02 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/pbralphaF.glsl @@ -92,7 +92,7 @@ void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float float calcLegacyDistanceAttenuation(float distance, float falloff); float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent); + vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear); void waterClip(vec3 pos); @@ -112,16 +112,14 @@ vec3 pbrBaseLight(vec3 diffuseColor, vec3 colorEmissive, float ao, vec3 additive, - vec3 atten, - out vec3 specContrib); + vec3 atten); vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, float metallic, vec3 n, // normal vec3 v, // surface point to camera - vec3 l, //surface point to light - out vec3 specContrib); + vec3 l); //surface point to light vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, float perceptualRoughness, @@ -132,7 +130,7 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, vec3 lp, // light position vec3 ld, // light direction (for spotlights) vec3 lightColor, - float lightSize, float falloff, float is_pointlight, inout float glare, float ambiance) + float lightSize, float falloff, float is_pointlight, float ambiance) { vec3 color = vec3(0,0,0); @@ -154,10 +152,7 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, vec3 intensity = spot_atten * dist_atten * lightColor * 3.0; //magic number to balance with legacy materials - vec3 speccol; - color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv, speccol); - speccol *= intensity; - glare += max(max(speccol.r, speccol.g), speccol.b); + color = intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv); } return color; @@ -166,7 +161,6 @@ vec3 calcPointLightOrSpotLight(vec3 diffuseColor, vec3 specularColor, void main() { vec3 color = vec3(0,0,0); - float glare = 0.0; vec3 light_dir = (sun_up_factor == 1) ? sun_dir : moon_dir; vec3 pos = vary_position; @@ -224,20 +218,15 @@ void main() float gloss = 1.0 - perceptualRoughness; vec3 irradiance = vec3(0); vec3 radiance = vec3(0); - sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true); - // Take maximium of legacy ambient vs irradiance sample as irradiance - // NOTE: ao is applied in pbrIbl (see pbrBaseLight), do not apply here - irradiance = max(amblit,irradiance); - + sampleReflectionProbes(irradiance, radiance, vary_position.xy*0.5+0.5, pos.xyz, norm.xyz, gloss, true, amblit); + vec3 diffuseColor; vec3 specularColor; calcDiffuseSpecular(col.rgb, metallic, diffuseColor, specularColor); vec3 v = -normalize(pos.xyz); - vec3 spec; - color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten, spec); - glare += max(max(spec.r, spec.g), spec.b); + color = pbrBaseLight(diffuseColor, specularColor, metallic, v, norm.xyz, perceptualRoughness, light_dir, sunlit_linear, scol, radiance, irradiance, colorEmissive, ao, additive, atten); color.rgb = atmosFragLightingLinear(color.rgb, additive, atten); @@ -249,7 +238,7 @@ void main() vec3 light = vec3(0); // Punctual lights -#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, glare, light_attenuation[i].w); +#define LIGHT_LOOP(i) light += calcPointLightOrSpotLight(diffuseColor, specularColor, perceptualRoughness, metallic, norm.xyz, pos.xyz, v, light_position[i].xyz, light_direction[i].xyz, light_diffuse[i].rgb, light_deferred_attenuation[i].x, light_deferred_attenuation[i].y, light_attenuation[i].z, light_attenuation[i].w); LIGHT_LOOP(1) LIGHT_LOOP(2) @@ -264,9 +253,6 @@ void main() float a = basecolor.a*vertex_color.a; - glare = min(glare, 1.0); - a = max(a, glare); - frag_color = max(vec4(color.rgb,a), vec4(0)); } diff --git a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl index aa6f5a3b62..52e71edcac 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/reflectionProbeF.glsl @@ -34,7 +34,7 @@ uniform mat3 env_mat; vec3 srgb_to_linear(vec3 c); void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent) + vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear) { ambenv = vec3(reflection_probe_ambiance * 0.25); @@ -44,9 +44,9 @@ void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, } void sampleReflectionProbesWater(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness) + vec2 tc, vec3 pos, vec3 norm, float glossiness, vec3 amblit_linear) { - sampleReflectionProbes(ambenv, glossenv, tc, pos, norm, glossiness, false); + sampleReflectionProbes(ambenv, glossenv, tc, pos, norm, glossiness, false, amblit_linear); } vec4 sampleReflectionProbesDebug(vec3 pos) @@ -56,7 +56,7 @@ vec4 sampleReflectionProbesDebug(vec3 pos) } void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent) + vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear) { ambenv = vec3(reflection_probe_ambiance * 0.25); diff --git a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl index 1537714bb7..5483a4e29c 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/fullbrightShinyF.glsl @@ -40,8 +40,6 @@ in vec3 vary_position; uniform samplerCube environmentMap; vec3 atmosFragLighting(vec3 light, vec3 additive, vec3 atten); -vec3 legacy_adjust_fullbright(vec3 c); -vec3 legacy_adjust(vec3 c); void calcAtmosphericVars(vec3 inPositionEye, vec3 light_dir, float ambFactor, out vec3 sunlit, out vec3 amblit, out vec3 additive, out vec3 atten); vec3 linear_to_srgb(vec3 c); @@ -49,7 +47,7 @@ vec3 srgb_to_linear(vec3 c); // reflection probe interface void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent); + vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); @@ -80,12 +78,10 @@ void main() vec3 legacyenv; vec3 norm = normalize(vary_texcoord1.xyz); vec4 spec = vec4(0,0,0,0); - sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, vec2(0), pos.xyz, norm.xyz, spec.a, env_intensity, false); + sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, vec2(0), pos.xyz, norm.xyz, spec.a, env_intensity, false, amblit); - color.rgb = legacy_adjust(color.rgb); color.rgb = srgb_to_linear(color.rgb); - color.rgb = legacy_adjust_fullbright(color.rgb); - + applyLegacyEnv(color.rgb, legacyenv, spec, pos, norm, env_intensity); color.rgb = atmosFragLighting(color.rgb, additive, atten); #endif diff --git a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl index 82e2de0c0f..acff03ec4b 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/materialF.glsl @@ -48,8 +48,6 @@ void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float vec3 srgb_to_linear(vec3 cs); vec3 linear_to_srgb(vec3 cs); -vec3 legacy_adjust(vec3 c); -vec3 legacy_adjust_fullbright(vec3 c); #if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) @@ -60,7 +58,7 @@ float sampleDirectionalShadow(vec3 pos, vec3 norm, vec2 pos_screen); #endif void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent); + vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm); void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); @@ -311,7 +309,6 @@ void main() #if (DIFFUSE_ALPHA_MODE == DIFFUSE_ALPHA_MODE_BLEND) //forward rendering, output lit linear color - diffcol.rgb = legacy_adjust(diffcol.rgb); diffcol.rgb = srgb_to_linear(diffcol.rgb); spec.rgb = srgb_to_linear(spec.rgb); spec.a = glossiness; // pack glossiness into spec alpha for lighting functions @@ -339,10 +336,9 @@ void main() vec3 ambenv; vec3 glossenv; vec3 legacyenv; - sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, pos.xy*0.5+0.5, pos.xyz, norm.xyz, glossiness, env, true); + sampleReflectionProbesLegacy(ambenv, glossenv, legacyenv, pos.xy*0.5+0.5, pos.xyz, norm.xyz, glossiness, env, true, amblit_linear); - // use sky settings ambient or irradiance map sample, whichever is brighter - color = max(amblit_linear, ambenv); + color = ambenv; float da = clamp(dot(norm.xyz, light_dir.xyz), 0.0, 1.0); vec3 sun_contrib = min(da, shadow) * sunlit_linear; @@ -378,7 +374,7 @@ void main() applyGlossEnv(color, glossenv, spec, pos.xyz, norm.xyz); } - color = mix(color.rgb, legacy_adjust_fullbright(diffcol.rgb), emissive); + color = mix(color.rgb, diffcol.rgb, emissive); if (env > 0.0) { // add environmentmap diff --git a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl index 2a096a98ec..ec8168465e 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/multiPointLightF.glsl @@ -52,7 +52,6 @@ vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensi vec2 getScreenXY(vec4 clip); vec2 getScreenCoord(vec4 clip); vec3 srgb_to_linear(vec3 c); -vec3 legacy_adjust(vec3 c); // Util vec3 hue_to_rgb(float hue); @@ -67,9 +66,6 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, void main() { -#if defined(LOCAL_LIGHT_KILL) - discard; // Bail immediately -#else vec3 final_color = vec3(0, 0, 0); vec2 tc = getScreenCoord(vary_fragcoord); vec3 pos = getPosition(tc).xyz; @@ -118,7 +114,7 @@ void main() float dist_atten = calcLegacyDistanceAttenuation(dist, falloff); - vec3 intensity = dist_atten * lightColor * 3.9; + vec3 intensity = dist_atten * lightColor * 3.25; final_color += intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv); } @@ -126,7 +122,6 @@ void main() } else { - diffuse.rgb = legacy_adjust(diffuse.rgb); diffuse = srgb_to_linear(diffuse); spec.rgb = srgb_to_linear(spec.rgb); @@ -174,7 +169,6 @@ void main() frag_color.rgb = max(final_color, vec3(0)); frag_color.a = 0.0; -#endif // LOCAL_LIGHT_KILL #ifdef IS_AMD_CARD // If it's AMD make sure the GLSL compiler sees the arrays referenced once by static index. Otherwise it seems to optimise the storage diff --git a/indra/newview/app_settings/shaders/class3/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/multiSpotLightF.glsl deleted file mode 100644 index 23120bbbbe..0000000000 --- a/indra/newview/app_settings/shaders/class3/deferred/multiSpotLightF.glsl +++ /dev/null @@ -1,263 +0,0 @@ -/** - * @file class3\deferred\multiSpotLightF.glsl - * - * $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$ - */ - -/*[EXTRA_CODE_HERE]*/ - -out vec4 frag_color; - -uniform sampler2D diffuseRect; -uniform sampler2D specularRect; -uniform sampler2D depthMap; -uniform sampler2D normalMap; -uniform sampler2D emissiveRect; // PBR linear packed Occlusion, Roughness, Metal. See: pbropaqueF.glsl -uniform samplerCube environmentMap; -uniform sampler2D lightMap; -uniform sampler2D projectionMap; // rgba -uniform sampler2D lightFunc; - -uniform mat4 proj_mat; //screen space to light space -uniform float proj_near; //near clip for projection -uniform vec3 proj_p; //plane projection is emitting from (in screen space) -uniform vec3 proj_n; -uniform float proj_focus; //distance from plane to begin blurring -uniform float proj_lod; //(number of mips in proj map) -uniform float proj_range; //range between near clip and far clip plane of projection -uniform float proj_ambient_lod; -uniform float proj_ambiance; -uniform float near_clip; -uniform float far_clip; - -uniform vec3 proj_origin; //origin of projection to be used for angular attenuation -uniform float sun_wash; -uniform int proj_shadow_idx; -uniform float shadow_fade; - -// Light params -uniform vec3 center; -uniform float size; -uniform vec3 color; -uniform float falloff; - -in vec4 vary_fragcoord; -uniform vec2 screen_res; - -uniform mat4 inv_proj; - -void calcHalfVectors(vec3 lv, vec3 n, vec3 v, out vec3 h, out vec3 l, out float nh, out float nl, out float nv, out float vh, out float lightDist); -float calcLegacyDistanceAttenuation(float distance, float falloff); -vec3 colorized_dot(float x); -bool clipProjectedLightVars(vec3 center, vec3 pos, out float dist, out float l_dist, out vec3 lv, out vec4 proj_tc ); -vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity); -vec3 getProjectedLightAmbiance(float amb_da, float attenuation, float lit, float nl, float noise, vec2 projected_uv); -vec3 getProjectedLightDiffuseColor(float light_distance, vec2 projected_uv ); -vec3 getProjectedLightSpecularColor(vec3 pos, vec3 n); -vec2 getScreenXY(vec4 clip); -vec2 getScreenCoord(vec4 clip); -vec3 srgb_to_linear(vec3 cs); -vec3 legacy_adjust(vec3 c); -vec4 texture2DLodSpecular(vec2 tc, float lod); - -vec4 getPosition(vec2 pos_screen); - -const float M_PI = 3.14159265; - -vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, - float perceptualRoughness, - float metallic, - vec3 n, // normal - vec3 v, // surface point to camera - vec3 l); //surface point to light - -void main() -{ -#if defined(LOCAL_LIGHT_KILL) - discard; -#else - vec3 final_color = vec3(0,0,0); - vec2 tc = getScreenCoord(vary_fragcoord); - vec3 pos = getPosition(tc).xyz; - - vec3 lv; - vec4 proj_tc; - float dist, l_dist; - if (clipProjectedLightVars(center, pos, dist, l_dist, lv, proj_tc)) - { - discard; - } - - float shadow = 1.0; - - if (proj_shadow_idx >= 0) - { - vec4 shd = texture(lightMap, tc); - shadow = (proj_shadow_idx==0)?shd.b:shd.a; - shadow += shadow_fade; - shadow = clamp(shadow, 0.0, 1.0); - } - - float envIntensity; - vec3 n; - vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity); - - float dist_atten = calcLegacyDistanceAttenuation(dist, falloff); - if (dist_atten <= 0.0) - { - discard; - } - - lv = proj_origin-pos.xyz; - vec3 h, l, v = -normalize(pos); - float nh, nl, nv, vh, lightDist; - calcHalfVectors(lv, n, v, h, l, nh, nl, nv, vh, lightDist); - - vec3 diffuse = texture(diffuseRect, tc).rgb; - vec4 spec = texture(specularRect, tc); - vec3 dlit = vec3(0, 0, 0); - vec3 slit = vec3(0, 0, 0); - - vec3 amb_rgb = vec3(0); - - if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR)) - { - vec3 colorEmissive = texture(emissiveRect, tc).rgb; - vec3 orm = spec.rgb; - float perceptualRoughness = orm.g; - float metallic = orm.b; - vec3 f0 = vec3(0.04); - vec3 baseColor = diffuse.rgb; - - vec3 diffuseColor = baseColor.rgb*(vec3(1.0)-f0); - diffuseColor *= 1.0 - metallic; - - vec3 specularColor = mix(f0, baseColor.rgb, metallic); - - // We need this additional test inside a light's frustum since a spotlight's ambiance can be applied - if (proj_tc.x > 0.0 && proj_tc.x < 1.0 - && proj_tc.y > 0.0 && proj_tc.y < 1.0) - { - float lit = 0.0; - float amb_da = 0.0; - - if (nl > 0.0) - { - amb_da += (nl*0.5 + 0.5) * proj_ambiance; - - dlit = getProjectedLightDiffuseColor( l_dist, proj_tc.xy ); - - vec3 intensity = dist_atten * dlit * 3.9 * shadow; // Legacy attenuation, magic number to balance with legacy materials - final_color += intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, normalize(lv)); - } - - amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ); - final_color += diffuse.rgb * amb_rgb; - } - } - else - { - diffuse = legacy_adjust(diffuse); - diffuse = srgb_to_linear(diffuse); - spec.rgb = srgb_to_linear(spec.rgb); - - if (proj_tc.z > 0.0 && - proj_tc.x < 1.0 && - proj_tc.y < 1.0 && - proj_tc.x > 0.0 && - proj_tc.y > 0.0) - { - float amb_da = 0; - float lit = 0.0; - - if (nl > 0.0) - { - lit = nl * dist_atten; - - dlit = getProjectedLightDiffuseColor( l_dist, proj_tc.xy ); - - final_color = dlit*lit*diffuse*shadow; - - // unshadowed for consistency between forward and deferred? - amb_da += (nl*0.5+0.5) /* * (1.0-shadow) */ * proj_ambiance; - } - - amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ); - final_color += diffuse.rgb * amb_rgb; - } - - if (spec.a > 0.0) - { - dlit *= min(nl*6.0, 1.0) * dist_atten; - - float fres = pow(1 - vh, 5)*0.4+0.5; - - float gtdenom = 2 * nh; - float gt = max(0, min(gtdenom * nv / vh, gtdenom * nl / vh)); - - if (nh > 0.0) - { - float scol = fres*texture(lightFunc, vec2(nh, spec.a)).r*gt/(nh*nl); - vec3 speccol = dlit*scol*spec.rgb*shadow; - speccol = clamp(speccol, vec3(0), vec3(1)); - final_color += speccol; - } - } - - if (envIntensity > 0.0) - { - vec3 ref = reflect(normalize(pos), n); - - //project from point pos in direction ref to plane proj_p, proj_n - vec3 pdelta = proj_p-pos; - float ds = dot(ref, proj_n); - - if (ds < 0.0) - { - vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; - - vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); - - if (stc.z > 0.0) - { - stc /= stc.w; - - if (stc.x < 1.0 && - stc.y < 1.0 && - stc.x > 0.0 && - stc.y > 0.0) - { - final_color += color.rgb * texture2DLodSpecular(stc.xy, (1 - spec.a) * (proj_lod * 0.6)).rgb * shadow * envIntensity; - } - } - } - } - } - - //not sure why, but this line prevents MATBUG-194 - final_color = max(final_color, vec3(0.0)); - - //output linear - frag_color.rgb = final_color; - frag_color.a = 0.0; -#endif // LOCAL_LIGHT_KILL -} diff --git a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl index 42ba96148c..31af1208bd 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/pointLightF.glsl @@ -57,7 +57,6 @@ vec4 getPosition(vec2 pos_screen); vec2 getScreenXY(vec4 clip); vec2 getScreenCoord(vec4 clip); vec3 srgb_to_linear(vec3 c); -vec3 legacy_adjust(vec3 c); float getDepth(vec2 tc); vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, @@ -107,7 +106,7 @@ void main() vec3 specularColor = mix(f0, baseColor.rgb, metallic); - vec3 intensity = dist_atten * color * 3.9; // Legacy attenuation, magic number to balance with legacy materials + vec3 intensity = dist_atten * color * 3.25; // Legacy attenuation, magic number to balance with legacy materials final_color += intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, normalize(lv)); } else @@ -116,7 +115,6 @@ void main() { discard; } - diffuse = legacy_adjust(diffuse); diffuse = srgb_to_linear(diffuse); spec.rgb = srgb_to_linear(spec.rgb); diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl index 41821def8e..906e66ecc8 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl @@ -530,7 +530,7 @@ vec3 tapRefMap(vec3 pos, vec3 dir, out float w, out float dw, float lod, vec3 c, // w - weight of sample (distance and angular attenuation) // dw - weight of sample (distance only) // i - index of probe -vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int i) +vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int i, vec3 amblit) { // parallax adjustment vec3 v; @@ -556,9 +556,12 @@ vec3 tapIrradianceMap(vec3 pos, vec3 dir, out float w, out float dw, vec3 c, int v -= c; v = env_mat * v; - { - return textureLod(irradianceProbes, vec4(v.xyz, refIndex[i].x), 0).rgb * refParams[i].x; - } + + vec3 col = textureLod(irradianceProbes, vec4(v.xyz, refIndex[i].x), 0).rgb * refParams[i].x; + + col = mix(amblit, col, min(refParams[i].x, 1.0)); + + return col; } vec3 sampleProbes(vec3 pos, vec3 dir, float lod) @@ -619,7 +622,7 @@ vec3 sampleProbes(vec3 pos, vec3 dir, float lod) return col[1]+col[0]; } -vec3 sampleProbeAmbient(vec3 pos, vec3 dir) +vec3 sampleProbeAmbient(vec3 pos, vec3 dir, vec3 amblit) { // modified copy/paste of sampleProbes follows, will likely diverge from sampleProbes further // as irradiance map mixing is tuned independently of radiance map mixing @@ -649,7 +652,7 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir) float w = 0; float dw = 0; - vec3 refcol = tapIrradianceMap(pos, dir, w, dw, refSphere[i].xyz, i); + vec3 refcol = tapIrradianceMap(pos, dir, w, dw, refSphere[i].xyz, i, amblit); col[p] += refcol*w; wsum[p] += w; @@ -679,14 +682,14 @@ vec3 sampleProbeAmbient(vec3 pos, vec3 dir) } void doProbeSample(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent) + vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit) { // TODO - don't hard code lods float reflection_lods = max_probe_lod; vec3 refnormpersp = reflect(pos.xyz, norm.xyz); - ambenv = sampleProbeAmbient(pos, norm); + ambenv = sampleProbeAmbient(pos, norm, amblit); float lod = (1.0-glossiness)*reflection_lods; glossenv = sampleProbes(pos, normalize(refnormpersp), lod); @@ -712,14 +715,14 @@ void doProbeSample(inout vec3 ambenv, inout vec3 glossenv, } void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent) + vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit) { preProbeSample(pos); - doProbeSample(ambenv, glossenv, tc, pos, norm, glossiness, transparent); + doProbeSample(ambenv, glossenv, tc, pos, norm, glossiness, transparent, amblit); } void sampleReflectionProbesWater(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness) + vec2 tc, vec3 pos, vec3 norm, float glossiness, vec3 amblit) { // don't sample automatic probes for water sample_automatic = false; @@ -728,7 +731,7 @@ void sampleReflectionProbesWater(inout vec3 ambenv, inout vec3 glossenv, // always include void probe on water probeIndex[probeInfluences++] = 0; - doProbeSample(ambenv, glossenv, tc, pos, norm, glossiness, false); + doProbeSample(ambenv, glossenv, tc, pos, norm, glossiness, false, amblit); // fudge factor to get PBR water at a similar luminance ot legacy water glossenv *= 0.4; @@ -783,14 +786,14 @@ vec4 sampleReflectionProbesDebug(vec3 pos) } void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent) + vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit) { float reflection_lods = max_probe_lod; preProbeSample(pos); vec3 refnormpersp = reflect(pos.xyz, norm.xyz); - ambenv = sampleProbeAmbient(pos, norm); + ambenv = sampleProbeAmbient(pos, norm, amblit); if (glossiness > 0.0) { diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 4ef003e0cb..35e99c5bd2 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -69,16 +69,15 @@ vec3 scaleSoftClipFragLinear(vec3 l); // reflection probe interface void sampleReflectionProbes(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent); + vec2 tc, vec3 pos, vec3 norm, float glossiness, bool transparent, vec3 amblit_linear); void sampleReflectionProbesLegacy(inout vec3 ambenv, inout vec3 glossenv, inout vec3 legacyenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent); + vec2 tc, vec3 pos, vec3 norm, float glossiness, float envIntensity, bool transparent, vec3 amblit_linear); void applyGlossEnv(inout vec3 color, vec3 glossenv, vec4 spec, vec3 pos, vec3 norm); void applyLegacyEnv(inout vec3 color, vec3 legacyenv, vec4 spec, vec3 pos, vec3 norm, float envIntensity); float getDepth(vec2 pos_screen); vec3 linear_to_srgb(vec3 c); vec3 srgb_to_linear(vec3 c); -vec3 legacy_adjust(vec3 c); uniform vec4 waterPlane; @@ -117,10 +116,10 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, vec3 l); //surface point to light -void adjustIrradiance(inout vec3 irradiance, vec3 amblit_linear, float ambocc) +void adjustIrradiance(inout vec3 irradiance, float ambocc) { // use sky settings ambient or irradiance map sample, whichever is brighter - irradiance = max(amblit_linear, irradiance); + //irradiance = max(amblit_linear, irradiance); #if defined(HAS_SSAO) irradiance = mix(ssao_effect_mat * min(irradiance.rgb*ssao_irradiance_scale, vec3(ssao_irradiance_max)), irradiance.rgb, ambocc); @@ -194,9 +193,9 @@ void main() // PBR IBL float gloss = 1.0 - perceptualRoughness; - sampleReflectionProbes(irradiance, radiance, tc, pos.xyz, norm.xyz, gloss, false); + sampleReflectionProbes(irradiance, radiance, tc, pos.xyz, norm.xyz, gloss, false, amblit_linear); - adjustIrradiance(irradiance, amblit_linear, ambocc); + adjustIrradiance(irradiance, ambocc); vec3 diffuseColor; vec3 specularColor; @@ -220,8 +219,6 @@ void main() else { // legacy shaders are still writng sRGB to gbuffer - baseColor.rgb = legacy_adjust(baseColor.rgb); - baseColor.rgb = srgb_to_linear(baseColor.rgb); spec.rgb = srgb_to_linear(spec.rgb); @@ -232,9 +229,9 @@ void main() vec3 glossenv = vec3(0); vec3 legacyenv = vec3(0); - sampleReflectionProbesLegacy(irradiance, glossenv, legacyenv, tc, pos.xyz, norm.xyz, spec.a, envIntensity, false); + sampleReflectionProbesLegacy(irradiance, glossenv, legacyenv, tc, pos.xyz, norm.xyz, spec.a, envIntensity, false, amblit_linear); - adjustIrradiance(irradiance, amblit_linear, ambocc); + adjustIrradiance(irradiance, ambocc); // apply lambertian IBL only (see pbrIbl) color.rgb = irradiance; diff --git a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl index 1ea801d7d7..d31b37fb60 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/spotLightF.glsl @@ -25,16 +25,6 @@ /*[EXTRA_CODE_HERE]*/ -#define DEBUG_ANY_LIGHT_TYPE 0 // Output green light cone -#define DEBUG_LEG_LIGHT_TYPE 0 // Show Legacy objects in green -#define DEBUG_PBR_LIGHT_TYPE 0 // Show PBR objects in green -#define DEBUG_PBR_SPOT 0 -#define DEBUG_PBR_SPOT_DIFFUSE 0 -#define DEBUG_PBR_SPOT_SPECULAR 0 - -#define DEBUG_SPOT_NL 0 // monochome area effected by light -#define DEBUG_SPOT_ZERO 0 // Output zero for spotlight - out vec4 frag_color; uniform sampler2D diffuseRect; @@ -64,11 +54,16 @@ uniform float sun_wash; uniform int proj_shadow_idx; uniform float shadow_fade; +// Light params +#if defined(MULTI_SPOTLIGHT) +uniform vec3 center; +#else +in vec3 trans_center; +#endif uniform float size; uniform vec3 color; uniform float falloff; -in vec3 trans_center; in vec4 vary_fragcoord; uniform vec2 screen_res; @@ -80,11 +75,8 @@ bool clipProjectedLightVars(vec3 center, vec3 pos, out float dist, out float l_d vec4 getNormalEnvIntensityFlags(vec2 screenpos, out vec3 n, out float envIntensity); vec3 getProjectedLightAmbiance(float amb_da, float attenuation, float lit, float nl, float noise, vec2 projected_uv); vec3 getProjectedLightDiffuseColor(float light_distance, vec2 projected_uv ); -vec3 getProjectedLightSpecularColor(vec3 pos, vec3 n); -vec2 getScreenXY(vec4 clip_point); -vec2 getScreenCoord(vec4 clip_point); -vec3 srgb_to_linear(vec3 c); -vec3 legacy_adjust(vec3 c); +vec2 getScreenCoord(vec4 clip); +vec3 srgb_to_linear(vec3 cs); vec4 texture2DLodSpecular(vec2 tc, float lod); vec4 getPosition(vec2 pos_screen); @@ -100,9 +92,6 @@ vec3 pbrPunctual(vec3 diffuseColor, vec3 specularColor, void main() { -#if defined(LOCAL_LIGHT_KILL) - discard; -#else vec3 final_color = vec3(0,0,0); vec2 tc = getScreenCoord(vary_fragcoord); vec3 pos = getPosition(tc).xyz; @@ -110,24 +99,31 @@ void main() vec3 lv; vec4 proj_tc; float dist, l_dist; - if (clipProjectedLightVars(trans_center, pos, dist, l_dist, lv, proj_tc)) + vec3 c; +#if defined(MULTI_SPOTLIGHT) + c = center; +#else + c = trans_center; +#endif + + if (clipProjectedLightVars(c, pos, dist, l_dist, lv, proj_tc)) { discard; } float shadow = 1.0; - + if (proj_shadow_idx >= 0) { vec4 shd = texture(lightMap, tc); - shadow = (proj_shadow_idx == 0) ? shd.b : shd.a; + shadow = (proj_shadow_idx==0)?shd.b:shd.a; shadow += shadow_fade; - shadow = clamp(shadow, 0.0, 1.0); + shadow = clamp(shadow, 0.0, 1.0); } float envIntensity; vec3 n; - vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity); // need `norm.w` for GET_GBUFFER_FLAG() + vec4 norm = getNormalEnvIntensityFlags(tc, n, envIntensity); float dist_atten = calcLegacyDistanceAttenuation(dist, falloff); if (dist_atten <= 0.0) @@ -135,7 +131,7 @@ void main() discard; } - lv = proj_origin-pos.xyz; // NOTE: Re-using lv + lv = proj_origin-pos.xyz; vec3 h, l, v = -normalize(pos); float nh, nl, nv, vh, lightDist; calcHalfVectors(lv, n, v, h, l, nh, nl, nv, vh, lightDist); @@ -146,10 +142,11 @@ void main() vec3 slit = vec3(0, 0, 0); vec3 amb_rgb = vec3(0); + if (GET_GBUFFER_FLAG(GBUFFER_FLAG_HAS_PBR)) { - vec3 colorEmissive = texture(emissiveRect, tc).rgb; - vec3 orm = spec.rgb; + vec3 colorEmissive = texture(emissiveRect, tc).rgb; + vec3 orm = spec.rgb; float perceptualRoughness = orm.g; float metallic = orm.b; vec3 f0 = vec3(0.04); @@ -167,33 +164,34 @@ void main() float lit = 0.0; float amb_da = 0.0; + lv = normalize(lv); + if (nl > 0.0) { amb_da += (nl*0.5 + 0.5) * proj_ambiance; dlit = getProjectedLightDiffuseColor( l_dist, proj_tc.xy ); - vec3 intensity = dist_atten * dlit * 3.9 * shadow; // Legacy attenuation - final_color += intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, normalize(lv)); + vec3 intensity = dist_atten * dlit * 3.25 * shadow; // Legacy attenuation, magic number to balance with legacy materials + final_color += intensity*pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, lv); } - amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ); - final_color += diffuse.rgb * amb_rgb; + amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ) * 3.25; //magic number to balance with legacy ambiance + final_color += amb_rgb * pbrPunctual(diffuseColor, specularColor, perceptualRoughness, metallic, n.xyz, v, -lv); } } else { - diffuse = legacy_adjust(diffuse); diffuse = srgb_to_linear(diffuse); spec.rgb = srgb_to_linear(spec.rgb); - + if (proj_tc.z > 0.0 && proj_tc.x < 1.0 && proj_tc.y < 1.0 && proj_tc.x > 0.0 && proj_tc.y > 0.0) { - float amb_da = proj_ambiance; + float amb_da = 0; float lit = 0.0; if (nl > 0.0) @@ -204,24 +202,23 @@ void main() final_color = dlit*lit*diffuse*shadow; + // unshadowed for consistency between forward and deferred? amb_da += (nl*0.5+0.5) /* * (1.0-shadow) */ * proj_ambiance; } - - vec3 amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ); - final_color += diffuse.rgb*amb_rgb; - #if DEBUG_LEG_LIGHT_TYPE - final_color = vec3(0,0.5,0); - #endif + + amb_rgb = getProjectedLightAmbiance( amb_da, dist_atten, lit, nl, 1.0, proj_tc.xy ); + final_color += diffuse.rgb * amb_rgb * max(dot(-normalize(lv), n), 0.0); } - + if (spec.a > 0.0) { dlit *= min(nl*6.0, 1.0) * dist_atten; - float fres = pow(1 - dot(h, v), 5)*0.4+0.5; + + float fres = pow(1 - vh, 5)*0.4+0.5; float gtdenom = 2 * nh; float gt = max(0, min(gtdenom * nv / vh, gtdenom * nl / vh)); - + if (nh > 0.0) { float scol = fres*texture(lightFunc, vec2(nh, spec.a)).r*gt/(nh*nl); @@ -229,26 +226,26 @@ void main() speccol = clamp(speccol, vec3(0), vec3(1)); final_color += speccol; } - } + } if (envIntensity > 0.0) { vec3 ref = reflect(normalize(pos), n); - + //project from point pos in direction ref to plane proj_p, proj_n vec3 pdelta = proj_p-pos; float ds = dot(ref, proj_n); - + if (ds < 0.0) { vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; - + vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); if (stc.z > 0.0) { stc /= stc.w; - + if (stc.x < 1.0 && stc.y < 1.0 && stc.x > 0.0 && @@ -261,24 +258,10 @@ void main() } } -#if DEBUG_PBR_SPOT_DIFFUSE - final_color = vec3(nl * dist_atten); -#endif -#if DEBUG_SPOT_NL - final_color = vec3(nl); -#endif -#if DEBUG_SPOT_ZERO - final_color = vec3(0,0,0); -#endif -#if DEBUG_ANY_LIGHT_TYPE - final_color = vec3(0,0.3333,0); -#endif - //not sure why, but this line prevents MATBUG-194 final_color = max(final_color, vec3(0.0)); - //output linear colors as gamma correction happens down stream + //output linear frag_color.rgb = final_color; frag_color.a = 0.0; -#endif // LOCAL_LIGHT_KILL } diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl index 8fee259933..4f79dd1ac5 100644 --- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl @@ -124,7 +124,7 @@ vec3 transform_normal(vec3 vNt) } void sampleReflectionProbesWater(inout vec3 ambenv, inout vec3 glossenv, - vec2 tc, vec3 pos, vec3 norm, float glossiness); + vec2 tc, vec3 pos, vec3 norm, float glossiness, vec3 amblit_linear); vec3 getPositionWithNDC(vec3 ndc); @@ -237,7 +237,7 @@ void main() vec3 irradiance = vec3(0); vec3 radiance = vec3(0); - sampleReflectionProbesWater(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss); + sampleReflectionProbesWater(irradiance, radiance, distort2, pos.xyz, wave_ibl.xyz, gloss, amblit); irradiance = vec3(0); diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 4bc83b099f..c341097dfc 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -42,7 +42,7 @@ RenderGamma 1 0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 8192 RenderObjectBump 1 1 -RenderLocalLights 1 1 +RenderLocalLightCount 1 4096 RenderTransparentWater 1 1 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 2 @@ -89,10 +89,10 @@ RenderAvatarMaxComplexity 1 35000 RenderFarClip 1 64 RenderFlexTimeFactor 1 0 RenderGlowResolutionPow 1 8 -RenderLocalLights 1 0 +RenderLocalLightCount 1 8 RenderMaxPartCount 1 0 RenderTransparentWater 1 0 -RenderReflectionsEnabled 1 0 +RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 @@ -120,7 +120,7 @@ RenderFarClip 1 96 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 8 RenderMaxPartCount 1 2048 -RenderLocalLights 1 1 +RenderLocalLightCount 1 256 RenderTransparentWater 1 0 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 @@ -150,7 +150,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 512 RenderTransparentWater 1 0 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -180,7 +180,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 1024 RenderTransparentWater 1 1 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -210,7 +210,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 2048 RenderTransparentWater 1 1 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -240,7 +240,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 4096 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 RenderTerrainPBRPlanarSampleCount 1 3 @@ -268,7 +268,7 @@ RenderAvatarPhysicsLODFactor 1 1.0 RenderFarClip 1 256 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 -RenderLocalLights 1 1 +RenderLocalLightCount 1 8192 RenderMaxPartCount 1 8192 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -308,7 +308,7 @@ list safe RenderAnisotropic 1 0 RenderAvatarMaxNonImpostors 1 16 RenderAvatarMaxComplexity 1 80000 -RenderLocalLights 1 0 +RenderLocalLightCount 1 0 RenderMaxPartCount 1 1024 RenderTerrainDetail 1 0 RenderTransparentWater 1 0 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index 97dad655a2..6927915f17 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -42,7 +42,7 @@ RenderGamma 1 0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 8192 RenderObjectBump 1 1 -RenderLocalLights 1 1 +RenderLocalLightCount 1 4096 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 RenderTerrainPBRPlanarSampleCount 1 3 @@ -86,7 +86,7 @@ RenderAvatarMaxComplexity 1 35000 RenderFarClip 1 64 RenderFlexTimeFactor 1 0 RenderGlowResolutionPow 1 8 -RenderLocalLights 1 0 +RenderLocalLightCount 1 8 RenderMaxPartCount 1 0 RenderTerrainDetail 1 0 RenderTerrainLODFactor 1 1 @@ -99,7 +99,7 @@ RenderUseAdvancedAtmospherics 1 0 RenderShadowDetail 1 0 WLSkyDetail 1 96 RenderFSAASamples 1 0 -RenderReflectionsEnabled 1 0 +RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 0 RenderScreenSpaceReflections 1 0 RenderReflectionProbeLevel 1 0 @@ -117,7 +117,7 @@ RenderFarClip 1 96 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 8 RenderMaxPartCount 1 2048 -RenderLocalLights 1 1 +RenderLocalLightCount 1 256 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 1.0 RenderTerrainPBRPlanarSampleCount 1 1 @@ -147,7 +147,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 512 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 RenderTerrainPBRPlanarSampleCount 1 1 @@ -177,7 +177,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 1024 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 RenderTerrainPBRPlanarSampleCount 1 1 @@ -207,7 +207,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 2048 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 RenderTerrainPBRPlanarSampleCount 1 3 @@ -237,7 +237,7 @@ RenderFarClip 1 128 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 RenderMaxPartCount 1 4096 -RenderLocalLights 1 1 +RenderLocalLightCount 1 4096 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 RenderTerrainPBRPlanarSampleCount 1 3 @@ -265,7 +265,7 @@ RenderAvatarMaxNonImpostors 1 16 RenderFarClip 1 256 RenderFlexTimeFactor 1 1.0 RenderGlowResolutionPow 1 9 -RenderLocalLights 1 1 +RenderLocalLightCount 1 8192 RenderMaxPartCount 1 8192 RenderTerrainDetail 1 1 RenderTerrainLODFactor 1 2.0 @@ -306,7 +306,7 @@ list safe RenderAnisotropic 1 0 RenderAvatarMaxNonImpostors 1 16 RenderAvatarMaxComplexity 1 80000 -RenderLocalLights 1 0 +RenderLocalLightCount 1 0 RenderMaxPartCount 1 1024 RenderTerrainDetail 1 0 RenderDeferredSSAO 0 0 @@ -324,7 +324,6 @@ RenderDeferredSSAO 1 0 list Intel RenderAnisotropic 1 0 -RenderLocalLights 1 0 RenderFSAASamples 1 0 list GL3 diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index 778d32e66c..39247b3f47 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -507,7 +507,7 @@ Call un.UserSettingsFiles SectionEnd ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Make sure the user can install +;; Make sure the user can install/uninstall ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Function CheckIfAdministrator DetailPrint $(CheckAdministratorInstDP) @@ -518,26 +518,8 @@ Function CheckIfAdministrator Quit lbl_is_admin: Return - FunctionEnd -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Make sure the user can uninstall -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -/* Unused -Function un.CheckIfAdministrator - DetailPrint $(CheckAdministratorUnInstDP) - UserInfo::GetAccountType - Pop $R0 - StrCmp $R0 "Admin" lbl_is_admin - MessageBox MB_OK $(CheckAdministratorUnInstMB) - Quit -lbl_is_admin: - Return - -FunctionEnd -*/ - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Function CheckWillUninstallV2 ;; diff --git a/indra/newview/installers/windows/lang_zh.nsi b/indra/newview/installers/windows/lang_zh.nsi index 7922d9df52..0985663041 100755 Binary files a/indra/newview/installers/windows/lang_zh.nsi and b/indra/newview/installers/windows/lang_zh.nsi differ diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 4c3891f302..a273afab52 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -120,6 +120,10 @@ const F64 CHAT_AGE_FAST_RATE = 3.0; const F32 MIN_FIDGET_TIME = 8.f; // seconds const F32 MAX_FIDGET_TIME = 20.f; // seconds +const S32 UI_FEATURE_VERSION = 1; +// For version 1: 1 - inventory, 2 - gltf +const S32 UI_FEATURE_FLAGS = 3; + // The agent instance. LLAgent gAgent; @@ -372,7 +376,7 @@ LLAgent::LLAgent() : mHideGroupTitle(FALSE), mGroupID(), - mInitialized(FALSE), + mInitialized(false), mListener(), mDoubleTapRunTimer(), @@ -401,6 +405,7 @@ LLAgent::LLAgent() : mHttpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID), mTeleportState(TELEPORT_NONE), mRegionp(NULL), + mInterestListMode(LLViewerRegion::IL_MODE_DEFAULT), mAgentOriginGlobal(), mPositionGlobal(), @@ -447,7 +452,7 @@ LLAgent::LLAgent() : mNextFidgetTime(0.f), mCurrentFidget(0), - mFirstLogin(FALSE), + mFirstLogin(false), mOutfitChosen(FALSE), mVoiceConnected(false), @@ -504,7 +509,7 @@ void LLAgent::init() mHttpPolicy = app_core_http.getPolicy(LLAppCoreHttp::AP_AGENT); - mInitialized = TRUE; + mInitialized = true; } //----------------------------------------------------------------------------- @@ -559,6 +564,93 @@ void LLAgent::onAppFocusGained() } } +void LLAgent::setFirstLogin(bool b) +{ + mFirstLogin = b; + + if (mFirstLogin) + { + // Don't notify new users about new features + if (getFeatureVersion() <= UI_FEATURE_VERSION) + { + setFeatureVersion(UI_FEATURE_VERSION, UI_FEATURE_FLAGS); + } + } +} + +void LLAgent::setFeatureVersion(S32 version, S32 flags) +{ + LLSD updated_version; + updated_version["version"] = version; + updated_version["flags"] = flags; + gSavedSettings.setLLSD("LastUIFeatureVersion", updated_version); +} + +S32 LLAgent::getFeatureVersion() +{ + S32 version; + S32 flags; + getFeatureVersionAndFlags(version, flags); + return version; +} + +void LLAgent::getFeatureVersionAndFlags(S32& version, S32& flags) +{ + version = 0; + flags = 0; + LLSD feature_version = gSavedSettings.getLLSD("LastUIFeatureVersion"); + if (feature_version.isInteger()) + { + version = feature_version.asInteger(); + flags = 1; // inventory flag + } + else if (feature_version.isMap()) + { + version = feature_version["version"]; + flags = feature_version["flags"]; + } + else if (!feature_version.isString() && !feature_version.isUndefined()) + { + // is something newer inside? + version = UI_FEATURE_VERSION; + flags = UI_FEATURE_FLAGS; + } +} + +void LLAgent::showLatestFeatureNotification(const std::string key) +{ + S32 version; + S32 flags; // a single release can have multiple new features + getFeatureVersionAndFlags(version, flags); + if (version <= UI_FEATURE_VERSION && (flags & UI_FEATURE_FLAGS) != UI_FEATURE_FLAGS) + { + S32 flag = 0; + + if (key == "inventory") + { + // Notify user about new thumbnail support + flag = 1; + } + + if (key == "gltf") + { + flag = 2; + } + + if ((flags & flag) == 0) + { + // Need to open on top even if called from onOpen, + // do on idle to make sure it's on top + LLSD floater_key(key); + doOnIdleOneTime([floater_key]() + { + LLFloaterReg::showInstance("new_feature_notification", floater_key); + }); + + setFeatureVersion(UI_FEATURE_VERSION, flags | flag); + } + } +} void LLAgent::ageChat() { @@ -894,11 +986,19 @@ boost::signals2::connection LLAgent::addParcelChangedCallback(parcel_changed_cal // static void LLAgent::capabilityReceivedCallback(const LLUUID ®ion_id, LLViewerRegion *regionp) -{ - if (regionp && regionp->getRegionID() == region_id) +{ // Changed regions and now have the region capabilities + if (regionp) { - regionp->requestSimulatorFeatures(); - LLAppViewer::instance()->updateNameLookupUrl(regionp); + if (regionp->getRegionID() == region_id) + { + regionp->requestSimulatorFeatures(); + LLAppViewer::instance()->updateNameLookupUrl(regionp); + } + + if (gAgent.getInterestListMode() == LLViewerRegion::IL_MODE_360) + { + gAgent.changeInterestListMode(LLViewerRegion::IL_MODE_360); + } } } @@ -1358,26 +1458,21 @@ LLVector3 LLAgent::getReferenceUpVector() void LLAgent::pitch(F32 angle) { // don't let user pitch if pointed almost all the way down or up - mFrameAgent.pitch(clampPitchToLimits(angle)); -} - -// Radians, positive is forward into ground -//----------------------------------------------------------------------------- -// clampPitchToLimits() -//----------------------------------------------------------------------------- -F32 LLAgent::clampPitchToLimits(F32 angle) -{ // A dot B = mag(A) * mag(B) * cos(angle between A and B) // so... cos(angle between A and B) = A dot B / mag(A) / mag(B) // = A dot B for unit vectors LLVector3 skyward = getReferenceUpVector(); - const F32 look_down_limit = 179.f * DEG_TO_RAD;; - const F32 look_up_limit = 1.f * DEG_TO_RAD; + // SL-19286 Avatar is upside down when viewed from below + // after left-clicking the mouse on the avatar and dragging down + // + // The issue is observed on angle below 10 degrees + const F32 look_down_limit = 179.f * DEG_TO_RAD; + const F32 look_up_limit = 10.f * DEG_TO_RAD; - F32 angle_from_skyward = acos( mFrameAgent.getAtAxis() * skyward ); + F32 angle_from_skyward = acos(mFrameAgent.getAtAxis() * skyward); // clamp pitch to limits if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit)) @@ -1388,8 +1483,11 @@ F32 LLAgent::clampPitchToLimits(F32 angle) { angle = look_up_limit - angle_from_skyward; } - - return angle; + + if (fabs(angle) > 1e-4) + { + mFrameAgent.pitch(angle); + } } @@ -2906,39 +3004,60 @@ void LLAgent::processMaturityPreferenceFromServer(const LLSD &result, U8 perferr handlePreferredMaturityResult(maturity); } +// Using a new capability, tell the simulator that we want it to send everything +// it knows about and not just what is in front of the camera, in its view +// frustum. We need this feature so that the contents of the region that appears +// in the 6 snapshots which we cannot see and is normally not "considered", is +// also rendered. Typically, this is turned on when the 360 capture floater is +// opened and turned off when it is closed. +// Note: for this version, we do not have a way to determine when "everything" +// has arrived and has been rendered so for now, the proposal is that users +// will need to experiment with the low resolution version and wait for some +// (hopefully) small period of time while the full contents resolves. +// Pass in a flag to ask the simulator/interest list to "send everything" or +// not (the default mode) +void LLAgent::changeInterestListMode(const std::string &new_mode) +{ + if (new_mode != mInterestListMode) + { + mInterestListMode = new_mode; + + // Change interest list mode for all regions. If they are already set for the current mode, + // the setting will have no effect. + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); + ++iter) + { + LLViewerRegion *regionp = *iter; + if (regionp && regionp->isAlive() && regionp->capabilitiesReceived()) + { + regionp->setInterestListMode(mInterestListMode); + } + } + } + else + { + LL_DEBUGS("360Capture") << "Agent interest list mode is already set to " << mInterestListMode << LL_ENDL; + } +} + bool LLAgent::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure) { - if (!getRegion()) + if (getRegion()) { - return false; + return getRegion()->requestPostCapability(capName, postData, cbSuccess, cbFailure); } - std::string url = getRegion()->getCapability(capName); - - if (url.empty()) - { - LL_WARNS("Agent") << "Could not retrieve region capability \"" << capName << "\"" << LL_ENDL; - return false; - } - - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, mHttpPolicy, postData, cbSuccess, cbFailure); - return true; + return false; } bool LLAgent::requestGetCapability(const std::string &capName, httpCallback_t cbSuccess, httpCallback_t cbFailure) { - std::string url; - - url = getRegionCapability(capName); - - if (url.empty()) + if (getRegion()) { - LL_WARNS("Agent") << "Could not retrieve region capability \"" << capName << "\"" << LL_ENDL; - return false; + return getRegion()->requestGetCapability(capName, cbSuccess, cbFailure); } - - LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(url, mHttpPolicy, cbSuccess, cbFailure); - return true; + return false; } BOOL LLAgent::getAdminOverride() const @@ -3704,7 +3823,6 @@ void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **) // take controls msg->getU32("Data", "Controls", controls, block_index ); msg->getBOOL("Data", "PassToAgent", passon, block_index ); - U32 total_count = 0; for (i = 0; i < TOTAL_CONTROLS; i++) { if (controls & ( 1 << i)) @@ -3717,7 +3835,6 @@ void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **) { gAgent.mControlsTakenCount[i]++; } - total_count++; } } } diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 498bea3c07..fd3a9b1d7b 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -117,15 +117,20 @@ private: //-------------------------------------------------------------------- public: void onAppFocusGained(); - void setFirstLogin(BOOL b) { mFirstLogin = b; } + void setFirstLogin(bool b); // Return TRUE if the database reported this login as the first for this particular user. - BOOL isFirstLogin() const { return mFirstLogin; } - BOOL isInitialized() const { return mInitialized; } + bool isFirstLogin() const { return mFirstLogin; } + bool isInitialized() const { return mInitialized; } + + void setFeatureVersion(S32 version, S32 flags); + S32 getFeatureVersion(); + void getFeatureVersionAndFlags(S32 &version, S32 &flags); + void showLatestFeatureNotification(const std::string key); public: std::string mMOTD; // Message of the day private: - BOOL mInitialized; - BOOL mFirstLogin; + bool mInitialized; + bool mFirstLogin; boost::shared_ptr mListener; //-------------------------------------------------------------------- @@ -294,10 +299,16 @@ public: boost::signals2::connection addRegionChangedCallback(const region_changed_signal_t::slot_type& cb); void removeRegionChangedCallback(boost::signals2::connection callback); + + void changeInterestListMode(const std::string & new_mode); + const std::string & getInterestListMode() const { return mInterestListMode; } + private: LLViewerRegion *mRegionp; region_changed_signal_t mRegionChangedSignal; + std::string mInterestListMode; // How agent wants regions to send updates + //-------------------------------------------------------------------- // History //-------------------------------------------------------------------- @@ -548,7 +559,6 @@ public: void roll(F32 angle); void yaw(F32 angle); LLVector3 getReferenceUpVector(); - F32 clampPitchToLimits(F32 angle); //-------------------------------------------------------------------- // Autopilot diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index 826a4ba761..29642d3f45 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -401,10 +401,15 @@ LLVector3 LLAgentCamera::calcFocusOffset(LLViewerObject *object, LLVector3 origi // if is avatar - don't do any funk heuristics to position the focal point // see DEV-30589 - if (object->isAvatar() || (object->isAnimatedObject() && object->getControlAvatar())) + if ((object->isAvatar() && !object->isRoot()) || (object->isAnimatedObject() && object->getControlAvatar())) { return original_focus_point - obj_pos; } + if (object->isAvatar()) + { + LLVOAvatar* av = object->asAvatar(); + return original_focus_point - av->getCharacterPosition(); + } LLQuaternion inv_obj_rot = ~obj_rot; // get inverse of rotation LLVector3 object_extents = object->getScale(); @@ -1765,13 +1770,24 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit) LL_WARNS() << "Null avatar drawable!" << LL_ENDL; return LLVector3d::zero; } + head_offset.clearVec(); + F32 fixup; + if (gAgentAvatarp->hasPelvisFixup(fixup)) + { + head_offset[VZ] -= fixup; + } + if (gAgentAvatarp->isSitting()) + { + head_offset.mdV[VZ] += 0.1; + } + if (gAgentAvatarp->isSitting() && gAgentAvatarp->getParent()) { gAgentAvatarp->updateHeadOffset(); - head_offset.mdV[VX] = gAgentAvatarp->mHeadOffset.mV[VX]; - head_offset.mdV[VY] = gAgentAvatarp->mHeadOffset.mV[VY]; - head_offset.mdV[VZ] = gAgentAvatarp->mHeadOffset.mV[VZ] + 0.1f; + head_offset.mdV[VX] += gAgentAvatarp->mHeadOffset.mV[VX]; + head_offset.mdV[VY] += gAgentAvatarp->mHeadOffset.mV[VY]; + head_offset.mdV[VZ] += gAgentAvatarp->mHeadOffset.mV[VZ]; const LLMatrix4& mat = ((LLViewerObject*) gAgentAvatarp->getParent())->getRenderMatrix(); camera_position_global = gAgent.getPosGlobalFromAgent ((gAgentAvatarp->getPosition()+ @@ -1779,11 +1795,7 @@ LLVector3d LLAgentCamera::calcCameraPositionTargetGlobal(BOOL *hit_limit) } else { - head_offset.mdV[VZ] = gAgentAvatarp->mHeadOffset.mV[VZ]; - if (gAgentAvatarp->isSitting()) - { - head_offset.mdV[VZ] += 0.1; - } + head_offset.mdV[VZ] += gAgentAvatarp->mHeadOffset.mV[VZ]; camera_position_global = gAgent.getPosGlobalFromAgent(gAgentAvatarp->getRenderPosition());//frame_center_global; head_offset = head_offset * gAgentAvatarp->getRenderRotation(); camera_position_global = camera_position_global + head_offset; diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index b7f5aeb8e0..77a3d47aea 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -35,6 +35,7 @@ #include "llcommandhandler.h" #include "llslurl.h" #include "llurldispatcher.h" +#include "llviewernetwork.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" @@ -148,7 +149,7 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const params.append(event_data["x"]); params.append(event_data["y"]); params.append(event_data["z"]); - LLCommandDispatcher::dispatch("teleport", params, LLSD(), NULL, LLCommandHandler::NAV_TYPE_CLICKED, true); + LLCommandDispatcher::dispatch("teleport", params, LLSD(), LLGridManager::getInstance()->getGrid(), NULL, LLCommandHandler::NAV_TYPE_CLICKED, true); // *TODO - lookup other LLCommandHandlers for "agent", "classified", "event", "group", "floater", "parcel", "login", login_refresh", "balance", "chat" // should we just compose LLCommandHandler and LLDispatchListener? } diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index c19ad2ae6f..acb1a37ff5 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -82,9 +82,9 @@ BOOL LLAgentUI::buildLocationString(std::string& str, ELocationFormat fmt,const if (!region || !parcel) return FALSE; - S32 pos_x = S32(agent_pos_region.mV[VX]); - S32 pos_y = S32(agent_pos_region.mV[VY]); - S32 pos_z = S32(agent_pos_region.mV[VZ]); + S32 pos_x = S32(agent_pos_region.mV[VX] + 0.5f); + S32 pos_y = S32(agent_pos_region.mV[VY] + 0.5f); + S32 pos_z = S32(agent_pos_region.mV[VZ] + 0.5f); // Round the numbers based on the velocity F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 2e769dc737..db99f20775 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -41,7 +41,6 @@ #include "llinventoryobserver.h" #include "llinventorypanel.h" #include "lllocaltextureobject.h" -#include "llmd5.h" #include "llnotificationsutil.h" #include "lloutfitobserver.h" #include "llsidepanelappearance.h" @@ -1307,8 +1306,9 @@ void LLAgentWearables::findAttachmentsAddRemoveInfo(LLInventoryModel::item_array } // Build up list of objects to be removed and items currently attached. - for (LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); - iter != gAgentAvatarp->mAttachmentPoints.end();) + LLVOAvatar::attachment_map_t::iterator iter = gAgentAvatarp->mAttachmentPoints.begin(); + LLVOAvatar::attachment_map_t::iterator end = gAgentAvatarp->mAttachmentPoints.end(); + while (iter != end) { LLVOAvatar::attachment_map_t::iterator curiter = iter++; LLViewerJointAttachment* attachment = curiter->second; @@ -1527,7 +1527,7 @@ bool LLAgentWearables::moveWearable(const LLViewerInventoryItem* item, bool clos } // static -void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id) +void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, const LLUUID& parent_id, std::function created_cb) { if (type == LLWearableType::WT_INVALID || type == LLWearableType::WT_NONE) return; @@ -1539,7 +1539,7 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con LLViewerWearable* wearable = LLWearableList::instance().createNewWearable(type, gAgentAvatarp); LLAssetType::EType asset_type = wearable->getAssetType(); - LLPointer cb; + LLPointer cb; if(wear) { cb = new LLBoostFuncInventoryCallback(wear_and_edit_cb); @@ -1548,6 +1548,10 @@ void LLAgentWearables::createWearable(LLWearableType::EType type, bool wear, con { cb = new LLBoostFuncInventoryCallback(wear_cb); } + if (created_cb != NULL) + { + cb->addOnFireFunc(created_cb); + } LLUUID folder_id; diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 2710262910..e20f5df7fa 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -129,7 +129,7 @@ protected: //-------------------------------------------------------------------- public: - static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null); + static void createWearable(LLWearableType::EType type, bool wear = false, const LLUUID& parent_id = LLUUID::null, std::function created_cb = NULL); static void editWearable(const LLUUID& item_id); bool moveWearable(const LLViewerInventoryItem* item, bool closer_to_body); diff --git a/indra/newview/llaisapi.cpp b/indra/newview/llaisapi.cpp index 005259bcb8..17e1a27934 100644 --- a/indra/newview/llaisapi.cpp +++ b/indra/newview/llaisapi.cpp @@ -29,11 +29,15 @@ #include "llaisapi.h" #include "llagent.h" +#include "llappviewer.h" #include "llcallbacklist.h" #include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llnotificationsutil.h" #include "llsdutil.h" #include "llviewerregion.h" -#include "llinventoryobserver.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" #include "llviewercontrol.h" ///---------------------------------------------------------------------------- @@ -43,11 +47,16 @@ //========================================================================= const std::string AISAPI::INVENTORY_CAP_NAME("InventoryAPIv3"); const std::string AISAPI::LIBRARY_CAP_NAME("LibraryAPIv3"); +const S32 AISAPI::HTTP_TIMEOUT = 180; std::list AISAPI::sPostponedQuery; const S32 MAX_SIMULTANEOUS_COROUTINES = 2048; +// AIS3 allows '*' requests, but in reality those will be cut at some point +// Specify own depth to be able to anticipate it and mark folders as incomplete +const S32 MAX_FOLDER_DEPTH_REQUEST = 50; + //------------------------------------------------------------------------- /*static*/ bool AISAPI::isAvailable() @@ -93,6 +102,10 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -100,7 +113,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c tid.generate(); std::string url = cap + std::string("/category/") + parentId.asString() + "?tid=" + tid.asString(); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + LL_DEBUGS("Inventory") << "url: " << url << " parentID " << parentId << " newInventory " << newInventory << LL_ENDL; // I may be suffering from golden hammer here, but the first part of this bind // is actually a static cast for &HttpCoroutineAdapter::postAndSuspend so that @@ -124,7 +137,7 @@ void AISAPI::CreateInventory(const LLUUID& parentId, const LLSD& newInventory, c (&LLCoreHttpUtil::HttpCoroutineAdapter::postAndSuspend), _1, _2, _3, _4, _5, _6); LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, - _1, postFn, url, parentId, newInventory, callback, COPYINVENTORY)); + _1, postFn, url, parentId, newInventory, callback, CREATEINVENTORY)); EnqueueAISCommand("CreateInventory", proc); } @@ -135,6 +148,10 @@ void AISAPI::SlamFolder(const LLUUID& folderId, const LLSD& newInventory, comple if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -170,6 +187,10 @@ void AISAPI::RemoveCategory(const LLUUID &categoryId, completion_t callback) if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -203,6 +224,10 @@ void AISAPI::RemoveItem(const LLUUID &itemId, completion_t callback) if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -235,6 +260,10 @@ void AISAPI::CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, b if (cap.empty()) { LL_WARNS("Inventory") << "Library cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -279,6 +308,10 @@ void AISAPI::PurgeDescendents(const LLUUID &categoryId, completion_t callback) if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -313,6 +346,10 @@ void AISAPI::UpdateCategory(const LLUUID &categoryId, const LLSD &updates, compl if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } std::string url = cap + std::string("/category/") + categoryId.asString(); @@ -345,6 +382,10 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t if (cap.empty()) { LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } std::string url = cap + std::string("/item/") + itemId.asString(); @@ -367,6 +408,380 @@ void AISAPI::UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t EnqueueAISCommand("UpdateItem", proc); } +/*static*/ +void AISAPI::FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback) +{ + std::string cap; + + cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/item/") + itemId.asString(); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, itemId, LLSD(), callback, FETCHITEM)); + + EnqueueAISCommand("FetchItem", proc); +} + +/*static*/ +void AISAPI::FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type, bool recursive, completion_t callback, S32 depth) +{ + std::string cap; + + cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/category/") + catId.asString() + "/children"; + + if (recursive) + { + // can specify depth=*, but server side is going to cap requests + // and reject everything 'over the top',. + depth = MAX_FOLDER_DEPTH_REQUEST; + } + else + { + depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); + } + + url += "?depth=" + std::to_string(depth); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + // get doesn't use body, can pass additional data + LLSD body; + body["depth"] = depth; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, catId, body, callback, FETCHCATEGORYCHILDREN)); + + EnqueueAISCommand("FetchCategoryChildren", proc); +} + +// some folders can be requested by name, like +// animatn | bodypart | clothing | current | favorite | gesture | inbox | landmark | lsltext +// lstndfnd | my_otfts | notecard | object | outbox | root | snapshot | sound | texture | trash +void AISAPI::FetchCategoryChildren(const std::string &identifier, bool recursive, completion_t callback, S32 depth) +{ + std::string cap; + + cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/category/") + identifier + "/children"; + + if (recursive) + { + // can specify depth=*, but server side is going to cap requests + // and reject everything 'over the top',. + depth = MAX_FOLDER_DEPTH_REQUEST; + } + else + { + depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); + } + + url += "?depth=" + std::to_string(depth); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + // get doesn't use body, can pass additional data + LLSD body; + body["depth"] = depth; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, LLUUID::null, body, callback, FETCHCATEGORYCHILDREN)); + + EnqueueAISCommand("FetchCategoryChildren", proc); +} + +/*static*/ +void AISAPI::FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type, bool recursive, completion_t callback, S32 depth) +{ + std::string cap; + + cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/category/") + catId.asString() + "/categories"; + + if (recursive) + { + // can specify depth=*, but server side is going to cap requests + // and reject everything 'over the top',. + depth = MAX_FOLDER_DEPTH_REQUEST; + } + else + { + depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); + } + + url += "?depth=" + std::to_string(depth); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + // get doesn't use body, can pass additional data + LLSD body; + body["depth"] = depth; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, catId, body, callback, FETCHCATEGORYCATEGORIES)); + + EnqueueAISCommand("FetchCategoryCategories", proc); +} + +void AISAPI::FetchCategorySubset(const LLUUID& catId, + const uuid_vec_t specificChildren, + ITEM_TYPE type, + bool recursive, + completion_t callback, + S32 depth) +{ + std::string cap = (type == INVENTORY) ? getInvCap() : getLibCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + if (specificChildren.empty()) + { + LL_WARNS("Inventory") << "Empty request!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + // category/any_folder_id/children?depth=*&children=child_id1,child_id2,child_id3 + std::string url = cap + std::string("/category/") + catId.asString() + "/children"; + + if (recursive) + { + depth = MAX_FOLDER_DEPTH_REQUEST; + } + else + { + depth = llmin(depth, MAX_FOLDER_DEPTH_REQUEST); + } + + uuid_vec_t::const_iterator iter = specificChildren.begin(); + uuid_vec_t::const_iterator end = specificChildren.end(); + + url += "?depth=" + std::to_string(depth) + "&children=" + iter->asString(); + iter++; + + while (iter != end) + { + url += "," + iter->asString(); + iter++; + } + + const S32 MAX_URL_LENGH = 2000; // RFC documentation specifies a maximum length of 2048 + if (url.length() > MAX_URL_LENGH) + { + LL_WARNS("Inventory") << "Request url is too long, url: " << url << LL_ENDL; + } + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + // get doesn't use body, can pass additional data + LLSD body; + body["depth"] = depth; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, catId, body, callback, FETCHCATEGORYSUBSET)); + + EnqueueAISCommand("FetchCategorySubset", proc); +} + +/*static*/ +// Will get COF folder, links in it and items those links point to +void AISAPI::FetchCOF(completion_t callback) +{ + std::string cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/category/current/links"); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), _1, _2, _3, _5, _6); + + LLSD body; + // Only cof folder will be full, but cof can contain an outfit + // link with embedded outfit folder for request to parse + body["depth"] = 0; + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro, + _1, getFn, url, LLUUID::null, body, callback, FETCHCOF)); + + EnqueueAISCommand("FetchCOF", proc); +} + +void AISAPI::FetchCategoryLinks(const LLUUID &catId, completion_t callback) +{ + std::string cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/category/") + catId.asString() + "/links"; + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend), + _1, _2, _3, _5, _6); + + LLSD body; + body["depth"] = 0; + LLCoprocedureManager::CoProcedure_t proc( + boost::bind(&AISAPI::InvokeAISCommandCoro, _1, getFn, url, LLUUID::null, body, callback, FETCHCATEGORYLINKS)); + + EnqueueAISCommand("FetchCategoryLinks", proc); +} + +/*static*/ +void AISAPI::FetchOrphans(completion_t callback) +{ + std::string cap = getInvCap(); + if (cap.empty()) + { + LL_WARNS("Inventory") << "Inventory cap not found!" << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } + return; + } + std::string url = cap + std::string("/orphans"); + + invokationFn_t getFn = boost::bind( + // Humans ignore next line. It is just a cast to specify which LLCoreHttpUtil::HttpCoroutineAdapter routine overload. + static_cast + //---- + // _1 -> httpAdapter + // _2 -> httpRequest + // _3 -> url + // _4 -> body + // _5 -> httpOptions + // _6 -> httpHeaders + (&LLCoreHttpUtil::HttpCoroutineAdapter::getAndSuspend) , _1 , _2 , _3 , _5 , _6); + + LLCoprocedureManager::CoProcedure_t proc(boost::bind(&AISAPI::InvokeAISCommandCoro , + _1 , getFn , url , LLUUID::null , LLSD() , callback , FETCHORPHANS)); + + EnqueueAISCommand("FetchOrphans" , proc); +} + /*static*/ void AISAPI::EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc) { @@ -417,22 +832,50 @@ void AISAPI::onIdle(void *userdata) } } +/*static*/ +void AISAPI::onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body) +{ + LLTimer timer; + if ( (type == UPDATECATEGORY || type == UPDATEITEM) + && gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) + { + dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); + } + + AISUpdate ais_update(update, type, request_body); + ais_update.doUpdate(); // execute the updates in the appropriate order. + LL_DEBUGS("Inventory", "AIS3") << "Elapsed processing: " << timer.getElapsedTimeF32() << LL_ENDL; +} + /*static*/ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter, invokationFn_t invoke, std::string url, LLUUID targetId, LLSD body, completion_t callback, COMMAND_TYPE type) { + if (gDisconnected) + { + if (callback) + { + callback(LLUUID::null); + } + return; + } + LLCore::HttpOptions::ptr_t httpOptions(new LLCore::HttpOptions); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest()); LLCore::HttpHeaders::ptr_t httpHeaders; - httpOptions->setTimeout(LLCoreHttpUtil::HTTP_REQUEST_EXPIRY_SECS); + httpOptions->setTimeout(HTTP_TIMEOUT); - LL_DEBUGS("Inventory") << "url: " << url << LL_ENDL; + LL_DEBUGS("Inventory") << "Request url: " << url << LL_ENDL; - LLSD result = invoke(httpAdapter, httpRequest, url, body, httpOptions, httpHeaders); - LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; - LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); + LLSD result; + LLSD httpResults; + LLCore::HttpStatus status; + + result = invoke(httpAdapter , httpRequest , url , body , httpOptions , httpHeaders); + httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; + status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status || !result.isMap()) { @@ -474,29 +917,127 @@ void AISAPI::InvokeAISCommandCoro(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t ht } } } + else if (status == LLCore::HttpStatus(HTTP_FORBIDDEN) /*403*/) + { + if (type == FETCHCATEGORYCHILDREN) + { + if (body.has("depth") && body["depth"].asInteger() == 0) + { + // Can't fetch a single folder with depth 0, folder is too big. + static bool first_call = true; + if (first_call) + { + first_call = false; + LLNotificationsUtil::add("InventoryLimitReachedAISAlert"); + } + else + { + LLNotificationsUtil::add("InventoryLimitReachedAIS"); + } + LL_WARNS("Inventory") << "Fetch failed, content is over limit, url: " << url << LL_ENDL; + } + else + { + // Result was too big, but situation is recoverable by requesting with lower depth + LL_DEBUGS("Inventory") << "Fetch failed, content is over limit, url: " << url << LL_ENDL; + } + } + } LL_WARNS("Inventory") << "Inventory error: " << status.toString() << LL_ENDL; LL_WARNS("Inventory") << ll_pretty_print_sd(result) << LL_ENDL; } - gInventory.onAISUpdateReceived("AISCommand", result); + LL_DEBUGS("Inventory", "AIS3") << "Result: " << result << LL_ENDL; + onUpdateReceived(result, type, body); if (callback && !callback.empty()) - { + { + bool needs_callback = true; LLUUID id(LLUUID::null); - if (result.has("category_id") && (type == COPYLIBRARYCATEGORY)) - { - id = result["category_id"]; - } + switch (type) + { + case COPYLIBRARYCATEGORY: + case FETCHCATEGORYCATEGORIES: + case FETCHCATEGORYCHILDREN: + case FETCHCATEGORYSUBSET: + case FETCHCATEGORYLINKS: + case FETCHCOF: + if (result.has("category_id")) + { + id = result["category_id"]; + } + break; + case FETCHITEM: + if (result.has("item_id")) + { + // Error message might contain an item_id!!! + id = result["item_id"]; + } + if (result.has("linked_id")) + { + id = result["linked_id"]; + } + break; + case CREATEINVENTORY: + // CREATEINVENTORY can have multiple callbacks + if (result.has("_created_categories")) + { + LLSD& cats = result["_created_categories"]; + LLSD::array_const_iterator cat_iter; + for (cat_iter = cats.beginArray(); cat_iter != cats.endArray(); ++cat_iter) + { + LLUUID cat_id = *cat_iter; + callback(cat_id); + needs_callback = false; + } + } + if (result.has("_created_items")) + { + LLSD& items = result["_created_items"]; + LLSD::array_const_iterator item_iter; + for (item_iter = items.beginArray(); item_iter != items.endArray(); ++item_iter) + { + LLUUID item_id = *item_iter; + callback(item_id); + needs_callback = false; + } + } + break; + default: + break; + } - callback(id); + if (needs_callback) + { + // Call callback at least once regardless of failure. + // UPDATEITEM doesn't expect an id + callback(id); + } } } //------------------------------------------------------------------------- -AISUpdate::AISUpdate(const LLSD& update) +AISUpdate::AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body) +: mType(type) { + mFetch = (type == AISAPI::FETCHITEM) + || (type == AISAPI::FETCHCATEGORYCHILDREN) + || (type == AISAPI::FETCHCATEGORYCATEGORIES) + || (type == AISAPI::FETCHCATEGORYSUBSET) + || (type == AISAPI::FETCHCOF) + || (type == AISAPI::FETCHCATEGORYLINKS) + || (type == AISAPI::FETCHORPHANS); + // parse update llsd into stuff to do or parse received items. + mFetchDepth = MAX_FOLDER_DEPTH_REQUEST; + if (mFetch && request_body.has("depth")) + { + mFetchDepth = request_body["depth"].asInteger(); + } + + mTimer.setTimerExpirySec(AIS_EXPIRY_SECONDS); + mTimer.start(); parseUpdate(update); } @@ -506,6 +1047,7 @@ void AISUpdate::clearParseResults() mCatDescendentsKnown.clear(); mCatVersionsUpdated.clear(); mItemsCreated.clear(); + mItemsLost.clear(); mItemsUpdated.clear(); mCategoriesCreated.clear(); mCategoriesUpdated.clear(); @@ -514,6 +1056,16 @@ void AISUpdate::clearParseResults() mCategoryIds.clear(); } +void AISUpdate::checkTimeout() +{ + if (mTimer.hasExpired()) + { + llcoro::suspend(); + LLCoros::checkStop(); + mTimer.setTimerExpirySec(AIS_EXPIRY_SECONDS); + } +} + void AISUpdate::parseUpdate(const LLSD& update) { clearParseResults(); @@ -601,24 +1153,37 @@ void AISUpdate::parseMeta(const LLSD& update) void AISUpdate::parseContent(const LLSD& update) { - if (update.has("linked_id")) + // Errors from a fetch request might contain id without + // full item or folder. + // Todo: Depending on error we might want to do something, + // like removing a 404 item or refetching parent folder + if (update.has("linked_id") && update.has("parent_id")) { - parseLink(update); + parseLink(update, mFetchDepth); } - else if (update.has("item_id")) + else if (update.has("item_id") && update.has("parent_id")) { parseItem(update); } - if (update.has("category_id")) - { - parseCategory(update); - } + if (mType == AISAPI::FETCHCATEGORYSUBSET) + { + // initial category is incomplete, don't process it, + // go for content instead + if (update.has("_embedded")) + { + parseEmbedded(update["_embedded"], mFetchDepth - 1); + } + } + else if (update.has("category_id") && update.has("parent_id")) + { + parseCategory(update, mFetchDepth); + } else { if (update.has("_embedded")) { - parseEmbedded(update["_embedded"]); + parseEmbedded(update["_embedded"], mFetchDepth); } } } @@ -636,7 +1201,17 @@ void AISUpdate::parseItem(const LLSD& item_map) BOOL rv = new_item->unpackMessage(item_map); if (rv) { - if (curr_item) + if (mFetch) + { + mItemsCreated[item_id] = new_item; + new_item->setComplete(true); + + if (new_item->getParentUUID().isNull()) + { + mItemsLost[item_id] = new_item; + } + } + else if (curr_item) { mItemsUpdated[item_id] = new_item; // This statement is here to cause a new entry with 0 @@ -648,6 +1223,7 @@ void AISUpdate::parseItem(const LLSD& item_map) { mItemsCreated[item_id] = new_item; mCatDescendentDeltas[new_item->getParentUUID()]++; + new_item->setComplete(true); } } else @@ -657,7 +1233,7 @@ void AISUpdate::parseItem(const LLSD& item_map) } } -void AISUpdate::parseLink(const LLSD& link_map) +void AISUpdate::parseLink(const LLSD& link_map, S32 depth) { LLUUID item_id = link_map["item_id"].asUUID(); LLPointer new_link(new LLViewerInventoryItem); @@ -671,7 +1247,24 @@ void AISUpdate::parseLink(const LLSD& link_map) if (rv) { const LLUUID& parent_id = new_link->getParentUUID(); - if (curr_link) + if (mFetch) + { + LLPermissions default_perms; + default_perms.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + default_perms.initMasks(PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE); + new_link->setPermissions(default_perms); + LLSaleInfo default_sale_info; + new_link->setSaleInfo(default_sale_info); + //LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL; + mItemsCreated[item_id] = new_link; + new_link->setComplete(true); + + if (new_link->getParentUUID().isNull()) + { + mItemsLost[item_id] = new_link; + } + } + else if (curr_link) { mItemsUpdated[item_id] = new_link; // This statement is here to cause a new entry with 0 @@ -690,7 +1283,13 @@ void AISUpdate::parseLink(const LLSD& link_map) //LL_DEBUGS("Inventory") << "creating link from llsd: " << ll_pretty_print_sd(link_map) << LL_ENDL; mItemsCreated[item_id] = new_link; mCatDescendentDeltas[parent_id]++; + new_link->setComplete(true); } + + if (link_map.has("_embedded")) + { + parseEmbedded(link_map["_embedded"], depth); + } } else { @@ -700,19 +1299,30 @@ void AISUpdate::parseLink(const LLSD& link_map) } -void AISUpdate::parseCategory(const LLSD& category_map) +void AISUpdate::parseCategory(const LLSD& category_map, S32 depth) { - LLUUID category_id = category_map["category_id"].asUUID(); + LLUUID category_id = category_map["category_id"].asUUID(); + S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN; - // Check descendent count first, as it may be needed - // to populate newly created categories - if (category_map.has("_embedded")) - { - parseDescendentCount(category_id, category_map["_embedded"]); - } + if (category_map.has("version")) + { + version = category_map["version"].asInteger(); + } + + LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id); + + if (curr_cat + && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN + && curr_cat->getDescendentCount() != LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN + && version > LLViewerInventoryCategory::VERSION_UNKNOWN + && version < curr_cat->getVersion()) + { + LL_WARNS() << "Got stale folder, known: " << curr_cat->getVersion() + << ", received: " << version << LL_ENDL; + return; + } LLPointer new_cat; - LLViewerInventoryCategory *curr_cat = gInventory.getCategory(category_id); if (curr_cat) { // Default to current values where not provided. @@ -732,13 +1342,58 @@ void AISUpdate::parseCategory(const LLSD& category_map) } BOOL rv = new_cat->unpackMessage(category_map); // *NOTE: unpackMessage does not unpack version or descendent count. - //if (category_map.has("version")) - //{ - // mCatVersionsUpdated[category_id] = category_map["version"].asInteger(); - //} if (rv) { - if (curr_cat) + // Check descendent count first, as it may be needed + // to populate newly created categories + if (category_map.has("_embedded")) + { + parseDescendentCount(category_id, new_cat->getPreferredType(), category_map["_embedded"]); + } + + if (mFetch) + { + uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id); + if (mCatDescendentsKnown.end() != lookup_it) + { + S32 descendent_count = lookup_it->second; + LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count + << " for category " << category_id << LL_ENDL; + new_cat->setDescendentCount(descendent_count); + + // set version only if we are sure this update has full data and embeded items + // since viewer uses version to decide if folder and content still need fetching + if (version > LLViewerInventoryCategory::VERSION_UNKNOWN + && depth >= 0) + { + if (curr_cat && curr_cat->getVersion() > version) + { + LL_WARNS("Inventory") << "Version was " << curr_cat->getVersion() + << ", but fetch returned version " << version + << " for category " << category_id << LL_ENDL; + } + else + { + LL_DEBUGS("Inventory") << "Setting version to " << version + << " for category " << category_id << LL_ENDL; + } + + new_cat->setVersion(version); + } + } + else if (curr_cat + && curr_cat->getVersion() > LLViewerInventoryCategory::VERSION_UNKNOWN + && version > curr_cat->getVersion()) + { + // Potentially should new_cat->setVersion(unknown) here, + // but might be waiting for a callback that would increment + LL_DEBUGS("Inventory") << "Category " << category_id + << " is stale. Known version: " << curr_cat->getVersion() + << " server version: " << version << LL_ENDL; + } + mCategoriesCreated[category_id] = new_cat; + } + else if (curr_cat) { mCategoriesUpdated[category_id] = new_cat; // This statement is here to cause a new entry with 0 @@ -751,20 +1406,22 @@ void AISUpdate::parseCategory(const LLSD& category_map) else { // Set version/descendents for newly created categories. - if (category_map.has("version")) - { - S32 version = category_map["version"].asInteger(); - LL_DEBUGS("Inventory") << "Setting version to " << version - << " for new category " << category_id << LL_ENDL; - new_cat->setVersion(version); - } - uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id); - if (mCatDescendentsKnown.end() != lookup_it) - { - S32 descendent_count = lookup_it->second; - LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count - << " for new category " << category_id << LL_ENDL; - new_cat->setDescendentCount(descendent_count); + uuid_int_map_t::const_iterator lookup_it = mCatDescendentsKnown.find(category_id); + if (mCatDescendentsKnown.end() != lookup_it) + { + S32 descendent_count = lookup_it->second; + LL_DEBUGS("Inventory") << "Setting descendents count to " << descendent_count + << " for new category " << category_id << LL_ENDL; + new_cat->setDescendentCount(descendent_count); + + // Don't set version unles correct children count is present + if (category_map.has("version")) + { + S32 version = category_map["version"].asInteger(); + LL_DEBUGS("Inventory") << "Setting version to " << version + << " for new category " << category_id << LL_ENDL; + new_cat->setVersion(version); + } } mCategoriesCreated[category_id] = new_cat; mCatDescendentDeltas[new_cat->getParentUUID()]++; @@ -779,28 +1436,35 @@ void AISUpdate::parseCategory(const LLSD& category_map) // Check for more embedded content. if (category_map.has("_embedded")) { - parseEmbedded(category_map["_embedded"]); + parseEmbedded(category_map["_embedded"], depth - 1); } } -void AISUpdate::parseDescendentCount(const LLUUID& category_id, const LLSD& embedded) +void AISUpdate::parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded) { - // We can only determine true descendent count if this contains all descendent types. - if (embedded.has("categories") && - embedded.has("links") && - embedded.has("items")) - { - mCatDescendentsKnown[category_id] = embedded["categories"].size(); - mCatDescendentsKnown[category_id] += embedded["links"].size(); - mCatDescendentsKnown[category_id] += embedded["items"].size(); - } + // We can only determine true descendent count if this contains all descendent types. + if (embedded.has("categories") && + embedded.has("links") && + embedded.has("items")) + { + mCatDescendentsKnown[category_id] = embedded["categories"].size(); + mCatDescendentsKnown[category_id] += embedded["links"].size(); + mCatDescendentsKnown[category_id] += embedded["items"].size(); + } + else if (mFetch && embedded.has("links") && (type == LLFolderType::FT_CURRENT_OUTFIT || type == LLFolderType::FT_OUTFIT)) + { + // COF and outfits contain links only + mCatDescendentsKnown[category_id] = embedded["links"].size(); + } } -void AISUpdate::parseEmbedded(const LLSD& embedded) +void AISUpdate::parseEmbedded(const LLSD& embedded, S32 depth) { + checkTimeout(); + if (embedded.has("links")) // _embedded in a category { - parseEmbeddedLinks(embedded["links"]); + parseEmbeddedLinks(embedded["links"], depth); } if (embedded.has("items")) // _embedded in a category { @@ -812,11 +1476,11 @@ void AISUpdate::parseEmbedded(const LLSD& embedded) } if (embedded.has("categories")) // _embedded in a category { - parseEmbeddedCategories(embedded["categories"]); + parseEmbeddedCategories(embedded["categories"], depth); } if (embedded.has("category")) // _embedded in a link { - parseEmbeddedCategory(embedded["category"]); + parseEmbeddedCategory(embedded["category"], depth); } } @@ -833,7 +1497,7 @@ void AISUpdate::parseUUIDArray(const LLSD& content, const std::string& name, uui } } -void AISUpdate::parseEmbeddedLinks(const LLSD& links) +void AISUpdate::parseEmbeddedLinks(const LLSD& links, S32 depth) { for(LLSD::map_const_iterator linkit = links.beginMap(), linkend = links.endMap(); @@ -841,13 +1505,13 @@ void AISUpdate::parseEmbeddedLinks(const LLSD& links) { const LLUUID link_id((*linkit).first); const LLSD& link_map = (*linkit).second; - if (mItemIds.end() == mItemIds.find(link_id)) + if (!mFetch && mItemIds.end() == mItemIds.find(link_id)) { LL_DEBUGS("Inventory") << "Ignoring link not in items list " << link_id << LL_ENDL; } else { - parseLink(link_map); + parseLink(link_map, depth); } } } @@ -857,7 +1521,7 @@ void AISUpdate::parseEmbeddedItem(const LLSD& item) // a single item (_embedded in a link) if (item.has("item_id")) { - if (mItemIds.end() != mItemIds.find(item["item_id"].asUUID())) + if (mFetch || mItemIds.end() != mItemIds.find(item["item_id"].asUUID())) { parseItem(item); } @@ -873,7 +1537,7 @@ void AISUpdate::parseEmbeddedItems(const LLSD& items) { const LLUUID item_id((*itemit).first); const LLSD& item_map = (*itemit).second; - if (mItemIds.end() == mItemIds.find(item_id)) + if (!mFetch && mItemIds.end() == mItemIds.find(item_id)) { LL_DEBUGS("Inventory") << "Ignoring item not in items list " << item_id << LL_ENDL; } @@ -884,19 +1548,19 @@ void AISUpdate::parseEmbeddedItems(const LLSD& items) } } -void AISUpdate::parseEmbeddedCategory(const LLSD& category) +void AISUpdate::parseEmbeddedCategory(const LLSD& category, S32 depth) { // a single category (_embedded in a link) if (category.has("category_id")) { - if (mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID())) + if (mFetch || mCategoryIds.end() != mCategoryIds.find(category["category_id"].asUUID())) { - parseCategory(category); + parseCategory(category, depth); } } } -void AISUpdate::parseEmbeddedCategories(const LLSD& categories) +void AISUpdate::parseEmbeddedCategories(const LLSD& categories, S32 depth) { // a map of categories (_embedded in a category) for(LLSD::map_const_iterator categoryit = categories.beginMap(), @@ -905,19 +1569,21 @@ void AISUpdate::parseEmbeddedCategories(const LLSD& categories) { const LLUUID category_id((*categoryit).first); const LLSD& category_map = (*categoryit).second; - if (mCategoryIds.end() == mCategoryIds.find(category_id)) + if (!mFetch && mCategoryIds.end() == mCategoryIds.find(category_id)) { LL_DEBUGS("Inventory") << "Ignoring category not in categories list " << category_id << LL_ENDL; } else { - parseCategory(category_map); + parseCategory(category_map, depth); } } } void AISUpdate::doUpdate() { + checkTimeout(); + // Do version/descendant accounting. for (std::map::const_iterator catit = mCatDescendentDeltas.begin(); catit != mCatDescendentDeltas.end(); ++catit) @@ -959,6 +1625,7 @@ void AISUpdate::doUpdate() } // CREATE CATEGORIES + const S32 MAX_UPDATE_BACKLOG = 50; // stall prevention for (deferred_category_map_t::const_iterator create_it = mCategoriesCreated.begin(); create_it != mCategoriesCreated.end(); ++create_it) { @@ -967,6 +1634,13 @@ void AISUpdate::doUpdate() gInventory.updateCategory(new_category, LLInventoryObserver::CREATE); LL_DEBUGS("Inventory") << "created category " << category_id << LL_ENDL; + + // fetching can receive massive amount of items and folders + if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG) + { + gInventory.notifyObservers(); + checkTimeout(); + } } // UPDATE CATEGORIES @@ -991,6 +1665,24 @@ void AISUpdate::doUpdate() } } + // LOST ITEMS + if (!mItemsLost.empty()) + { + LL_INFOS("Inventory") << "Received " << (S32)mItemsLost.size() << " items without a parent" << LL_ENDL; + const LLUUID lost_uuid(gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); + if (lost_uuid.notNull()) + { + for (deferred_item_map_t::const_iterator lost_it = mItemsLost.begin(); + lost_it != mItemsLost.end(); ++lost_it) + { + LLPointer new_item = lost_it->second; + + new_item->setParent(lost_uuid); + new_item->updateParentOnServer(FALSE); + } + } + } + // CREATE ITEMS for (deferred_item_map_t::const_iterator create_it = mItemsCreated.begin(); create_it != mItemsCreated.end(); ++create_it) @@ -1003,6 +1695,13 @@ void AISUpdate::doUpdate() // case this is create. LL_DEBUGS("Inventory") << "created item " << item_id << LL_ENDL; gInventory.updateItem(new_item, LLInventoryObserver::CREATE); + + // fetching can receive massive amount of items and folders + if (gInventory.getChangedIDs().size() > MAX_UPDATE_BACKLOG) + { + gInventory.notifyObservers(); + checkTimeout(); + } } // UPDATE ITEMS @@ -1063,6 +1762,8 @@ void AISUpdate::doUpdate() } } + checkTimeout(); + gInventory.notifyObservers(); } diff --git a/indra/newview/llaisapi.h b/indra/newview/llaisapi.h index 856f3fc180..f254f21e42 100644 --- a/indra/newview/llaisapi.h +++ b/indra/newview/llaisapi.h @@ -38,6 +38,12 @@ class AISAPI { public: + static const S32 HTTP_TIMEOUT; + typedef enum { + INVENTORY, + LIBRARY + } ITEM_TYPE; + typedef boost::function completion_t; static bool isAvailable(); @@ -50,9 +56,16 @@ public: static void PurgeDescendents(const LLUUID &categoryId, completion_t callback = completion_t()); static void UpdateCategory(const LLUUID &categoryId, const LLSD &updates, completion_t callback = completion_t()); static void UpdateItem(const LLUUID &itemId, const LLSD &updates, completion_t callback = completion_t()); + static void FetchItem(const LLUUID &itemId, ITEM_TYPE type, completion_t callback = completion_t()); + static void FetchCategoryChildren(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); + static void FetchCategoryChildren(const std::string &identifier, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); + static void FetchCategoryCategories(const LLUUID &catId, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); + static void FetchCategorySubset(const LLUUID& catId, const uuid_vec_t specificChildren, ITEM_TYPE type = AISAPI::ITEM_TYPE::INVENTORY, bool recursive = false, completion_t callback = completion_t(), S32 depth = 0); + static void FetchCOF(completion_t callback = completion_t()); + static void FetchCategoryLinks(const LLUUID &catId, completion_t callback = completion_t()); + static void FetchOrphans(completion_t callback = completion_t() ); static void CopyLibraryCategory(const LLUUID& sourceId, const LLUUID& destId, bool copySubfolders, completion_t callback = completion_t()); -private: typedef enum { COPYINVENTORY, SLAMFOLDER, @@ -61,9 +74,18 @@ private: PURGEDESCENDENTS, UPDATECATEGORY, UPDATEITEM, - COPYLIBRARYCATEGORY + COPYLIBRARYCATEGORY, + CREATEINVENTORY, + FETCHITEM, + FETCHCATEGORYCHILDREN, + FETCHCATEGORYCATEGORIES, + FETCHCATEGORYSUBSET, + FETCHCOF, + FETCHORPHANS, + FETCHCATEGORYLINKS } COMMAND_TYPE; +private: static const std::string INVENTORY_CAP_NAME; static const std::string LIBRARY_CAP_NAME; @@ -72,6 +94,7 @@ private: static void EnqueueAISCommand(const std::string &procName, LLCoprocedureManager::CoProcedure_t proc); static void onIdle(void *userdata); // launches postponed AIS commands + static void onUpdateReceived(const LLSD& update, COMMAND_TYPE type, const LLSD& request_body); static std::string getInvCap(); static std::string getLibCap(); @@ -87,24 +110,29 @@ private: class AISUpdate { public: - AISUpdate(const LLSD& update); + AISUpdate(const LLSD& update, AISAPI::COMMAND_TYPE type, const LLSD& request_body); void parseUpdate(const LLSD& update); void parseMeta(const LLSD& update); void parseContent(const LLSD& update); void parseUUIDArray(const LLSD& content, const std::string& name, uuid_list_t& ids); - void parseLink(const LLSD& link_map); + void parseLink(const LLSD& link_map, S32 depth); void parseItem(const LLSD& link_map); - void parseCategory(const LLSD& link_map); - void parseDescendentCount(const LLUUID& category_id, const LLSD& embedded); - void parseEmbedded(const LLSD& embedded); - void parseEmbeddedLinks(const LLSD& links); + void parseCategory(const LLSD& link_map, S32 depth); + void parseDescendentCount(const LLUUID& category_id, LLFolderType::EType type, const LLSD& embedded); + void parseEmbedded(const LLSD& embedded, S32 depth); + void parseEmbeddedLinks(const LLSD& links, S32 depth); void parseEmbeddedItems(const LLSD& items); - void parseEmbeddedCategories(const LLSD& categories); + void parseEmbeddedCategories(const LLSD& categories, S32 depth); void parseEmbeddedItem(const LLSD& item); - void parseEmbeddedCategory(const LLSD& category); + void parseEmbeddedCategory(const LLSD& category, S32 depth); void doUpdate(); private: void clearParseResults(); + void checkTimeout(); + + // Fetch can return large packets of data, throttle it to not cause lags + // Todo: make throttle work over all fetch requests isntead of per-request + const F32 AIS_EXPIRY_SECONDS = 0.008f; typedef std::map uuid_int_map_t; uuid_int_map_t mCatDescendentDeltas; @@ -113,6 +141,7 @@ private: typedef std::map > deferred_item_map_t; deferred_item_map_t mItemsCreated; + deferred_item_map_t mItemsLost; deferred_item_map_t mItemsUpdated; typedef std::map > deferred_category_map_t; deferred_category_map_t mCategoriesCreated; @@ -123,6 +152,10 @@ private: uuid_list_t mObjectsDeletedIds; uuid_list_t mItemIds; uuid_list_t mCategoryIds; + bool mFetch; + S32 mFetchDepth; + LLTimer mTimer; + AISAPI::COMMAND_TYPE mType; }; #endif diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index 3c93a9df7e..8010b84c20 100644 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -40,8 +40,11 @@ #include "llgesturemgr.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryobserver.h" +#include "llmd5.h" #include "llnotificationsutil.h" +#include "llmd5.h" #include "lloutfitobserver.h" #include "lloutfitslist.h" #include "llselectmgr.h" @@ -145,7 +148,10 @@ public: // requests will be throttled from a non-trusted browser LLAppearanceHandler() : LLCommandHandler("appearance", UNTRUSTED_THROTTLE) {} - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { // support secondlife:///app/appearance/show, but for now we just // make all secondlife:///app/appearance SLapps behave this way @@ -585,6 +591,71 @@ LLUpdateAppearanceAndEditWearableOnDestroy::~LLUpdateAppearanceAndEditWearableOn } } +class LLBrokenLinkObserver : public LLInventoryObserver +{ +public: + LLUUID mUUID; + bool mEnforceItemRestrictions; + bool mEnforceOrdering; + nullary_func_t mPostUpdateFunc; + + LLBrokenLinkObserver(const LLUUID& uuid, + bool enforce_item_restrictions , + bool enforce_ordering , + nullary_func_t post_update_func) : + mUUID(uuid), + mEnforceItemRestrictions(enforce_item_restrictions), + mEnforceOrdering(enforce_ordering), + mPostUpdateFunc(post_update_func) + { + } + /* virtual */ void changed(U32 mask); + void postProcess(); +}; + +void LLBrokenLinkObserver::changed(U32 mask) +{ + if (mask & LLInventoryObserver::REBUILD) + { + // This observer should be executed after LLInventoryPanel::itemChanged(), + // but if it isn't, consider calling updateAppearanceFromCOF with a delay + const uuid_set_t& changed_item_ids = gInventory.getChangedIDs(); + for (uuid_set_t::const_iterator it = changed_item_ids.begin(); it != changed_item_ids.end(); ++it) + { + const LLUUID& id = *it; + if (id == mUUID) + { + // Might not be processed yet and it is not a + // good idea to update appearane here, postpone. + doOnIdleOneTime([this]() + { + postProcess(); + }); + + gInventory.removeObserver(this); + return; + } + } + } +} + +void LLBrokenLinkObserver::postProcess() +{ + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + llassert(item && !item->getIsBrokenLink()); // the whole point was to get a correct link + if (item && item->getIsBrokenLink()) + { + LL_INFOS_ONCE("Avatar") << "Outfit link broken despite being regenerated" << LL_ENDL; + LL_DEBUGS("Avatar", "Inventory") << "Outfit link " << mUUID << " \"" << item->getName() << "\" is broken despite being regenerated" << LL_ENDL; + } + + LLAppearanceMgr::instance().updateAppearanceFromCOF( + mEnforceItemRestrictions , + mEnforceOrdering , + mPostUpdateFunc); + delete this; +} + struct LLFoundData { @@ -1701,12 +1772,18 @@ void LLAppearanceMgr::shallowCopyCategory(const LLUUID& src_id, const LLUUID& ds { parent_id = gInventory.getRootFolderID(); } - LLUUID subfolder_id = gInventory.createNewCategory( parent_id, - LLFolderType::FT_NONE, - src_cat->getName()); - shallowCopyCategoryContents(src_id, subfolder_id, cb); + gInventory.createNewCategory( + parent_id, + LLFolderType::FT_NONE, + src_cat->getName(), + [src_id, cb](const LLUUID &new_id) + { + LLAppearanceMgr::getInstance()->shallowCopyCategoryContents(src_id, new_id, cb); - gInventory.notifyObservers(); + gInventory.notifyObservers(); + }, + src_cat->getThumbnailUUID() + ); } void LLAppearanceMgr::slamCategoryLinks(const LLUUID& src_id, const LLUUID& dst_id, @@ -2409,6 +2486,39 @@ void LLAppearanceMgr::updateAppearanceFromCOF(bool enforce_item_restrictions, LL_DEBUGS("Avatar") << self_av_string() << "starting" << LL_ENDL; + if (gInventory.hasPosiblyBrockenLinks()) + { + // Inventory has either broken links or links that + // haven't loaded yet. + // Check if LLAppearanceMgr needs to wait. + LLUUID current_outfit_id = getCOF(); + LLInventoryModel::item_array_t cof_items; + LLInventoryModel::cat_array_t cof_cats; + LLFindBrokenLinks is_brocken_link; + gInventory.collectDescendentsIf(current_outfit_id, + cof_cats, + cof_items, + LLInventoryModel::EXCLUDE_TRASH, + is_brocken_link); + + if (cof_items.size() > 0) + { + // Some links haven't loaded yet, but fetch isn't complete so + // links are likely fine and we will have to wait for them to + // load + if (LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive()) + { + + LLBrokenLinkObserver* observer = new LLBrokenLinkObserver(cof_items.front()->getUUID(), + enforce_item_restrictions, + enforce_ordering, + post_update_func); + gInventory.addObserver(observer); + return; + } + } + } + if (enforce_item_restrictions) { // The point here is just to call @@ -2725,22 +2835,29 @@ void LLAppearanceMgr::wearCategoryFinal(LLUUID& cat_id, bool copy_items, bool ap { pid = gInventory.getRootFolderID(); } - - LLUUID new_cat_id = gInventory.createNewCategory( + + gInventory.createNewCategory( pid, LLFolderType::FT_NONE, - name); + name, + [cat_id, append](const LLUUID& new_cat_id) + { + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat_id, cats, items); + // Create a CopyMgr that will copy items, manage its own destruction + new LLCallAfterInventoryCopyMgr( + *items, new_cat_id, std::string("wear_inventory_category_callback"), + boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar, + LLAppearanceMgr::getInstance(), + gInventory.getCategory(new_cat_id), + append)); - // Create a CopyMgr that will copy items, manage its own destruction - new LLCallAfterInventoryCopyMgr( - *items, new_cat_id, std::string("wear_inventory_category_callback"), - boost::bind(&LLAppearanceMgr::wearInventoryCategoryOnAvatar, - LLAppearanceMgr::getInstance(), - gInventory.getCategory(new_cat_id), - append)); - - // BAP fixes a lag in display of created dir. - gInventory.notifyObservers(); + // BAP fixes a lag in display of created dir. + gInventory.notifyObservers(); + }, + cat->getThumbnailUUID() + ); } else { @@ -3198,7 +3315,7 @@ void LLAppearanceMgr::copyLibraryGestures() // Copy gestures LLUUID lib_gesture_cat_id = - gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE,false); + gInventory.findLibraryCategoryUUIDForType(LLFolderType::FT_GESTURE); if (lib_gesture_cat_id.isNull()) { LL_WARNS() << "Unable to copy gestures, source category not found" << LL_ENDL; @@ -3709,7 +3826,7 @@ void LLAppearanceMgr::serverAppearanceUpdateCoro(LLCoreHttpUtil::HttpCoroutineAd if (cofVersion == LLViewerInventoryCategory::VERSION_UNKNOWN) { - LL_WARNS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL; + LL_INFOS("AVatar") << "COF version is unknown... not requesting until COF version is known." << LL_ENDL; return; } else @@ -3981,26 +4098,15 @@ void LLAppearanceMgr::makeNewOutfitLinks(const std::string& new_folder_name, boo // First, make a folder in the My Outfits directory. const LLUUID parent_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); - if (AISAPI::isAvailable()) - { - // cap-based category creation was buggy until recently. use - // existence of AIS as an indicator the fix is present. Does - // not actually use AIS to create the category. - inventory_func_type func = boost::bind(&LLAppearanceMgr::onOutfitFolderCreated,this,_1,show_panel); - gInventory.createNewCategory( - parent_id, - LLFolderType::FT_OUTFIT, - new_folder_name, - func); - } - else - { - LLUUID folder_id = gInventory.createNewCategory( - parent_id, - LLFolderType::FT_OUTFIT, - new_folder_name); - onOutfitFolderCreated(folder_id, show_panel); - } + + gInventory.createNewCategory( + parent_id, + LLFolderType::FT_OUTFIT, + new_folder_name, + [show_panel](const LLUUID &new_cat_id) + { + LLAppearanceMgr::getInstance()->onOutfitFolderCreated(new_cat_id, show_panel); + }); } void LLAppearanceMgr::wearBaseOutfit() @@ -4333,6 +4439,73 @@ public: ~CallAfterCategoryFetchStage1() { } + /*virtual*/ void startFetch() + { + bool ais3 = AISAPI::isAvailable(); + for (uuid_vec_t::const_iterator it = mIDs.begin(); it != mIDs.end(); ++it) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(*it); + if (!cat) continue; + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + // CHECK IT: isCategoryComplete() checks both version and descendant count but + // fetch() only works for Unknown version and doesn't care about descentants, + // as result fetch won't start and folder will potentially get stuck as + // incomplete in observer. + // Likely either both should use only version or both should check descendants. + cat->fetch(); //blindly fetch it without seeing if anything else is fetching it. + mIncomplete.push_back(*it); //Add to list of things being downloaded for this observer. + } + else if (!isCategoryComplete(cat)) + { + LL_DEBUGS("Inventory") << "Categoty " << *it << " incomplete despite having version" << LL_ENDL; + LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true); + mIncomplete.push_back(*it); + } + else if (ais3) + { + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items); + + if (items) + { + S32 complete_count = 0; + S32 incomplete_count = 0; + for (LLInventoryModel::item_array_t::const_iterator it = items->begin(); it < items->end(); ++it) + { + if (!(*it)->isFinished()) + { + incomplete_count++; + } + else + { + complete_count++; + } + } + // AIS can fetch couple items, but if there + // is more than a dozen it will be very slow + // it's faster to get whole folder in such case + if (incomplete_count > LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS + || (incomplete_count > 1 && complete_count == 0)) + { + LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch(*it, true); + mIncomplete.push_back(*it); + } + else + { + // let stage2 handle incomplete ones + mComplete.push_back(*it); + } + } + // else should have been handled by isCategoryComplete + } + else + { + mComplete.push_back(*it); + } + } + } virtual void done() { if (mComplete.size() <= 0) @@ -4349,13 +4522,11 @@ public: // What we do here is get the complete information on the // items in the requested category, and set up an observer // that will wait for that to happen. - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(mComplete.front(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); - S32 count = item_array.size(); + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(mComplete.front(), cats, items); + + S32 count = items->size(); if(!count) { LL_WARNS() << "Nothing fetched in category " << mComplete.front() @@ -4367,11 +4538,13 @@ public: return; } - LL_INFOS() << "stage1 got " << item_array.size() << " items, passing to stage2 " << LL_ENDL; + LLViewerInventoryCategory* cat = gInventory.getCategory(mComplete.front()); + S32 version = cat ? cat->getVersion() : -2; + LL_INFOS() << "stage1, category " << mComplete.front() << " got " << count << " items, version " << version << " passing to stage2 " << LL_ENDL; uuid_vec_t ids; for(S32 i = 0; i < count; ++i) { - ids.push_back(item_array.at(i)->getUUID()); + ids.push_back(items->at(i)->getUUID()); } gInventory.removeObserver(this); @@ -4396,18 +4569,78 @@ protected: nullary_func_t mCallable; }; +void callAfterCOFFetch(nullary_func_t cb) +{ + LLUUID cat_id = LLAppearanceMgr::instance().getCOF(); + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + + if (AISAPI::isAvailable()) + { + // Mark cof (update timer) so that background fetch won't request it + cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE); + // For reliability assume that we have no relevant cache, so + // fetch cof along with items cof's links point to. + AISAPI::FetchCOF([cb](const LLUUID& id) + { + cb(); + LLUUID cat_id = LLAppearanceMgr::instance().getCOF(); + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (cat) + { + cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); + } + }); + } + else + { + LL_INFOS() << "AIS API v3 not available, using callAfterCategoryFetch" << LL_ENDL; + // startup should have marked folder as fetching, remove that + cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); + callAfterCategoryFetch(cat_id, cb); + } +} + void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb) { - CallAfterCategoryFetchStage1 *stage1 = new CallAfterCategoryFetchStage1(cat_id, cb); - stage1->startFetch(); - if (stage1->isFinished()) - { - stage1->done(); - } - else - { - gInventory.addObserver(stage1); - } + CallAfterCategoryFetchStage1* stage1 = new CallAfterCategoryFetchStage1(cat_id, cb); + stage1->startFetch(); + if (stage1->isFinished()) + { + stage1->done(); + } + else + { + gInventory.addObserver(stage1); + } +} + +void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb) +{ + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + if (AISAPI::isAvailable()) + { + // Mark folder (update timer) so that background fetch won't request it + cat->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE); + // Assume that we have no relevant cache. Fetch folder, and items folder's links point to. + AISAPI::FetchCategoryLinks(cat_id, + [cb, cat_id](const LLUUID &id) + { + cb(); + LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); + if (cat) + { + cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); + } + }); + } + else + { + LL_WARNS() << "AIS API v3 not available, can't use AISAPI::FetchCOF" << LL_ENDL; + // startup should have marked folder as fetching, remove that + cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); + callAfterCategoryFetch(cat_id, cb); + } + } void add_wearable_type_counts(const uuid_vec_t& ids, @@ -4470,8 +4703,10 @@ public: // not allowed from outside the app LLWearFolderHandler() : LLCommandHandler("wear_folder", UNTRUSTED_BLOCK) { } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& tokens, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { LLSD::UUID folder_uuid; diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index cf953d21ac..43839e47a6 100644 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -338,7 +338,9 @@ public: LLUUID findDescendentCategoryIDByName(const LLUUID& parent_id,const std::string& name); // Invoke a given callable after category contents are fully fetched. +void callAfterCOFFetch(nullary_func_t cb); void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb); +void callAfterCategoryLinksFetch(const LLUUID &cat_id, nullary_func_t cb); // Wear all items in a uuid vector. void wear_multiple(const uuid_vec_t& ids, bool replace); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index edbae465f7..6d34930cb8 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -36,6 +36,7 @@ #include "llenvironment.h" #include "llerrorcontrol.h" #include "lleventtimer.h" +#include "llfile.h" #include "llviewertexturelist.h" #include "llgroupmgr.h" #include "llagent.h" @@ -214,7 +215,7 @@ #include "llcommandlineparser.h" #include "llfloatermemleak.h" #include "llfloaterreg.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h" #include "llfloatersnapshot.h" #include "llsidepanelinventory.h" #include "llatmosphere.h" @@ -1519,9 +1520,14 @@ bool LLAppViewer::doFrame() pingMainloopTimeout("Main:Snapshot"); gPipeline.mReflectionMapManager.update(); LLFloaterSnapshot::update(); // take snapshots - LLFloaterSimpleOutfitSnapshot::update(); + LLFloaterSimpleSnapshot::update(); gGLActive = FALSE; } + + if (LLViewerStatsRecorder::instanceExists()) + { + LLViewerStatsRecorder::instance().idle(); + } } } @@ -2974,9 +2980,33 @@ void LLAppViewer::initStrings() std::string strings_path_full = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, strings_file); if (strings_path_full.empty() || !LLFile::isfile(strings_path_full)) { + if (strings_path_full.empty()) + { + LL_WARNS() << "The file '" << strings_file << "' is not found" << LL_ENDL; + } + else + { + llstat st; + int rc = LLFile::stat(strings_path_full, &st); + if (rc != 0) + { + LL_WARNS() << "The file '" << strings_path_full << "' failed to get status. Error code: " << rc << LL_ENDL; + } + else if (S_ISDIR(st.st_mode)) + { + LL_WARNS() << "The filename '" << strings_path_full << "' is a directory name" << LL_ENDL; + } + else + { + LL_WARNS() << "The filename '" << strings_path_full << "' doesn't seem to be a regular file name" << LL_ENDL; + } + } + // initial check to make sure files are there failed gDirUtilp->dumpCurrentDirectories(LLError::LEVEL_WARN); - LL_ERRS() << "Viewer failed to find localization and UI files. Please reinstall viewer from https://secondlife.com/support/downloads/ and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL; + LL_ERRS() << "Viewer failed to find localization and UI files." + << " Please reinstall viewer from https://secondlife.com/support/downloads" + << " and contact https://support.secondlife.com if issue persists after reinstall." << LL_ENDL; } LLTransUtil::parseStrings(strings_file, default_trans_args); LLTransUtil::parseLanguageStrings("language_settings.xml"); @@ -3575,8 +3605,6 @@ void LLAppViewer::writeSystemInfo() // "CrashNotHandled" is set here, while things are running well, // in case of a freeze. If there is a freeze, the crash logger will be launched // and can read this value from the debug_info.log. - // If the crash is handled by LLAppViewer::handleViewerCrash, ie not a freeze, - // then the value of "CrashNotHandled" will be set to true. gDebugInfo["CrashNotHandled"] = LLSD::Boolean(true); #else // LL_BUGSPLAT // "CrashNotHandled" is obsolete; it used (not very successsfully) @@ -3668,163 +3696,6 @@ void getFileList() } #endif -void LLAppViewer::handleViewerCrash() -{ - LL_INFOS("CRASHREPORT") << "Handle viewer crash entry." << LL_ENDL; - - LL_INFOS("CRASHREPORT") << "Last render pool type: " << LLPipeline::sCurRenderPoolType << LL_ENDL ; - - LLMemory::logMemoryInfo(true) ; - - //print out recorded call stacks if there are any. - LLError::LLCallStacks::print(); - - LLAppViewer* pApp = LLAppViewer::instance(); - if (pApp->beingDebugged()) - { - // This will drop us into the debugger. - abort(); - } - - if (LLApp::isCrashloggerDisabled()) - { - abort(); - } - - // Returns whether a dialog was shown. - // Only do the logic in here once - if (pApp->mReportedCrash) - { - return; - } - pApp->mReportedCrash = TRUE; - - // Insert crash host url (url to post crash log to) if configured. - std::string crashHostUrl = gSavedSettings.get("CrashHostUrl"); - if(crashHostUrl != "") - { - gDebugInfo["Dynamic"]["CrashHostUrl"] = crashHostUrl; - } - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if ( parcel && parcel->getMusicURL()[0]) - { - gDebugInfo["Dynamic"]["ParcelMusicURL"] = parcel->getMusicURL(); - } - if ( parcel && parcel->getMediaURL()[0]) - { - gDebugInfo["Dynamic"]["ParcelMediaURL"] = parcel->getMediaURL(); - } - - gDebugInfo["Dynamic"]["SessionLength"] = F32(LLFrameTimer::getElapsedSeconds()); - gDebugInfo["Dynamic"]["RAMInfo"]["Allocated"] = LLSD::Integer(LLMemory::getCurrentRSS() / 1024); - - if(gLogoutInProgress) - { - gDebugInfo["Dynamic"]["LastExecEvent"] = LAST_EXEC_LOGOUT_CRASH; - } - else - { - gDebugInfo["Dynamic"]["LastExecEvent"] = gLLErrorActivated ? LAST_EXEC_LLERROR_CRASH : LAST_EXEC_OTHER_CRASH; - } - - if(gAgent.getRegion()) - { - gDebugInfo["Dynamic"]["CurrentSimHost"] = gAgent.getRegion()->getSimHostName(); - gDebugInfo["Dynamic"]["CurrentRegion"] = gAgent.getRegion()->getName(); - - const LLVector3& loc = gAgent.getPositionAgent(); - gDebugInfo["Dynamic"]["CurrentLocationX"] = loc.mV[0]; - gDebugInfo["Dynamic"]["CurrentLocationY"] = loc.mV[1]; - gDebugInfo["Dynamic"]["CurrentLocationZ"] = loc.mV[2]; - } - - if(LLAppViewer::instance()->mMainloopTimeout) - { - gDebugInfo["Dynamic"]["MainloopTimeoutState"] = LLAppViewer::instance()->mMainloopTimeout->getState(); - } - - // The crash is being handled here so set this value to false. - // Otherwise the crash logger will think this crash was a freeze. - gDebugInfo["Dynamic"]["CrashNotHandled"] = LLSD::Boolean(false); - - //Write out the crash status file - //Use marker file style setup, as that's the simplest, especially since - //we're already in a crash situation - if (gDirUtilp) - { - std::string crash_marker_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, - gLLErrorActivated - ? LLERROR_MARKER_FILE_NAME - : ERROR_MARKER_FILE_NAME); - LLAPRFile crash_marker_file ; - crash_marker_file.open(crash_marker_file_name, LL_APR_WB); - if (crash_marker_file.getFileHandle()) - { - LL_INFOS("MarkerFile") << "Created crash marker file " << crash_marker_file_name << LL_ENDL; - recordMarkerVersion(crash_marker_file); - } - else - { - LL_WARNS("MarkerFile") << "Cannot create error marker file " << crash_marker_file_name << LL_ENDL; - } - } - else - { - LL_WARNS("MarkerFile") << "No gDirUtilp with which to create error marker file name" << LL_ENDL; - } - -#ifdef LL_WINDOWS - Sleep(200); -#endif - - char *minidump_file = pApp->getMiniDumpFilename(); - LL_DEBUGS("CRASHREPORT") << "minidump file name " << minidump_file << LL_ENDL; - if(minidump_file && minidump_file[0] != 0) - { - gDebugInfo["Dynamic"]["MinidumpPath"] = minidump_file; - } - else - { -#ifdef LL_WINDOWS - getFileList(); -#else - LL_WARNS("CRASHREPORT") << "no minidump file?" << LL_ENDL; -#endif - } - gDebugInfo["Dynamic"]["CrashType"]="crash"; - - if (gMessageSystem && gDirUtilp) - { - std::string filename; - filename = gDirUtilp->getExpandedFilename(LL_PATH_DUMP, "stats.log"); - LL_DEBUGS("CRASHREPORT") << "recording stats " << filename << LL_ENDL; - llofstream file(filename.c_str(), std::ios_base::binary); - if(file.good()) - { - gMessageSystem->summarizeLogs(file); - file.close(); - } - else - { - LL_WARNS("CRASHREPORT") << "problem recording stats" << LL_ENDL; - } - } - - if (gMessageSystem) - { - gMessageSystem->getCircuitInfo(gDebugInfo["CircuitInfo"]); - gMessageSystem->stopLogging(); - } - - if (LLWorld::instanceExists()) LLWorld::getInstance()->getInfo(gDebugInfo["Dynamic"]); - - gDebugInfo["FatalMessage"] = LLError::getFatalMessage(); - - // Close the debug file - pApp->writeDebugInfo(false); //false answers the isStatic question with the least overhead. -} - // static void LLAppViewer::recordMarkerVersion(LLAPRFile& marker_file) { @@ -4298,7 +4169,7 @@ U32 LLAppViewer::getObjectCacheVersion() { // Viewer object cache version, change if object update // format changes. JC - const U32 INDRA_OBJECT_CACHE_VERSION = 16; + const U32 INDRA_OBJECT_CACHE_VERSION = 17; return INDRA_OBJECT_CACHE_VERSION; } diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 6e35da1b10..6d1496d517 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -115,7 +115,6 @@ public: virtual bool restoreErrorTrap() = 0; // Require platform specific override to reset error handling mechanism. // return false if the error trap needed restoration. - static void handleViewerCrash(); // Hey! The viewer crashed. Do this, soon. void checkForCrash(); // Thread accessors diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index dc487967fc..9f58f90326 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -80,8 +80,6 @@ int main( int argc, char **argv ) // install unexpected exception handler gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler); - // install crash handlers - viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash); bool ok = viewer_app_ptr->init(); if(!ok) diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index cb5cac6f2d..8b313a321b 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -83,8 +83,6 @@ void constructViewer() } gViewerAppPtr = new LLAppViewerMacOSX(); - - gViewerAppPtr->setErrorHandler(LLAppViewer::handleViewerCrash); } bool initViewer() diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index fde8ff5c1f..8cf80f388b 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -428,8 +428,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance, gOldTerminateHandler = std::set_terminate(exceptionTerminateHandler); - viewer_app_ptr->setErrorHandler(LLAppViewer::handleViewerCrash); - // Set a debug info flag to indicate if multiple instances are running. bool found_other_instance = !create_app_mutex(); gDebugInfo["FoundOtherInstanceAtStartup"] = LLSD::Boolean(found_other_instance); diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index d43048a8b6..d3fce306bc 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -31,6 +31,7 @@ #include "llagent.h" #include "llappearancemgr.h" #include "llinventorymodel.h" +#include "llstartup.h" #include "lltooldraganddrop.h" // pack_permissions_slam #include "llviewerinventory.h" #include "llviewerregion.h" @@ -239,6 +240,13 @@ void LLAttachmentsMgr::linkRecentlyArrivedAttachments() return; } + if (LLAppearanceMgr::instance().getCOFVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + // Wait for cof to load + LL_DEBUGS_ONCE("Avatar") << "Received atachments, but cof isn't loaded yet, postponing processing" << LL_ENDL; + return; + } + LL_DEBUGS("Avatar") << "ATT checking COF linkability for " << mRecentlyArrivedAttachments.size() << " recently arrived items" << LL_ENDL; @@ -372,7 +380,7 @@ void LLAttachmentsMgr::onAttachmentArrived(const LLUUID& inv_item_id) { LLTimer timer; bool expected = mAttachmentRequests.getTime(inv_item_id, timer); - if (!expected) + if (!expected && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT) { LLInventoryItem *item = gInventory.getItem(inv_item_id); LL_WARNS() << "ATT Attachment was unexpected or arrived after " << MAX_ATTACHMENT_REQUEST_LIFETIME << " seconds: " @@ -411,11 +419,15 @@ void LLAttachmentsMgr::onDetachCompleted(const LLUUID& inv_item_id) LL_DEBUGS("Avatar") << "ATT all detach requests have completed" << LL_ENDL; } } - else + else if (!LLApp::isExiting()) { LL_WARNS() << "ATT unexpected detach for " << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL; } + else + { + LL_DEBUGS("Avatar") << "ATT detach on shutdown for " << (item ? item->getName() : "UNKNOWN") << " " << inv_item_id << LL_ENDL; + } LL_DEBUGS("Avatar") << "ATT detached item flagging as questionable for COF link checking " << (item ? item->getName() : "UNKNOWN") << " id " << inv_item_id << LL_ENDL; diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 3e450e6dec..313339f131 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -730,39 +730,55 @@ namespace action_give_inventory /** * Checks My Inventory visibility. */ + static bool is_give_inventory_acceptable_ids(const std::set inventory_selected_uuids) + { + if (inventory_selected_uuids.empty()) return false; // nothing selected + + bool acceptable = false; + std::set::const_iterator it = inventory_selected_uuids.begin(); + const std::set::const_iterator it_end = inventory_selected_uuids.end(); + for (; it != it_end; ++it) + { + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); + // any category can be offered. + if (inv_cat) + { + acceptable = true; + continue; + } + + LLViewerInventoryItem* inv_item = gInventory.getItem(*it); + // check if inventory item can be given + if (LLGiveInventory::isInventoryGiveAcceptable(inv_item)) + { + acceptable = true; + continue; + } + + // there are neither item nor category in inventory + acceptable = false; + break; + } + return acceptable; + } static bool is_give_inventory_acceptable(LLInventoryPanel* panel = NULL) { // check selection in the panel - const std::set inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel); - if (inventory_selected_uuids.empty()) return false; // nothing selected + std::set inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel); + if (inventory_selected_uuids.empty()) + { + if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode()) + { + inventory_selected_uuids.insert(panel->getRootFolderID()); + } + else + { + return false; // nothing selected + } + } - bool acceptable = false; - std::set::const_iterator it = inventory_selected_uuids.begin(); - const std::set::const_iterator it_end = inventory_selected_uuids.end(); - for (; it != it_end; ++it) - { - LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); - // any category can be offered. - if (inv_cat) - { - acceptable = true; - continue; - } - - LLViewerInventoryItem* inv_item = gInventory.getItem(*it); - // check if inventory item can be given - if (LLGiveInventory::isInventoryGiveAcceptable(inv_item)) - { - acceptable = true; - continue; - } - - // there are neither item nor category in inventory - acceptable = false; - break; - } - return acceptable; + return is_give_inventory_acceptable_ids(inventory_selected_uuids); } static void build_items_string(const std::set& inventory_selected_uuids , std::string& items_string) @@ -890,46 +906,65 @@ namespace action_give_inventory * @param avatar_names - avatar names request to be sent. * @param avatar_uuids - avatar names request to be sent. */ - static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector avatar_names, LLInventoryPanel* panel = NULL) - { - llassert(avatar_names.size() == avatar_uuids.size()); - const std::set inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel); - if (inventory_selected_uuids.empty()) - { - return; - } + static void give_inventory_ids(const uuid_vec_t& avatar_uuids, const std::vector avatar_names, const uuid_set_t inventory_selected_uuids) + { + llassert(avatar_names.size() == avatar_uuids.size()); - std::string residents; - LLAvatarActions::buildResidentsString(avatar_names, residents, true); + if (inventory_selected_uuids.empty()) + { + return; + } - std::string items; - build_items_string(inventory_selected_uuids, items); + std::string residents; + LLAvatarActions::buildResidentsString(avatar_names, residents, true); - int folders_count = 0; - std::set::const_iterator it = inventory_selected_uuids.begin(); + std::string items; + build_items_string(inventory_selected_uuids, items); - //traverse through selected inventory items and count folders among them - for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it) - { - LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); - if (NULL != inv_cat) - { - folders_count++; - } - } + int folders_count = 0; + std::set::const_iterator it = inventory_selected_uuids.begin(); - // EXP-1599 - // In case of sharing multiple folders, make the confirmation - // dialog contain a warning that only one folder can be shared at a time. - std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation"; - LLSD substitutions; - substitutions["RESIDENTS"] = residents; - substitutions["ITEMS"] = items; - LLShareInfo::instance().mAvatarNames = avatar_names; - LLShareInfo::instance().mAvatarUuids = avatar_uuids; - LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids)); - } + //traverse through selected inventory items and count folders among them + for ( ; it != inventory_selected_uuids.end() && folders_count <=1 ; ++it) + { + LLViewerInventoryCategory* inv_cat = gInventory.getCategory(*it); + if (NULL != inv_cat) + { + folders_count++; + } + } + + // EXP-1599 + // In case of sharing multiple folders, make the confirmation + // dialog contain a warning that only one folder can be shared at a time. + std::string notification = (folders_count > 1) ? "ShareFolderConfirmation" : "ShareItemsConfirmation"; + LLSD substitutions; + substitutions["RESIDENTS"] = residents; + substitutions["ITEMS"] = items; + LLShareInfo::instance().mAvatarNames = avatar_names; + LLShareInfo::instance().mAvatarUuids = avatar_uuids; + LLNotificationsUtil::add(notification, substitutions, LLSD(), boost::bind(&give_inventory_cb, _1, _2, inventory_selected_uuids)); + } + + static void give_inventory(const uuid_vec_t& avatar_uuids, const std::vector avatar_names, LLInventoryPanel* panel = NULL) + { + llassert(avatar_names.size() == avatar_uuids.size()); + std::set inventory_selected_uuids = LLAvatarActions::getInventorySelectedUUIDs(panel);; + + if (inventory_selected_uuids.empty()) + { + if(panel && panel->getRootFolder() && panel->getRootFolder()->isSingleFolderMode()) + { + inventory_selected_uuids.insert(panel->getRootFolderID()); + } + else + { + return; + } + } + give_inventory_ids(avatar_uuids, avatar_names, inventory_selected_uuids); + } } // static @@ -1037,6 +1072,28 @@ void LLAvatarActions::shareWithAvatars(LLView * panel) LLNotificationsUtil::add("ShareNotification"); } +//static +void LLAvatarActions::shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater) +{ + using namespace action_give_inventory; + + LLFloaterAvatarPicker* picker = + LLFloaterAvatarPicker::show(boost::bind(give_inventory_ids, _1, _2, inventory_selected_uuids), TRUE, FALSE, FALSE, root_floater->getName()); + if (!picker) + { + return; + } + + picker->setOkBtnEnableCb(boost::bind(is_give_inventory_acceptable_ids, inventory_selected_uuids)); + picker->openFriendsTab(); + + if (root_floater) + { + root_floater->addDependentFloater(picker); + } + LLNotificationsUtil::add("ShareNotification"); +} + // static bool LLAvatarActions::canShareSelectedItems(LLInventoryPanel* inv_panel /* = NULL*/) { diff --git a/indra/newview/llavataractions.h b/indra/newview/llavataractions.h index 86183cc119..8a0f40dd52 100644 --- a/indra/newview/llavataractions.h +++ b/indra/newview/llavataractions.h @@ -133,6 +133,7 @@ public: * Share items with the picked avatars. */ static void shareWithAvatars(LLView * panel); + static void shareWithAvatars(const uuid_set_t inventory_selected_uuids, LLFloater* root_floater); /** * Block/unblock the avatar by id. diff --git a/indra/newview/llavatarpropertiesprocessor.h b/indra/newview/llavatarpropertiesprocessor.h index f778634d25..10cde35f9c 100644 --- a/indra/newview/llavatarpropertiesprocessor.h +++ b/indra/newview/llavatarpropertiesprocessor.h @@ -85,6 +85,7 @@ struct LLAvatarData std::string profile_url; U8 caption_index; std::string caption_text; + std::string customer_type; U32 flags; BOOL allow_publish; }; diff --git a/indra/newview/llbuycurrencyhtml.cpp b/indra/newview/llbuycurrencyhtml.cpp index 7ad06f8eaa..37de89a48b 100644 --- a/indra/newview/llbuycurrencyhtml.cpp +++ b/indra/newview/llbuycurrencyhtml.cpp @@ -43,7 +43,7 @@ public: // requests will be throttled from a non-trusted browser LLBuyCurrencyHTMLHandler() : LLCommandHandler( "buycurrencyhtml", UNTRUSTED_THROTTLE) {} - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { std::string action( "" ); if ( params.size() >= 1 ) diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 9a608fba8e..43dc10ef5f 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -82,7 +82,7 @@ public: // requests will be throttled from a non-trusted browser LLObjectIMHandler() : LLCommandHandler("objectim", UNTRUSTED_THROTTLE) {} - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (params.size() < 1) { diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index 1c22e055bb..dc2cc57f0f 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -57,7 +57,7 @@ class LLObjectHandler : public LLCommandHandler public: LLObjectHandler() : LLCommandHandler("object", UNTRUSTED_BLOCK) { } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (params.size() < 2) return false; diff --git a/indra/newview/llcommanddispatcherlistener.cpp b/indra/newview/llcommanddispatcherlistener.cpp index 518f5bc374..46ec97d5c0 100644 --- a/indra/newview/llcommanddispatcherlistener.cpp +++ b/indra/newview/llcommanddispatcherlistener.cpp @@ -64,10 +64,10 @@ void LLCommandDispatcherListener::dispatch(const LLSD& params) const // But for testing, allow a caller to specify untrusted. trusted_browser = params["trusted"].asBoolean(); } - LLCommandDispatcher::dispatch( - params["cmd"], + LLCommandDispatcher::dispatch(params["cmd"], params["params"], params["query"], + "", NULL, LLCommandHandler::NAV_TYPE_CLICKED, trusted_browser); diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index 9640b05b06..caa27e530b 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -62,6 +62,7 @@ public: bool dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web, const std::string& nav_type, bool trusted_browser); @@ -98,6 +99,7 @@ void LLCommandHandlerRegistry::add(const char* cmd, bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web, const std::string& nav_type, bool trusted_browser) @@ -165,7 +167,7 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, } } if (!info.mHandler) return false; - return info.mHandler->handle(params, query_map, web); + return info.mHandler->handle(params, query_map, grid, web); } void LLCommandHandlerRegistry::notifySlurlBlocked() @@ -220,12 +222,13 @@ LLCommandHandler::~LLCommandHandler() bool LLCommandDispatcher::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web, const std::string& nav_type, bool trusted_browser) { return LLCommandHandlerRegistry::instance().dispatch( - cmd, params, query_map, web, nav_type, trusted_browser); + cmd, params, query_map, grid, web, nav_type, trusted_browser); } static std::string lookup(LLCommandHandler::EUntrustedAccess value); diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h index 486feecca6..1a354b04f7 100644 --- a/indra/newview/llcommandhandler.h +++ b/indra/newview/llcommandhandler.h @@ -42,7 +42,7 @@ public: LLFooHandler() : LLCommandHandler("foo", UNTRUSTED_BLOCK) { } // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (tokens.size() < 1) return false; @@ -90,6 +90,7 @@ public: virtual bool handle(const LLSD& params, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web) = 0; // For URL secondlife:///app/foo/bar/baz?cat=1&dog=2 // @params - array of "bar", "baz", possibly empty @@ -106,6 +107,7 @@ public: static bool dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web, const std::string& nav_type, bool trusted_browser); diff --git a/indra/newview/llcontrolavatar.cpp b/indra/newview/llcontrolavatar.cpp index 9b06936c1c..7def9c045f 100644 --- a/indra/newview/llcontrolavatar.cpp +++ b/indra/newview/llcontrolavatar.cpp @@ -47,6 +47,7 @@ LLControlAvatar::LLControlAvatar(const LLUUID& id, const LLPCode pcode, LLViewer mGlobalScale(1.0f), mMarkedForDeath(false), mRootVolp(NULL), + mControlAVBridge(NULL), mScaleConstraintFixup(1.0), mRegionChanged(false) { @@ -376,6 +377,12 @@ void LLControlAvatar::idleUpdate(LLAgent &agent, const F64 &time) } } +void LLControlAvatar::markDead() +{ + super::markDead(); + mControlAVBridge = NULL; +} + bool LLControlAvatar::computeNeedsUpdate() { computeUpdatePeriod(); diff --git a/indra/newview/llcontrolavatar.h b/indra/newview/llcontrolavatar.h index ea91d70e69..34db285514 100644 --- a/indra/newview/llcontrolavatar.h +++ b/indra/newview/llcontrolavatar.h @@ -35,9 +35,12 @@ class LLControlAvatar: { LOG_CLASS(LLControlAvatar); + using super = LLVOAvatar; + public: LLControlAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); - virtual void initInstance(); // Called after construction to initialize the class. + virtual void initInstance(); // Called after construction to initialize the class. + virtual void markDead(); virtual ~LLControlAvatar(); // If this is an attachment, return the avatar it is attached to. Otherwise NULL. @@ -88,6 +91,7 @@ public: F32 mGlobalScale; LLVOVolume *mRootVolp; + class LLControlAVBridge* mControlAVBridge; bool mMarkedForDeath; diff --git a/indra/newview/llconversationmodel.h b/indra/newview/llconversationmodel.h index 7c6980a7e6..3f607d434e 100644 --- a/indra/newview/llconversationmodel.h +++ b/indra/newview/llconversationmodel.h @@ -111,6 +111,7 @@ public: virtual void previewItem( void ); virtual void selectItem(void) { } virtual void showProperties(void); + virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {} // Methods used in sorting (see LLConversationSort::operator()) EConversationType const getType() const { return mConvType; } @@ -249,7 +250,7 @@ public: bool check(const LLFolderViewModelItem* item) { return true; } bool checkFolder(const LLFolderViewModelItem* folder) const { return true; } void setEmptyLookupMessage(const std::string& message) { } - std::string getEmptyLookupMessage() const { return mEmpty; } + std::string getEmptyLookupMessage(bool is_empty_folder = false) const { return mEmpty; } bool showAllResults() const { return true; } std::string::size_type getStringMatchOffset(LLFolderViewModelItem* item) const { return std::string::npos; } std::string::size_type getFilterStringSize() const { return 0; } diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index 6b9c159291..ac80f1f73b 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -760,6 +760,19 @@ void LLDrawable::movePartition() if (part) { part->move(this, getSpatialGroup()); + + // SL-18251 "On-screen animesh characters using pelvis offset animations + // disappear when root goes off-screen" + // + // Update extents of the root node when Control Avatar changes it's bounds + if (mRenderType == LLPipeline::RENDER_TYPE_CONTROL_AV && isRoot()) + { + LLControlAvatar* controlAvatar = dynamic_cast(getVObj().get()); + if (controlAvatar && controlAvatar->mControlAVBridge) + { + ((LLSpatialGroup*)controlAvatar->mControlAVBridge->mOctree->getListener(0))->setState(LLViewerOctreeGroup::DIRTY); + } + } } } @@ -1206,10 +1219,11 @@ LLSpatialPartition* LLDrawable::getSpatialPartition() { setSpatialBridge(new LLHUDBridge(this, getRegion())); } - else if (mVObjp->isAnimatedObject() && mVObjp->getControlAvatar()) - { - setSpatialBridge(new LLControlAVBridge(this, getRegion())); - } + else if (mVObjp->isAnimatedObject() && mVObjp->getControlAvatar()) + { + setSpatialBridge(new LLControlAVBridge(this, getRegion())); + mVObjp->getControlAvatar()->mControlAVBridge = (LLControlAVBridge*)getSpatialBridge(); + } // check HUD first, because HUD is also attachment else if (mVObjp->isAttachment()) { diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index a9f35b3360..fca0f1c978 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -696,6 +696,18 @@ void teardown_texture_matrix(LLDrawInfo& params) } } +void LLRenderPass::pushGLTFBatches(U32 type, bool textured) +{ + if (textured) + { + pushGLTFBatches(type); + } + else + { + pushRiggedGLTFBatches(type); + } +} + void LLRenderPass::pushGLTFBatches(U32 type) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; @@ -711,6 +723,21 @@ void LLRenderPass::pushGLTFBatches(U32 type) } } +void LLRenderPass::pushUntexturedGLTFBatches(U32 type) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + auto* begin = gPipeline.beginRenderMap(type); + auto* end = gPipeline.endRenderMap(type); + for (LLCullResult::drawinfo_iterator i = begin; i != end; ) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("pushGLTFBatch"); + LLDrawInfo& params = **i; + LLCullResult::increment_iterator(i, end); + + pushUntexturedGLTFBatch(params); + } +} + void LLRenderPass::pushGLTFBatch(LLDrawInfo& params) { auto& mat = params.mGLTFMaterial; @@ -729,6 +756,30 @@ void LLRenderPass::pushGLTFBatch(LLDrawInfo& params) teardown_texture_matrix(params); } +void LLRenderPass::pushUntexturedGLTFBatch(LLDrawInfo& params) +{ + auto& mat = params.mGLTFMaterial; + + LLGLDisable cull_face(mat->mDoubleSided ? GL_CULL_FACE : 0); + + applyModelMatrix(params); + + params.mVertexBuffer->setBuffer(); + params.mVertexBuffer->drawRange(LLRender::TRIANGLES, params.mStart, params.mEnd, params.mCount, params.mOffset); +} + +void LLRenderPass::pushRiggedGLTFBatches(U32 type, bool textured) +{ + if (textured) + { + pushRiggedGLTFBatches(type); + } + else + { + pushUntexturedRiggedGLTFBatches(type); + } +} + void LLRenderPass::pushRiggedGLTFBatches(U32 type) { LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; @@ -747,6 +798,25 @@ void LLRenderPass::pushRiggedGLTFBatches(U32 type) } } +void LLRenderPass::pushUntexturedRiggedGLTFBatches(U32 type) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWPOOL; + LLVOAvatar* lastAvatar = nullptr; + U64 lastMeshId = 0; + + auto* begin = gPipeline.beginRenderMap(type); + auto* end = gPipeline.endRenderMap(type); + for (LLCullResult::drawinfo_iterator i = begin; i != end; ) + { + LL_PROFILE_ZONE_NAMED_CATEGORY_DRAWPOOL("pushRiggedGLTFBatch"); + LLDrawInfo& params = **i; + LLCullResult::increment_iterator(i, end); + + pushUntexturedRiggedGLTFBatch(params, lastAvatar, lastMeshId); + } +} + + void LLRenderPass::pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId) { if (params.mAvatar.notNull() && (lastAvatar != params.mAvatar || lastMeshId != params.mSkinInfo->mHash)) @@ -759,3 +829,15 @@ void LLRenderPass::pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvat pushGLTFBatch(params); } +void LLRenderPass::pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId) +{ + if (params.mAvatar.notNull() && (lastAvatar != params.mAvatar || lastMeshId != params.mSkinInfo->mHash)) + { + uploadMatrixPalette(params); + lastAvatar = params.mAvatar; + lastMeshId = params.mSkinInfo->mHash; + } + + pushUntexturedGLTFBatch(params); +} + diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index c0bcb2b48d..320c3f5cf6 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -356,10 +356,29 @@ public: void pushRiggedBatches(U32 type, bool texture = true, bool batch_textures = false); void pushUntexturedRiggedBatches(U32 type); + + // push full GLTF batches + // assumes draw infos of given type have valid GLTF materials void pushGLTFBatches(U32 type); - void pushGLTFBatch(LLDrawInfo& params); + + // like pushGLTFBatches, but will not bind textures or set up texture transforms + void pushUntexturedGLTFBatches(U32 type); + + // helper function for dispatching to textured or untextured pass based on bool + void pushGLTFBatches(U32 type, bool textured); + + + // rigged variants of above void pushRiggedGLTFBatches(U32 type); + void pushRiggedGLTFBatches(U32 type, bool textured); + void pushUntexturedRiggedGLTFBatches(U32 type); + + // push a single GLTF draw call + void pushGLTFBatch(LLDrawInfo& params); void pushRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId); + void pushUntexturedGLTFBatch(LLDrawInfo& params); + void pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, LLVOAvatar*& lastAvatar, U64& lastMeshId); + void pushMaskBatches(U32 type, bool texture = true, bool batch_textures = false); void pushRiggedMaskBatches(U32 type, bool texture = true, bool batch_textures = false); void pushBatch(LLDrawInfo& params, bool texture, bool batch_textures = false); diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 07b7be0fc8..052a1d796a 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -204,6 +204,10 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) prepare_alpha_shader(pbr_shader, false, true, water_sign); + // explicitly unbind here so render loop doesn't make assumptions about the last shader + // already being setup for rendering + LLGLSLShader::unbind(); + if (!LLPipeline::sRenderingHUDs) { // first pass, render rigged objects only and render to depth buffer @@ -214,7 +218,7 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) forwardRender(); // final pass, render to depth for depth of field effects - if (!LLPipeline::sImpostorRender && gSavedSettings.getBOOL("RenderDepthOfField") && !gCubeSnapshot && !LLPipeline::sRenderingHUDs) + if (!LLPipeline::sImpostorRender && gSavedSettings.getBOOL("RenderDepthOfField") && !gCubeSnapshot && !LLPipeline::sRenderingHUDs && getType() == LLDrawPool::POOL_ALPHA_POST_WATER) { //update depth buffer sampler simple_shader = fullbright_shader = &gDeferredFullbrightAlphaMaskProgram; @@ -282,6 +286,7 @@ void LLDrawPoolAlpha::renderDebugAlpha() gGL.diffuseColor4f(1, 0, 0, 1); gGL.getTexUnit(0)->bindFast(LLViewerFetchedTexture::getSmokeImage()); + renderAlphaHighlight(); pushUntexturedBatches(LLRenderPass::PASS_ALPHA_MASK); @@ -509,6 +514,7 @@ void LLDrawPoolAlpha::renderPbrEmissives(std::vector& emissives) for (LLDrawInfo* draw : emissives) { llassert(draw->mGLTFMaterial); + LLGLDisable cull_face(draw->mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); draw->mGLTFMaterial->bind(draw->mTexture); draw->mVertexBuffer->setBuffer(); draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); @@ -564,6 +570,7 @@ void LLDrawPoolAlpha::renderRiggedPbrEmissives(std::vector& emissiv lastMeshId = draw->mSkinInfo->mHash; } + LLGLDisable cull_face(draw->mGLTFMaterial->mDoubleSided ? GL_CULL_FACE : 0); draw->mGLTFMaterial->bind(draw->mTexture); draw->mVertexBuffer->setBuffer(); draw->mVertexBuffer->drawRange(LLRender::TRIANGLES, draw->mStart, draw->mEnd, draw->mCount, draw->mOffset); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index f3a6c4a3a7..342b76d93b 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -171,8 +171,8 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass) is_deferred_render = true; if (LLPipeline::sImpostorRender) - { //impostor pass does not have rigid or impostor rendering - pass += 2; + { //impostor pass does not have impostor rendering + ++pass; } switch (pass) @@ -198,7 +198,7 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass) if (LLPipeline::sImpostorRender) { - pass += 2; + ++pass; } switch (pass) @@ -418,7 +418,7 @@ void LLDrawPoolAvatar::render(S32 pass) LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR; if (LLPipeline::sImpostorRender) { - renderAvatars(NULL, pass+2); + renderAvatars(NULL, ++pass); return; } @@ -433,7 +433,7 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) if (LLPipeline::sImpostorRender) { //impostor render does not have impostors or rigid rendering - pass += 2; + ++pass; } switch (pass) @@ -461,7 +461,7 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass) if (LLPipeline::sImpostorRender) { - pass += 2; + ++pass; } switch (pass) diff --git a/indra/newview/llenvironment.cpp b/indra/newview/llenvironment.cpp index d73a486877..edc7bdef5f 100644 --- a/indra/newview/llenvironment.cpp +++ b/indra/newview/llenvironment.cpp @@ -816,7 +816,7 @@ const F64Seconds LLEnvironment::TRANSITION_ALTITUDE(5.0f); const LLUUID LLEnvironment::KNOWN_SKY_SUNRISE("01e41537-ff51-2f1f-8ef7-17e4df760bfb"); const LLUUID LLEnvironment::KNOWN_SKY_MIDDAY("651510b8-5f4d-8991-1592-e7eeab2a5a06"); -const LLUUID LLEnvironment::KNOWN_SKY_LEGACY_MIDDAY("6c83e853-e7f8-cad7-8ee6-5f31c453721c"); +const LLUUID LLEnvironment::KNOWN_SKY_LEGACY_MIDDAY("cef49723-0292-af49-9b14-9598a616b8a3"); const LLUUID LLEnvironment::KNOWN_SKY_SUNSET("084e26cd-a900-28e8-08d0-64a9de5c15e2"); const LLUUID LLEnvironment::KNOWN_SKY_MIDNIGHT("8a01b97a-cb20-c1ea-ac63-f7ea84ad0090"); @@ -2954,12 +2954,20 @@ void LLEnvironment::DayTransition::animate() setWater(mNextInstance->getWater()); }); + + // pause probe updates and reset reflection maps on sky change + gPipeline.mReflectionMapManager.pause(); + gPipeline.mReflectionMapManager.reset(); + mSky = mStartSky->buildClone(); mBlenderSky = std::make_shared(mSky, mStartSky, mNextInstance->getSky(), mTransitionTime); mBlenderSky->setOnFinished( [this](LLSettingsBlender::ptr_t blender) { mBlenderSky.reset(); + // resume reflection probe updates + gPipeline.mReflectionMapManager.resume(); + if (!mBlenderSky && !mBlenderWater) LLEnvironment::instance().mCurrentEnvironment = mNextInstance; else @@ -3550,12 +3558,19 @@ namespace LLSettingsSky::ptr_t target_sky(start_sky->buildClone()); mInjectedSky->setSource(target_sky); + // clear reflection probes and pause updates during sky change + gPipeline.mReflectionMapManager.pause(); + gPipeline.mReflectionMapManager.reset(); + mBlenderSky = std::make_shared(target_sky, start_sky, psky, transition); mBlenderSky->setOnFinished( [this, psky](LLSettingsBlender::ptr_t blender) { mBlenderSky.reset(); mInjectedSky->setSource(psky); + + // resume updating reflection probes when done animating sky + gPipeline.mReflectionMapManager.resume(); setSky(mInjectedSky); if (!mBlenderWater && (countExperiencesActive() == 0)) { diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp index f1a44a68c9..788b61b381 100644 --- a/indra/newview/lleventnotifier.cpp +++ b/indra/newview/lleventnotifier.cpp @@ -43,8 +43,10 @@ class LLEventHandler : public LLCommandHandler public: // requires trusted browser to trigger LLEventHandler() : LLCommandHandler("event", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (params.size() < 2) { diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 3c71d3e49d..c1776705f9 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1144,22 +1144,32 @@ void push_for_transform(LLVertexBuffer* buff, U32 source_count, U32 dest_count) } BOOL LLFace::getGeometryVolume(const LLVolume& volume, - const S32 &f, - const LLMatrix4& mat_vert_in, const LLMatrix3& mat_norm_in, - const U16 &index_offset, - bool force_rebuild) + S32 face_index, + const LLMatrix4& mat_vert_in, + const LLMatrix3& mat_norm_in, + U16 index_offset, + bool force_rebuild, + bool no_debug_assert) { LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE; llassert(verify()); - if (volume.getNumVolumeFaces() <= f) { - LL_WARNS() << "Attempt get volume face out of range! Total Faces: " << volume.getNumVolumeFaces() << " Attempt get access to: " << f << LL_ENDL; - return FALSE; - } + if (face_index < 0 || face_index >= volume.getNumVolumeFaces()) + { + if (gDebugGL) + { + LL_WARNS() << "Face index is out of bounds!" << LL_ENDL; + LL_WARNS() << "Attempt get volume face out of range!" + " Total Faces: " << volume.getNumVolumeFaces() << + " Attempt get access to: " << face_index << LL_ENDL; + llassert(no_debug_assert); + } + return FALSE; + } bool rigged = isState(RIGGED); - const LLVolumeFace &vf = volume.getVolumeFace(f); + const LLVolumeFace &vf = volume.getVolumeFace(face_index); S32 num_vertices = (S32)vf.mNumVertices; S32 num_indices = (S32) vf.mNumIndices; @@ -1174,14 +1184,14 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { if (gDebugGL) { - llassert(false); - LL_WARNS() << "Index buffer overflow!" << LL_ENDL; + LL_WARNS() << "Index buffer overflow!" << LL_ENDL; LL_WARNS() << "Indices Count: " << mIndicesCount << " VF Num Indices: " << num_indices << " Indices Index: " << mIndicesIndex << " VB Num Indices: " << mVertexBuffer->getNumIndices() << LL_ENDL; - LL_WARNS() << " Face Index: " << f + LL_WARNS() << " Face Index: " << face_index << " Pool Type: " << mPoolType << LL_ENDL; + llassert(no_debug_assert); } return FALSE; } @@ -1190,8 +1200,8 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { if (gDebugGL) { - llassert(false); LL_WARNS() << "Vertex buffer overflow!" << LL_ENDL; + llassert(no_debug_assert); } return FALSE; } @@ -1228,7 +1238,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, bool rebuild_tangent = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TANGENT); bool rebuild_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4); - const LLTextureEntry *tep = mVObjp->getTE(f); + const LLTextureEntry *tep = mVObjp->getTE(face_index); const U8 bump_code = tep ? tep->getBumpmap() : 0; BOOL is_static = mDrawablep->isStatic(); @@ -1443,7 +1453,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, if (bump_code) { - mVObjp->getVolume()->genTangents(f); + mVObjp->getVolume()->genTangents(face_index); F32 offset_multiple; switch( bump_code ) { @@ -1492,7 +1502,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, U8 texgen = getTextureEntry()->getTexGen(); if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT) { //planar texgen needs binormals - mVObjp->getVolume()->genTangents(f); + mVObjp->getVolume()->genTangents(face_index); } U8 tex_mode = 0; @@ -1775,7 +1785,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, { mVertexBuffer->getTexCoord1Strider(tex_coords1, mGeomIndex, mGeomCount); - mVObjp->getVolume()->genTangents(f); + mVObjp->getVolume()->genTangents(face_index); for (S32 i = 0; i < num_vertices; i++) { @@ -1895,7 +1905,7 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, mVertexBuffer->getTangentStrider(tangent, mGeomIndex, mGeomCount); F32* tangents = (F32*) tangent.get(); - mVObjp->getVolume()->genTangents(f); + mVObjp->getVolume()->genTangents(face_index); LLVector4Logical mask; mask.clear(); diff --git a/indra/newview/llface.h b/indra/newview/llface.h index 0a66dc6ba6..eb3b47d6d6 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -156,10 +156,12 @@ public: void updateRebuildFlags(); bool canRenderAsMask(); // logic helper BOOL getGeometryVolume(const LLVolume& volume, - const S32 &f, - const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, - const U16 &index_offset, - bool force_rebuild = false); + S32 face_index, + const LLMatrix4& mat_vert, + const LLMatrix3& mat_normal, + U16 index_offset, + bool force_rebuild = false, + bool no_debug_assert = false); // For avatar U16 getGeometryAvatar( diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp index c2667bedaa..4ad136e13a 100644 --- a/indra/newview/llfilepicker.cpp +++ b/indra/newview/llfilepicker.cpp @@ -194,11 +194,11 @@ BOOL LLFilePicker::setupFilter(ELoadFilter filter) mOFN.lpstrFilter = ANIM_FILTER \ L"\0"; break; - case FFLOAD_GLTF: - mOFN.lpstrFilter = GLTF_FILTER \ - L"\0"; - break; - case FFLOAD_COLLADA: + case FFLOAD_GLTF: + mOFN.lpstrFilter = GLTF_FILTER \ + L"\0"; + break; + case FFLOAD_COLLADA: mOFN.lpstrFilter = COLLADA_FILTER \ L"\0"; break; @@ -780,7 +780,6 @@ void set_nav_save_data(LLFilePicker::ESaveFilter filter, std::string &extension, creator = "\?\?\?\?"; extension = "xaf"; break; - case LLFilePicker::FFSAVE_GLTF: type = "\?\?\?\?"; creator = "\?\?\?\?"; diff --git a/indra/newview/llfilepicker_mac.mm b/indra/newview/llfilepicker_mac.mm index e5965abbd6..4dd8bea4e1 100644 --- a/indra/newview/llfilepicker_mac.mm +++ b/indra/newview/llfilepicker_mac.mm @@ -103,7 +103,6 @@ std::unique_ptr> doLoadDialog(const std::vector* allowed_types, } } -std::unique_ptr doSaveDialog(const std::string* file, +std::unique_ptr doSaveDialog(const std::string* file, const std::string* type, const std::string* creator, const std::string* extension, diff --git a/indra/newview/llfloater360capture.cpp b/indra/newview/llfloater360capture.cpp index 23f86e2361..2c638fa959 100644 --- a/indra/newview/llfloater360capture.cpp +++ b/indra/newview/llfloater360capture.cpp @@ -64,12 +64,10 @@ LLFloater360Capture::LLFloater360Capture(const LLSD& key) // such time as we ask it not to (the dtor). If we crash or // otherwise, exit before this is turned off, the Simulator // will take care of cleaning up for us. - if (gSavedSettings.getBOOL("360CaptureUseInterestListCap")) - { - // send everything to us for as long as this floater is open - const bool send_everything = true; - changeInterestListMode(send_everything); - } + mStartILMode = gAgent.getInterestListMode(); + + // send everything to us for as long as this floater is open + gAgent.changeInterestListMode(LLViewerRegion::IL_MODE_360); } LLFloater360Capture::~LLFloater360Capture() @@ -81,13 +79,15 @@ LLFloater360Capture::~LLFloater360Capture() mWebBrowser->unloadMediaSource(); } - // Tell the Simulator not to send us everything anymore - // and revert to the regular "keyhole" frustum of interest + // Restore interest list mode to the state when started + // Normally LLFloater360Capture tells the Simulator send everything + // and now reverts to the regular "keyhole" frustum of interest // list updates. - if (!LLApp::isExiting() && gSavedSettings.getBOOL("360CaptureUseInterestListCap")) + if (!LLApp::isExiting() && + gSavedSettings.getBOOL("360CaptureUseInterestListCap") && + mStartILMode != gAgent.getInterestListMode()) { - const bool send_everything = false; - changeInterestListMode(send_everything); + gAgent.changeInterestListMode(mStartILMode); } } @@ -170,52 +170,6 @@ void LLFloater360Capture::onChooseQualityRadioGroup() setSourceImageSize(); } -// Using a new capability, tell the simulator that we want it to send everything -// it knows about and not just what is in front of the camera, in its view -// frustum. We need this feature so that the contents of the region that appears -// in the 6 snapshots which we cannot see and is normally not "considered", is -// also rendered. Typically, this is turned on when the 360 capture floater is -// opened and turned off when it is closed. -// Note: for this version, we do not have a way to determine when "everything" -// has arrived and has been rendered so for now, the proposal is that users -// will need to experiment with the low resolution version and wait for some -// (hopefully) small period of time while the full contents resolves. -// Pass in a flag to ask the simulator/interest list to "send everything" or -// not (the default mode) -void LLFloater360Capture::changeInterestListMode(bool send_everything) -{ - LLSD body; - - if (send_everything) - { - body["mode"] = LLSD::String("360"); - } - else - { - body["mode"] = LLSD::String("default"); - } - - if (gAgent.requestPostCapability("InterestList", body, [](const LLSD & response) - { - LL_INFOS("360Capture") << - "InterestList capability responded: \n" << - ll_pretty_print_sd(response) << - LL_ENDL; - })) - { - LL_INFOS("360Capture") << - "Successfully posted an InterestList capability request with payload: \n" << - ll_pretty_print_sd(body) << - LL_ENDL; - } - else - { - LL_INFOS("360Capture") << - "Unable to post an InterestList capability request with payload: \n" << - ll_pretty_print_sd(body) << - LL_ENDL; - } -} // There is is a setting (360CaptureSourceImageSize) that holds the size // (width == height since it's a square) of each of the 6 source snapshots. @@ -632,11 +586,8 @@ void LLFloater360Capture::capture360Images() // display time to encode all 6 images. It tends to be a fairly linear // time for each so we don't need to worry about displaying the time // for each - this gives us plenty to use for optimizing - LL_INFOS("360Capture") << - "Time to encode and save 6 images was " << - encode_time_total << - " seconds" << - LL_ENDL; + LL_INFOS("360Capture") << "Time to encode and save 6 images was " << + encode_time_total << " seconds" << LL_ENDL; // Write the JavaScript file footer (the bottom of the file after the // declarations of the actual data URLs array). The footer comprises of @@ -668,7 +619,7 @@ void LLFloater360Capture::capture360Images() // as a change - only the subsequent 5 are if (camera_changed_times < 5) { - LL_INFOS("360Capture") << "Warning: we only captured " << camera_changed_times << " images." << LL_ENDL; + LL_WARNS("360Capture") << "360 image capture expected 5 or more images, only captured " << camera_changed_times << " images." << LL_ENDL; } // now we have the 6 shots saved in a well specified location, diff --git a/indra/newview/llfloater360capture.h b/indra/newview/llfloater360capture.h index 8f765c0b1b..3fb2c7f3c7 100644 --- a/indra/newview/llfloater360capture.h +++ b/indra/newview/llfloater360capture.h @@ -50,8 +50,6 @@ class LLFloater360Capture: void onOpen(const LLSD& key) override; void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) override; - void changeInterestListMode(bool send_everything); - const std::string getHTMLBaseFolder(); void capture360Images(); @@ -93,6 +91,8 @@ class LLFloater360Capture: std::string mImageSaveDir; LLPointer mRawImages[6]; + + std::string mStartILMode; }; #endif // LL_FLOATER_360CAPTURE_H diff --git a/indra/newview/llfloaterbulkpermission.cpp b/indra/newview/llfloaterbulkpermission.cpp index a3cc939f85..abc9cdbcc2 100644 --- a/indra/newview/llfloaterbulkpermission.cpp +++ b/indra/newview/llfloaterbulkpermission.cpp @@ -76,6 +76,8 @@ BOOL LLFloaterBulkPermission::postBuild() mBulkChangeIncludeSounds = gSavedSettings.getBOOL("BulkChangeIncludeSounds"); mBulkChangeIncludeTextures = gSavedSettings.getBOOL("BulkChangeIncludeTextures"); mBulkChangeIncludeSettings = gSavedSettings.getBOOL("BulkChangeIncludeSettings"); + mBulkChangeIncludeMaterials = gSavedSettings.getBOOL("BulkChangeIncludeMaterials"); + mBulkChangeShareWithGroup = gSavedSettings.getBOOL("BulkChangeShareWithGroup"); mBulkChangeEveryoneCopy = gSavedSettings.getBOOL("BulkChangeEveryoneCopy"); mBulkChangeNextOwnerModify = gSavedSettings.getBOOL("BulkChangeNextOwnerModify"); @@ -188,6 +190,8 @@ void LLFloaterBulkPermission::onCloseBtn() gSavedSettings.setBOOL("BulkChangeIncludeSounds", mBulkChangeIncludeSounds); gSavedSettings.setBOOL("BulkChangeIncludeTextures", mBulkChangeIncludeTextures); gSavedSettings.setBOOL("BulkChangeIncludeSettings", mBulkChangeIncludeSettings); + gSavedSettings.setBOOL("BulkChangeIncludeMaterials", mBulkChangeIncludeMaterials); + gSavedSettings.setBOOL("BulkChangeShareWithGroup", mBulkChangeShareWithGroup); gSavedSettings.setBOOL("BulkChangeEveryoneCopy", mBulkChangeEveryoneCopy); gSavedSettings.setBOOL("BulkChangeNextOwnerModify", mBulkChangeNextOwnerModify); @@ -284,6 +288,7 @@ void LLFloaterBulkPermission::doCheckUncheckAll(BOOL check) gSavedSettings.setBOOL("BulkChangeIncludeSounds" , check); gSavedSettings.setBOOL("BulkChangeIncludeTextures" , check); gSavedSettings.setBOOL("BulkChangeIncludeSettings" , check); + gSavedSettings.setBOOL("BulkChangeIncludeMaterials" , check); } diff --git a/indra/newview/llfloaterbulkpermission.h b/indra/newview/llfloaterbulkpermission.h index 1afc876bba..ab5d568667 100644 --- a/indra/newview/llfloaterbulkpermission.h +++ b/indra/newview/llfloaterbulkpermission.h @@ -103,6 +103,8 @@ private: bool mBulkChangeIncludeSounds; bool mBulkChangeIncludeTextures; bool mBulkChangeIncludeSettings; + bool mBulkChangeIncludeMaterials; + bool mBulkChangeShareWithGroup; bool mBulkChangeEveryoneCopy; bool mBulkChangeNextOwnerModify; diff --git a/indra/newview/llfloatercamerapresets.cpp b/indra/newview/llfloatercamerapresets.cpp index 300c945a85..a1cbffd094 100644 --- a/indra/newview/llfloatercamerapresets.cpp +++ b/indra/newview/llfloatercamerapresets.cpp @@ -24,6 +24,7 @@ */ #include "llviewerprecompiledheaders.h" +#include "llfloatercamera.h" #include "llfloatercamerapresets.h" #include "llfloaterreg.h" #include "llnotificationsutil.h" @@ -40,7 +41,8 @@ LLFloaterCameraPresets::~LLFloaterCameraPresets() BOOL LLFloaterCameraPresets::postBuild() { mPresetList = getChild("preset_list"); - + mPresetList->setCommitCallback(boost::bind(&LLFloaterCameraPresets::onSelectionChange, this)); + mPresetList->setCommitOnSelectionChange(true); LLPresetsManager::getInstance()->setPresetListChangeCameraCallback(boost::bind(&LLFloaterCameraPresets::populateList, this)); return TRUE; @@ -58,6 +60,7 @@ void LLFloaterCameraPresets::populateList() std::list preset_names; presetsMgr->loadPresetNamesFromDir(PRESETS_CAMERA, preset_names, DEFAULT_BOTTOM); + std::string active_preset = gSavedSettings.getString("PresetCameraActive"); for (std::list::const_iterator it = preset_names.begin(); it != preset_names.end(); ++it) { @@ -66,6 +69,19 @@ void LLFloaterCameraPresets::populateList() LLCameraPresetFlatItem* item = new LLCameraPresetFlatItem(name, is_default); item->postBuild(); mPresetList->addItem(item); + if(name == active_preset) + { + mPresetList->selectItem(item); + } + } +} + +void LLFloaterCameraPresets::onSelectionChange() +{ + LLCameraPresetFlatItem* selected_preset = dynamic_cast(mPresetList->getSelectedItem()); + if(selected_preset) + { + LLFloaterCamera::switchToPreset(selected_preset->getPresetName()); } } diff --git a/indra/newview/llfloatercamerapresets.h b/indra/newview/llfloatercamerapresets.h index 66430fa399..4430a4209e 100644 --- a/indra/newview/llfloatercamerapresets.h +++ b/indra/newview/llfloatercamerapresets.h @@ -38,6 +38,7 @@ class LLFloaterCameraPresets : public LLFloater virtual void onOpen(const LLSD& key); void populateList(); + void onSelectionChange(); private: LLFloaterCameraPresets(const LLSD& key); @@ -58,6 +59,8 @@ public: virtual void onMouseEnter(S32 x, S32 y, MASK mask); virtual void onMouseLeave(S32 x, S32 y, MASK mask); + std::string getPresetName() { return mPresetName; } + private: void onDeleteBtnClick(); void onResetBtnClick(); diff --git a/indra/newview/llfloaterchangeitemthumbnail.cpp b/indra/newview/llfloaterchangeitemthumbnail.cpp new file mode 100644 index 0000000000..f54240f6f4 --- /dev/null +++ b/indra/newview/llfloaterchangeitemthumbnail.cpp @@ -0,0 +1,955 @@ +/** + * @file llfloaterchangeitemthumbnail.cpp + * @brief LLFloaterChangeItemThumbnail class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llfloaterchangeitemthumbnail.h" + +#include "llbutton.h" +#include "llclipboard.h" +#include "lliconctrl.h" +#include "llinventoryfunctions.h" +#include "llinventoryicon.h" +#include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llfloaterreg.h" +#include "llfloatersimplesnapshot.h" +#include "lllineeditor.h" +#include "llnotificationsutil.h" +#include "lltextbox.h" +#include "lltexturectrl.h" +#include "llthumbnailctrl.h" +#include "llviewerfoldertype.h" +#include "llviewermenufile.h" +#include "llviewerobjectlist.h" +#include "llviewertexturelist.h" +#include "llwindow.h" + + +class LLThumbnailImagePicker : public LLFilePickerThread +{ +public: + LLThumbnailImagePicker(const LLUUID &item_id); + LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id); + ~LLThumbnailImagePicker(); + void notify(const std::vector& filenames) override; + +private: + LLUUID mInventoryId; + LLUUID mTaskId; +}; + +LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id) + : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE) + , mInventoryId(item_id) +{ +} + +LLThumbnailImagePicker::LLThumbnailImagePicker(const LLUUID &item_id, const LLUUID &task_id) + : LLFilePickerThread(LLFilePicker::FFLOAD_IMAGE) + , mInventoryId(item_id) + , mTaskId(task_id) +{ +} + +LLThumbnailImagePicker::~LLThumbnailImagePicker() +{ +} + +void LLThumbnailImagePicker::notify(const std::vector& filenames) +{ + if (filenames.empty()) + { + return; + } + std::string file_path = filenames[0]; + if (file_path.empty()) + { + return; + } + + LLFloaterSimpleSnapshot::uploadThumbnail(file_path, mInventoryId, mTaskId); +} + +LLFloaterChangeItemThumbnail::LLFloaterChangeItemThumbnail(const LLSD& key) + : LLFloater(key) + , mObserverInitialized(false) + , mTooltipState(TOOLTIP_NONE) +{ +} + +LLFloaterChangeItemThumbnail::~LLFloaterChangeItemThumbnail() +{ + gInventory.removeObserver(this); + removeVOInventoryListener(); +} + +BOOL LLFloaterChangeItemThumbnail::postBuild() +{ + mItemNameText = getChild("item_name"); + mItemTypeIcon = getChild("item_type_icon"); + mThumbnailCtrl = getChild("item_thumbnail"); + mToolTipTextBox = getChild("tooltip_text"); + + LLSD tooltip_text; + mToolTipTextBox->setValue(tooltip_text); + + LLButton *upload_local = getChild("upload_local"); + upload_local->setClickedCallback(onUploadLocal, (void*)this); + upload_local->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_LOCAL)); + upload_local->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_LOCAL)); + + LLButton *upload_snapshot = getChild("upload_snapshot"); + upload_snapshot->setClickedCallback(onUploadSnapshot, (void*)this); + upload_snapshot->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT)); + upload_snapshot->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_UPLOAD_SNAPSHOT)); + + LLButton *use_texture = getChild("use_texture"); + use_texture->setClickedCallback(onUseTexture, (void*)this); + use_texture->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_USE_TEXTURE)); + use_texture->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_USE_TEXTURE)); + + mCopyToClipboardBtn = getChild("copy_to_clipboard"); + mCopyToClipboardBtn->setClickedCallback(onCopyToClipboard, (void*)this); + mCopyToClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD)); + mCopyToClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_TO_CLIPBOARD)); + + mPasteFromClipboardBtn = getChild("paste_from_clipboard"); + mPasteFromClipboardBtn->setClickedCallback(onPasteFromClipboard, (void*)this); + mPasteFromClipboardBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD)); + mPasteFromClipboardBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_COPY_FROM_CLIPBOARD)); + + mRemoveImageBtn = getChild("remove_image"); + mRemoveImageBtn->setClickedCallback(onRemove, (void*)this); + mRemoveImageBtn->setMouseEnterCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseEnter, this, _1, _2, TOOLTIP_REMOVE)); + mRemoveImageBtn->setMouseLeaveCallback(boost::bind(&LLFloaterChangeItemThumbnail::onButtonMouseLeave, this, _1, _2, TOOLTIP_REMOVE)); + + return LLFloater::postBuild(); +} + +void LLFloaterChangeItemThumbnail::onOpen(const LLSD& key) +{ + if (!key.has("item_id") && !key.isUUID()) + { + closeFloater(); + } + + if (key.isUUID()) + { + mItemId = key.asUUID(); + } + else + { + mItemId = key["item_id"].asUUID(); + mTaskId = key["task_id"].asUUID(); + } + + refreshFromInventory(); +} + +void LLFloaterChangeItemThumbnail::onFocusReceived() +{ + mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); +} + +void LLFloaterChangeItemThumbnail::onMouseEnter(S32 x, S32 y, MASK mask) +{ + mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); +} + +BOOL LLFloaterChangeItemThumbnail::handleDragAndDrop( + S32 x, + S32 y, + MASK mask, + BOOL drop, + EDragAndDropType cargo_type, + void *cargo_data, + EAcceptance *accept, + std::string& tooltip_msg) +{ + if (cargo_type == DAD_TEXTURE) + { + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + if (item->getAssetUUID().notNull()) + { + if (drop) + { + assignAndValidateAsset(item->getAssetUUID()); + } + + *accept = ACCEPT_YES_SINGLE; + } + else + { + *accept = ACCEPT_NO; + } + } + else + { + *accept = ACCEPT_NO; + } + + LL_DEBUGS("UserInput") << "dragAndDrop handled by LLFloaterChangeItemThumbnail " << getKey() << LL_ENDL; + + return TRUE; +} + +void LLFloaterChangeItemThumbnail::changed(U32 mask) +{ + //LLInventoryObserver + + if (mTaskId.notNull() || mItemId.isNull()) + { + // Task inventory or not set up yet + return; + } + + const std::set& mChangedItemIDs = gInventory.getChangedIDs(); + std::set::const_iterator it; + + for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) + { + // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) + if (*it == mItemId) + { + // if there's a change we're interested in. + if ((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) + { + refreshFromInventory(); + } + } + } +} + +void LLFloaterChangeItemThumbnail::inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* user_data) +{ + //LLVOInventoryListener + refreshFromInventory(); +} + +LLInventoryObject* LLFloaterChangeItemThumbnail::getInventoryObject() +{ + LLInventoryObject* obj = NULL; + if (mTaskId.isNull()) + { + // it is in agent inventory + if (!mObserverInitialized) + { + gInventory.addObserver(this); + mObserverInitialized = true; + } + + obj = gInventory.getObject(mItemId); + } + else + { + LLViewerObject* object = gObjectList.findObject(mTaskId); + if (object) + { + if (!mObserverInitialized) + { + registerVOInventoryListener(object, NULL); + mObserverInitialized = false; + } + + obj = object->getInventoryObject(mItemId); + } + } + return obj; +} + +void LLFloaterChangeItemThumbnail::refreshFromInventory() +{ + LLInventoryObject* obj = getInventoryObject(); + if (!obj) + { + closeFloater(); + } + + if (obj) + { + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + bool in_trash = gInventory.isObjectDescendentOf(obj->getUUID(), trash_id); + if (in_trash && obj->getUUID() != trash_id) + { + // Close properties when moving to trash + // Aren't supposed to view properties from trash + closeFloater(); + } + else + { + refreshFromObject(obj); + } + } + else + { + closeFloater(); + } +} + +class LLIsOutfitTextureType : public LLInventoryCollectFunctor +{ +public: + LLIsOutfitTextureType() {} + virtual ~LLIsOutfitTextureType() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + +bool LLIsOutfitTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return item && (item->getType() == LLAssetType::AT_TEXTURE); +} + +void LLFloaterChangeItemThumbnail::refreshFromObject(LLInventoryObject* obj) +{ + LLUIImagePtr icon_img; + LLUUID thumbnail_id = obj->getThumbnailUUID(); + + LLViewerInventoryItem* item = dynamic_cast(obj); + if (item) + { + setTitle(getString("title_item_thumbnail")); + + icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE); + mRemoveImageBtn->setEnabled(thumbnail_id.notNull() && ((item->getActualType() != LLAssetType::AT_TEXTURE) || (item->getAssetUUID() != thumbnail_id))); + } + else + { + LLViewerInventoryCategory* cat = dynamic_cast(obj); + + if (cat) + { + setTitle(getString("title_folder_thumbnail")); + icon_img = LLUI::getUIImage(LLViewerFolderType::lookupIconName(cat->getPreferredType(), true)); + + if (thumbnail_id.isNull() && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) + { + // Legacy support, check if there is an image inside + + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + // Not LLIsOfAssetType, because we allow links + LLIsOutfitTextureType f; + gInventory.getDirectDescendentsOf(mItemId, cats, items, f); + + if (1 == items.size()) + { + LLViewerInventoryItem* item = items.front(); + if (item && item->getIsLinkType()) + { + item = item->getLinkedItem(); + } + if (item) + { + thumbnail_id = item->getAssetUUID(); + if (thumbnail_id.notNull()) + { + // per SL-19188, set this image as a thumbnail + LL_INFOS() << "Setting image " << thumbnail_id + << " from outfit as a thumbnail for inventory object " << obj->getUUID() + << LL_ENDL; + assignAndValidateAsset(thumbnail_id, true); + } + } + } + } + + mRemoveImageBtn->setEnabled(thumbnail_id.notNull()); + } + } + mItemTypeIcon->setImage(icon_img); + mItemNameText->setValue(obj->getName()); + + mThumbnailCtrl->setValue(thumbnail_id); + + mCopyToClipboardBtn->setEnabled(thumbnail_id.notNull()); + mPasteFromClipboardBtn->setEnabled(LLClipboard::instance().hasContents()); + + // todo: some elements might not support setting thumbnails + // since they already have them + // It is unclear how system folders should function +} + +void LLFloaterChangeItemThumbnail::onUploadLocal(void *userdata) +{ + LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + + (new LLThumbnailImagePicker(self->mItemId, self->mTaskId))->getFile(); + + LLFloater* floaterp = self->mPickerHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } + floaterp = self->mSnapshotHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } +} + +void LLFloaterChangeItemThumbnail::onUploadSnapshot(void *userdata) +{ + LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + + LLFloater* floaterp = self->mSnapshotHandle.get(); + // Show the dialog + if (floaterp) + { + floaterp->openFloater(); + } + else + { + LLSD key; + key["item_id"] = self->mItemId; + key["task_id"] = self->mTaskId; + LLFloaterSimpleSnapshot* snapshot_floater = (LLFloaterSimpleSnapshot*)LLFloaterReg::showInstance("simple_snapshot", key, true); + if (snapshot_floater) + { + self->addDependentFloater(snapshot_floater); + self->mSnapshotHandle = snapshot_floater->getHandle(); + snapshot_floater->setOwner(self); + } + } + + floaterp = self->mPickerHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } +} + +void LLFloaterChangeItemThumbnail::onUseTexture(void *userdata) +{ + LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + LLInventoryObject* obj = self->getInventoryObject(); + if (obj) + { + self->showTexturePicker(obj->getThumbnailUUID()); + } + + LLFloater* floaterp = self->mSnapshotHandle.get(); + if (floaterp) + { + floaterp->closeFloater(); + } +} + +void LLFloaterChangeItemThumbnail::onCopyToClipboard(void *userdata) +{ + LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + LLInventoryObject* obj = self->getInventoryObject(); + if (obj) + { + LLClipboard::instance().reset(); + LLClipboard::instance().addToClipboard(obj->getThumbnailUUID(), LLAssetType::AT_NONE); + self->mPasteFromClipboardBtn->setEnabled(true); + } +} + +void LLFloaterChangeItemThumbnail::onPasteFromClipboard(void *userdata) +{ + LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + if (objects.size() > 0) + { + LLUUID potential_uuid = objects[0]; + LLUUID asset_id; + + if (potential_uuid.notNull()) + { + LLViewerInventoryItem* item = gInventory.getItem(potential_uuid); + if (item) + { + // no point checking snapshot? + if (item->getType() == LLAssetType::AT_TEXTURE) + { + bool copy = item->getPermissions().allowCopyBy(gAgent.getID()); + bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + + if (copy && xfer) + { + asset_id = item->getAssetUUID(); + } + else + { + LLNotificationsUtil::add("ThumbnailInsufficientPermissions"); + return; + } + } + } + else + { + // assume that this is a texture + asset_id = potential_uuid; + } + } + + LLInventoryObject* obj = self->getInventoryObject(); + if (obj && obj->getThumbnailUUID() == asset_id) + { + // nothing to do + return; + } + if (asset_id.notNull()) + { + self->assignAndValidateAsset(asset_id); + } + // else show 'buffer has no texture' warning? + } +} + +void LLFloaterChangeItemThumbnail::onRemove(void *userdata) +{ + LLFloaterChangeItemThumbnail *self = (LLFloaterChangeItemThumbnail*)userdata; + + LLSD payload; + payload["item_id"] = self->mItemId; + payload["object_id"] = self->mTaskId; + LLNotificationsUtil::add("DeleteThumbnail", LLSD(), payload, boost::bind(&LLFloaterChangeItemThumbnail::onRemovalConfirmation, _1, _2, self->getHandle())); +} + +// static +void LLFloaterChangeItemThumbnail::onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle handle) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0 && !handle.isDead() && !handle.get()->isDead()) + { + LLFloaterChangeItemThumbnail* self = (LLFloaterChangeItemThumbnail*)handle.get(); + self->setThumbnailId(LLUUID::null); + } +} + +struct ImageLoadedData +{ + LLUUID mThumbnailId; + LLUUID mObjectId; + LLHandle mFloaterHandle; + bool mSilent; + // Keep image reference to prevent deletion on timeout + LLPointer mTexturep; +}; + +void LLFloaterChangeItemThumbnail::assignAndValidateAsset(const LLUUID &asset_id, bool silent) +{ + LLPointer texturep = LLViewerTextureManager::getFetchedTexture(asset_id); + if (texturep->isMissingAsset()) + { + LL_WARNS() << "Attempted to assign missing asset " << asset_id << LL_ENDL; + if (!silent) + { + LLNotificationsUtil::add("ThumbnailDimentionsLimit"); + } + } + else if (texturep->getFullWidth() == 0) + { + if (silent) + { + mExpectingAssetId = LLUUID::null; + } + else + { + // don't warn user multiple times if some textures took their time + mExpectingAssetId = asset_id; + } + ImageLoadedData *data = new ImageLoadedData(); + data->mObjectId = mItemId; + data->mThumbnailId = asset_id; + data->mFloaterHandle = getHandle(); + data->mSilent = silent; + data->mTexturep = texturep; + + texturep->setLoadedCallback(onImageDataLoaded, + MAX_DISCARD_LEVEL, // Don't need full image, just size data + FALSE, + FALSE, + (void*)data, + NULL, + FALSE); + } + else + { + if (validateAsset(asset_id)) + { + setThumbnailId(asset_id); + } + else if (!silent) + { + LLNotificationsUtil::add("ThumbnailDimentionsLimit"); + } + } +} +bool LLFloaterChangeItemThumbnail::validateAsset(const LLUUID &asset_id) +{ + if (asset_id.isNull()) + { + return false; + } + + LLPointer texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD); + + if (!texturep) + { + return false; + } + + if (texturep->isMissingAsset()) + { + return false; + } + + if (texturep->getFullWidth() != texturep->getFullHeight()) + { + return false; + } + + if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX + || texturep->getFullHeight() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX) + { + return false; + } + + if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN + || texturep->getFullHeight() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN) + { + return false; + } + return true; +} + +//static +void LLFloaterChangeItemThumbnail::onImageDataLoaded( + BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata) +{ + if (!userdata) return; + + if (!final && success) return; //not done yet + + ImageLoadedData* data = (ImageLoadedData*)userdata; + + if (success) + { + // Update the item, set it even if floater is dead + if (validateAsset(data->mThumbnailId)) + { + setThumbnailId(data->mThumbnailId, data->mObjectId); + } + else if (!data->mSilent) + { + // Should this only appear if floater is alive? + LLNotificationsUtil::add("ThumbnailDimentionsLimit"); + } + } + + // Update floater + if (!data->mSilent && !data->mFloaterHandle.isDead()) + { + LLFloaterChangeItemThumbnail* self = static_cast(data->mFloaterHandle.get()); + if (self && self->mExpectingAssetId == data->mThumbnailId) + { + self->mExpectingAssetId = LLUUID::null; + } + } + + delete data; +} + +//static +void LLFloaterChangeItemThumbnail::onFullImageLoaded( + BOOL success, + LLViewerFetchedTexture* src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata) +{ + if (!userdata) return; + + if (!final && success) return; //not done yet + + ImageLoadedData* data = (ImageLoadedData*)userdata; + + if (success) + { + if (src_vi->getFullWidth() != src_vi->getFullHeight() + || src_vi->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN) + { + if (!data->mSilent) + { + LLNotificationsUtil::add("ThumbnailDimentionsLimit"); + } + } + else if (src_vi->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX) + { + LLFloaterSimpleSnapshot::uploadThumbnail(src, data->mObjectId, LLUUID::null); + } + else + { + setThumbnailId(data->mThumbnailId, data->mObjectId); + } + } + + delete data; +} + +void LLFloaterChangeItemThumbnail::showTexturePicker(const LLUUID &thumbnail_id) +{ + // show hourglass cursor when loading inventory window + getWindow()->setCursor(UI_CURSOR_WAIT); + + LLFloater* floaterp = mPickerHandle.get(); + // Show the dialog + if (floaterp) + { + floaterp->openFloater(); + } + else + { + floaterp = new LLFloaterTexturePicker( + this, + thumbnail_id, + thumbnail_id, + thumbnail_id, + FALSE, + TRUE, + "SELECT PHOTO", + PERM_NONE, + PERM_NONE, + FALSE, + NULL); + + mPickerHandle = floaterp->getHandle(); + + LLFloaterTexturePicker* texture_floaterp = dynamic_cast(floaterp); + if (texture_floaterp) + { + //texture_floaterp->setTextureSelectedCallback(); + //texture_floaterp->setOnUpdateImageStatsCallback(); + texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource, const LLUUID&, const LLUUID&) + { + if (op == LLTextureCtrl::TEXTURE_SELECT) + { + onTexturePickerCommit(); + } + } + ); + + texture_floaterp->setLocalTextureEnabled(FALSE); + texture_floaterp->setBakeTextureEnabled(FALSE); + texture_floaterp->setCanApplyImmediately(false); + texture_floaterp->setCanApply(false, true, false /*Hide 'preview disabled'*/); + texture_floaterp->setMinDimentionsLimits(LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN); + + addDependentFloater(texture_floaterp); + } + + floaterp->openFloater(); + } + floaterp->setFocus(TRUE); +} + +void LLFloaterChangeItemThumbnail::onTexturePickerCommit() +{ + LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mPickerHandle.get(); + + if (floaterp) + { + LLUUID asset_id = floaterp->getAssetID(); + + if (asset_id.isNull()) + { + setThumbnailId(asset_id); + return; + } + + LLInventoryObject* obj = getInventoryObject(); + if (obj && obj->getThumbnailUUID() == asset_id) + { + // nothing to do + return; + } + + LLPointer texturep = LLViewerTextureManager::findFetchedTexture(asset_id, TEX_LIST_STANDARD); + if (!texturep) + { + LL_WARNS() << "Image " << asset_id << " doesn't exist" << LL_ENDL; + return; + } + + if (texturep->isMissingAsset()) + { + LL_WARNS() << "Image " << asset_id << " is missing" << LL_ENDL; + return; + } + + if (texturep->getFullWidth() != texturep->getFullHeight()) + { + LLNotificationsUtil::add("ThumbnailDimentionsLimit"); + return; + } + + if (texturep->getFullWidth() < LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN + && texturep->getFullWidth() > 0) + { + LLNotificationsUtil::add("ThumbnailDimentionsLimit"); + return; + } + + if (texturep->getFullWidth() > LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX + || texturep->getFullWidth() == 0) + { + if (texturep->isFullyLoaded() + && (texturep->getCachedRawImageLevel() == 0 || texturep->getRawImageLevel() == 0) + && (texturep->isCachedRawImageReady() || texturep->isRawImageValid())) + { + if (texturep->isRawImageValid()) + { + LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getRawImage(), mItemId, mTaskId); + } + else + { + LLFloaterSimpleSnapshot::uploadThumbnail(texturep->getCachedRawImage(), mItemId, mTaskId); + } + } + else + { + ImageLoadedData* data = new ImageLoadedData(); + data->mObjectId = mItemId; + data->mThumbnailId = asset_id; + data->mFloaterHandle = getHandle(); + data->mSilent = false; + data->mTexturep = texturep; + + texturep->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + texturep->setMinDiscardLevel(0); + texturep->setLoadedCallback(onFullImageLoaded, + 0, // Need best quality + TRUE, + FALSE, + (void*)data, + NULL, + FALSE); + texturep->forceToSaveRawImage(0); + } + return; + } + + setThumbnailId(asset_id); + } +} + + +void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID &new_thumbnail_id) +{ + LLInventoryObject* obj = getInventoryObject(); + if (!obj) + { + return; + } + + if (mTaskId.notNull()) + { + LL_ERRS() << "Not implemented yet" << LL_ENDL; + return; + } + + setThumbnailId(new_thumbnail_id, mItemId, obj); +} + +void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id) +{ + LLInventoryObject* obj = gInventory.getObject(object_id); + if (!obj) + { + return; + } + + setThumbnailId(new_thumbnail_id, object_id, obj); +} +void LLFloaterChangeItemThumbnail::setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj) +{ + if (obj->getThumbnailUUID() != new_thumbnail_id) + { + LLSD updates; + if (new_thumbnail_id.notNull()) + { + // At the moment server expects id as a string + updates["thumbnail"] = LLSD().with("asset_id", new_thumbnail_id.asString()); + } + else + { + // No thumbnail isntead of 'null id thumbnail' + updates["thumbnail"] = LLSD(); + } + LLViewerInventoryCategory* view_folder = dynamic_cast(obj); + if (view_folder) + { + update_inventory_category(object_id, updates, NULL); + } + LLViewerInventoryItem* view_item = dynamic_cast(obj); + if (view_item) + { + update_inventory_item(object_id, updates, NULL); + } + } +} + +void LLFloaterChangeItemThumbnail::onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state) +{ + mTooltipState = state; + + std::string tooltip_text; + std::string tooltip_name = "tooltip_" + button->getName(); + if (hasString(tooltip_name)) + { + tooltip_text = getString(tooltip_name); + } + + mToolTipTextBox->setValue(tooltip_text); +} + +void LLFloaterChangeItemThumbnail::onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state) +{ + if (mTooltipState == state) + { + mTooltipState = TOOLTIP_NONE; + LLSD tooltip_text; + mToolTipTextBox->setValue(tooltip_text); + } +} + diff --git a/indra/newview/llfloaterchangeitemthumbnail.h b/indra/newview/llfloaterchangeitemthumbnail.h new file mode 100644 index 0000000000..a91e9b8ee9 --- /dev/null +++ b/indra/newview/llfloaterchangeitemthumbnail.h @@ -0,0 +1,139 @@ +/** + * @file llfloaterchangeitemthumbnail.h + * @brief LLFloaterChangeItemThumbnail class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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_LLFLOATERCHANGEITEMTHUMBNAIL_H +#define LL_LLFLOATERCHANGEITEMTHUMBNAIL_H + +#include "llfloater.h" +#include "llinventoryobserver.h" +#include "llvoinventorylistener.h" + +class LLButton; +class LLIconCtrl; +class LLTextBox; +class LLThumbnailCtrl; +class LLUICtrl; +class LLViewerInventoryItem; +class LLViewerFetchedTexture; + +class LLFloaterChangeItemThumbnail : public LLFloater, public LLInventoryObserver, public LLVOInventoryListener +{ +public: + LLFloaterChangeItemThumbnail(const LLSD& key); + ~LLFloaterChangeItemThumbnail(); + + BOOL postBuild() override; + void onOpen(const LLSD& key) override; + void onFocusReceived() override; + void onMouseEnter(S32 x, S32 y, MASK mask) override; + + BOOL handleDragAndDrop( + S32 x, + S32 y, + MASK mask, + BOOL drop, + EDragAndDropType cargo_type, + void *cargo_data, + EAcceptance *accept, + std::string& tooltip_msg) override; + + void changed(U32 mask) override; + void inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* user_data) override; + + static bool validateAsset(const LLUUID &asset_id); + +private: + + LLInventoryObject* getInventoryObject(); + void refreshFromInventory(); + void refreshFromObject(LLInventoryObject* obj); + + static void onUploadLocal(void*); + static void onUploadSnapshot(void*); + static void onUseTexture(void*); + static void onCopyToClipboard(void*); + static void onPasteFromClipboard(void*); + static void onRemove(void*); + static void onRemovalConfirmation(const LLSD& notification, const LLSD& response, LLHandle handle); + + void assignAndValidateAsset(const LLUUID &asset_id, bool silent = false); + static void onImageDataLoaded(BOOL success, + LLViewerFetchedTexture *src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata); + static void onFullImageLoaded(BOOL success, + LLViewerFetchedTexture* src_vi, + LLImageRaw* src, + LLImageRaw* aux_src, + S32 discard_level, + BOOL final, + void* userdata); + + void showTexturePicker(const LLUUID &thumbnail_id); + void onTexturePickerCommit(); + + void setThumbnailId(const LLUUID &new_thumbnail_id); + static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id); + static void setThumbnailId(const LLUUID& new_thumbnail_id, const LLUUID& object_id, LLInventoryObject* obj); + + enum EToolTipState + { + TOOLTIP_NONE, + TOOLTIP_UPLOAD_LOCAL, + TOOLTIP_UPLOAD_SNAPSHOT, + TOOLTIP_USE_TEXTURE, + TOOLTIP_COPY_TO_CLIPBOARD, + TOOLTIP_COPY_FROM_CLIPBOARD, + TOOLTIP_REMOVE, + }; + + void onButtonMouseEnter(LLUICtrl* button, const LLSD& param, EToolTipState state); + void onButtonMouseLeave(LLUICtrl* button, const LLSD& param, EToolTipState state); + + bool mObserverInitialized; + EToolTipState mTooltipState; + LLUUID mItemId; + LLUUID mTaskId; + LLUUID mExpectingAssetId; + + LLIconCtrl *mItemTypeIcon; + LLUICtrl *mItemNameText; + LLThumbnailCtrl *mThumbnailCtrl; + LLTextBox *mToolTipTextBox; + LLButton *mCopyToClipboardBtn; + LLButton *mPasteFromClipboardBtn; + LLButton *mRemoveImageBtn; + + LLHandle mPickerHandle; + LLHandle mSnapshotHandle; +}; +#endif // LL_LLFLOATERCHANGEITEMTHUMBNAIL_H diff --git a/indra/newview/llfloatereditenvironmentbase.cpp b/indra/newview/llfloatereditenvironmentbase.cpp index 2850951668..cd24d79b7f 100644 --- a/indra/newview/llfloatereditenvironmentbase.cpp +++ b/indra/newview/llfloatereditenvironmentbase.cpp @@ -260,7 +260,7 @@ void LLFloaterEditEnvironmentBase::onSaveAsCommit(const LLSD& notification, cons } else if (mInventoryItem) { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); LLUUID parent_id = mInventoryItem->getParentUUID(); if (marketplacelistings_id == parent_id || gInventory.isObjectDescendentOf(mInventoryItem->getUUID(), gInventory.getLibraryRootFolderID())) { diff --git a/indra/newview/llfloatereditextdaycycle.cpp b/indra/newview/llfloatereditextdaycycle.cpp index 6e8143384a..bb47feaa95 100644 --- a/indra/newview/llfloatereditextdaycycle.cpp +++ b/indra/newview/llfloatereditextdaycycle.cpp @@ -475,6 +475,8 @@ void LLFloaterEditExtDayCycle::refresh() void LLFloaterEditExtDayCycle::setEditSettingsAndUpdate(const LLSettingsBase::ptr_t &settings) { setEditDayCycle(std::dynamic_pointer_cast(settings)); + + showHDRNotification(std::dynamic_pointer_cast(settings)); } void LLFloaterEditExtDayCycle::setEditDayCycle(const LLSettingsDay::ptr_t &pday) @@ -1710,6 +1712,28 @@ void LLFloaterEditExtDayCycle::onPickerCommitSetting(LLUUID item_id, S32 track) } } +void LLFloaterEditExtDayCycle::showHDRNotification(const LLSettingsDay::ptr_t &pday) +{ + for (U32 i = LLSettingsDay::TRACK_GROUND_LEVEL; i <= LLSettingsDay::TRACK_MAX; i++) + { + LLSettingsDay::CycleTrack_t &day_track = pday->getCycleTrack(i); + + LLSettingsDay::CycleTrack_t::iterator iter = day_track.begin(); + LLSettingsDay::CycleTrack_t::iterator end = day_track.end(); + + while (iter != end) + { + LLSettingsSky::ptr_t sky = std::static_pointer_cast(iter->second); + if (sky && sky->canAutoAdjust()) + { + LLNotificationsUtil::add("AutoAdjustHDRSky"); + return; + } + iter++; + } + } +} + void LLFloaterEditExtDayCycle::onAssetLoadedForInsertion(LLUUID item_id, LLUUID asset_id, LLSettingsBase::ptr_t settings, S32 status, S32 source_track, S32 dest_track, LLSettingsBase::TrackPosition frame) { std::function cb = [this, settings, frame, source_track, dest_track]() diff --git a/indra/newview/llfloatereditextdaycycle.h b/indra/newview/llfloatereditextdaycycle.h index ab5d12fa36..025a2ee5d1 100644 --- a/indra/newview/llfloatereditextdaycycle.h +++ b/indra/newview/llfloatereditextdaycycle.h @@ -188,6 +188,8 @@ private: bool isRemovingFrameAllowed(); bool isAddingFrameAllowed(); + void showHDRNotification(const LLSettingsDay::ptr_t &pday); + LLSettingsDay::ptr_t mEditDay; // edited copy LLSettingsDay::Seconds mDayLength; U32 mCurrentTrack; diff --git a/indra/newview/llfloaterenvironmentadjust.cpp b/indra/newview/llfloaterenvironmentadjust.cpp index 18be4fffda..f3133ecb37 100644 --- a/indra/newview/llfloaterenvironmentadjust.cpp +++ b/indra/newview/llfloaterenvironmentadjust.cpp @@ -495,10 +495,12 @@ void LLFloaterEnvironmentAdjust::updateGammaLabel() if (ambiance != 0.f) { childSetValue("scene_gamma_label", getString("hdr_string")); + getChild(FIELD_SKY_SCENE_GAMMA)->setToolTip(getString("hdr_tooltip")); } else { childSetValue("scene_gamma_label", getString("brightness_string")); + getChild(FIELD_SKY_SCENE_GAMMA)->setToolTip(std::string()); } } diff --git a/indra/newview/llfloaterexperienceprofile.cpp b/indra/newview/llfloaterexperienceprofile.cpp index a99a096ea7..f6afdd29fb 100644 --- a/indra/newview/llfloaterexperienceprofile.cpp +++ b/indra/newview/llfloaterexperienceprofile.cpp @@ -92,8 +92,10 @@ class LLExperienceHandler : public LLCommandHandler public: LLExperienceHandler() : LLCommandHandler("experience", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if(params.size() != 2 || params[1].asString() != "profile") return false; diff --git a/indra/newview/llfloaterforgetuser.cpp b/indra/newview/llfloaterforgetuser.cpp index 97b022699f..f576ce7a76 100644 --- a/indra/newview/llfloaterforgetuser.cpp +++ b/indra/newview/llfloaterforgetuser.cpp @@ -164,6 +164,12 @@ bool LLFloaterForgetUser::onConfirmLogout(const LLSD& notification, const LLSD& if (option == 0) { // Remove creds + std::string grid_id = LLGridManager::getInstance()->getGridId(grid); + if (grid_id.empty()) + { + grid_id = grid; + } + gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, LLStartUp::getUserId()); // doesn't write gSecAPIHandler->removeFromCredentialMap("login_list", grid, LLStartUp::getUserId()); LLPointer cred = gSecAPIHandler->loadCredential(grid); @@ -228,7 +234,13 @@ void LLFloaterForgetUser::processForgetUser() void LLFloaterForgetUser::forgetUser(const std::string &userid, const std::string &fav_id, const std::string &grid, bool delete_data) { // Remove creds - gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid); + std::string grid_id = LLGridManager::getInstance()->getGridId(grid); + if (grid_id.empty()) + { + grid_id = grid; + } + gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid_id, userid); // doesn't write + gSecAPIHandler->removeFromCredentialMap("login_list", grid, userid); // write operation LLPointer cred = gSecAPIHandler->loadCredential(grid); if (cred.notNull() && cred->userID() == userid) diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp index d17889bed1..f29046c513 100644 --- a/indra/newview/llfloatergesture.cpp +++ b/indra/newview/llfloatergesture.cpp @@ -211,7 +211,7 @@ BOOL LLFloaterGesture::postBuild() getChildView("play_btn")->setVisible( true); getChildView("stop_btn")->setVisible( false); setDefaultBtn("play_btn"); - mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE, false); + mGestureFolderID = gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); uuid_vec_t folders; folders.push_back(mGestureFolderID); diff --git a/indra/newview/llfloaterhandler.cpp b/indra/newview/llfloaterhandler.cpp index 8ebb14149c..b66049de7f 100644 --- a/indra/newview/llfloaterhandler.cpp +++ b/indra/newview/llfloaterhandler.cpp @@ -49,7 +49,7 @@ LLFloater* get_parent_floater(LLView* view) } -bool LLFloaterHandler::handle(const LLSD ¶ms, const LLSD &query_map, LLMediaCtrl *web) +bool LLFloaterHandler::handle(const LLSD ¶ms, const LLSD &query_map, const std::string& grid, LLMediaCtrl *web) { if (params.size() < 1) return false; LLFloater* floater = NULL; diff --git a/indra/newview/llfloaterhandler.h b/indra/newview/llfloaterhandler.h index 5915642d66..959c972275 100644 --- a/indra/newview/llfloaterhandler.h +++ b/indra/newview/llfloaterhandler.h @@ -33,7 +33,7 @@ class LLFloaterHandler { public: LLFloaterHandler() : LLCommandHandler("floater", UNTRUSTED_BLOCK) { } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web); + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web); }; #endif diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index 2720b7fcf7..011ad67011 100644 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -289,6 +289,9 @@ void LLFloaterIMContainer::onOpen(const LLSD& key) LLMultiFloater::onOpen(key); reSelectConversation(); assignResizeLimits(); + + LLFloaterIMSessionTab* session_floater = LLFloaterIMSessionTab::getConversation(mSelectedSession); + session_floater->onOpen(key); } // virtual diff --git a/indra/newview/llfloaterimnearbychat.cpp b/indra/newview/llfloaterimnearbychat.cpp index f1807f1c5b..bb4cc9bca3 100644 --- a/indra/newview/llfloaterimnearbychat.cpp +++ b/indra/newview/llfloaterimnearbychat.cpp @@ -108,6 +108,8 @@ LLFloaterIMNearbyChat::LLFloaterIMNearbyChat(const LLSD& llsd) mEnableCallbackRegistrar.add("Avatar.EnableGearItem", boost::bind(&cb_do_nothing)); mCommitCallbackRegistrar.add("Avatar.GearDoToSelected", boost::bind(&cb_do_nothing)); mEnableCallbackRegistrar.add("Avatar.CheckGearItem", boost::bind(&cb_do_nothing)); + + mMinFloaterHeight = EXPANDED_MIN_HEIGHT; } //static @@ -901,8 +903,10 @@ public: LLChatCommandHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } // Your code here - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& tokens, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { bool retval = false; // Need at least 2 tokens to have a valid message. diff --git a/indra/newview/llfloaterimsessiontab.cpp b/indra/newview/llfloaterimsessiontab.cpp index 78271369d2..0b0dce29fb 100644 --- a/indra/newview/llfloaterimsessiontab.cpp +++ b/indra/newview/llfloaterimsessiontab.cpp @@ -92,6 +92,8 @@ LLFloaterIMSessionTab::LLFloaterIMSessionTab(const LLSD& session_id) mEnableCallbackRegistrar.add("Avatar.EnableItem", boost::bind(&LLFloaterIMSessionTab::enableContextMenuItem, this, _2)); mCommitCallbackRegistrar.add("Avatar.DoToSelected", boost::bind(&LLFloaterIMSessionTab::doToSelected, this, _2)); mCommitCallbackRegistrar.add("Group.DoToSelected", boost::bind(&cb_group_do_nothing)); + + mMinFloaterHeight = getMinHeight(); } LLFloaterIMSessionTab::~LLFloaterIMSessionTab() @@ -934,10 +936,13 @@ void LLFloaterIMSessionTab::reshapeFloater(bool collapse) S32 height = mContentPanel->getRect().getHeight() + mToolbarPanel->getRect().getHeight() + mChatLayoutPanel->getRect().getHeight() - mChatLayoutPanelHeight + 2; floater_rect.mTop -= height; + + setResizeLimits(getMinWidth(), floater_rect.getHeight()); } else { floater_rect.mTop = floater_rect.mBottom + mFloaterHeight; + setResizeLimits(getMinWidth(), mMinFloaterHeight); } enableResizeCtrls(true, true, !collapse); @@ -962,6 +967,7 @@ void LLFloaterIMSessionTab::restoreFloater() setShape(floater_rect, true); mBodyStack->updateLayout(); mExpandCollapseLineBtn->setImageOverlay(getString("expandline_icon")); + setResizeLimits(getMinWidth(), mMinFloaterHeight); setMessagePaneExpanded(true); saveCollapsedState(); mInputEditor->enableSingleLineMode(false); @@ -980,6 +986,8 @@ void LLFloaterIMSessionTab::onOpen(const LLSD& key) } mInputButtonPanel->setVisible(isTornOff()); + + setFocus(TRUE); } diff --git a/indra/newview/llfloaterimsessiontab.h b/indra/newview/llfloaterimsessiontab.h index 9f00917647..d478922617 100644 --- a/indra/newview/llfloaterimsessiontab.h +++ b/indra/newview/llfloaterimsessiontab.h @@ -152,6 +152,7 @@ protected: bool mMessagePaneExpanded; bool mIsParticipantListExpanded; + S32 mMinFloaterHeight; LLIMModel::LLIMSession* mSession; diff --git a/indra/newview/llfloaterinventorysettings.cpp b/indra/newview/llfloaterinventorysettings.cpp new file mode 100644 index 0000000000..29d6e90a33 --- /dev/null +++ b/indra/newview/llfloaterinventorysettings.cpp @@ -0,0 +1,44 @@ +/** + * @file llfloaterinventorysettings.cpp + * @brief LLFloaterInventorySettings class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llfloaterinventorysettings.h" + +LLFloaterInventorySettings::LLFloaterInventorySettings(const LLSD& key) + : LLFloater(key) +{ +} + +LLFloaterInventorySettings::~LLFloaterInventorySettings() +{} + +BOOL LLFloaterInventorySettings::postBuild() +{ + getChild("ok_btn")->setCommitCallback(boost::bind(&LLFloater::closeFloater, this, false)); + return TRUE; +} + diff --git a/indra/newview/llfloaterinventorysettings.h b/indra/newview/llfloaterinventorysettings.h new file mode 100644 index 0000000000..50304276c7 --- /dev/null +++ b/indra/newview/llfloaterinventorysettings.h @@ -0,0 +1,45 @@ +/** + * @file llfloaterinventorysettings.h + * @brief LLFloaterInventorySettings class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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_LLFLOATERINVENTORYSETTINGS_H +#define LL_LLFLOATERINVENTORYSETTINGS_H + +#include "llfloater.h" + +class LLFloaterInventorySettings + : public LLFloater +{ + friend class LLFloaterReg; + +public: + virtual BOOL postBuild(); + +private: + LLFloaterInventorySettings(const LLSD& key); + ~LLFloaterInventorySettings(); +}; + +#endif diff --git a/indra/newview/llfloaterlinkreplace.cpp b/indra/newview/llfloaterlinkreplace.cpp index 8ee7a72055..b42c49c607 100644 --- a/indra/newview/llfloaterlinkreplace.cpp +++ b/indra/newview/llfloaterlinkreplace.cpp @@ -335,8 +335,8 @@ BOOL LLFloaterLinkReplace::tick() void LLFloaterLinkReplace::processBatch(LLInventoryModel::item_array_t items) { const LLViewerInventoryItem* target_item = gInventory.getItem(mTargetUUID); - const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID cof_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID outfit_folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); for (LLInventoryModel::item_array_t::iterator it = items.begin(); it != items.end(); ++it) { diff --git a/indra/newview/llfloatermarketplacelistings.cpp b/indra/newview/llfloatermarketplacelistings.cpp index e755e9924c..71b3b16809 100644 --- a/indra/newview/llfloatermarketplacelistings.cpp +++ b/indra/newview/llfloatermarketplacelistings.cpp @@ -41,8 +41,11 @@ #include "llnotificationmanager.h" #include "llnotificationsutil.h" #include "llsidepaneliteminfo.h" +#include "llsidepaneltaskinfo.h" +#include "lltabcontainer.h" #include "lltextbox.h" #include "lltrans.h" +#include "llviewerwindow.h" ///---------------------------------------------------------------------------- /// LLPanelMarketplaceListings @@ -227,18 +230,31 @@ void LLPanelMarketplaceListings::onTabChange() void LLPanelMarketplaceListings::onAddButtonClicked() { - // Find active panel - LLInventoryPanel* panel = (LLInventoryPanel*)getChild("marketplace_filter_tabs")->getCurrentPanel(); - if (panel) - { - LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - llassert(marketplacelistings_id.notNull()); - LLFolderType::EType preferred_type = LLFolderType::lookup("category"); - LLUUID category = gInventory.createNewCategory(marketplacelistings_id, preferred_type, LLStringUtil::null); - gInventory.notifyObservers(); - panel->setSelectionByID(category, TRUE); - panel->getRootFolder()->setNeedsAutoRename(TRUE); + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + llassert(marketplacelistings_id.notNull()); + LLFolderType::EType preferred_type = LLFolderType::lookup("category"); + LLHandle handle = getHandle(); + gInventory.createNewCategory( + marketplacelistings_id, + preferred_type, + LLStringUtil::null, + [handle](const LLUUID &new_cat_id) + { + // Find active panel + LLPanel *marketplace_panel = handle.get(); + if (!marketplace_panel) + { + return; + } + LLInventoryPanel* panel = (LLInventoryPanel*)marketplace_panel->getChild("marketplace_filter_tabs")->getCurrentPanel(); + if (panel) + { + gInventory.notifyObservers(); + panel->setSelectionByID(new_cat_id, TRUE); + panel->getRootFolder()->setNeedsAutoRename(TRUE); + } } + ); } void LLPanelMarketplaceListings::onAuditButtonClicked() @@ -359,6 +375,7 @@ LLFloaterMarketplaceListings::LLFloaterMarketplaceListings(const LLSD& key) , mInventoryTitle(NULL) , mPanelListings(NULL) , mPanelListingsSet(false) +, mRootFolderCreating(false) { } @@ -431,7 +448,7 @@ void LLFloaterMarketplaceListings::fetchContents() { LLMarketplaceData::instance().setDataFetchedSignal(boost::bind(&LLFloaterMarketplaceListings::updateView, this)); LLMarketplaceData::instance().setSLMDataFetched(MarketplaceFetchCodes::MARKET_FETCH_LOADING); - LLInventoryModelBackgroundFetch::instance().start(mRootFolderId); + LLInventoryModelBackgroundFetch::instance().start(mRootFolderId, true); LLMarketplaceData::instance().getSLMListings(); } } @@ -444,15 +461,50 @@ void LLFloaterMarketplaceListings::setRootFolder() // If we are *not* a merchant or we have no market place connection established yet, do nothing return; } + if (!gInventory.isInventoryUsable()) + { + return; + } + LLFolderType::EType preferred_type = LLFolderType::FT_MARKETPLACE_LISTINGS; // We are a merchant. Get the Marketplace listings folder, create it if needs be. - LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true); - if (marketplacelistings_id.isNull()) - { - // We should never get there unless the inventory fails badly - LL_ERRS("SLM") << "Inventory problem: failure to create the marketplace listings folder for a merchant!" << LL_ENDL; - return; - } + LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(preferred_type); + + if (marketplacelistings_id.isNull()) + { + if (!mRootFolderCreating) + { + mRootFolderCreating = true; + gInventory.createNewCategory( + gInventory.getRootFolderID(), + preferred_type, + LLStringUtil::null, + [](const LLUUID &new_cat_id) + { + LLFloaterMarketplaceListings* marketplace = LLFloaterReg::findTypedInstance("marketplace_listings"); + if (marketplace) + { + if (new_cat_id.notNull()) + { + // will call setRootFolder again + marketplace->updateView(); + } + // don't update in case of failure, createNewCategory can return + // immediately if cap is missing and will cause a loop + else + { + // unblock + marketplace->mRootFolderCreating = false; + LL_WARNS("SLM") << "Inventory warning: Failed to create marketplace listings folder for a merchant" << LL_ENDL; + } + } + } + ); + } + return; + } + + mRootFolderCreating = false; // No longer need to observe new category creation if (mCategoryAddedObserver && gInventory.containsObserver(mCategoryAddedObserver)) @@ -540,6 +592,11 @@ void LLFloaterMarketplaceListings::updateView() { setRootFolder(); } + if (mRootFolderCreating) + { + // waiting for callback + return; + } // Update the bottom initializing status and progress dial if we are initializing or if we're a merchant and still loading if ((mkt_status <= MarketplaceStatusCodes::MARKET_PLACE_INITIALIZING) || (is_merchant && (data_fetched <= MarketplaceFetchCodes::MARKET_FETCH_LOADING)) ) @@ -843,14 +900,17 @@ void LLFloaterMarketplaceValidation::onOpen(const LLSD& key) LLUUID cat_id(key.asUUID()); if (cat_id.isNull()) { - cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + cat_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); } // Validates the folder if (cat_id.notNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); - validate_marketplacelistings(cat, boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), false); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + cat_id, + NULL, + boost::bind(&LLFloaterMarketplaceValidation::appendMessage, this, _1, _2, _3), + false); } // Handle the listing folder being processed @@ -954,18 +1014,44 @@ LLFloaterItemProperties::~LLFloaterItemProperties() BOOL LLFloaterItemProperties::postBuild() { - // On the standalone properties floater, we have no need for a back button... - LLSidepanelItemInfo* panel = getChild("item_panel"); - LLButton* back_btn = panel->getChild("back_btn"); - back_btn->setVisible(FALSE); - return LLFloater::postBuild(); } void LLFloaterItemProperties::onOpen(const LLSD& key) { // Tell the panel which item it needs to visualize - LLSidepanelItemInfo* panel = getChild("item_panel"); - panel->setItemID(key["id"].asUUID()); + LLPanel* panel = findChild("sidepanel"); + + LLSidepanelItemInfo* item_panel = dynamic_cast(panel); + if (item_panel) + { + item_panel->setItemID(key["id"].asUUID()); + if (key.has("object")) + { + item_panel->setObjectID(key["object"].asUUID()); + } + item_panel->setParentFloater(this); + } + + LLSidepanelTaskInfo* task_panel = dynamic_cast(panel); + if (task_panel) + { + task_panel->setObjectSelection(LLSelectMgr::getInstance()->getSelection()); + } } +LLMultiItemProperties::LLMultiItemProperties(const LLSD& key) + : LLMultiFloater(LLSD()) +{ + // start with a small rect in the top-left corner ; will get resized + LLRect rect; + rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 350, 350); + setRect(rect); + LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup(key.asString()); + if (last_floater) + { + stackWith(*last_floater); + } + setTitle(LLTrans::getString("MultiPropertiesTitle")); + buildTabContainer(); +} diff --git a/indra/newview/llfloatermarketplacelistings.h b/indra/newview/llfloatermarketplacelistings.h index ffc098e28a..085e517a9d 100644 --- a/indra/newview/llfloatermarketplacelistings.h +++ b/indra/newview/llfloatermarketplacelistings.h @@ -33,6 +33,7 @@ #include "llinventorypanel.h" #include "llnotificationptr.h" #include "llmodaldialog.h" +#include "llmultifloater.h" #include "lltexteditor.h" class LLInventoryCategoriesObserver; @@ -139,6 +140,7 @@ private: LLTextBox * mInventoryTitle; LLUUID mRootFolderId; + bool mRootFolderCreating; LLPanelMarketplaceListings * mPanelListings; bool mPanelListingsSet; }; @@ -223,4 +225,10 @@ public: private: }; +class LLMultiItemProperties : public LLMultiFloater +{ +public: + LLMultiItemProperties(const LLSD& key); +}; + #endif // LL_LLFLOATERMARKETPLACELISTINGS_H diff --git a/indra/newview/llfloaternewfeaturenotification.cpp b/indra/newview/llfloaternewfeaturenotification.cpp new file mode 100644 index 0000000000..1e50024967 --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.cpp @@ -0,0 +1,83 @@ +/** + * @file llfloaternewfeaturenotification.cpp + * @brief LLFloaterNewFeatureNotification class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llfloaternewfeaturenotification.h" + + +LLFloaterNewFeatureNotification::LLFloaterNewFeatureNotification(const LLSD& key) + : LLFloater(key) +{ +} + +LLFloaterNewFeatureNotification::~LLFloaterNewFeatureNotification() +{ +} + +BOOL LLFloaterNewFeatureNotification::postBuild() +{ + setCanDrag(FALSE); + getChild("close_btn")->setCommitCallback(boost::bind(&LLFloaterNewFeatureNotification::onCloseBtn, this)); + + const std::string title_txt = "title_txt"; + const std::string dsc_txt = "description_txt"; + std::string feature = "_" + getKey().asString(); + + getChild(title_txt)->setValue(getString(title_txt + feature)); + getChild(dsc_txt)->setValue(getString(dsc_txt + feature)); + + if (getKey().asString() == "gltf") + { + LLRect rect = getRect(); + // make automatic? + reshape(rect.getWidth() + 90, rect.getHeight() + 45); + } + + return TRUE; +} + +void LLFloaterNewFeatureNotification::onOpen(const LLSD& key) +{ + centerOnScreen(); +} + +void LLFloaterNewFeatureNotification::onCloseBtn() +{ + closeFloater(); +} + +void LLFloaterNewFeatureNotification::centerOnScreen() +{ + LLVector2 window_size = LLUI::getInstance()->getWindowSize(); + centerWithin(LLRect(0, 0, ll_round(window_size.mV[VX]), ll_round(window_size.mV[VY]))); + LLFloaterView* parent = dynamic_cast(getParent()); + if (parent) + { + parent->bringToFront(this); + } +} + diff --git a/indra/newview/llfloaternewfeaturenotification.h b/indra/newview/llfloaternewfeaturenotification.h new file mode 100644 index 0000000000..95501451dc --- /dev/null +++ b/indra/newview/llfloaternewfeaturenotification.h @@ -0,0 +1,49 @@ +/** + * @file llfloaternewfeaturenotification.h + * @brief LLFloaterNewFeatureNotification class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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_FLOATER_NEW_FEATURE_NOTOFICATION_H +#define LL_FLOATER_NEW_FEATURE_NOTOFICATION_H + +#include "llfloater.h" + +class LLFloaterNewFeatureNotification: + public LLFloater +{ + friend class LLFloaterReg; +public: + BOOL postBuild() override; + void onOpen(const LLSD& key) override; + +private: + LLFloaterNewFeatureNotification(const LLSD& key); + /*virtual*/ ~LLFloaterNewFeatureNotification(); + + void centerOnScreen(); + + void onCloseBtn(); +}; + +#endif diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index a682064dad..d3ab22f792 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -164,34 +164,12 @@ void LLFloaterOpenObject::moveToInventory(bool wear, bool replace) } inventory_func_type func = boost::bind(LLFloaterOpenObject::callbackCreateInventoryCategory,_1,object_id,wear,replace); - LLUUID category_id = gInventory.createNewCategory(parent_category_id, - LLFolderType::FT_NONE, - name, - func); - - //If we get a null category ID, we are using a capability in createNewCategory and we will - //handle the following in the callbackCreateInventoryCategory routine. - if ( category_id.notNull() ) - { - LLCatAndWear* data = new LLCatAndWear; - data->mCatID = category_id; - data->mWear = wear; - data->mFolderResponded = false; - data->mReplace = replace; - - // Copy and/or move the items into the newly created folder. - // Ignore any "you're going to break this item" messages. - BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE, - callbackMoveInventory, - (void*)data); - if (!success) - { - delete data; - data = NULL; - - LLNotificationsUtil::add("OpenObjectCannotCopy"); - } - } + // D567 copy thumbnail info + gInventory.createNewCategory( + parent_category_id, + LLFolderType::FT_NONE, + name, + func); } // static @@ -206,9 +184,14 @@ void LLFloaterOpenObject::callbackCreateInventoryCategory(const LLUUID& category // Copy and/or move the items into the newly created folder. // Ignore any "you're going to break this item" messages. - BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE, - callbackMoveInventory, - (void*)wear_data); + BOOL success = move_inv_category_world_to_agent(object_id, + category_id, + TRUE, + [](S32 result, void* data, const LLMoveInv*) + { + callbackMoveInventory(result, data); + }, + (void*)wear_data); if (!success) { delete wear_data; diff --git a/indra/newview/llfloateroutfitphotopreview.cpp b/indra/newview/llfloateroutfitphotopreview.cpp deleted file mode 100644 index ade258aef7..0000000000 --- a/indra/newview/llfloateroutfitphotopreview.cpp +++ /dev/null @@ -1,288 +0,0 @@ -/** - * @file llfloateroutfitphotopreview.cpp - * @brief LLFloaterOutfitPhotoPreview class implementation - * - * $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 "llwindow.h" - -#include "llfloateroutfitphotopreview.h" - -#include "llagent.h" -#include "llappearancemgr.h" -#include "llbutton.h" -#include "llcombobox.h" -#include "llfilepicker.h" -#include "llfloaterreg.h" -#include "llimagetga.h" -#include "llimagepng.h" -#include "llinventory.h" -#include "llinventorymodel.h" -#include "llnotificationsutil.h" -#include "llresmgr.h" -#include "lltrans.h" -#include "lltextbox.h" -#include "lltextureview.h" -#include "llui.h" -#include "llviewerinventory.h" -#include "llviewertexture.h" -#include "llviewertexturelist.h" -#include "lluictrlfactory.h" -#include "llviewerwindow.h" -#include "lllineeditor.h" - -const S32 MAX_OUTFIT_PHOTO_WIDTH = 256; -const S32 MAX_OUTFIT_PHOTO_HEIGHT = 256; - -const S32 CLIENT_RECT_VPAD = 4; - -LLFloaterOutfitPhotoPreview::LLFloaterOutfitPhotoPreview(const LLSD& key) - : LLPreview(key), - mUpdateDimensions(TRUE), - mImage(NULL), - mOutfitID(LLUUID()), - mImageOldBoostLevel(LLGLTexture::BOOST_NONE), - mExceedLimits(FALSE) -{ - updateImageID(); -} - -LLFloaterOutfitPhotoPreview::~LLFloaterOutfitPhotoPreview() -{ - LLLoadedCallbackEntry::cleanUpCallbackList(&mCallbackTextureList) ; - - if (mImage.notNull()) - { - mImage->setBoostLevel(mImageOldBoostLevel); - mImage = NULL; - } -} - -// virtual -BOOL LLFloaterOutfitPhotoPreview::postBuild() -{ - getChild("ok_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onOkBtn, this)); - getChild("cancel_btn")->setClickedCallback(boost::bind(&LLFloaterOutfitPhotoPreview::onCancelBtn, this)); - - return LLPreview::postBuild(); -} - -void LLFloaterOutfitPhotoPreview::draw() -{ - updateDimensions(); - - LLPreview::draw(); - - if (!isMinimized()) - { - LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - const LLRect& border = mClientRect; - LLRect interior = mClientRect; - interior.stretch( -PREVIEW_BORDER_WIDTH ); - - // ...border - gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f)); - gl_rect_2d_checkerboard( interior ); - - if ( mImage.notNull() ) - { - // Draw the texture - gGL.diffuseColor3f( 1.f, 1.f, 1.f ); - gl_draw_scaled_image(interior.mLeft, - interior.mBottom, - interior.getWidth(), - interior.getHeight(), - mImage); - - // Pump the texture priority - F32 pixel_area = (F32)(interior.getWidth() * interior.getHeight() ); - mImage->addTextureStats( pixel_area ); - - S32 int_width = interior.getWidth(); - S32 int_height = interior.getHeight(); - mImage->setKnownDrawSize(int_width, int_height); - } - } - -} - -// virtual -void LLFloaterOutfitPhotoPreview::reshape(S32 width, S32 height, BOOL called_from_parent) -{ - LLPreview::reshape(width, height, called_from_parent); - - LLRect dim_rect(getChildView("dimensions")->getRect()); - - S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE; - - S32 info_height = dim_rect.mTop + CLIENT_RECT_VPAD; - - 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 ; - - S32 client_width = client_rect.getWidth(); - S32 client_height = client_width; - - if(client_height > client_rect.getHeight()) - { - client_height = client_rect.getHeight(); - client_width = client_height; - } - mClientRect.setLeftTopAndSize(client_rect.getCenterX() - (client_width / 2), client_rect.getCenterY() + (client_height / 2), client_width, client_height); - -} - - -void LLFloaterOutfitPhotoPreview::updateDimensions() -{ - if (!mImage) - { - return; - } - if ((mImage->getFullWidth() * mImage->getFullHeight()) == 0) - { - return; - } - - if (mAssetStatus != PREVIEW_ASSET_LOADED) - { - mAssetStatus = PREVIEW_ASSET_LOADED; - mUpdateDimensions = TRUE; - } - - getChild("dimensions")->setTextArg("[WIDTH]", llformat("%d", mImage->getFullWidth())); - getChild("dimensions")->setTextArg("[HEIGHT]", llformat("%d", mImage->getFullHeight())); - - if ((mImage->getFullWidth() <= MAX_OUTFIT_PHOTO_WIDTH) && (mImage->getFullHeight() <= MAX_OUTFIT_PHOTO_HEIGHT)) - { - getChild("ok_btn")->setEnabled(TRUE); - mExceedLimits = FALSE; - } - else - { - mExceedLimits = TRUE; - LLStringUtil::format_map_t args; - args["MAX_WIDTH"] = llformat("%d", MAX_OUTFIT_PHOTO_WIDTH); - args["MAX_HEIGHT"] = llformat("%d", MAX_OUTFIT_PHOTO_HEIGHT); - std::string label = getString("exceed_limits", args); - getChild("notification")->setValue(label); - getChild("notification")->setColor(LLColor4::yellow); - getChild("ok_btn")->setEnabled(FALSE); - } - - if (mUpdateDimensions) - { - mUpdateDimensions = FALSE; - - reshape(getRect().getWidth(), getRect().getHeight()); - gFloaterView->adjustToFitScreen(this, FALSE); - } -} - -void LLFloaterOutfitPhotoPreview::loadAsset() -{ - if (mImage.notNull()) - { - mImage->setBoostLevel(mImageOldBoostLevel); - } - mImage = LLViewerTextureManager::getFetchedTexture(mImageID, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); - mImageOldBoostLevel = mImage->getBoostLevel(); - mImage->setBoostLevel(LLGLTexture::BOOST_PREVIEW); - mImage->forceToSaveRawImage(0) ; - mAssetStatus = PREVIEW_ASSET_LOADING; - mUpdateDimensions = TRUE; - updateDimensions(); -} - -LLPreview::EAssetStatus LLFloaterOutfitPhotoPreview::getAssetStatus() -{ - if (mImage.notNull() && (mImage->getFullWidth() * mImage->getFullHeight() > 0)) - { - mAssetStatus = PREVIEW_ASSET_LOADED; - } - return mAssetStatus; -} - -void LLFloaterOutfitPhotoPreview::updateImageID() -{ - const LLViewerInventoryItem *item = static_cast(getItem()); - if(item) - { - mImageID = item->getAssetUUID(); - } - else - { - mImageID = mItemUUID; - } - -} - -/* virtual */ -void LLFloaterOutfitPhotoPreview::setObjectID(const LLUUID& object_id) -{ - mObjectUUID = object_id; - - const LLUUID old_image_id = mImageID; - - updateImageID(); - if (mImageID != old_image_id) - { - mAssetStatus = PREVIEW_ASSET_UNLOADED; - loadAsset(); - } - refreshFromItem(); -} - -void LLFloaterOutfitPhotoPreview::setOutfitID(const LLUUID& outfit_id) -{ - mOutfitID = outfit_id; - LLViewerInventoryCategory* outfit_folder = gInventory.getCategory(mOutfitID); - if(outfit_folder && !mExceedLimits) - { - getChild("notification")->setValue( getString("photo_confirmation")); - getChild("notification")->setTextArg("[OUTFIT]", outfit_folder->getName()); - getChild("notification")->setColor(LLColor4::white); - } - -} - -void LLFloaterOutfitPhotoPreview::onOkBtn() -{ - if(mOutfitID.notNull() && getItem()) - { - LLAppearanceMgr::instance().removeOutfitPhoto(mOutfitID); - LLPointer cb = NULL; - link_inventory_object(mOutfitID, LLConstPointer(getItem()), cb); - } - closeFloater(); -} - -void LLFloaterOutfitPhotoPreview::onCancelBtn() -{ - closeFloater(); -} diff --git a/indra/newview/llfloateroutfitphotopreview.h b/indra/newview/llfloateroutfitphotopreview.h deleted file mode 100644 index a1e7b58abe..0000000000 --- a/indra/newview/llfloateroutfitphotopreview.h +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @file llfloateroutfitphotopreview.h - * @brief LLFloaterOutfitPhotoPreview class definition - * - * $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$ - */ - -#ifndef LL_LLFLOATEROUTFITPHOTOPREVIEW_H -#define LL_LLFLOATEROUTFITPHOTOPREVIEW_H - -#include "llpreview.h" -#include "llbutton.h" -#include "llframetimer.h" -#include "llviewertexture.h" - -class LLComboBox; -class LLImageRaw; - -class LLFloaterOutfitPhotoPreview : public LLPreview -{ -public: - LLFloaterOutfitPhotoPreview(const LLSD& key); - ~LLFloaterOutfitPhotoPreview(); - - virtual void draw(); - - virtual void loadAsset(); - virtual EAssetStatus getAssetStatus(); - - virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); - - /*virtual*/ void setObjectID(const LLUUID& object_id); - - void setOutfitID(const LLUUID& outfit_id); - void onOkBtn(); - void onCancelBtn(); - -protected: - void init(); - /* virtual */ BOOL postBuild(); - -private: - void updateImageID(); // set what image is being uploaded. - void updateDimensions(); - LLUUID mImageID; - LLUUID mOutfitID; - LLPointer mImage; - S32 mImageOldBoostLevel; - - // This is stored off in a member variable, because the save-as - // button and drag and drop functionality need to know. - BOOL mUpdateDimensions; - - BOOL mExceedLimits; - - LLLoadedCallbackEntry::source_callback_list_t mCallbackTextureList ; -}; -#endif // LL_LLFLOATEROUTFITPHOTOPREVIEW_H diff --git a/indra/newview/llfloaterperms.cpp b/indra/newview/llfloaterperms.cpp index fb55c6c6c4..b53839977e 100644 --- a/indra/newview/llfloaterperms.cpp +++ b/indra/newview/llfloaterperms.cpp @@ -122,7 +122,7 @@ const std::string LLFloaterPermsDefault::sCategoryNames[CAT_LAST] = "Notecards", "Gestures", "Wearables", - "Settings" + "Settings", "Materials" }; diff --git a/indra/newview/llfloaterperms.h b/indra/newview/llfloaterperms.h index 02359a256e..97ddec7cba 100644 --- a/indra/newview/llfloaterperms.h +++ b/indra/newview/llfloaterperms.h @@ -75,6 +75,7 @@ enum Categories CAT_GESTURES, CAT_WEARABLES, CAT_SETTINGS, + CAT_MATERIALS, CAT_LAST }; diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp deleted file mode 100644 index 64ad40f419..0000000000 --- a/indra/newview/llfloaterproperties.cpp +++ /dev/null @@ -1,891 +0,0 @@ -/** - * @file llfloaterproperties.cpp - * @brief A floater which shows an inventory item's properties. - * - * $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 "llfloaterproperties.h" - -#include -#include -#include "llcachename.h" -#include "llavatarnamecache.h" -#include "lldbstrings.h" -#include "llfloaterreg.h" - -#include "llagent.h" -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "llcombobox.h" -#include "llavataractions.h" -#include "llinventorydefines.h" -#include "llinventoryobserver.h" -#include "llinventorymodel.h" -#include "lllineeditor.h" -//#include "llspinctrl.h" -#include "llradiogroup.h" -#include "llresmgr.h" -#include "roles_constants.h" -#include "llselectmgr.h" -#include "lltextbox.h" -#include "lltrans.h" -#include "lluiconstants.h" -#include "llviewerinventory.h" -#include "llviewerobjectlist.h" -#include "llviewerregion.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llgroupactions.h" - -#include "lluictrlfactory.h" - - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLPropertiesObserver -// -// helper class to watch the inventory. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -// Ugh. This can't be a singleton because it needs to remove itself -// from the inventory observer list when destroyed, which could -// happen after gInventory has already been destroyed if a singleton. -// Instead, do our own ref counting and create / destroy it as needed -class LLPropertiesObserver : public LLInventoryObserver -{ -public: - LLPropertiesObserver(LLFloaterProperties* floater) - : mFloater(floater) - { - gInventory.addObserver(this); - } - virtual ~LLPropertiesObserver() - { - gInventory.removeObserver(this); - } - virtual void changed(U32 mask); -private: - LLFloaterProperties* mFloater; // Not a handle because LLFloaterProperties is managing LLPropertiesObserver -}; - -void LLPropertiesObserver::changed(U32 mask) -{ - // if there's a change we're interested in. - if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) - { - mFloater->dirty(); - } -} - - - -///---------------------------------------------------------------------------- -/// Class LLFloaterProperties -///---------------------------------------------------------------------------- - -// Default constructor -LLFloaterProperties::LLFloaterProperties(const LLUUID& item_id) - : LLFloater(mItemID), - mItemID(item_id), - mDirty(TRUE) -{ - mPropertiesObserver = new LLPropertiesObserver(this); -} - -// Destroys the object -LLFloaterProperties::~LLFloaterProperties() -{ - delete mPropertiesObserver; - mPropertiesObserver = NULL; -} - -// virtual -BOOL LLFloaterProperties::postBuild() -{ - // build the UI - // item name & description - getChild("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); - getChild("LabelItemName")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitName,this)); - getChild("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); - getChild("LabelItemDesc")->setCommitCallback(boost::bind(&LLFloaterProperties:: onCommitDescription, this)); - // Creator information - getChild("BtnCreator")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickCreator,this)); - // owner information - getChild("BtnOwner")->setCommitCallback(boost::bind(&LLFloaterProperties::onClickOwner,this)); - // acquired date - // owner permissions - // Permissions debug text - // group permissions - getChild("CheckShareWithGroup")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); - // everyone permissions - getChild("CheckEveryoneCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); - // next owner permissions - getChild("CheckNextOwnerModify")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); - getChild("CheckNextOwnerCopy")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); - getChild("CheckNextOwnerTransfer")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitPermissions, this)); - // Mark for sale or not, and sale info - getChild("CheckPurchase")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); - getChild("ComboBoxSaleType")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleType, this)); - // "Price" label for edit - getChild("Edit Cost")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); - // The UI has been built, now fill in all the values - refresh(); - - return TRUE; -} - -// virtual -void LLFloaterProperties::onOpen(const LLSD& key) -{ - refresh(); -} - -void LLFloaterProperties::refresh() -{ - LLInventoryItem* item = findItem(); - if(item) - { - refreshFromItem(item); - } - else - { - //RN: it is possible that the container object is in the middle of an inventory refresh - // causing findItem() to fail, so just temporarily disable everything - - mDirty = TRUE; - - const char* enableNames[]={ - "LabelItemName", - "LabelItemDesc", - "LabelCreatorName", - "BtnCreator", - "LabelOwnerName", - "BtnOwner", - "CheckOwnerModify", - "CheckOwnerCopy", - "CheckOwnerTransfer", - "CheckShareWithGroup", - "CheckEveryoneCopy", - "CheckNextOwnerModify", - "CheckNextOwnerCopy", - "CheckNextOwnerTransfer", - "CheckPurchase", - "ComboBoxSaleType", - "Edit Cost" - }; - for(size_t t=0; tsetEnabled(false); - } - const char* hideNames[]={ - "BaseMaskDebug", - "OwnerMaskDebug", - "GroupMaskDebug", - "EveryoneMaskDebug", - "NextMaskDebug" - }; - for(size_t t=0; tsetVisible(false); - } - } -} - -void LLFloaterProperties::draw() -{ - if (mDirty) - { - // RN: clear dirty first because refresh can set dirty to TRUE - mDirty = FALSE; - refresh(); - } - - LLFloater::draw(); -} - -void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) -{ - //////////////////////// - // PERMISSIONS LOOKUP // - //////////////////////// - - // do not enable the UI for incomplete items. - LLViewerInventoryItem* i = (LLViewerInventoryItem*)item; - BOOL is_complete = i->isFinished(); - const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(i->getInventoryType()); - const BOOL is_calling_card = (i->getInventoryType() == LLInventoryType::IT_CALLINGCARD); - const LLPermissions& perm = item->getPermissions(); - const BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm, - GP_OBJECT_MANIPULATE); - const BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm, - GP_OBJECT_SET_SALE) && - !cannot_restrict_permissions; - const BOOL is_link = i->getIsLinkType(); - - // You need permission to modify the object to modify an inventory - // item in it. - LLViewerObject* object = NULL; - if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID); - BOOL is_obj_modify = TRUE; - if(object) - { - is_obj_modify = object->permOwnerModify(); - } - - ////////////////////// - // ITEM NAME & DESC // - ////////////////////// - BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm, - GP_OBJECT_MANIPULATE) - && is_obj_modify && is_complete; - - getChildView("LabelItemNameTitle")->setEnabled(TRUE); - getChildView("LabelItemName")->setEnabled(is_modifiable && !is_calling_card); // for now, don't allow rename of calling cards - getChild("LabelItemName")->setValue(item->getName()); - getChildView("LabelItemDescTitle")->setEnabled(TRUE); - getChildView("LabelItemDesc")->setEnabled(is_modifiable); - getChildView("IconLocked")->setVisible(!is_modifiable); - getChild("LabelItemDesc")->setValue(item->getDescription()); - - ////////////////// - // CREATOR NAME // - ////////////////// - if(!gCacheName) return; - if(!gAgent.getRegion()) return; - - if (item->getCreatorUUID().notNull()) - { - LLAvatarName av_name; - LLAvatarNameCache::get(item->getCreatorUUID(), &av_name); - getChildView("BtnCreator")->setEnabled(TRUE); - getChildView("LabelCreatorTitle")->setEnabled(TRUE); - getChildView("LabelCreatorName")->setEnabled(TRUE); - getChild("LabelCreatorName")->setValue(av_name.getUserName()); - } - else - { - getChildView("BtnCreator")->setEnabled(FALSE); - getChildView("LabelCreatorTitle")->setEnabled(FALSE); - getChildView("LabelCreatorName")->setEnabled(FALSE); - getChild("LabelCreatorName")->setValue(getString("unknown")); - } - - //////////////// - // OWNER NAME // - //////////////// - if(perm.isOwned()) - { - std::string name; - if (perm.isGroupOwned()) - { - gCacheName->getGroupName(perm.getGroup(), name); - } - else - { - LLAvatarName av_name; - LLAvatarNameCache::get(perm.getOwner(), &av_name); - name = av_name.getUserName(); - } - getChildView("BtnOwner")->setEnabled(TRUE); - getChildView("LabelOwnerTitle")->setEnabled(TRUE); - getChildView("LabelOwnerName")->setEnabled(TRUE); - getChild("LabelOwnerName")->setValue(name); - } - else - { - getChildView("BtnOwner")->setEnabled(FALSE); - getChildView("LabelOwnerTitle")->setEnabled(FALSE); - getChildView("LabelOwnerName")->setEnabled(FALSE); - getChild("LabelOwnerName")->setValue(getString("public")); - } - - ////////////////// - // ACQUIRE DATE // - ////////////////// - - time_t time_utc = item->getCreationDate(); - if (0 == time_utc) - { - getChild("LabelAcquiredDate")->setValue(getString("unknown")); - } - else - { - std::string timeStr = getString("acquiredDate"); - LLSD substitution; - substitution["datetime"] = (S32) time_utc; - LLStringUtil::format (timeStr, substitution); - getChild("LabelAcquiredDate")->setValue(timeStr); - } - - /////////////////////// - // OWNER PERMISSIONS // - /////////////////////// - if(can_agent_manipulate) - { - getChild("OwnerLabel")->setValue(getString("you_can")); - } - else - { - getChild("OwnerLabel")->setValue(getString("owner_can")); - } - - U32 base_mask = perm.getMaskBase(); - U32 owner_mask = perm.getMaskOwner(); - U32 group_mask = perm.getMaskGroup(); - U32 everyone_mask = perm.getMaskEveryone(); - U32 next_owner_mask = perm.getMaskNextOwner(); - - getChildView("OwnerLabel")->setEnabled(TRUE); - getChildView("CheckOwnerModify")->setEnabled(FALSE); - getChild("CheckOwnerModify")->setValue(LLSD((BOOL)(owner_mask & PERM_MODIFY))); - getChildView("CheckOwnerCopy")->setEnabled(FALSE); - getChild("CheckOwnerCopy")->setValue(LLSD((BOOL)(owner_mask & PERM_COPY))); - getChildView("CheckOwnerTransfer")->setEnabled(FALSE); - getChild("CheckOwnerTransfer")->setValue(LLSD((BOOL)(owner_mask & PERM_TRANSFER))); - - /////////////////////// - // DEBUG PERMISSIONS // - /////////////////////// - - if( gSavedSettings.getBOOL("DebugPermissions") ) - { - BOOL slam_perm = FALSE; - BOOL overwrite_group = FALSE; - BOOL overwrite_everyone = FALSE; - - if (item->getType() == LLAssetType::AT_OBJECT) - { - U32 flags = item->getFlags(); - slam_perm = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM; - overwrite_everyone = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE; - overwrite_group = flags & LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP; - } - - std::string perm_string; - - perm_string = "B: "; - perm_string += mask_to_string(base_mask); - getChild("BaseMaskDebug")->setValue(perm_string); - getChildView("BaseMaskDebug")->setVisible(TRUE); - - perm_string = "O: "; - perm_string += mask_to_string(owner_mask); - getChild("OwnerMaskDebug")->setValue(perm_string); - getChildView("OwnerMaskDebug")->setVisible(TRUE); - - perm_string = "G"; - perm_string += overwrite_group ? "*: " : ": "; - perm_string += mask_to_string(group_mask); - getChild("GroupMaskDebug")->setValue(perm_string); - getChildView("GroupMaskDebug")->setVisible(TRUE); - - perm_string = "E"; - perm_string += overwrite_everyone ? "*: " : ": "; - perm_string += mask_to_string(everyone_mask); - getChild("EveryoneMaskDebug")->setValue(perm_string); - getChildView("EveryoneMaskDebug")->setVisible(TRUE); - - perm_string = "N"; - perm_string += slam_perm ? "*: " : ": "; - perm_string += mask_to_string(next_owner_mask); - getChild("NextMaskDebug")->setValue(perm_string); - getChildView("NextMaskDebug")->setVisible(TRUE); - } - else - { - getChildView("BaseMaskDebug")->setVisible(FALSE); - getChildView("OwnerMaskDebug")->setVisible(FALSE); - getChildView("GroupMaskDebug")->setVisible(FALSE); - getChildView("EveryoneMaskDebug")->setVisible(FALSE); - getChildView("NextMaskDebug")->setVisible(FALSE); - } - - ///////////// - // SHARING // - ///////////// - - // Check for ability to change values. - if (is_link || cannot_restrict_permissions) - { - getChildView("CheckShareWithGroup")->setEnabled(FALSE); - getChildView("CheckEveryoneCopy")->setEnabled(FALSE); - } - else if (is_obj_modify && can_agent_manipulate) - { - getChildView("CheckShareWithGroup")->setEnabled(TRUE); - getChildView("CheckEveryoneCopy")->setEnabled((owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER)); - } - else - { - getChildView("CheckShareWithGroup")->setEnabled(FALSE); - getChildView("CheckEveryoneCopy")->setEnabled(FALSE); - } - - // Set values. - BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE; - BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE; - BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE; - - if (is_group_copy && is_group_modify && is_group_move) - { - getChild("CheckShareWithGroup")->setValue(LLSD((BOOL)TRUE)); - - LLCheckBoxCtrl* ctl = getChild("CheckShareWithGroup"); - if(ctl) - { - ctl->setTentative(FALSE); - } - } - else if (!is_group_copy && !is_group_modify && !is_group_move) - { - getChild("CheckShareWithGroup")->setValue(LLSD((BOOL)FALSE)); - LLCheckBoxCtrl* ctl = getChild("CheckShareWithGroup"); - if(ctl) - { - ctl->setTentative(FALSE); - } - } - else - { - LLCheckBoxCtrl* ctl = getChild("CheckShareWithGroup"); - if(ctl) - { - ctl->setTentative(TRUE); - ctl->set(TRUE); - } - } - - getChild("CheckEveryoneCopy")->setValue(LLSD((BOOL)(everyone_mask & PERM_COPY))); - - /////////////// - // SALE INFO // - /////////////// - - const LLSaleInfo& sale_info = item->getSaleInfo(); - BOOL is_for_sale = sale_info.isForSale(); - LLComboBox* combo_sale_type = getChild("ComboBoxSaleType"); - LLUICtrl* edit_cost = getChild("Edit Cost"); - - // Check for ability to change values. - if (is_obj_modify && can_agent_sell - && gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE)) - { - getChildView("CheckPurchase")->setEnabled(is_complete); - - getChildView("NextOwnerLabel")->setEnabled(TRUE); - getChildView("CheckNextOwnerModify")->setEnabled((base_mask & PERM_MODIFY) && !cannot_restrict_permissions); - getChildView("CheckNextOwnerCopy")->setEnabled((base_mask & PERM_COPY) && !cannot_restrict_permissions); - getChildView("CheckNextOwnerTransfer")->setEnabled((next_owner_mask & PERM_COPY) && !cannot_restrict_permissions); - - combo_sale_type->setEnabled(is_complete && is_for_sale); - edit_cost->setEnabled(is_complete && is_for_sale); - } - else - { - getChildView("CheckPurchase")->setEnabled(FALSE); - - getChildView("NextOwnerLabel")->setEnabled(FALSE); - getChildView("CheckNextOwnerModify")->setEnabled(FALSE); - getChildView("CheckNextOwnerCopy")->setEnabled(FALSE); - getChildView("CheckNextOwnerTransfer")->setEnabled(FALSE); - - combo_sale_type->setEnabled(FALSE); - edit_cost->setEnabled(FALSE); - } - - // Set values. - getChild("CheckPurchase")->setValue(is_for_sale); - getChild("CheckNextOwnerModify")->setValue(LLSD(BOOL(next_owner_mask & PERM_MODIFY))); - getChild("CheckNextOwnerCopy")->setValue(LLSD(BOOL(next_owner_mask & PERM_COPY))); - getChild("CheckNextOwnerTransfer")->setValue(LLSD(BOOL(next_owner_mask & PERM_TRANSFER))); - - if (is_for_sale) - { - S32 numerical_price; - numerical_price = sale_info.getSalePrice(); - edit_cost->setValue(llformat("%d",numerical_price)); - combo_sale_type->setValue(sale_info.getSaleType()); - } - else - { - edit_cost->setValue(llformat("%d",0)); - combo_sale_type->setValue(LLSaleInfo::FS_COPY); - } -} - -void LLFloaterProperties::onClickCreator() -{ - LLInventoryItem* item = findItem(); - if(!item) return; - if(!item->getCreatorUUID().isNull()) - { - LLAvatarActions::showProfile(item->getCreatorUUID()); - } -} - -// static -void LLFloaterProperties::onClickOwner() -{ - LLInventoryItem* item = findItem(); - if(!item) return; - if(item->getPermissions().isGroupOwned()) - { - LLGroupActions::show(item->getPermissions().getGroup()); - } - else - { - LLAvatarActions::showProfile(item->getPermissions().getOwner()); - } -} - -// static -void LLFloaterProperties::onCommitName() -{ - //LL_INFOS() << "LLFloaterProperties::onCommitName()" << LL_ENDL; - LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); - if(!item) - { - return; - } - LLLineEditor* labelItemName = getChild("LabelItemName"); - - if(labelItemName&& - (item->getName() != labelItemName->getText()) && - (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) ) - { - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->rename(labelItemName->getText()); - if(mObjectID.isNull()) - { - new_item->updateServer(FALSE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } - else - { - LLViewerObject* object = gObjectList.findObject(mObjectID); - if(object) - { - object->updateInventory( - new_item, - TASK_INVENTORY_ITEM_KEY, - false); - } - } - } -} - -void LLFloaterProperties::onCommitDescription() -{ - //LL_INFOS() << "LLFloaterProperties::onCommitDescription()" << LL_ENDL; - LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); - if(!item) return; - - LLLineEditor* labelItemDesc = getChild("LabelItemDesc"); - if(!labelItemDesc) - { - return; - } - if((item->getDescription() != labelItemDesc->getText()) && - (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE))) - { - LLPointer new_item = new LLViewerInventoryItem(item); - - new_item->setDescription(labelItemDesc->getText()); - if(mObjectID.isNull()) - { - new_item->updateServer(FALSE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } - else - { - LLViewerObject* object = gObjectList.findObject(mObjectID); - if(object) - { - object->updateInventory( - new_item, - TASK_INVENTORY_ITEM_KEY, - false); - } - } - } -} - -// static -void LLFloaterProperties::onCommitPermissions() -{ - //LL_INFOS() << "LLFloaterProperties::onCommitPermissions()" << LL_ENDL; - LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); - if(!item) return; - LLPermissions perm(item->getPermissions()); - - - LLCheckBoxCtrl* CheckShareWithGroup = getChild("CheckShareWithGroup"); - - if(CheckShareWithGroup) - { - perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(), - CheckShareWithGroup->get(), - PERM_MODIFY | PERM_MOVE | PERM_COPY); - } - LLCheckBoxCtrl* CheckEveryoneCopy = getChild("CheckEveryoneCopy"); - if(CheckEveryoneCopy) - { - perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(), - CheckEveryoneCopy->get(), PERM_COPY); - } - - LLCheckBoxCtrl* CheckNextOwnerModify = getChild("CheckNextOwnerModify"); - if(CheckNextOwnerModify) - { - perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(), - CheckNextOwnerModify->get(), PERM_MODIFY); - } - LLCheckBoxCtrl* CheckNextOwnerCopy = getChild("CheckNextOwnerCopy"); - if(CheckNextOwnerCopy) - { - perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(), - CheckNextOwnerCopy->get(), PERM_COPY); - } - LLCheckBoxCtrl* CheckNextOwnerTransfer = getChild("CheckNextOwnerTransfer"); - if(CheckNextOwnerTransfer) - { - perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(), - CheckNextOwnerTransfer->get(), PERM_TRANSFER); - } - if(perm != item->getPermissions() - && item->isFinished()) - { - LLPointer new_item = new LLViewerInventoryItem(item); - new_item->setPermissions(perm); - U32 flags = new_item->getFlags(); - // If next owner permissions have changed (and this is an object) - // then set the slam permissions flag so that they are applied on rez. - if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner()) - && (item->getType() == LLAssetType::AT_OBJECT)) - { - flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_PERM; - } - // If everyone permissions have changed (and this is an object) - // then set the overwrite everyone permissions flag so they - // are applied on rez. - if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone()) - && (item->getType() == LLAssetType::AT_OBJECT)) - { - flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE; - } - // If group permissions have changed (and this is an object) - // then set the overwrite group permissions flag so they - // are applied on rez. - if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup()) - && (item->getType() == LLAssetType::AT_OBJECT)) - { - flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP; - } - new_item->setFlags(flags); - if(mObjectID.isNull()) - { - new_item->updateServer(FALSE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } - else - { - LLViewerObject* object = gObjectList.findObject(mObjectID); - if(object) - { - object->updateInventory( - new_item, - TASK_INVENTORY_ITEM_KEY, - false); - } - } - } - else - { - // need to make sure we don't just follow the click - refresh(); - } -} - -// static -void LLFloaterProperties::onCommitSaleInfo() -{ - //LL_INFOS() << "LLFloaterProperties::onCommitSaleInfo()" << LL_ENDL; - updateSaleInfo(); -} - -// static -void LLFloaterProperties::onCommitSaleType() -{ - //LL_INFOS() << "LLFloaterProperties::onCommitSaleType()" << LL_ENDL; - updateSaleInfo(); -} - -void LLFloaterProperties::updateSaleInfo() -{ - LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem(); - if(!item) return; - LLSaleInfo sale_info(item->getSaleInfo()); - if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE)) - { - getChild("CheckPurchase")->setValue(LLSD((BOOL)FALSE)); - } - - if((BOOL)getChild("CheckPurchase")->getValue()) - { - // turn on sale info - LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY; - - LLComboBox* combo_sale_type = getChild("ComboBoxSaleType"); - if (combo_sale_type) - { - sale_type = static_cast(combo_sale_type->getValue().asInteger()); - } - - if (sale_type == LLSaleInfo::FS_COPY - && !gAgent.allowOperation(PERM_COPY, item->getPermissions(), - GP_OBJECT_SET_SALE)) - { - sale_type = LLSaleInfo::FS_ORIGINAL; - } - - - - S32 price = -1; - price = getChild("Edit Cost")->getValue().asInteger();; - - // Invalid data - turn off the sale - if (price < 0) - { - sale_type = LLSaleInfo::FS_NOT; - price = 0; - } - - sale_info.setSaleType(sale_type); - sale_info.setSalePrice(price); - } - else - { - sale_info.setSaleType(LLSaleInfo::FS_NOT); - } - if(sale_info != item->getSaleInfo() - && item->isFinished()) - { - LLPointer new_item = new LLViewerInventoryItem(item); - - // Force an update on the sale price at rez - if (item->getType() == LLAssetType::AT_OBJECT) - { - U32 flags = new_item->getFlags(); - flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_SLAM_SALE; - new_item->setFlags(flags); - } - - new_item->setSaleInfo(sale_info); - if(mObjectID.isNull()) - { - // This is in the agent's inventory. - new_item->updateServer(FALSE); - gInventory.updateItem(new_item); - gInventory.notifyObservers(); - } - else - { - // This is in an object's contents. - LLViewerObject* object = gObjectList.findObject(mObjectID); - if(object) - { - object->updateInventory( - new_item, - TASK_INVENTORY_ITEM_KEY, - false); - } - } - } - else - { - // need to make sure we don't just follow the click - refresh(); - } -} - -LLInventoryItem* LLFloaterProperties::findItem() const -{ - LLInventoryItem* item = NULL; - if(mObjectID.isNull()) - { - // it is in agent inventory - item = gInventory.getItem(mItemID); - } - else - { - LLViewerObject* object = gObjectList.findObject(mObjectID); - if(object) - { - item = (LLInventoryItem*)object->getInventoryObject(mItemID); - } - } - return item; -} - -//static -void LLFloaterProperties::dirtyAll() -{ - LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("properties"); - for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); - iter != inst_list.end(); ++iter) - { - LLFloaterProperties* floater = dynamic_cast(*iter); - llassert(floater); // else cast failed - wrong type D: - if (floater) - { - floater->dirty(); - } - } -} - -///---------------------------------------------------------------------------- -/// LLMultiProperties -///---------------------------------------------------------------------------- - -LLMultiProperties::LLMultiProperties() - : LLMultiFloater(LLSD()) -{ - // start with a small rect in the top-left corner ; will get resized - LLRect rect; - rect.setLeftTopAndSize(0, gViewerWindow->getWindowHeightScaled(), 20, 20); - setRect(rect); - LLFloater* last_floater = LLFloaterReg::getLastFloaterInGroup("properties"); - if (last_floater) - { - stackWith(*last_floater); - } - setTitle(LLTrans::getString("MultiPropertiesTitle")); - buildTabContainer(); -} - -///---------------------------------------------------------------------------- -/// Local function definitions -///---------------------------------------------------------------------------- diff --git a/indra/newview/llfloaterproperties.h b/indra/newview/llfloaterproperties.h deleted file mode 100644 index aa3fcec337..0000000000 --- a/indra/newview/llfloaterproperties.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @file llfloaterproperties.h - * @brief A floater which shows an inventory item's properties. - * - * $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$ - */ - -#ifndef LL_LLFLOATERPROPERTIES_H -#define LL_LLFLOATERPROPERTIES_H - -#include -#include "llmultifloater.h" -#include "lliconctrl.h" - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLFloaterProperties -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LLButton; -class LLCheckBoxCtrl; -class LLInventoryItem; -class LLLineEditor; -class LLRadioGroup; -class LLTextBox; - -class LLPropertiesObserver; - -class LLFloaterProperties : public LLFloater -{ -public: - LLFloaterProperties(const LLUUID& item_id); - /*virtual*/ ~LLFloaterProperties(); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& key); - void setObjectID(const LLUUID& object_id) { mObjectID = object_id; } - - void dirty() { mDirty = TRUE; } - void refresh(); - - static void dirtyAll(); - -protected: - // ui callbacks - void onClickCreator(); - void onClickOwner(); - void onCommitName(); - void onCommitDescription(); - void onCommitPermissions(); - void onCommitSaleInfo(); - void onCommitSaleType(); - void updateSaleInfo(); - - LLInventoryItem* findItem() const; - - void refreshFromItem(LLInventoryItem* item); - virtual void draw(); - -protected: - // The item id of the inventory item in question. - LLUUID mItemID; - - // mObjectID will have a value if it is associated with a task in - // the world, and will be == LLUUID::null if it's in the agent - // inventory. - LLUUID mObjectID; - - BOOL mDirty; - - LLPropertiesObserver* mPropertiesObserver; -}; - -class LLMultiProperties : public LLMultiFloater -{ -public: - LLMultiProperties(); -}; - -#endif // LL_LLFLOATERPROPERTIES_H diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 2635ac3a71..15ac46dc21 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -317,7 +317,6 @@ void LLFloaterRegionInfo::onOpen(const LLSD& key) } refreshFromRegion(gAgent.getRegion()); requestRegionInfo(); - requestMeshRezInfo(); if (!mGodLevelChangeSlot.connected()) { @@ -1026,19 +1025,6 @@ bool LLPanelRegionGeneralInfo::onMessageCommit(const LLSD& notification, const L return false; } -void LLFloaterRegionInfo::requestMeshRezInfo() -{ - std::string sim_console_url = gAgent.getRegionCapability("SimConsoleAsync"); - - if (!sim_console_url.empty()) - { - std::string request_str = "get mesh_rez_enabled"; - - LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(sim_console_url, LLSD(request_str), - "Requested mesh_rez_enabled", "Error requesting mesh_rez_enabled"); - } -} - // setregioninfo // strings[0] = 'Y' - block terraform, 'N' - not // strings[1] = 'Y' - block fly, 'N' - not @@ -2943,7 +2929,7 @@ BOOL LLPanelEstateAccess::postBuild() if (banned_name_list) { banned_name_list->setCommitOnSelectionChange(TRUE); - banned_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS); + banned_name_list->setMaxItemCount(ESTATE_MAX_BANNED_IDS); } getChild("banned_search_input")->setCommitCallback(boost::bind(&LLPanelEstateAccess::onBannedSearchEdit, this, _2)); @@ -3087,10 +3073,10 @@ void LLPanelEstateAccess::onClickAddBannedAgent() { LLCtrlListInterface *list = childGetListInterface("banned_avatar_name_list"); if (!list) return; - if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS) + if (list->getItemCount() >= ESTATE_MAX_BANNED_IDS) { LLSD args; - args["MAX_BANNED"] = llformat("%d", ESTATE_MAX_ACCESS_IDS); + args["MAX_BANNED"] = llformat("%d", ESTATE_MAX_BANNED_IDS); LLNotificationsUtil::add("MaxBannedAgentsOnRegion", args); return; } @@ -3328,13 +3314,13 @@ void LLPanelEstateAccess::accessAddCore3(const uuid_vec_t& ids, std::vectorgetChild("banned_avatar_name_list"); LLNameListCtrl* em_list = panel->getChild("estate_manager_name_list"); int currentCount = (name_list ? name_list->getItemCount() : 0); - if (ids.size() + currentCount > ESTATE_MAX_ACCESS_IDS) + if (ids.size() + currentCount > ESTATE_MAX_BANNED_IDS) { LLSD args; args["NUM_ADDED"] = llformat("%d", ids.size()); - args["MAX_AGENTS"] = llformat("%d", ESTATE_MAX_ACCESS_IDS); + args["MAX_AGENTS"] = llformat("%d", ESTATE_MAX_BANNED_IDS); args["LIST_TYPE"] = LLTrans::getString("RegionInfoListTypeBannedAgents"); - args["NUM_EXCESS"] = llformat("%d", (ids.size() + currentCount) - ESTATE_MAX_ACCESS_IDS); + args["NUM_EXCESS"] = llformat("%d", (ids.size() + currentCount) - ESTATE_MAX_BANNED_IDS); LLNotificationsUtil::add("MaxAgentOnRegionBatch", args); delete change_info; return; @@ -3713,7 +3699,7 @@ void LLPanelEstateAccess::requestEstateGetAccessCoro(std::string url) { LLStringUtil::format_map_t args; args["[BANNEDAGENTS]"] = llformat("%d", result["BannedAgents"].size()); - args["[MAXBANNED]"] = llformat("%d", ESTATE_MAX_ACCESS_IDS); + args["[MAXBANNED]"] = llformat("%d", ESTATE_MAX_BANNED_IDS); std::string msg = LLTrans::getString("RegionInfoBannedResidents", args); panel->getChild("ban_resident_label")->setValue(LLSD(msg)); diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h index bfdeb9440d..91fd54fcf9 100644 --- a/indra/newview/llfloaterregioninfo.h +++ b/indra/newview/llfloaterregioninfo.h @@ -102,7 +102,6 @@ public: void onRegionChanged(); void requestRegionInfo(); - void requestMeshRezInfo(); void enableTopButtons(); void disableTopButtons(); diff --git a/indra/newview/llfloatersearch.cpp b/indra/newview/llfloatersearch.cpp index 7e6af45515..37ae80fa8f 100644 --- a/indra/newview/llfloatersearch.cpp +++ b/indra/newview/llfloatersearch.cpp @@ -46,7 +46,7 @@ class LLSearchHandler : public LLCommandHandler public: // requires trusted browser to trigger LLSearchHandler() : LLCommandHandler("search", UNTRUSTED_CLICK_ONLY) { } - bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableSearch")) { diff --git a/indra/newview/llfloatersidepanelcontainer.cpp b/indra/newview/llfloatersidepanelcontainer.cpp index dd7ce40e97..a875d33679 100644 --- a/indra/newview/llfloatersidepanelcontainer.cpp +++ b/indra/newview/llfloatersidepanelcontainer.cpp @@ -90,6 +90,29 @@ void LLFloaterSidePanelContainer::closeFloater(bool app_quitting) } } +LLFloater* LLFloaterSidePanelContainer::getTopmostInventoryFloater() +{ + LLFloater* topmost_floater = NULL; + S32 z_min = S32_MAX; + + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloater* inventory_floater = (*iter); + + if (inventory_floater && inventory_floater->getVisible()) + { + S32 z_order = gFloaterView->getZOrder(inventory_floater); + if (z_order < z_min) + { + z_min = z_order; + topmost_floater = inventory_floater; + } + } + } + return topmost_floater; +} + LLPanel* LLFloaterSidePanelContainer::openChildPanel(const std::string& panel_name, const LLSD& params) { LLView* view = findChildView(panel_name, true); diff --git a/indra/newview/llfloatersidepanelcontainer.h b/indra/newview/llfloatersidepanelcontainer.h index 20baf28184..5e7e755d1f 100644 --- a/indra/newview/llfloatersidepanelcontainer.h +++ b/indra/newview/llfloatersidepanelcontainer.h @@ -57,6 +57,8 @@ public: LLPanel* openChildPanel(const std::string& panel_name, const LLSD& params); + static LLFloater* getTopmostInventoryFloater(); + static void showPanel(const std::string& floater_name, const LLSD& key); static void showPanel(const std::string& floater_name, const std::string& panel_name, const LLSD& key); diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.cpp b/indra/newview/llfloatersimpleoutfitsnapshot.cpp deleted file mode 100644 index bab2efbbd5..0000000000 --- a/indra/newview/llfloatersimpleoutfitsnapshot.cpp +++ /dev/null @@ -1,333 +0,0 @@ -/** -* @file llfloatersimpleoutfitsnapshot.cpp -* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery -* -* $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 "llfloatersimpleoutfitsnapshot.h" - -#include "llfloaterreg.h" -#include "llimagefiltersmanager.h" -#include "llstatusbar.h" // can_afford_transaction() -#include "llnotificationsutil.h" -#include "llagentbenefits.h" -#include "llviewercontrol.h" - -LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView = NULL; - -const S32 OUTFIT_SNAPSHOT_WIDTH = 256; -const S32 OUTFIT_SNAPSHOT_HEIGHT = 256; - -static LLDefaultChildRegistry::Register r("simple_snapshot_outfit_floater_view"); - -///---------------------------------------------------------------------------- -/// Class LLFloaterSimpleOutfitSnapshot::Impl -///---------------------------------------------------------------------------- - -LLSnapshotModel::ESnapshotFormat LLFloaterSimpleOutfitSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater) -{ - return LLSnapshotModel::SNAPSHOT_FORMAT_PNG; -} - -LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleOutfitSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater) -{ - return LLSnapshotModel::SNAPSHOT_TYPE_COLOR; -} - -void LLFloaterSimpleOutfitSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater) -{ - LLSnapshotLivePreview* previewp = getPreviewView(); - updateResolution(floater); - if (previewp) - { - previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE); - previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG); - previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR); - } -} - -std::string LLFloaterSimpleOutfitSnapshot::Impl::getSnapshotPanelPrefix() -{ - return "panel_outfit_snapshot_"; -} - -void LLFloaterSimpleOutfitSnapshot::Impl::updateResolution(void* data) -{ - LLFloaterSimpleOutfitSnapshot *view = (LLFloaterSimpleOutfitSnapshot *)data; - - if (!view) - { - llassert(view); - return; - } - - S32 width = OUTFIT_SNAPSHOT_WIDTH; - S32 height = OUTFIT_SNAPSHOT_HEIGHT; - - LLSnapshotLivePreview* previewp = getPreviewView(); - if (previewp) - { - S32 original_width = 0, original_height = 0; - previewp->getSize(original_width, original_height); - - if (gSavedSettings.getBOOL("RenderHUDInSnapshot")) - { //clamp snapshot resolution to window size when showing UI HUD in snapshot - width = llmin(width, gViewerWindow->getWindowWidthRaw()); - height = llmin(height, gViewerWindow->getWindowHeightRaw()); - } - - llassert(width > 0 && height > 0); - - previewp->setSize(width, height); - - if (original_width != width || original_height != height) - { - // hide old preview as the aspect ratio could be wrong - checkAutoSnapshot(previewp, FALSE); - previewp->updateSnapshot(TRUE); - } - } -} - -void LLFloaterSimpleOutfitSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg) -{ - switch (status) - { - case STATUS_READY: - mFloater->setCtrlsEnabled(true); - break; - case STATUS_WORKING: - mFloater->setCtrlsEnabled(false); - break; - case STATUS_FINISHED: - mFloater->setCtrlsEnabled(true); - break; - } - - mStatus = status; -} - -///----------------------------------------------------------------re------------ -/// Class LLFloaterSimpleOutfitSnapshot -///---------------------------------------------------------------------------- - -LLFloaterSimpleOutfitSnapshot::LLFloaterSimpleOutfitSnapshot(const LLSD& key) - : LLFloaterSnapshotBase(key), - mOutfitGallery(NULL) -{ - impl = new Impl(this); -} - -LLFloaterSimpleOutfitSnapshot::~LLFloaterSimpleOutfitSnapshot() -{ -} - -BOOL LLFloaterSimpleOutfitSnapshot::postBuild() -{ - getChild("save_btn")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost())); - - childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this); - childSetAction("save_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onSend, this)); - childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleOutfitSnapshot::onCancel, this)); - - mThumbnailPlaceholder = getChild("thumbnail_placeholder"); - - // create preview window - LLRect full_screen_rect = getRootView()->getRect(); - LLSnapshotLivePreview::Params p; - p.rect(full_screen_rect); - LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); - LLView* parent_view = gSnapshotFloaterView->getParent(); - - parent_view->removeChild(gSnapshotFloaterView); - // make sure preview is below snapshot floater - parent_view->addChild(previewp); - parent_view->addChild(gSnapshotFloaterView); - - //move snapshot floater to special purpose snapshotfloaterview - gFloaterView->removeChild(this); - gSnapshotFloaterView->addChild(this); - - impl->mPreviewHandle = previewp->getHandle(); - previewp->setContainer(this); - impl->updateControls(this); - impl->setAdvanced(true); - impl->setSkipReshaping(true); - - previewp->mKeepAspectRatio = FALSE; - previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); - previewp->setAllowRenderUI(false); - - return TRUE; -} -const S32 PREVIEW_OFFSET_X = 12; -const S32 PREVIEW_OFFSET_Y = 70; - -void LLFloaterSimpleOutfitSnapshot::draw() -{ - LLSnapshotLivePreview* previewp = getPreviewView(); - - if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock())) - { - // don't render snapshot window in snapshot, even if "show ui" is turned on - return; - } - - LLFloater::draw(); - - if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible()) - { - if(previewp->getThumbnailImage()) - { - bool working = impl->getStatus() == ImplBase::STATUS_WORKING; - const LLRect& thumbnail_rect = getThumbnailPlaceholderRect(); - const S32 thumbnail_w = previewp->getThumbnailWidth(); - const S32 thumbnail_h = previewp->getThumbnailHeight(); - - S32 offset_x = PREVIEW_OFFSET_X; - S32 offset_y = PREVIEW_OFFSET_Y; - - gGL.matrixMode(LLRender::MM_MODELVIEW); - // Apply floater transparency to the texture unless the floater is focused. - F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); - LLColor4 color = working ? LLColor4::grey4 : LLColor4::white; - gl_draw_scaled_image(offset_x, offset_y, - thumbnail_w, thumbnail_h, - previewp->getThumbnailImage(), color % alpha); -#if LL_DARWIN - std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "OutfitSnapshotMacMask" : "OutfitSnapshotMacMask2"; -#else - std::string alpha_color = getTransparencyType() == TT_ACTIVE ? "FloaterFocusBackgroundColor" : "DkGray"; -#endif - - previewp->drawPreviewRect(offset_x, offset_y, LLUIColorTable::instance().getColor(alpha_color)); - - gGL.pushUIMatrix(); - LLUI::translate((F32) thumbnail_rect.mLeft, (F32) thumbnail_rect.mBottom); - mThumbnailPlaceholder->draw(); - gGL.popUIMatrix(); - } - } - impl->updateLayout(this); -} - -void LLFloaterSimpleOutfitSnapshot::onOpen(const LLSD& key) -{ - LLSnapshotLivePreview* preview = getPreviewView(); - if (preview) - { - preview->updateSnapshot(TRUE); - } - focusFirstItem(FALSE); - gSnapshotFloaterView->setEnabled(TRUE); - gSnapshotFloaterView->setVisible(TRUE); - gSnapshotFloaterView->adjustToFitScreen(this, FALSE); - - impl->updateControls(this); - impl->setStatus(ImplBase::STATUS_READY); -} - -void LLFloaterSimpleOutfitSnapshot::onCancel() -{ - closeFloater(); -} - -void LLFloaterSimpleOutfitSnapshot::onSend() -{ - S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); - if (can_afford_transaction(expected_upload_cost)) - { - saveTexture(); - postSave(); - } - else - { - LLSD args; - args["COST"] = llformat("%d", expected_upload_cost); - LLNotificationsUtil::add("ErrorPhotoCannotAfford", args); - inventorySaveFailed(); - } -} - -void LLFloaterSimpleOutfitSnapshot::postSave() -{ - impl->setStatus(ImplBase::STATUS_WORKING); -} - -// static -void LLFloaterSimpleOutfitSnapshot::update() -{ - LLFloaterSimpleOutfitSnapshot* inst = findInstance(); - if (inst != NULL) - { - inst->impl->updateLivePreview(); - } -} - - -// static -LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::findInstance() -{ - return LLFloaterReg::findTypedInstance("simple_outfit_snapshot"); -} - -// static -LLFloaterSimpleOutfitSnapshot* LLFloaterSimpleOutfitSnapshot::getInstance() -{ - return LLFloaterReg::getTypedInstance("simple_outfit_snapshot"); -} - -void LLFloaterSimpleOutfitSnapshot::saveTexture() -{ - LLSnapshotLivePreview* previewp = getPreviewView(); - if (!previewp) - { - llassert(previewp != NULL); - return; - } - - if (mOutfitGallery) - { - mOutfitGallery->onBeforeOutfitSnapshotSave(); - } - previewp->saveTexture(TRUE, getOutfitID().asString()); - if (mOutfitGallery) - { - mOutfitGallery->onAfterOutfitSnapshotSave(); - } - closeFloater(); -} - -///---------------------------------------------------------------------------- -/// Class LLSimpleOutfitSnapshotFloaterView -///---------------------------------------------------------------------------- - -LLSimpleOutfitSnapshotFloaterView::LLSimpleOutfitSnapshotFloaterView(const Params& p) : LLFloaterView(p) -{ -} - -LLSimpleOutfitSnapshotFloaterView::~LLSimpleOutfitSnapshotFloaterView() -{ -} diff --git a/indra/newview/llfloatersimplesnapshot.cpp b/indra/newview/llfloatersimplesnapshot.cpp new file mode 100644 index 0000000000..58604c5628 --- /dev/null +++ b/indra/newview/llfloatersimplesnapshot.cpp @@ -0,0 +1,499 @@ +/** +* @file llfloatersimplesnapshot.cpp +* @brief Snapshot preview window for saving as a thumbnail +* +* $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 "llfloatersimplesnapshot.h" + +#include "llfloaterreg.h" +#include "llimagefiltersmanager.h" +#include "llinventorymodel.h" +#include "llinventoryobserver.h" +#include "llstatusbar.h" // can_afford_transaction() +#include "llnotificationsutil.h" +#include "llagent.h" +#include "llagentbenefits.h" +#include "llviewercontrol.h" +#include "llviewertexturelist.h" + + + +LLSimpleSnapshotFloaterView* gSimpleSnapshotFloaterView = NULL; + +const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MAX = 256; +const S32 LLFloaterSimpleSnapshot::THUMBNAIL_SNAPSHOT_DIM_MIN = 64; + +// Thumbnail posting coro + +static const std::string THUMBNAIL_UPLOAD_CAP = "InventoryThumbnailUpload"; + +void post_thumbnail_image_coro(std::string cap_url, std::string path_to_image, LLSD first_data) +{ + 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; + } + if (!result.has("uploader")) + { + // todo: notification? + LL_WARNS("AvatarProperties") << "Failed to get uploader cap, response contains no data." << LL_ENDL; + return; + } + std::string uploader_cap = result["uploader"].asString(); + if (uploader_cap.empty()) + { + LL_WARNS("AvatarProperties") << "Failed to get uploader cap, cap invalid." << LL_ENDL; + return; + } + + // 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; + } + 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_DEBUGS("Thumbnail") << result << LL_ENDL; + + if (!status) + { + LL_WARNS("Thumbnail") << "Failed to upload image " << status.toString() << LL_ENDL; + return; + } + + if (result["state"].asString() != "complete") + { + if (result.has("message")) + { + LL_WARNS("Thumbnail") << "Failed to upload image, state " << result["state"] << " message: " << result["message"] << LL_ENDL; + } + else + { + LL_WARNS("Thumbnail") << "Failed to upload image " << result << LL_ENDL; + } + return; + } + + if (first_data.has("category_id")) + { + LLUUID cat_id = first_data["category_id"].asUUID(); + LLViewerInventoryCategory* cat = gInventory.getCategory(cat_id); + if (cat) + { + cat->setThumbnailUUID(result["new_asset"].asUUID()); + } + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, cat_id); + } + if (first_data.has("item_id")) + { + LLUUID item_id = first_data["item_id"].asUUID(); + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (item) + { + item->setThumbnailUUID(result["new_asset"].asUUID()); + } + // Are we supposed to get BulkUpdateInventory? + gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id); + } +} + +///---------------------------------------------------------------------------- +/// Class LLFloaterSimpleSnapshot::Impl +///---------------------------------------------------------------------------- + +LLSnapshotModel::ESnapshotFormat LLFloaterSimpleSnapshot::Impl::getImageFormat(LLFloaterSnapshotBase* floater) +{ + return LLSnapshotModel::SNAPSHOT_FORMAT_PNG; +} + +LLSnapshotModel::ESnapshotLayerType LLFloaterSimpleSnapshot::Impl::getLayerType(LLFloaterSnapshotBase* floater) +{ + return LLSnapshotModel::SNAPSHOT_TYPE_COLOR; +} + +void LLFloaterSimpleSnapshot::Impl::updateControls(LLFloaterSnapshotBase* floater) +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + updateResolution(floater); + if (previewp) + { + previewp->setSnapshotType(LLSnapshotModel::ESnapshotType::SNAPSHOT_TEXTURE); + previewp->setSnapshotFormat(LLSnapshotModel::ESnapshotFormat::SNAPSHOT_FORMAT_PNG); + previewp->setSnapshotBufferType(LLSnapshotModel::ESnapshotLayerType::SNAPSHOT_TYPE_COLOR); + } +} + +std::string LLFloaterSimpleSnapshot::Impl::getSnapshotPanelPrefix() +{ + return "panel_outfit_snapshot_"; +} + +void LLFloaterSimpleSnapshot::Impl::updateResolution(void* data) +{ + LLFloaterSimpleSnapshot *view = (LLFloaterSimpleSnapshot *)data; + + if (!view) + { + llassert(view); + return; + } + + S32 width = THUMBNAIL_SNAPSHOT_DIM_MAX; + S32 height = THUMBNAIL_SNAPSHOT_DIM_MAX; + + LLSnapshotLivePreview* previewp = getPreviewView(); + if (previewp) + { + S32 original_width = 0, original_height = 0; + previewp->getSize(original_width, original_height); + + if (gSavedSettings.getBOOL("RenderHUDInSnapshot")) + { //clamp snapshot resolution to window size when showing UI HUD in snapshot + width = llmin(width, gViewerWindow->getWindowWidthRaw()); + height = llmin(height, gViewerWindow->getWindowHeightRaw()); + } + + llassert(width > 0 && height > 0); + + previewp->setSize(width, height); + + if (original_width != width || original_height != height) + { + // hide old preview as the aspect ratio could be wrong + checkAutoSnapshot(previewp, FALSE); + previewp->updateSnapshot(TRUE); + } + } +} + +void LLFloaterSimpleSnapshot::Impl::setStatus(EStatus status, bool ok, const std::string& msg) +{ + switch (status) + { + case STATUS_READY: + mFloater->setCtrlsEnabled(true); + break; + case STATUS_WORKING: + mFloater->setCtrlsEnabled(false); + break; + case STATUS_FINISHED: + mFloater->setCtrlsEnabled(true); + break; + } + + mStatus = status; +} + +///----------------------------------------------------------------re------------ +/// Class LLFloaterSimpleSnapshot +///---------------------------------------------------------------------------- + +LLFloaterSimpleSnapshot::LLFloaterSimpleSnapshot(const LLSD& key) + : LLFloaterSnapshotBase(key) + , mOwner(NULL) + , mContextConeOpacity(0.f) +{ + impl = new Impl(this); +} + +LLFloaterSimpleSnapshot::~LLFloaterSimpleSnapshot() +{ +} + +BOOL LLFloaterSimpleSnapshot::postBuild() +{ + childSetAction("new_snapshot_btn", ImplBase::onClickNewSnapshot, this); + childSetAction("save_btn", boost::bind(&LLFloaterSimpleSnapshot::onSend, this)); + childSetAction("cancel_btn", boost::bind(&LLFloaterSimpleSnapshot::onCancel, this)); + + mThumbnailPlaceholder = getChild("thumbnail_placeholder"); + + // create preview window + LLRect full_screen_rect = getRootView()->getRect(); + LLSnapshotLivePreview::Params p; + p.rect(full_screen_rect); + LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(p); + + // Do not move LLFloaterSimpleSnapshot floater into gSnapshotFloaterView + // since it can be a dependednt floater and does not draw UI + + impl->mPreviewHandle = previewp->getHandle(); + previewp->setContainer(this); + impl->updateControls(this); + impl->setAdvanced(true); + impl->setSkipReshaping(true); + + previewp->mKeepAspectRatio = FALSE; + previewp->setThumbnailPlaceholderRect(getThumbnailPlaceholderRect()); + previewp->setAllowRenderUI(false); + previewp->setThumbnailSubsampled(TRUE); + + return TRUE; +} + +const S32 PREVIEW_OFFSET_Y = 70; + +void LLFloaterSimpleSnapshot::draw() +{ + if (mOwner) + { + static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); + drawConeToOwner(mContextConeOpacity, max_opacity, mOwner); + } + + LLSnapshotLivePreview* previewp = getPreviewView(); + + if (previewp && (previewp->isSnapshotActive() || previewp->getThumbnailLock())) + { + // don't render snapshot window in snapshot, even if "show ui" is turned on + return; + } + + LLFloater::draw(); + + if (previewp && !isMinimized() && mThumbnailPlaceholder->getVisible()) + { + if(previewp->getThumbnailImage()) + { + bool working = impl->getStatus() == ImplBase::STATUS_WORKING; + const S32 thumbnail_w = previewp->getThumbnailWidth(); + const S32 thumbnail_h = previewp->getThumbnailHeight(); + + LLRect local_rect = getLocalRect(); + S32 offset_x = (local_rect.getWidth() - thumbnail_w) / 2; + S32 offset_y = PREVIEW_OFFSET_Y; + + gGL.matrixMode(LLRender::MM_MODELVIEW); + // Apply floater transparency to the texture unless the floater is focused. + F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + LLColor4 color = working ? LLColor4::grey4 : LLColor4::white; + gl_draw_scaled_image(offset_x, offset_y, + thumbnail_w, thumbnail_h, + previewp->getThumbnailImage(), color % alpha); + } + } + impl->updateLayout(this); +} + +void LLFloaterSimpleSnapshot::onOpen(const LLSD& key) +{ + LLSnapshotLivePreview* preview = getPreviewView(); + if (preview) + { + preview->updateSnapshot(TRUE); + } + focusFirstItem(FALSE); + gSnapshotFloaterView->setEnabled(TRUE); + gSnapshotFloaterView->setVisible(TRUE); + gSnapshotFloaterView->adjustToFitScreen(this, FALSE); + + impl->updateControls(this); + impl->setStatus(ImplBase::STATUS_READY); + + mInventoryId = key["item_id"].asUUID(); + mTaskId = key["task_id"].asUUID(); +} + +void LLFloaterSimpleSnapshot::onCancel() +{ + closeFloater(); +} + +void LLFloaterSimpleSnapshot::onSend() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + + std::string temp_file = gDirUtilp->getTempFilename(); + if (previewp->createUploadFile(temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN)) + { + uploadImageUploadFile(temp_file, mInventoryId, mTaskId); + closeFloater(); + } + else + { + LLSD notif_args; + notif_args["REASON"] = LLImage::getLastError().c_str(); + LLNotificationsUtil::add("CannotUploadTexture", notif_args); + } +} + +void LLFloaterSimpleSnapshot::postSave() +{ + impl->setStatus(ImplBase::STATUS_WORKING); +} + +// static +void LLFloaterSimpleSnapshot::uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id) +{ + // generate a temp texture file for coroutine + std::string temp_file = gDirUtilp->getTempFilename(); + U32 codec = LLImageBase::getCodecFromExtension(gDirUtilp->getExtension(file_path)); + if (!LLViewerTextureList::createUploadFile(file_path, temp_file, codec, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN, true)) + { + LLSD notif_args; + notif_args["REASON"] = LLImage::getLastError().c_str(); + LLNotificationsUtil::add("CannotUploadTexture", notif_args); + LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL; + return; + } + uploadImageUploadFile(temp_file, inventory_id, task_id); +} + +// static +void LLFloaterSimpleSnapshot::uploadThumbnail(LLPointer raw_image, const LLUUID& inventory_id, const LLUUID& task_id) +{ + std::string temp_file = gDirUtilp->getTempFilename(); + if (!LLViewerTextureList::createUploadFile(raw_image, temp_file, THUMBNAIL_SNAPSHOT_DIM_MAX, THUMBNAIL_SNAPSHOT_DIM_MIN)) + { + LLSD notif_args; + notif_args["REASON"] = LLImage::getLastError().c_str(); + LLNotificationsUtil::add("CannotUploadTexture", notif_args); + LL_WARNS("Thumbnail") << "Failed to upload thumbnail for " << inventory_id << " " << task_id << ", reason: " << notif_args["REASON"].asString() << LL_ENDL; + return; + } + uploadImageUploadFile(temp_file, inventory_id, task_id); +} + +// static +void LLFloaterSimpleSnapshot::uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id) +{ + LLSD data; + + if (task_id.notNull()) + { + data["item_id"] = inventory_id; + data["task_id"] = task_id; + } + else if (gInventory.getCategory(inventory_id)) + { + data["category_id"] = inventory_id; + } + else + { + data["item_id"] = inventory_id; + } + + std::string cap_url = gAgent.getRegionCapability(THUMBNAIL_UPLOAD_CAP); + if (cap_url.empty()) + { + LLSD args; + args["CAPABILITY"] = THUMBNAIL_UPLOAD_CAP; + LLNotificationsUtil::add("RegionCapabilityRequestError", args); + LL_WARNS("Thumbnail") << "Failed to upload profile image for item " << inventory_id << " " << task_id << ", no cap found" << LL_ENDL; + return; + } + + LLCoros::instance().launch("postAgentUserImageCoro", + boost::bind(post_thumbnail_image_coro, cap_url, temp_file, data)); +} + +void LLFloaterSimpleSnapshot::update() +{ + // initializes snapshots when needed + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("simple_snapshot"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); + iter != inst_list.end(); ++iter) + { + LLFloaterSimpleSnapshot* floater = dynamic_cast(*iter); + if (floater) + { + floater->impl->updateLivePreview(); + } + } +} + + +// static +LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::findInstance(const LLSD &key) +{ + return LLFloaterReg::findTypedInstance("simple_snapshot", key); +} + +// static +LLFloaterSimpleSnapshot* LLFloaterSimpleSnapshot::getInstance(const LLSD &key) +{ + return LLFloaterReg::getTypedInstance("simple_snapshot", key); +} + +void LLFloaterSimpleSnapshot::saveTexture() +{ + LLSnapshotLivePreview* previewp = getPreviewView(); + if (!previewp) + { + llassert(previewp != NULL); + return; + } + + previewp->saveTexture(TRUE, getInventoryId().asString()); + closeFloater(); +} + +///---------------------------------------------------------------------------- +/// Class LLSimpleOutfitSnapshotFloaterView +///---------------------------------------------------------------------------- + +LLSimpleSnapshotFloaterView::LLSimpleSnapshotFloaterView(const Params& p) : LLFloaterView(p) +{ +} + +LLSimpleSnapshotFloaterView::~LLSimpleSnapshotFloaterView() +{ +} diff --git a/indra/newview/llfloatersimpleoutfitsnapshot.h b/indra/newview/llfloatersimplesnapshot.h similarity index 62% rename from indra/newview/llfloatersimpleoutfitsnapshot.h rename to indra/newview/llfloatersimplesnapshot.h index cc9a6c5d1e..91a81ee5c3 100644 --- a/indra/newview/llfloatersimpleoutfitsnapshot.h +++ b/indra/newview/llfloatersimplesnapshot.h @@ -1,6 +1,6 @@ /** -* @file llfloatersimpleoutfitsnapshot.h -* @brief Snapshot preview window for saving as an outfit thumbnail in visual outfit gallery +* @file llfloatersimplesnapshot.h +* @brief Snapshot preview window for saving as a thumbnail * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Second Life Viewer Source Code @@ -24,26 +24,25 @@ * $/LicenseInfo$ */ -#ifndef LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H -#define LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H +#ifndef LL_LLFLOATERSIMPLESNAPSHOT_H +#define LL_LLFLOATERSIMPLESNAPSHOT_H #include "llfloater.h" #include "llfloatersnapshot.h" -#include "lloutfitgallery.h" #include "llsnapshotlivepreview.h" ///---------------------------------------------------------------------------- -/// Class LLFloaterSimpleOutfitSnapshot +/// Class LLFloaterSimpleSnapshot ///---------------------------------------------------------------------------- -class LLFloaterSimpleOutfitSnapshot : public LLFloaterSnapshotBase +class LLFloaterSimpleSnapshot : public LLFloaterSnapshotBase { - LOG_CLASS(LLFloaterSimpleOutfitSnapshot); + LOG_CLASS(LLFloaterSimpleSnapshot); public: - LLFloaterSimpleOutfitSnapshot(const LLSD& key); - ~LLFloaterSimpleOutfitSnapshot(); + LLFloaterSimpleSnapshot(const LLSD& key); + ~LLFloaterSimpleSnapshot(); BOOL postBuild(); void onOpen(const LLSD& key); @@ -51,36 +50,48 @@ public: static void update(); - static LLFloaterSimpleOutfitSnapshot* getInstance(); - static LLFloaterSimpleOutfitSnapshot* findInstance(); + static LLFloaterSimpleSnapshot* getInstance(const LLSD &key); + static LLFloaterSimpleSnapshot* findInstance(const LLSD &key); void saveTexture(); const LLRect& getThumbnailPlaceholderRect() { return mThumbnailPlaceholder->getRect(); } - void setOutfitID(LLUUID id) { mOutfitID = id; } - LLUUID getOutfitID() { return mOutfitID; } - void setGallery(LLOutfitGallery* gallery) { mOutfitGallery = gallery; } + void setInventoryId(const LLUUID &inventory_id) { mInventoryId = inventory_id; } + LLUUID getInventoryId() { return mInventoryId; } + void setTaskId(const LLUUID &task_id) { mTaskId = task_id; } + void setOwner(LLView *owner_view) { mOwner = owner_view; } void postSave(); + static void uploadThumbnail(const std::string &file_path, const LLUUID &inventory_id, const LLUUID &task_id); + static void uploadThumbnail(LLPointer raw_image, const LLUUID& inventory_id, const LLUUID& task_id); class Impl; friend class Impl; + static const S32 THUMBNAIL_SNAPSHOT_DIM_MAX; + static const S32 THUMBNAIL_SNAPSHOT_DIM_MIN; + private: void onSend(); void onCancel(); - LLUUID mOutfitID; - LLOutfitGallery* mOutfitGallery; + // uploads upload-ready file + static void uploadImageUploadFile(const std::string &temp_file, const LLUUID &inventory_id, const LLUUID &task_id); + + LLUUID mInventoryId; + LLUUID mTaskId; + + LLView* mOwner; + F32 mContextConeOpacity; }; ///---------------------------------------------------------------------------- -/// Class LLFloaterSimpleOutfitSnapshot::Impl +/// Class LLFloaterSimpleSnapshot::Impl ///---------------------------------------------------------------------------- -class LLFloaterSimpleOutfitSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase +class LLFloaterSimpleSnapshot::Impl : public LLFloaterSnapshotBase::ImplBase { - LOG_CLASS(LLFloaterSimpleOutfitSnapshot::Impl); + LOG_CLASS(LLFloaterSimpleSnapshot::Impl); public: Impl(LLFloaterSnapshotBase* floater) : LLFloaterSnapshotBase::ImplBase(floater) @@ -108,7 +119,7 @@ private: /// Class LLSimpleOutfitSnapshotFloaterView ///---------------------------------------------------------------------------- -class LLSimpleOutfitSnapshotFloaterView : public LLFloaterView +class LLSimpleSnapshotFloaterView : public LLFloaterView { public: struct Params @@ -117,13 +128,13 @@ public: }; protected: - LLSimpleOutfitSnapshotFloaterView(const Params& p); + LLSimpleSnapshotFloaterView(const Params& p); friend class LLUICtrlFactory; public: - virtual ~LLSimpleOutfitSnapshotFloaterView(); + virtual ~LLSimpleSnapshotFloaterView(); }; -extern LLSimpleOutfitSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView; +extern LLSimpleSnapshotFloaterView* gSimpleOutfitSnapshotFloaterView; -#endif // LL_LLFLOATERSIMPLEOUTFITSNAPSHOT_H +#endif // LL_LLFLOATERSIMPLESNAPSHOT_H diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index b6acba6558..5fb4fb9b07 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -925,6 +925,9 @@ void LLFloaterTools::onClose(bool app_quitting) // hide the advanced object weights floater LLFloaterReg::hideInstance("object_weights"); + // hide gltf material editor + LLFloaterReg::hideInstance("live_material_editor"); + // prepare content for next call mPanelContents->clearContents(); diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp index bd49405f34..9f277021c8 100644 --- a/indra/newview/llfloatertopobjects.cpp +++ b/indra/newview/llfloatertopobjects.cpp @@ -49,6 +49,7 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "lluictrlfactory.h" +#include "llviewerobjectlist.h" #include "llviewerwindow.h" #include "llfloaterregioninfo.h" @@ -83,6 +84,8 @@ LLFloaterTopObjects::LLFloaterTopObjects(const LLSD& key) mCommitCallbackRegistrar.add("TopObjects.GetByOwnerName", boost::bind(&LLFloaterTopObjects::onGetByOwnerName, this)); mCommitCallbackRegistrar.add("TopObjects.GetByParcelName", boost::bind(&LLFloaterTopObjects::onGetByParcelName, this)); mCommitCallbackRegistrar.add("TopObjects.CommitObjectsList",boost::bind(&LLFloaterTopObjects::onCommitObjectsList, this)); + + mCommitCallbackRegistrar.add("TopObjects.TeleportToSelected", boost::bind(&LLFloaterTopObjects::teleportToSelectedObject, this)); } LLFloaterTopObjects::~LLFloaterTopObjects() @@ -92,10 +95,11 @@ LLFloaterTopObjects::~LLFloaterTopObjects() // virtual BOOL LLFloaterTopObjects::postBuild() { - LLScrollListCtrl *objects_list = getChild("objects_list"); - getChild("objects_list")->setFocus(TRUE); - objects_list->setDoubleClickCallback(onDoubleClickObjectsList, this); - objects_list->setCommitOnSelectionChange(TRUE); + mObjectsScrollList = getChild("objects_list"); + mObjectsScrollList->setFocus(TRUE); + mObjectsScrollList->setDoubleClickCallback(onDoubleClickObjectsList, this); + mObjectsScrollList->setCommitOnSelectionChange(TRUE); + mObjectsScrollList->setCommitCallback(boost::bind(&LLFloaterTopObjects::onSelectionChanged, this)); setDefaultBtn("show_beacon_btn"); @@ -421,6 +425,8 @@ void LLFloaterTopObjects::clearList() mObjectListData.clear(); mObjectListIDs.clear(); mtotalScore = 0.f; + + onSelectionChanged(); } @@ -507,3 +513,38 @@ void LLFloaterTopObjects::showBeacon() std::string tooltip(""); LLTracker::trackLocation(pos_global, name, tooltip, LLTracker::LOCATION_ITEM); } + +void LLFloaterTopObjects::teleportToSelectedObject() +{ + std::vector selected_items = mObjectsScrollList->getAllSelected(); + if (selected_items.size() == 1) + { + LLScrollListItem* first_selected = selected_items.front(); + + LLVector3d teleport_location; + LLViewerObject *viewer_object = gObjectList.findObject(first_selected->getUUID()); + if (viewer_object == NULL) + { + // If we cannot find the object in the viewer list, teleport to the last reported position + std::string pos_string = first_selected->getColumn(3)->getValue().asString(); + + F32 x, y, z; + S32 matched = sscanf(pos_string.c_str(), "<%g,%g,%g>", &x, &y, &z); + if (matched != 3) return; + + LLVector3 pos_agent(x, y, z); + teleport_location = gAgent.getPosGlobalFromAgent(pos_agent); + } + else + { + // If we can find the object in the viewer list, teleport to the known current position + teleport_location = viewer_object->getPositionGlobal(); + } + gAgent.teleportViaLocationLookAt(teleport_location); + } +} + +void LLFloaterTopObjects::onSelectionChanged() +{ + getChildView("teleport_btn")->setEnabled(mObjectsScrollList->getNumSelected() == 1); +} diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h index 3138249c7a..b73401e50d 100644 --- a/indra/newview/llfloatertopobjects.h +++ b/indra/newview/llfloatertopobjects.h @@ -30,6 +30,7 @@ #include "llfloater.h" class LLUICtrl; +class LLScrollListCtrl; // Bits for simulator performance query flags enum LAND_STAT_FLAGS @@ -75,6 +76,7 @@ private: void initColumns(LLCtrlListInterface *list); void onCommitObjectsList(); + void onSelectionChanged(); static void onDoubleClickObjectsList(void* data); void onClickShowBeacon(); @@ -90,6 +92,7 @@ private: void onGetByParcelName(); void showBeacon(); + void teleportToSelectedObject(); private: std::string mMethod; @@ -106,6 +109,7 @@ private: F32 mtotalScore; static LLFloaterTopObjects* sInstance; + LLScrollListCtrl* mObjectsScrollList; }; #endif diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index 082bb888b1..d29ecbbf95 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -45,15 +45,9 @@ LLFloaterTranslationSettings::LLFloaterTranslationSettings(const LLSD& key) : LLFloater(key) , mMachineTranslationCB(NULL) -, mLanguageCombo(NULL) -, mTranslationServiceRadioGroup(NULL) -, mBingAPIKeyEditor(NULL) -, mGoogleAPIKeyEditor(NULL) -, mBingVerifyBtn(NULL) -, mGoogleVerifyBtn(NULL) -, mOKBtn(NULL) -, mBingKeyVerified(false) +, mAzureKeyVerified(false) , mGoogleKeyVerified(false) +, mDeepLKeyVerified(false) { } @@ -63,24 +57,54 @@ BOOL LLFloaterTranslationSettings::postBuild() mMachineTranslationCB = getChild("translate_chat_checkbox"); mLanguageCombo = getChild("translate_language_combo"); mTranslationServiceRadioGroup = getChild("translation_service_rg"); - mBingAPIKeyEditor = getChild("bing_api_key"); + mAzureAPIEndpointEditor = getChild("azure_api_endpoint_combo"); + mAzureAPIKeyEditor = getChild("azure_api_key"); + mAzureAPIRegionEditor = getChild("azure_api_region"); mGoogleAPIKeyEditor = getChild("google_api_key"); - mBingVerifyBtn = getChild("verify_bing_api_key_btn"); + mDeepLAPIDomainCombo = getChild("deepl_api_domain_combo"); + mDeepLAPIKeyEditor = getChild("deepl_api_key"); + mAzureVerifyBtn = getChild("verify_azure_api_key_btn"); mGoogleVerifyBtn = getChild("verify_google_api_key_btn"); + mDeepLVerifyBtn = getChild("verify_deepl_api_key_btn"); mOKBtn = getChild("ok_btn"); mMachineTranslationCB->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); mTranslationServiceRadioGroup->setCommitCallback(boost::bind(&LLFloaterTranslationSettings::updateControlsEnabledState, this)); mOKBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnOK, this)); getChild("cancel_btn")->setClickedCallback(boost::bind(&LLFloater::closeFloater, this, false)); - mBingVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnBingVerify, this)); + mAzureVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnAzureVerify, this)); mGoogleVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnGoogleVerify, this)); + mDeepLVerifyBtn->setClickedCallback(boost::bind(&LLFloaterTranslationSettings::onBtnDeepLVerify, this)); + + mAzureAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mAzureAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onAzureKeyEdited, this), NULL); + mAzureAPIRegionEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mAzureAPIRegionEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onAzureKeyEdited, this), NULL); + + mAzureAPIEndpointEditor->setFocusLostCallback([this](LLFocusableElement*) + { + setAzureVerified(false, false, 0); + }); + mAzureAPIEndpointEditor->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) + { + setAzureVerified(false, false, 0); + }); - mBingAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); - mBingAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onBingKeyEdited, this), NULL); mGoogleAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); mGoogleAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onGoogleKeyEdited, this), NULL); + mDeepLAPIKeyEditor->setFocusReceivedCallback(boost::bind(&LLFloaterTranslationSettings::onEditorFocused, this, _1)); + mDeepLAPIKeyEditor->setKeystrokeCallback(boost::bind(&LLFloaterTranslationSettings::onDeepLKeyEdited, this), NULL); + + mDeepLAPIDomainCombo->setFocusLostCallback([this](LLFocusableElement*) + { + setDeepLVerified(false, false, 0); + }); + mDeepLAPIDomainCombo->setCommitCallback([this](LLUICtrl* ctrl, const LLSD& param) + { + setDeepLVerified(false, false, 0); + }); + center(); return TRUE; } @@ -92,17 +116,28 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key) mLanguageCombo->setSelectedByValue(gSavedSettings.getString("TranslateLanguage"), TRUE); mTranslationServiceRadioGroup->setSelectedByValue(gSavedSettings.getString("TranslationService"), TRUE); - std::string bing_key = gSavedSettings.getString("BingTranslateAPIKey"); - if (!bing_key.empty()) + LLSD azure_key = gSavedSettings.getLLSD("AzureTranslateAPIKey"); + if (azure_key.isMap() && !azure_key["id"].asString().empty()) { - mBingAPIKeyEditor->setText(bing_key); - mBingAPIKeyEditor->setTentative(FALSE); - verifyKey(LLTranslate::SERVICE_BING, bing_key, false); + mAzureAPIKeyEditor->setText(azure_key["id"].asString()); + mAzureAPIKeyEditor->setTentative(false); + if (azure_key.has("region") && !azure_key["region"].asString().empty()) + { + mAzureAPIRegionEditor->setText(azure_key["region"].asString()); + mAzureAPIRegionEditor->setTentative(false); + } + else + { + mAzureAPIRegionEditor->setTentative(true); + } + mAzureAPIEndpointEditor->setValue(azure_key["endpoint"]); + verifyKey(LLTranslate::SERVICE_AZURE, azure_key, false); } else { - mBingAPIKeyEditor->setTentative(TRUE); - mBingKeyVerified = FALSE; + mAzureAPIKeyEditor->setTentative(TRUE); + mAzureAPIRegionEditor->setTentative(true); + mAzureKeyVerified = FALSE; } std::string google_key = gSavedSettings.getString("GoogleTranslateAPIKey"); @@ -118,39 +153,74 @@ void LLFloaterTranslationSettings::onOpen(const LLSD& key) mGoogleKeyVerified = FALSE; } + LLSD deepl_key = gSavedSettings.getLLSD("DeepLTranslateAPIKey"); + if (deepl_key.isMap() && !deepl_key["id"].asString().empty()) + { + mDeepLAPIKeyEditor->setText(deepl_key["id"].asString()); + mDeepLAPIKeyEditor->setTentative(false); + mDeepLAPIDomainCombo->setValue(deepl_key["domain"]); + verifyKey(LLTranslate::SERVICE_DEEPL, deepl_key, false); + } + else + { + mDeepLAPIKeyEditor->setTentative(TRUE); + mDeepLKeyVerified = FALSE; + } + updateControlsEnabledState(); } -void LLFloaterTranslationSettings::setBingVerified(bool ok, bool alert) +void LLFloaterTranslationSettings::setAzureVerified(bool ok, bool alert, S32 status) { if (alert) { - showAlert(ok ? "bing_api_key_verified" : "bing_api_key_not_verified"); + showAlert(ok ? "azure_api_key_verified" : "azure_api_key_not_verified", status); } - mBingKeyVerified = ok; + mAzureKeyVerified = ok; updateControlsEnabledState(); } -void LLFloaterTranslationSettings::setGoogleVerified(bool ok, bool alert) +void LLFloaterTranslationSettings::setGoogleVerified(bool ok, bool alert, S32 status) { if (alert) { - showAlert(ok ? "google_api_key_verified" : "google_api_key_not_verified"); + showAlert(ok ? "google_api_key_verified" : "google_api_key_not_verified", status); } mGoogleKeyVerified = ok; updateControlsEnabledState(); } +void LLFloaterTranslationSettings::setDeepLVerified(bool ok, bool alert, S32 status) +{ + if (alert) + { + showAlert(ok ? "deepl_api_key_verified" : "deepl_api_key_not_verified", status); + } + + mDeepLKeyVerified = ok; + updateControlsEnabledState(); +} + std::string LLFloaterTranslationSettings::getSelectedService() const { return mTranslationServiceRadioGroup->getSelectedValue().asString(); } -std::string LLFloaterTranslationSettings::getEnteredBingKey() const +LLSD LLFloaterTranslationSettings::getEnteredAzureKey() const { - return mBingAPIKeyEditor->getTentative() ? LLStringUtil::null : mBingAPIKeyEditor->getText(); + LLSD key; + if (!mAzureAPIKeyEditor->getTentative()) + { + key["endpoint"] = mAzureAPIEndpointEditor->getValue(); + key["id"] = mAzureAPIKeyEditor->getText(); + if (!mAzureAPIRegionEditor->getTentative()) + { + key["region"] = mAzureAPIRegionEditor->getText(); + } + } + return key; } std::string LLFloaterTranslationSettings::getEnteredGoogleKey() const @@ -158,10 +228,26 @@ std::string LLFloaterTranslationSettings::getEnteredGoogleKey() const return mGoogleAPIKeyEditor->getTentative() ? LLStringUtil::null : mGoogleAPIKeyEditor->getText(); } -void LLFloaterTranslationSettings::showAlert(const std::string& msg_name) const +LLSD LLFloaterTranslationSettings::getEnteredDeepLKey() const { + LLSD key; + if (!mDeepLAPIKeyEditor->getTentative()) + { + key["domain"] = mDeepLAPIDomainCombo->getValue(); + key["id"] = mDeepLAPIKeyEditor->getText(); + } + return key; +} + +void LLFloaterTranslationSettings::showAlert(const std::string& msg_name, S32 status) const +{ + LLStringUtil::format_map_t string_args; + // For now just show an http error code, whole 'reason' string might be added later + string_args["[STATUS]"] = llformat("%d", status); + std::string message = getString(msg_name, string_args); + LLSD args; - args["MESSAGE"] = getString(msg_name); + args["MESSAGE"] = message; LLNotificationsUtil::add("GenericAlert", args); } @@ -170,34 +256,51 @@ void LLFloaterTranslationSettings::updateControlsEnabledState() // Enable/disable controls based on the checkbox value. bool on = mMachineTranslationCB->getValue().asBoolean(); std::string service = getSelectedService(); - bool bing_selected = service == "bing"; + bool azure_selected = service == "azure"; bool google_selected = service == "google"; + bool deepl_selected = service == "deepl"; mTranslationServiceRadioGroup->setEnabled(on); mLanguageCombo->setEnabled(on); - getChild("bing_api_key_label")->setEnabled(on); - mBingAPIKeyEditor->setEnabled(on); + // MS Azure + getChild("azure_api_endoint_label")->setEnabled(on); + mAzureAPIEndpointEditor->setEnabled(on && azure_selected); + getChild("azure_api_key_label")->setEnabled(on); + mAzureAPIKeyEditor->setEnabled(on && azure_selected); + getChild("azure_api_region_label")->setEnabled(on); + mAzureAPIRegionEditor->setEnabled(on && azure_selected); - getChild("google_api_key_label")->setEnabled(on); - mGoogleAPIKeyEditor->setEnabled(on); + mAzureVerifyBtn->setEnabled(on && azure_selected && + !mAzureKeyVerified && getEnteredAzureKey().isMap()); - mBingAPIKeyEditor->setEnabled(on && bing_selected); - mGoogleAPIKeyEditor->setEnabled(on && google_selected); + // Google + getChild("google_api_key_label")->setEnabled(on); + mGoogleAPIKeyEditor->setEnabled(on && google_selected); - mBingVerifyBtn->setEnabled(on && bing_selected && - !mBingKeyVerified && !getEnteredBingKey().empty()); mGoogleVerifyBtn->setEnabled(on && google_selected && !mGoogleKeyVerified && !getEnteredGoogleKey().empty()); - bool service_verified = (bing_selected && mBingKeyVerified) || (google_selected && mGoogleKeyVerified); + // DeepL + getChild("deepl_api_domain_label")->setEnabled(on); + mDeepLAPIDomainCombo->setEnabled(on && deepl_selected); + getChild("deepl_api_key_label")->setEnabled(on); + mDeepLAPIKeyEditor->setEnabled(on && deepl_selected); + + mDeepLVerifyBtn->setEnabled(on && deepl_selected && + !mDeepLKeyVerified && getEnteredDeepLKey().isMap()); + + bool service_verified = + (azure_selected && mAzureKeyVerified) + || (google_selected && mGoogleKeyVerified) + || (deepl_selected && mDeepLKeyVerified); gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified); mOKBtn->setEnabled(!on || service_verified); } /*static*/ -void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, bool alert) +void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, bool alert, S32 status) { LLFloaterTranslationSettings* floater = LLFloaterReg::getTypedInstance("prefs_translation"); @@ -210,20 +313,23 @@ void LLFloaterTranslationSettings::setVerificationStatus(int service, bool ok, b switch (service) { - case LLTranslate::SERVICE_BING: - floater->setBingVerified(ok, alert); + case LLTranslate::SERVICE_AZURE: + floater->setAzureVerified(ok, alert, status); break; case LLTranslate::SERVICE_GOOGLE: - floater->setGoogleVerified(ok, alert); + floater->setGoogleVerified(ok, alert, status); + break; + case LLTranslate::SERVICE_DEEPL: + floater->setDeepLVerified(ok, alert, status); break; } } -void LLFloaterTranslationSettings::verifyKey(int service, const std::string& key, bool alert) +void LLFloaterTranslationSettings::verifyKey(int service, const LLSD& key, bool alert) { LLTranslate::verifyKey(static_cast(service), key, - boost::bind(&LLFloaterTranslationSettings::setVerificationStatus, _1, _2, alert)); + boost::bind(&LLFloaterTranslationSettings::setVerificationStatus, _1, _2, alert, _3)); } void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control) @@ -239,11 +345,13 @@ void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control) } } -void LLFloaterTranslationSettings::onBingKeyEdited() +void LLFloaterTranslationSettings::onAzureKeyEdited() { - if (mBingAPIKeyEditor->isDirty()) + if (mAzureAPIKeyEditor->isDirty() + || mAzureAPIRegionEditor->isDirty()) { - setBingVerified(false, false); + // todo: verify mAzureAPIEndpointEditor url + setAzureVerified(false, false, 0); } } @@ -251,16 +359,24 @@ void LLFloaterTranslationSettings::onGoogleKeyEdited() { if (mGoogleAPIKeyEditor->isDirty()) { - setGoogleVerified(false, false); + setGoogleVerified(false, false, 0); } } -void LLFloaterTranslationSettings::onBtnBingVerify() +void LLFloaterTranslationSettings::onDeepLKeyEdited() { - std::string key = getEnteredBingKey(); - if (!key.empty()) + if (mDeepLAPIKeyEditor->isDirty()) + { + setDeepLVerified(false, false, 0); + } +} + +void LLFloaterTranslationSettings::onBtnAzureVerify() +{ + LLSD key = getEnteredAzureKey(); + if (key.isMap()) { - verifyKey(LLTranslate::SERVICE_BING, key); + verifyKey(LLTranslate::SERVICE_AZURE, key); } } @@ -269,26 +385,40 @@ void LLFloaterTranslationSettings::onBtnGoogleVerify() std::string key = getEnteredGoogleKey(); if (!key.empty()) { - verifyKey(LLTranslate::SERVICE_GOOGLE, key); + verifyKey(LLTranslate::SERVICE_GOOGLE, LLSD(key)); } } + +void LLFloaterTranslationSettings::onBtnDeepLVerify() +{ + LLSD key = getEnteredDeepLKey(); + if (key.isMap()) + { + verifyKey(LLTranslate::SERVICE_DEEPL, key); + } +} + void LLFloaterTranslationSettings::onClose(bool app_quitting) { std::string service = gSavedSettings.getString("TranslationService"); - bool bing_selected = service == "bing"; + bool azure_selected = service == "azure"; bool google_selected = service == "google"; + bool deepl_selected = service == "deepl"; - bool service_verified = (bing_selected && mBingKeyVerified) || (google_selected && mGoogleKeyVerified); - gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified); - + bool service_verified = + (azure_selected && mAzureKeyVerified) + || (google_selected && mGoogleKeyVerified) + || (deepl_selected && mDeepLKeyVerified); + gSavedPerAccountSettings.setBOOL("TranslatingEnabled", service_verified); } void LLFloaterTranslationSettings::onBtnOK() { gSavedSettings.setBOOL("TranslateChat", mMachineTranslationCB->getValue().asBoolean()); gSavedSettings.setString("TranslateLanguage", mLanguageCombo->getSelectedValue().asString()); gSavedSettings.setString("TranslationService", getSelectedService()); - gSavedSettings.setString("BingTranslateAPIKey", getEnteredBingKey()); + gSavedSettings.setLLSD("AzureTranslateAPIKey", getEnteredAzureKey()); gSavedSettings.setString("GoogleTranslateAPIKey", getEnteredGoogleKey()); + gSavedSettings.setLLSD("DeepLTranslateAPIKey", getEnteredDeepLKey()); closeFloater(false); } diff --git a/indra/newview/llfloatertranslationsettings.h b/indra/newview/llfloatertranslationsettings.h index 2a15eacded..eff0803fdd 100644 --- a/indra/newview/llfloatertranslationsettings.h +++ b/indra/newview/llfloatertranslationsettings.h @@ -42,38 +42,48 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); - void setBingVerified(bool ok, bool alert); - void setGoogleVerified(bool ok, bool alert); + void setAzureVerified(bool ok, bool alert, S32 status); + void setGoogleVerified(bool ok, bool alert, S32 status); + void setDeepLVerified(bool ok, bool alert, S32 status); void onClose(bool app_quitting); private: std::string getSelectedService() const; - std::string getEnteredBingKey() const; + LLSD getEnteredAzureKey() const; std::string getEnteredGoogleKey() const; - void showAlert(const std::string& msg_name) const; + LLSD getEnteredDeepLKey() const; + void showAlert(const std::string& msg_name, S32 status) const; void updateControlsEnabledState(); - void verifyKey(int service, const std::string& key, bool alert = true); + void verifyKey(int service, const LLSD& key, bool alert = true); void onEditorFocused(LLFocusableElement* control); - void onBingKeyEdited(); + void onAzureKeyEdited(); void onGoogleKeyEdited(); - void onBtnBingVerify(); + void onDeepLKeyEdited(); + void onBtnAzureVerify(); void onBtnGoogleVerify(); + void onBtnDeepLVerify(); void onBtnOK(); - static void setVerificationStatus(int service, bool alert, bool ok); + static void setVerificationStatus(int service, bool alert, bool ok, S32 status); LLCheckBoxCtrl* mMachineTranslationCB; LLComboBox* mLanguageCombo; - LLLineEditor* mBingAPIKeyEditor; + LLComboBox* mAzureAPIEndpointEditor; + LLLineEditor* mAzureAPIKeyEditor; + LLLineEditor* mAzureAPIRegionEditor; LLLineEditor* mGoogleAPIKeyEditor; + LLComboBox* mDeepLAPIDomainCombo; + LLLineEditor* mDeepLAPIKeyEditor; LLRadioGroup* mTranslationServiceRadioGroup; - LLButton* mBingVerifyBtn; + LLButton* mAzureVerifyBtn; LLButton* mGoogleVerifyBtn; + LLButton* mDeepLVerifyBtn; LLButton* mOKBtn; - bool mBingKeyVerified; + bool mAzureKeyVerified; bool mGoogleKeyVerified; + bool mDeepLKeyVerified; }; #endif // LL_LLFLOATERTRANSLATIONSETTINGS_H diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 704abd269f..8f3ec8af05 100755 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -124,8 +124,10 @@ public: // requires trusted browser to trigger LLWorldMapHandler() : LLCommandHandler("worldmap", UNTRUSTED_CLICK_ONLY ) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableWorldMap")) { @@ -163,7 +165,10 @@ public: { } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableWorldMap")) { diff --git a/indra/newview/llfolderviewmodelinventory.h b/indra/newview/llfolderviewmodelinventory.h index de28091c32..1649b2eed7 100644 --- a/indra/newview/llfolderviewmodelinventory.h +++ b/indra/newview/llfolderviewmodelinventory.h @@ -39,12 +39,14 @@ class LLFolderViewModelItemInventory public: LLFolderViewModelItemInventory(class LLFolderViewModelInventory& root_view_model); virtual const LLUUID& getUUID() const = 0; + virtual const LLUUID& getThumbnailUUID() const = 0; virtual time_t getCreationDate() const = 0; // UTC seconds virtual void setCreationDate(time_t creation_date_utc) = 0; virtual PermissionMask getPermissionMask() const = 0; virtual LLFolderType::EType getPreferredType() const = 0; virtual void showProperties(void) = 0; virtual BOOL isItemInTrash( void) const { return FALSE; } // TODO: make into pure virtual. + virtual bool isItemInOutfits() const { return false; } virtual BOOL isAgentInventory() const { return FALSE; } virtual BOOL isUpToDate() const = 0; virtual void addChild(LLFolderViewModelItem* child); diff --git a/indra/newview/llfriendcard.cpp b/indra/newview/llfriendcard.cpp index e395da7f1e..ce28915d93 100644 --- a/indra/newview/llfriendcard.cpp +++ b/indra/newview/llfriendcard.cpp @@ -478,14 +478,24 @@ void LLFriendCardsManager::ensureFriendsFolderExists() LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL; } - friends_folder_ID = gInventory.createNewCategory(calling_cards_folder_ID, - LLFolderType::FT_CALLINGCARD, get_friend_folder_name()); - - gInventory.createNewCategory(friends_folder_ID, - LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); - - // Now when we have all needed folders we can sync their contents with buddies list. - syncFriendsFolder(); + gInventory.createNewCategory( + calling_cards_folder_ID, + LLFolderType::FT_CALLINGCARD, + get_friend_folder_name(), + [](const LLUUID &new_category_id) + { + gInventory.createNewCategory( + new_category_id, + LLFolderType::FT_CALLINGCARD, + get_friend_all_subfolder_name(), + [](const LLUUID &new_category_id) + { + // Now when we have all needed folders we can sync their contents with buddies list. + LLFriendCardsManager::getInstance()->syncFriendsFolder(); + } + ); + } + ); } } @@ -510,11 +520,16 @@ void LLFriendCardsManager::ensureFriendsAllFolderExists() LL_WARNS() << "Failed to find \"" << cat_name << "\" category descendents in Category Tree." << LL_ENDL; } - friends_all_folder_ID = gInventory.createNewCategory(friends_folder_ID, - LLFolderType::FT_CALLINGCARD, get_friend_all_subfolder_name()); - - // Now when we have all needed folders we can sync their contents with buddies list. - syncFriendsFolder(); + gInventory.createNewCategory( + friends_folder_ID, + LLFolderType::FT_CALLINGCARD, + get_friend_all_subfolder_name(), + [](const LLUUID &new_cat_id) + { + // Now when we have all needed folders we can sync their contents with buddies list. + LLFriendCardsManager::getInstance()->syncFriendsFolder(); + } + ); } } diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp index 098fe89265..1b22b903fc 100644 --- a/indra/newview/llglsandbox.cpp +++ b/indra/newview/llglsandbox.cpp @@ -741,6 +741,12 @@ void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLV gGL.end(); } +void LLViewerParcelMgr::resetCollisionTimer() +{ + mCollisionTimer.reset(); + mRenderCollision = TRUE; +} + void draw_line_cube(F32 width, const LLVector3& center) { width = 0.5f * width; diff --git a/indra/newview/llgltfmateriallist.cpp b/indra/newview/llgltfmateriallist.cpp index 7994515b61..edd0394533 100644 --- a/indra/newview/llgltfmateriallist.cpp +++ b/indra/newview/llgltfmateriallist.cpp @@ -160,9 +160,9 @@ public: // sides - array of S32 indices of texture entries // gltf_json - array of corresponding Strings of GLTF json for override data - LLSD message; bool success = true; +#if 0 //deprecated for(const std::string& llsdRaw : strings) { std::istringstream llsdData(llsdRaw); @@ -198,6 +198,7 @@ public: applyData(object_override); } +#endif return success; } @@ -213,6 +214,7 @@ public: { // Parse the data +#if 0 // DEPRECATED LL::WorkQueue::ptr_t main_queue = LL::WorkQueue::getInstance("mainloop"); LL::WorkQueue::ptr_t general_queue = LL::WorkQueue::getInstance("General"); @@ -235,24 +237,17 @@ public: results.reserve(sides.size()); // parse json - std::unordered_map::const_iterator iter = sides.begin(); - std::unordered_map::const_iterator end = sides.end(); + std::unordered_map::const_iterator iter = sides.begin(); + std::unordered_map::const_iterator end = sides.end(); while (iter != end) { - std::string warn_msg, error_msg; - ReturnData result; - bool success = result.mMaterial.fromJSON(iter->second, warn_msg, error_msg); - - result.mSuccess = success; + result.mMaterial.applyOverrideLLSD(iter->second); + + result.mSuccess = true; result.mSide = iter->first; - if (!success) - { - LL_WARNS("GLTF") << "failed to parse GLTF override data. errors: " << error_msg << " | warnings: " << warn_msg << LL_ENDL; - } - results.push_back(result); iter++; } @@ -318,6 +313,7 @@ public: } }); } +#endif } private: @@ -330,6 +326,89 @@ namespace LLGLTFMaterialOverrideDispatchHandler handle_gltf_override_message; } +void LLGLTFMaterialList::applyOverrideMessage(LLMessageSystem* msg, const std::string& data_in) +{ + std::istringstream str(data_in); + + LLSD data; + + LLSDSerialize::fromNotation(data, str, data_in.length()); + + const LLHost& host = msg->getSender(); + + LLViewerRegion* region = LLWorld::instance().getRegion(host); + llassert(region); + + if (region) + { + U32 local_id = data.get("id").asInteger(); + LLUUID id; + gObjectList.getUUIDFromLocal(id, local_id, host.getAddress(), host.getPort()); + LLViewerObject* obj = gObjectList.findObject(id); + + // NOTE: obj may be null if the viewer hasn't heard about the object yet, cache update in any case + + if (obj && gShowObjectUpdates) + { // display a cyan blip for override updates when "Show Updates to Objects" enabled + LLColor4 color(0.f, 1.f, 1.f, 1.f); + gPipeline.addDebugBlip(obj->getPositionAgent(), color); + } + + const LLSD& tes = data["te"]; + const LLSD& od = data["od"]; + + constexpr U32 MAX_TES = 45; + bool has_te[MAX_TES] = { false }; + + if (tes.isArray()) // NOTE: if no "te" array exists, this is a malformed message (null out all overrides will come in as an empty te array) + { + LLGLTFOverrideCacheEntry cache; + cache.mLocalId = local_id; + cache.mObjectId = id; + cache.mRegionHandle = region->getHandle(); + + U32 count = llmin(tes.size(), MAX_TES); + for (U32 i = 0; i < count; ++i) + { + LLGLTFMaterial* mat = new LLGLTFMaterial(); // setTEGLTFMaterialOverride and cache will take ownership + mat->applyOverrideLLSD(od[i]); + + S32 te = tes[i].asInteger(); + + has_te[te] = true; + cache.mSides[te] = od[i]; + cache.mGLTFMaterial[te] = mat; + + if (obj) + { + obj->setTEGLTFMaterialOverride(te, mat); + if (obj->getTE(te) && obj->getTE(te)->isSelected()) + { + handle_gltf_override_message.doSelectionCallbacks(id, te); + } + } + } + + if (obj) + { // null out overrides on TEs that shouldn't have them + U32 count = llmin(obj->getNumTEs(), MAX_TES); + for (U32 i = 0; i < count; ++i) + { + LLTextureEntry* te = obj->getTE(i); + if (!has_te[i] && te && te->getGLTFMaterialOverride()) + { + obj->setTEGLTFMaterialOverride(i, nullptr); + handle_gltf_override_message.doSelectionCallbacks(id, i); + } + } + } + + region->cacheFullUpdateGLTFOverride(cache); + } + + } +} + void LLGLTFMaterialList::queueOverrideUpdate(const LLUUID& id, S32 side, LLGLTFMaterial* override_data) { #if 0 diff --git a/indra/newview/llgltfmateriallist.h b/indra/newview/llgltfmateriallist.h index ce8781baba..7317214019 100644 --- a/indra/newview/llgltfmateriallist.h +++ b/indra/newview/llgltfmateriallist.h @@ -101,6 +101,9 @@ public: static void loadCacheOverrides(const LLGLTFOverrideCacheEntry& override); + // Apply an override update with the given data + void applyOverrideMessage(LLMessageSystem* msg, const std::string& data); + private: friend class LLGLTFMaterialOverrideDispatchHandler; // save an override update that we got from the simulator for later (for example, if an override arrived for an unknown object) diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index aec6c23e34..043316ccca 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -78,8 +78,10 @@ public: return true; } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& tokens, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_STARTED) { diff --git a/indra/newview/llimprocessing.cpp b/indra/newview/llimprocessing.cpp index 3536b83989..948793681d 100644 --- a/indra/newview/llimprocessing.cpp +++ b/indra/newview/llimprocessing.cpp @@ -1631,14 +1631,19 @@ void LLIMProcessing::requestOfflineMessagesCoro(std::string url) from_group = message_data["from_group"].asString() == "Y"; } - + EInstantMessage dialog = static_cast(message_data["dialog"].asInteger()); + LLUUID session_id = message_data["transaction-id"].asUUID(); + if (session_id.isNull() && dialog == IM_FROM_TASK) + { + session_id = message_data["asset_id"].asUUID(); + } LLIMProcessing::processNewMessage( message_data["from_agent_id"].asUUID(), from_group, message_data["to_agent_id"].asUUID(), message_data.has("offline") ? static_cast(message_data["offline"].asInteger()) : IM_OFFLINE, - static_cast(message_data["dialog"].asInteger()), - message_data["transaction-id"].asUUID(), + dialog, + session_id, static_cast(message_data["timestamp"].asInteger()), message_data["from_agent_name"].asString(), message_data["message"].asString(), diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index 3d13985f08..491c90c571 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -670,9 +670,7 @@ void LLInspectObject::onClickOpen() void LLInspectObject::onClickMoreInfo() { - LLSD key; - key["task"] = "task"; - LLFloaterSidePanelContainer::showPanel("inventory", key); + LLFloaterReg::showInstance("task_properties"); closeFloater(); } diff --git a/indra/newview/llinspecttexture.cpp b/indra/newview/llinspecttexture.cpp new file mode 100644 index 0000000000..da4e3c0949 --- /dev/null +++ b/indra/newview/llinspecttexture.cpp @@ -0,0 +1,249 @@ +/** + * @file llinspecttexture.cpp + * + * $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 "llinspect.h" +#include "llinspecttexture.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "lltexturectrl.h" +#include "lltrans.h" +#include "llviewertexturelist.h" + + +// ============================================================================ +// Helper functions +// + +LLToolTip* LLInspectTextureUtil::createInventoryToolTip(LLToolTip::Params p) +{ + const LLSD& sdTooltip = p.create_params; + + if (sdTooltip.has("thumbnail_id") && sdTooltip["thumbnail_id"].asUUID().notNull()) + { + // go straight for thumbnail regardless of type + // TODO: make a tooltip factory? + return LLUICtrlFactory::create(p); + } + + LLInventoryType::EType eInvType = (sdTooltip.has("inv_type")) ? (LLInventoryType::EType)sdTooltip["inv_type"].asInteger() : LLInventoryType::IT_NONE; + switch (eInvType) + { + case LLInventoryType::IT_CATEGORY: + { + if (sdTooltip.has("item_id")) + { + const LLUUID idCategory = sdTooltip["item_id"].asUUID(); + LLViewerInventoryCategory* cat = gInventory.getCategory(idCategory); + if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + // Not LLIsOfAssetType, because we allow links + LLIsTextureType f; + gInventory.getDirectDescendentsOf(idCategory, cats, items, f); + + // Exactly one texture found => show the texture tooltip + if (1 == items.size()) + { + LLViewerInventoryItem* item = items.front(); + if (item && item->getIsLinkType()) + { + item = item->getLinkedItem(); + } + if (item) + { + // Note: LLFloaterChangeItemThumbnail will attempt to write this + // into folder's thumbnail id when opened + p.create_params.getValue()["thumbnail_id"] = item->getAssetUUID(); + return LLUICtrlFactory::create(p); + } + } + } + } + + // No or more than one texture found => show default tooltip + return LLUICtrlFactory::create(p); + } + default: + return LLUICtrlFactory::create(p); + } +} + +// ============================================================================ +// LLTexturePreviewView helper class +// + +class LLTexturePreviewView : public LLView +{ +public: + LLTexturePreviewView(const LLView::Params& p); + ~LLTexturePreviewView(); + +public: + void draw() override; + +public: + void setImageFromAssetId(const LLUUID& idAsset); + void setImageFromItemId(const LLUUID& idItem); + +protected: + LLPointer m_Image; + S32 mImageBoostLevel = LLGLTexture::BOOST_NONE; + std::string mLoadingText; +}; + + +LLTexturePreviewView::LLTexturePreviewView(const LLView::Params& p) + : LLView(p) +{ + mLoadingText = LLTrans::getString("texture_loading"); +} + +LLTexturePreviewView::~LLTexturePreviewView() +{ + if (m_Image) + { + m_Image->setBoostLevel(mImageBoostLevel); + m_Image = nullptr; + } +} + +void LLTexturePreviewView::draw() +{ + LLView::draw(); + + if (m_Image) + { + LLRect rctClient = getLocalRect(); + + if (4 == m_Image->getComponents()) + { + const LLColor4 color(.098f, .098f, .098f); + gl_rect_2d(rctClient, color, TRUE); + } + gl_draw_scaled_image(rctClient.mLeft, rctClient.mBottom, rctClient.getWidth(), rctClient.getHeight(), m_Image); + + bool isLoading = (!m_Image->isFullyLoaded()) && (m_Image->getDiscardLevel() > 0); + if (isLoading) + LLFontGL::getFontSansSerif()->renderUTF8(mLoadingText, 0, llfloor(rctClient.mLeft + 3), llfloor(rctClient.mTop - 25), LLColor4::white, LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::DROP_SHADOW); + m_Image->addTextureStats((isLoading) ? MAX_IMAGE_AREA : (F32)(rctClient.getWidth() * rctClient.getHeight())); + } +} + +void LLTexturePreviewView::setImageFromAssetId(const LLUUID& idAsset) +{ + m_Image = LLViewerTextureManager::getFetchedTexture(idAsset, FTT_DEFAULT, MIPMAP_TRUE, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + if (m_Image) + { + mImageBoostLevel = m_Image->getBoostLevel(); + m_Image->setBoostLevel(LLGLTexture::BOOST_PREVIEW); + m_Image->forceToSaveRawImage(0); + if ( (!m_Image->isFullyLoaded()) && (!m_Image->hasFetcher()) ) + { + if (m_Image->isInFastCacheList()) + { + m_Image->loadFromFastCache(); + } + gTextureList.forceImmediateUpdate(m_Image); + } + } +} + +void LLTexturePreviewView::setImageFromItemId(const LLUUID& idItem) +{ + const LLViewerInventoryItem* pItem = gInventory.getItem(idItem); + setImageFromAssetId( (pItem) ? pItem->getAssetUUID() : LLUUID::null ); +} + +// ============================================================================ +// LLTextureToolTip class +// + +LLTextureToolTip::LLTextureToolTip(const LLToolTip::Params& p) + : LLToolTip(p) + , mPreviewView(nullptr) + , mPreviewSize(256) +{ + mMaxWidth = llmax(mMaxWidth, mPreviewSize); + + // Currently has to share params with LLToolTip, override values + setBackgroundColor(LLColor4::black); + setTransparentColor(LLColor4::black); + setBorderVisible(true); +} + +LLTextureToolTip::~LLTextureToolTip() +{ +} + +void LLTextureToolTip::initFromParams(const LLToolTip::Params& p) +{ + LLToolTip::initFromParams(p); + + // Create and add the preview control + LLView::Params p_preview; + p_preview.name = "texture_preview"; + LLRect rctPreview; + rctPreview.setOriginAndSize(mPadding, mTextBox->getRect().mTop, mPreviewSize, mPreviewSize); + p_preview.rect = rctPreview; + mPreviewView = LLUICtrlFactory::create(p_preview); + addChild(mPreviewView); + + // Parse the control params + const LLSD& sdTextureParams = p.create_params; + if (sdTextureParams.has("thumbnail_id")) + { + mPreviewView->setImageFromAssetId(sdTextureParams["thumbnail_id"].asUUID()); + } + else if (sdTextureParams.has("item_id")) + { + mPreviewView->setImageFromItemId(sdTextureParams["item_id"].asUUID()); + } + + // Currently has to share params with LLToolTip, override values manually + // Todo: provide from own params instead, may be like object inspector does it + LLViewBorder::Params border_params; + border_params.border_thickness(LLPANEL_BORDER_WIDTH); + border_params.highlight_light_color(LLColor4::white); + border_params.highlight_dark_color(LLColor4::white); + border_params.shadow_light_color(LLColor4::white); + border_params.shadow_dark_color(LLColor4::white); + addBorder(border_params); + setBorderVisible(true); + + setBackgroundColor(LLColor4::black); + setBackgroundVisible(true); + setBackgroundOpaque(true); + setBackgroundImage(nullptr); + setTransparentImage(nullptr); + + mTextBox->setColor(LLColor4::white); + + snapToChildren(); +} + +// ============================================================================ diff --git a/indra/llcommon/llerrorthread.h b/indra/newview/llinspecttexture.h similarity index 64% rename from indra/llcommon/llerrorthread.h rename to indra/newview/llinspecttexture.h index 474cef3a50..ff0d80b825 100644 --- a/indra/llcommon/llerrorthread.h +++ b/indra/newview/llinspecttexture.h @@ -1,46 +1,49 @@ -/** - * @file llerrorthread.h - * @brief Specialized thread to handle runtime errors. +/** + * @file llinspecttexture.h * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ + * $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_LLERRORTHREAD_H -#define LL_LLERRORTHREAD_H +#pragma once -#include "llthread.h" +#include "lltooltip.h" -class LL_COMMON_API LLErrorThread : public LLThread +class LLTexturePreviewView; + +namespace LLInspectTextureUtil +{ + LLToolTip* createInventoryToolTip(LLToolTip::Params p); +} + +class LLTextureToolTip : public LLToolTip { public: - LLErrorThread(); - ~LLErrorThread(); + LLTextureToolTip(const LLToolTip::Params& p); + ~LLTextureToolTip(); - /*virtual*/ void run(void); - void setUserData(void *user_data); - void *getUserData() const; +public: + void initFromParams(const LLToolTip::Params& p) override; protected: - void* mUserDatap; // User data associated with this thread + LLTexturePreviewView* mPreviewView; + S32 mPreviewSize; }; - -#endif // LL_LLERRORTHREAD_H diff --git a/indra/newview/llinspecttoast.cpp b/indra/newview/llinspecttoast.cpp index 68801b0895..6f93a78ca6 100644 --- a/indra/newview/llinspecttoast.cpp +++ b/indra/newview/llinspecttoast.cpp @@ -47,6 +47,7 @@ public: /*virtual*/ void onOpen(const LLSD& notification_id); /*virtual*/ BOOL handleToolTip(S32 x, S32 y, MASK mask); + /*virtual*/ void deleteAllChildren(); /*virtual*/ void removeChild(LLView* child); private: void onToastDestroy(LLToast * toast); @@ -122,6 +123,12 @@ BOOL LLInspectToast::handleToolTip(S32 x, S32 y, MASK mask) return LLFloater::handleToolTip(x, y, mask); } +void LLInspectToast::deleteAllChildren() +{ + mPanel = NULL; + LLInspect::deleteAllChildren(); +} + // virtual void LLInspectToast::removeChild(LLView* child) { diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 2665cb1b92..22dd884b91 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -41,7 +41,6 @@ #include "llfloateropenobject.h" #include "llfloaterreg.h" #include "llfloatermarketplacelistings.h" -#include "llfloateroutfitphotopreview.h" #include "llfloatersidepanelcontainer.h" #include "llsidepanelinventory.h" #include "llfloaterworldmap.h" @@ -90,29 +89,13 @@ void copy_slurl_to_clipboard_callback_inv(const std::string& slurl); -typedef std::pair two_uuids_t; -typedef std::list two_uuids_list_t; - const F32 SOUND_GAIN = 1.0f; -struct LLMoveInv -{ - LLUUID mObjectID; - LLUUID mCategoryID; - two_uuids_list_t mMoveList; - void (*mCallback)(S32, void*); - void* mUserData; -}; - using namespace LLOldEvents; // Function declarations -bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr); bool confirm_attachment_rez(const LLSD& notification, const LLSD& response); void teleport_via_landmark(const LLUUID& asset_id); -static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); -static bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit); -static BOOL can_move_to_landmarks(LLInventoryItem* inv_item); static bool check_category(LLInventoryModel* model, const LLUUID& cat_id, LLInventoryPanel* active_panel, @@ -138,6 +121,12 @@ bool isMarketplaceSendAction(const std::string& action) return ("send_to_marketplace" == action); } +bool isPanelActive(const std::string& panel_name) +{ + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + return (active_panel && (active_panel->getName() == panel_name)); +} + // Used by LLFolderBridge as callback for directory fetching recursion class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver { @@ -173,6 +162,65 @@ public: } }; +class LLPasteIntoFolderCallback: public LLInventoryCallback +{ +public: + LLPasteIntoFolderCallback(LLHandle& handle) + : mInventoryPanel(handle) + { + } + ~LLPasteIntoFolderCallback() + { + processItems(); + } + + void fire(const LLUUID& inv_item) + { + mChangedIds.push_back(inv_item); + } + + void processItems() + { + LLInventoryPanel* panel = mInventoryPanel.get(); + bool has_elements = false; + for (LLUUID& inv_item : mChangedIds) + { + LLInventoryItem* item = gInventory.getItem(inv_item); + if (item && panel) + { + LLUUID root_id = panel->getRootFolderID(); + + if (inv_item == root_id) + { + return; + } + + LLFolderViewItem* item = panel->getItemByID(inv_item); + if (item) + { + if (!has_elements) + { + panel->clearSelection(); + panel->getRootFolder()->clearSelection(); + panel->getRootFolder()->requestArrange(); + panel->getRootFolder()->update(); + has_elements = true; + } + panel->getRootFolder()->changeSelection(item, TRUE); + } + } + } + + if (has_elements) + { + panel->getRootFolder()->scrollToShowSelection(); + } + } +private: + LLHandle mInventoryPanel; + std::vector mChangedIds; +}; + // +=================================================+ // | LLInvFVBridge | // +=================================================+ @@ -212,54 +260,17 @@ const std::string& LLInvFVBridge::getDisplayName() const std::string LLInvFVBridge::getSearchableDescription() const { - const LLInventoryModel* model = getInventoryModel(); - if (model) - { - const LLInventoryItem *item = model->getItem(mUUID); - if(item) - { - std::string desc = item->getDescription(); - LLStringUtil::toUpper(desc); - return desc; - } - } - return LLStringUtil::null; + return get_searchable_description(getInventoryModel(), mUUID); } std::string LLInvFVBridge::getSearchableCreatorName() const { - const LLInventoryModel* model = getInventoryModel(); - if (model) - { - const LLInventoryItem *item = model->getItem(mUUID); - if(item) - { - LLAvatarName av_name; - if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) - { - std::string username = av_name.getUserName(); - LLStringUtil::toUpper(username); - return username; - } - } - } - return LLStringUtil::null; + return get_searchable_creator_name(getInventoryModel(), mUUID); } std::string LLInvFVBridge::getSearchableUUIDString() const { - const LLInventoryModel* model = getInventoryModel(); - if (model) - { - const LLViewerInventoryItem *item = model->getItem(mUUID); - if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery())) - { - std::string uuid = item->getAssetUUID().asString(); - LLStringUtil::toUpper(uuid); - return uuid; - } - } - return LLStringUtil::null; + return get_searchable_UUID(getInventoryModel(), mUUID); } // Folders have full perms @@ -327,7 +338,7 @@ BOOL LLInvFVBridge::cutToClipboard() const LLInventoryObject* obj = gInventory.getObject(mUUID); if (obj && isItemMovable() && isItemRemovable()) { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const BOOL cut_from_marketplacelistings = gInventory.isObjectDescendentOf(mUUID, marketplacelistings_id); if (cut_from_marketplacelistings && (LLMarketplaceData::instance().isInActiveFolder(mUUID) || @@ -407,6 +418,32 @@ void LLInvFVBridge::showProperties() } } +void LLInvFVBridge::navigateToFolder(bool new_window, bool change_mode) +{ + if(new_window) + { + mInventoryPanel.get()->openSingleViewInventory(mUUID); + } + else + { + if(change_mode) + { + LLInventoryPanel::setSFViewAndOpenFolder(mInventoryPanel.get(), mUUID); + } + else + { + LLInventorySingleFolderPanel* panel = dynamic_cast(mInventoryPanel.get()); + if (!panel || !getInventoryModel() || mUUID.isNull()) + { + return; + } + + panel->changeFolderRoot(mUUID); + } + + } +} + void LLInvFVBridge::removeBatch(std::vector& batch) { // Deactivate gestures when moving them into Trash @@ -800,6 +837,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, menuentry_vec_t &disabled_items, U32 flags) { const LLInventoryObject *obj = getInventoryObject(); + bool single_folder_root = (mRoot == NULL); if (obj) { @@ -814,7 +852,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Copy")); } - if (isAgentInventory()) + if (isAgentInventory() && !single_folder_root) { items.push_back(std::string("New folder from selected")); items.push_back(std::string("Subfolder Separator")); @@ -848,7 +886,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, items.push_back(std::string("Find Links")); } - if (!isInboxFolder()) + if (!isInboxFolder() && !single_folder_root) { items.push_back(std::string("Rename")); if (!isItemRenameable() || ((flags & FIRST_SELECTED_ITEM) == 0)) @@ -856,14 +894,20 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, disabled_items.push_back(std::string("Rename")); } } - + + items.push_back(std::string("thumbnail")); + if (isLibraryItem()) + { + disabled_items.push_back(std::string("thumbnail")); + } + + LLViewerInventoryItem *inv_item = gInventory.getItem(mUUID); if (show_asset_id) { items.push_back(std::string("Copy Asset UUID")); bool is_asset_knowable = false; - LLViewerInventoryItem* inv_item = gInventory.getItem(mUUID); if (inv_item) { is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(inv_item->getType()); @@ -876,6 +920,8 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, } } + if(!single_folder_root) + { items.push_back(std::string("Cut")); if (!isItemMovable() || !isItemRemovable()) { @@ -897,6 +943,7 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, } } } + } } } @@ -925,16 +972,12 @@ void LLInvFVBridge::getClipboardEntries(bool show_asset_id, items.push_back(std::string("Paste Separator")); } - addDeleteContextMenuOptions(items, disabled_items); + if(!single_folder_root) + { + addDeleteContextMenuOptions(items, disabled_items); + } - // If multiple items are selected, disable properties (if it exists). - if ((flags & FIRST_SELECTED_ITEM) == 0) - { - disabled_items.push_back(std::string("Properties")); - } - - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); - if (active_panel && (active_panel->getName() != "All Items")) + if (!isPanelActive("All Items") && !isPanelActive("comb_single_folder_inv")) { items.push_back(std::string("Show in Main Panel")); } @@ -1025,7 +1068,7 @@ void LLInvFVBridge::addDeleteContextMenuOptions(menuentry_vec_t &items, items.push_back(std::string("Delete")); - if (!isItemRemovable()) + if (!isItemRemovable() || isPanelActive("Favorite Items")) { disabled_items.push_back(std::string("Delete")); } @@ -1256,6 +1299,16 @@ BOOL LLInvFVBridge::isLinkedObjectInTrash() const return FALSE; } +bool LLInvFVBridge::isItemInOutfits() const +{ + const LLInventoryModel* model = getInventoryModel(); + if(!model) return false; + + const LLUUID my_outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + return isCOFFolder() || (my_outfits_cat == mUUID) || model->isObjectDescendentOf(mUUID, my_outfits_cat); +} + BOOL LLInvFVBridge::isLinkedObjectMissing() const { const LLInventoryObject *obj = getInventoryObject(); @@ -1286,7 +1339,7 @@ BOOL LLInvFVBridge::isCOFFolder() const // *TODO : Suppress isInboxFolder() once Merchant Outbox is fully deprecated BOOL LLInvFVBridge::isInboxFolder() const { - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); if (inbox_id.isNull()) { @@ -1298,7 +1351,7 @@ BOOL LLInvFVBridge::isInboxFolder() const BOOL LLInvFVBridge::isMarketplaceListingsFolder() const { - const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID folder_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (folder_id.isNull()) { @@ -1606,7 +1659,7 @@ bool LLInvFVBridge::canListOnMarketplaceNow() const { std::string error_msg; LLInventoryModel* model = getInventoryModel(); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.notNull()) { LLViewerInventoryCategory * master_folder = model->getCategory(marketplacelistings_id); @@ -1705,6 +1758,12 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) restoreItem(); return; } + else if ("thumbnail" == action) + { + LLSD data(mUUID); + LLFloaterReg::showInstance("change_item_thumbnail", data); + return; + } else if ("copy_uuid" == action) { // Single item only @@ -1759,7 +1818,7 @@ void LLItemBridge::performAction(LLInventoryModel* model, std::string action) { LLInventoryItem* itemp = model->getItem(mUUID); if (!itemp) return; - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); // Note: For a single item, if it's not a copy, then it's a move move_item_to_marketplacelistings(itemp, marketplacelistings_id, ("copy_to_marketplace_listings" == action)); } @@ -1973,9 +2032,9 @@ std::string LLItemBridge::getLabelSuffix() const { // String table is loaded before login screen and inventory items are // loaded after login, so LLTrans should be ready. - static std::string NO_COPY = LLTrans::getString("no_copy"); - static std::string NO_MOD = LLTrans::getString("no_modify"); - static std::string NO_XFER = LLTrans::getString("no_transfer"); + static std::string NO_COPY = LLTrans::getString("no_copy_lbl"); + static std::string NO_MOD = LLTrans::getString("no_modify_lbl"); + static std::string NO_XFER = LLTrans::getString("no_transfer_lbl"); static std::string LINK = LLTrans::getString("link"); static std::string BROKEN_LINK = LLTrans::getString("broken_link"); std::string suffix; @@ -1996,17 +2055,20 @@ std::string LLItemBridge::getLabelSuffix() const BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID()); if (!copy) { + suffix += " "; suffix += NO_COPY; } BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID()); if (!mod) { - suffix += NO_MOD; + suffix += suffix.empty() ? " " : ","; + suffix += NO_MOD; } BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); if (!xfer) { + suffix += suffix.empty() ? " " : ","; suffix += NO_XFER; } } @@ -2175,6 +2237,21 @@ LLViewerInventoryItem* LLItemBridge::getItem() const return item; } +const LLUUID& LLItemBridge::getThumbnailUUID() const +{ + LLViewerInventoryItem* item = NULL; + LLInventoryModel* model = getInventoryModel(); + if(model) + { + item = (LLViewerInventoryItem*)model->getItem(mUUID); + } + if (item) + { + return item->getThumbnailUUID(); + } + return LLUUID::null; +} + BOOL LLItemBridge::isItemPermissive() const { LLViewerInventoryItem* item = getItem(); @@ -2258,13 +2335,32 @@ void LLFolderBridge::buildDisplayName() const std::string LLFolderBridge::getLabelSuffix() const { static LLCachedControl folder_loading_message_delay(gSavedSettings, "FolderLoadingMessageWaitTime", 0.5f); + static LLCachedControl xui_debug(gSavedSettings, "DebugShowXUINames", 0); if (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= folder_loading_message_delay()) { return llformat(" ( %s ) ", LLTrans::getString("LoadingData").c_str()); } std::string suffix = ""; - if(mShowDescendantsCount) + if (xui_debug) + { + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(getUUID(), cats, items); + + LLViewerInventoryCategory* cat = gInventory.getCategory(getUUID()); + if (cat) + { + LLStringUtil::format_map_t args; + args["[FOLDER_COUNT]"] = llformat("%d", cats->size()); + args["[ITEMS_COUNT]"] = llformat("%d", items->size()); + args["[VERSION]"] = llformat("%d", cat->getVersion()); + args["[VIEWER_DESCENDANT_COUNT]"] = llformat("%d", cats->size() + items->size()); + args["[SERVER_DESCENDANT_COUNT]"] = llformat("%d", cat->getDescendentCount()); + suffix = " " + LLTrans::getString("InventoryFolderDebug", args); + } + } + else if(mShowDescendantsCount) { LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; @@ -2288,6 +2384,16 @@ LLFontGL::StyleFlags LLFolderBridge::getLabelStyle() const return LLFontGL::NORMAL; } +const LLUUID& LLFolderBridge::getThumbnailUUID() const +{ + LLViewerInventoryCategory* cat = getCategory(); + if (cat) + { + return cat->getThumbnailUUID(); + } + return LLUUID::null; +} + void LLFolderBridge::update() { // we know we have children but haven't fetched them (doesn't obey filter) @@ -2498,7 +2604,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link, - BOOL user_confirm) + BOOL user_confirm, + LLPointer cb) { LLInventoryModel* model = getInventoryModel(); @@ -2515,8 +2622,8 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (!filter) return false; const LLUUID &cat_id = inv_cat->getUUID(); - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const LLUUID from_folder_uuid = inv_cat->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); @@ -2534,10 +2641,10 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, if (is_agent_inventory) { - const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); - const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); - const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND, false); + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); @@ -2803,7 +2910,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, { // Category can contains objects, // create a new folder and populate it with links to original objects - dropToMyOutfits(inv_cat); + dropToMyOutfits(inv_cat, cb); } // if target is current outfit folder we use link else if (move_is_into_current_outfit && @@ -2813,14 +2920,16 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, // traverse category and add all contents to currently worn. BOOL append = true; LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); + if (cb) cb->fire(inv_cat->getUUID()); } else if (move_is_into_marketplacelistings) { move_folder_to_marketplacelistings(inv_cat, mUUID); + if (cb) cb->fire(inv_cat->getUUID()); } else { - if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX))) { set_dad_inbox_object(cat_id); } @@ -2832,6 +2941,7 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, (LLViewerInventoryCategory*)inv_cat, mUUID, move_is_into_trash); + if (cb) cb->fire(inv_cat->getUUID()); } if (move_is_from_marketplacelistings) { @@ -2850,14 +2960,20 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); if (version_folder_id.notNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); - if (!validate_marketplacelistings(cat,NULL)) + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) { - LLMarketplaceData::instance().activateListing(version_folder_id,false); + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } } + ); } // In all cases, update the listing we moved from so suffix are updated update_marketplace_category(from_folder_uuid); + if (cb) cb->fire(inv_cat->getUUID()); } } } @@ -2871,7 +2987,22 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat, } else { - accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, NULL, NULL, filter); + // Todo: fix me. moving from task inventory doesn't have a completion callback, + // yet making a copy creates new item id so this doesn't work right + std::function callback = [cb](S32, void*, const LLMoveInv* move_inv) mutable + { + two_uuids_list_t::const_iterator move_it; + for (move_it = move_inv->mMoveList.begin(); + move_it != move_inv->mMoveList.end(); + ++move_it) + { + if (cb) + { + cb->fire(move_it->second); + } + } + }; + accept = move_inv_category_world_to_agent(cat_id, mUUID, drop, callback, NULL, filter); } } else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) @@ -2942,7 +3073,7 @@ void warn_move_inventory(LLViewerObject* object, boost::shared_ptr mo BOOL move_inv_category_world_to_agent(const LLUUID& object_id, const LLUUID& category_id, BOOL drop, - void (*callback)(S32, void*), + std::function callback, void* user_data, LLInventoryFilter* filter) { @@ -3245,6 +3376,12 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) return; } + else if ("thumbnail" == action) + { + LLSD data(mUUID); + LLFloaterReg::showInstance("change_item_thumbnail", data); + return; + } else if ("paste" == action) { pasteFromClipboard(); @@ -3314,18 +3451,26 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) if (depth_nesting_in_marketplace(mUUID) == 1) { LLUUID version_folder_id = LLMarketplaceData::instance().getVersionFolder(mUUID); - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); mMessage = ""; - if (!validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3))) + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [this](bool result) { - LLSD subs; - subs["[ERROR_CODE]"] = mMessage; - LLNotificationsUtil::add("MerchantListingFailed", subs); - } - else - { - LLMarketplaceData::instance().activateListing(mUUID,true); - } + // todo: might need to ensure bridge/mUUID exists or this will cause crashes + if (!result) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + else + { + LLMarketplaceData::instance().activateListing(mUUID, true); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3) + ); } return; } @@ -3333,18 +3478,27 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { if (depth_nesting_in_marketplace(mUUID) == 2) { - LLInventoryCategory* category = gInventory.getCategory(mUUID); mMessage = ""; - if (!validate_marketplacelistings(category,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false,2)) + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) { - LLSD subs; - subs["[ERROR_CODE]"] = mMessage; - LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); - } - else - { - LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); - } + if (!result) + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantFolderActivationFailed", subs); + } + else + { + LLInventoryCategory* category = gInventory.getCategory(mUUID); + LLMarketplaceData::instance().setVersionFolder(category->getParentUUID(), mUUID); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + false, + 2); } return; } @@ -3367,29 +3521,44 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) } else if ("marketplace_create_listing" == action) { - LLViewerInventoryCategory* cat = gInventory.getCategory(mUUID); mMessage = ""; - bool validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),false); - if (!validates) + + // first run vithout fix_hierarchy, second run with fix_hierarchy + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) { - mMessage = ""; - validates = validate_marketplacelistings(cat,boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3),true); - if (validates) + if (!result) { - LLNotificationsUtil::add("MerchantForceValidateListing"); + mMessage = ""; + + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + mUUID, + [this](bool result) + { + if (result) + { + LLNotificationsUtil::add("MerchantForceValidateListing"); + LLMarketplaceData::instance().createListing(mUUID); + } + else + { + LLSD subs; + subs["[ERROR_CODE]"] = mMessage; + LLNotificationsUtil::add("MerchantListingFailed", subs); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + true); } - } + else + { + LLMarketplaceData::instance().createListing(mUUID); + } + }, + boost::bind(&LLFolderBridge::gatherMessage, this, _1, _2, _3), + false); - if (!validates) - { - LLSD subs; - subs["[ERROR_CODE]"] = mMessage; - LLNotificationsUtil::add("MerchantListingFailed", subs); - } - else - { - LLMarketplaceData::instance().createListing(mUUID); - } return; } else if ("marketplace_disassociate_listing" == action) @@ -3433,7 +3602,7 @@ void LLFolderBridge::performAction(LLInventoryModel* model, std::string action) { LLInventoryCategory * cat = gInventory.getCategory(mUUID); if (!cat) return; - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); move_folder_to_marketplacelistings(cat, marketplacelistings_id, ("move_to_marketplace_listings" != action), (("copy_or_move_to_marketplace_listings" == action))); } } @@ -3683,7 +3852,7 @@ void LLFolderBridge::pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if (model && isClipboardPasteable()) { - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); const BOOL paste_into_marketplacelistings = model->isObjectDescendentOf(mUUID, marketplacelistings_id); BOOL cut_from_marketplacelistings = FALSE; @@ -3744,11 +3913,11 @@ void LLFolderBridge::perform_pasteFromClipboard() LLInventoryModel* model = getInventoryModel(); if (model && isClipboardPasteable()) { - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); - const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); @@ -3759,6 +3928,13 @@ void LLFolderBridge::perform_pasteFromClipboard() std::vector objects; LLClipboard::instance().pasteFromClipboard(objects); + + LLPointer cb = NULL; + LLInventoryPanel* panel = mInventoryPanel.get(); + if (panel->getRootFolder()->isSingleFolderMode() && panel->getRootFolderID() == mUUID) + { + cb = new LLPasteIntoFolderCallback(mInventoryPanel); + } LLViewerInventoryCategory * dest_folder = getCategory(); if (move_is_into_marketplacelistings) @@ -3834,7 +4010,7 @@ void LLFolderBridge::perform_pasteFromClipboard() { if (!move_is_into_my_outfits && item && can_move_to_outfit(item, move_is_into_current_outfit)) { - dropToOutfit(item, move_is_into_current_outfit); + dropToOutfit(item, move_is_into_current_outfit, cb); } else if (move_is_into_my_outfits && LLAssetType::AT_CATEGORY == obj->getType()) { @@ -3842,7 +4018,7 @@ void LLFolderBridge::perform_pasteFromClipboard() U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit"); if (cat && can_move_to_my_outfits(model, cat, max_items_to_wear)) { - dropToMyOutfits(cat); + dropToMyOutfits(cat, cb); } else { @@ -3858,7 +4034,7 @@ void LLFolderBridge::perform_pasteFromClipboard() { if (item && can_move_to_outfit(item, move_is_into_current_outfit)) { - dropToOutfit(item, move_is_into_current_outfit); + dropToOutfit(item, move_is_into_current_outfit, cb); } else { @@ -3877,11 +4053,12 @@ void LLFolderBridge::perform_pasteFromClipboard() { //changeItemParent() implicity calls dirtyFilter changeItemParent(model, viitem, parent_id, FALSE); + if (cb) cb->fire(item_id); } } else { - dropToFavorites(item); + dropToFavorites(item, cb); } } } @@ -3909,6 +4086,7 @@ void LLFolderBridge::perform_pasteFromClipboard() //changeCategoryParent() implicity calls dirtyFilter changeCategoryParent(model, vicat, parent_id, FALSE); } + if (cb) cb->fire(item_id); } } else @@ -3930,6 +4108,7 @@ void LLFolderBridge::perform_pasteFromClipboard() //changeItemParent() implicity calls dirtyFilter changeItemParent(model, viitem, parent_id, FALSE); } + if (cb) cb->fire(item_id); } } } @@ -3950,6 +4129,7 @@ void LLFolderBridge::perform_pasteFromClipboard() { copy_inventory_category(model, vicat, parent_id); } + if (cb) cb->fire(item_id); } } else @@ -3965,11 +4145,13 @@ void LLFolderBridge::perform_pasteFromClipboard() // Stop pasting into the marketplace as soon as we get an error break; } + if (cb) cb->fire(item_id); } else if (item->getIsLinkType()) { - link_inventory_object(parent_id, item_id, - LLPointer(NULL)); + link_inventory_object(parent_id, + item_id, + cb); } else { @@ -3979,7 +4161,7 @@ void LLFolderBridge::perform_pasteFromClipboard() item->getUUID(), parent_id, std::string(), - LLPointer(NULL)); + cb); } } } @@ -3996,9 +4178,9 @@ void LLFolderBridge::pasteLinkFromClipboard() LLInventoryModel* model = getInventoryModel(); if(model) { - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); const BOOL move_is_into_my_outfits = (mUUID == my_outifts_id) || model->isObjectDescendentOf(mUUID, my_outifts_id); @@ -4015,6 +4197,14 @@ void LLFolderBridge::pasteLinkFromClipboard() std::vector objects; LLClipboard::instance().pasteFromClipboard(objects); + + LLPointer cb = NULL; + LLInventoryPanel* panel = mInventoryPanel.get(); + if (panel->getRootFolder()->isSingleFolderMode()) + { + cb = new LLPasteIntoFolderCallback(mInventoryPanel); + } + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) @@ -4025,12 +4215,12 @@ void LLFolderBridge::pasteLinkFromClipboard() LLInventoryItem *item = model->getItem(object_id); if (item && can_move_to_outfit(item, move_is_into_current_outfit)) { - dropToOutfit(item, move_is_into_current_outfit); + dropToOutfit(item, move_is_into_current_outfit, cb); } } else if (LLConstPointer obj = model->getObject(object_id)) { - link_inventory_object(parent_id, obj, LLPointer(NULL)); + link_inventory_object(parent_id, obj, cb); } } // Change mode to paste for next paste @@ -4068,8 +4258,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); const LLUUID &favorites = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); - const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID &marketplace_listings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); if (outfits_id == mUUID) { @@ -4091,13 +4281,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items } disabled_items.push_back(std::string("New Folder")); - disabled_items.push_back(std::string("New Script")); - disabled_items.push_back(std::string("New Note")); - disabled_items.push_back(std::string("New Settings")); - disabled_items.push_back(std::string("New Gesture")); - disabled_items.push_back(std::string("New Material")); - disabled_items.push_back(std::string("New Clothes")); - disabled_items.push_back(std::string("New Body Parts")); disabled_items.push_back(std::string("upload_def")); } if (favorites == mUUID) @@ -4120,12 +4303,6 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK) { disabled_items.push_back(std::string("New Folder")); - disabled_items.push_back(std::string("New Script")); - disabled_items.push_back(std::string("New Note")); - disabled_items.push_back(std::string("New Gesture")); - disabled_items.push_back(std::string("New Material")); - disabled_items.push_back(std::string("New Clothes")); - disabled_items.push_back(std::string("New Body Parts")); disabled_items.push_back(std::string("upload_def")); } if (marketplace_listings_id == mUUID) @@ -4135,14 +4312,14 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items disabled_items.push_back(std::string("Cut")); disabled_items.push_back(std::string("Delete")); } + + if (isPanelActive("Favorite Items")) + { + disabled_items.push_back(std::string("Delete")); + } if(trash_id == mUUID) { - bool is_recent_panel = false; - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); - if (active_panel && (active_panel->getName() == "Recent Items")) - { - is_recent_panel = true; - } + bool is_recent_panel = isPanelActive("Recent Items"); // This is the trash. items.push_back(std::string("Empty Trash")); @@ -4161,6 +4338,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items { disabled_items.push_back(std::string("Empty Trash")); } + + items.push_back(std::string("thumbnail")); } else if(isItemInTrash()) { @@ -4187,22 +4366,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items } if (!isMarketplaceListingsFolder()) { - items.push_back(std::string("New Script")); - items.push_back(std::string("New Note")); - items.push_back(std::string("New Gesture")); - items.push_back(std::string("New Material")); - items.push_back(std::string("New Clothes")); - items.push_back(std::string("New Body Parts")); - items.push_back(std::string("New Settings")); items.push_back(std::string("upload_def")); - - menu_items_added = true; - - if (!LLEnvironment::instance().isInventoryEnabled()) - { - disabled_items.push_back("New Settings"); - } - } if (menu_items_added) { @@ -4217,6 +4381,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) { items.push_back(std::string("Rename")); + items.push_back(std::string("thumbnail")); addDeleteContextMenuOptions(items, disabled_items); // EXT-4030: disallow deletion of currently worn outfit @@ -4231,6 +4396,7 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items if (model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) == mUUID) { items.push_back(std::string("Copy outfit list to clipboard")); + addOpenFolderMenuOptions(flags, items); } //Added by aura to force inventory pull on right-click to display folder options correctly. 07-17-06 @@ -4337,9 +4503,12 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& if(!category) return; const LLUUID trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); - if (trash_id == mUUID) return; - if (isItemInTrash()) return; - + if ((trash_id == mUUID) || isItemInTrash()) + { + addOpenFolderMenuOptions(flags, items); + return; + } + if (!isItemRemovable()) { disabled_items.push_back(std::string("Delete")); @@ -4352,7 +4521,7 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& const bool is_agent_inventory = isAgentInventory(); // Only enable calling-card related options for non-system folders. - if (!is_system_folder && is_agent_inventory) + if (!is_system_folder && is_agent_inventory && (mRoot != NULL)) { LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD); if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard)) @@ -4372,6 +4541,14 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& disabled_items.push_back(std::string("New folder from selected")); } + //skip the rest options in single-folder mode + if (mRoot == NULL) + { + return; + } + + addOpenFolderMenuOptions(flags, items); + #ifndef LL_RELEASE_FOR_DOWNLOAD if (LLFolderType::lookupIsProtectedType(type) && is_agent_inventory) { @@ -4397,26 +4574,29 @@ void LLFolderBridge::buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& if (type != LLFolderType::FT_OUTFIT) { items.push_back(std::string("Add To Outfit")); + if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID)) + { + disabled_items.push_back(std::string("Add To Outfit")); + } } items.push_back(std::string("Replace Outfit")); + if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) + { + disabled_items.push_back(std::string("Replace Outfit")); + } } if (is_agent_inventory) { items.push_back(std::string("Folder Wearables Separator")); + // Note: If user tries to unwear "My Inventory", it's going to deactivate everything including gestures + // Might be safer to disable this for "My Inventory" items.push_back(std::string("Remove From Outfit")); - if (!LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) - { - disabled_items.push_back(std::string("Remove From Outfit")); - } - } - if (!LLAppearanceMgr::instance().getCanReplaceCOF(mUUID)) - { - disabled_items.push_back(std::string("Replace Outfit")); - } - if (!LLAppearanceMgr::instance().getCanAddToCOF(mUUID)) - { - disabled_items.push_back(std::string("Add To Outfit")); + if (type != LLFolderType::FT_ROOT_INVENTORY // Unless COF is empty, whih shouldn't be, warrantied to have worn items + && !LLAppearanceMgr::getCanRemoveFromCOF(mUUID)) // expensive from root! + { + disabled_items.push_back(std::string("Remove From Outfit")); + } } items.push_back(std::string("Outfit Separator")); @@ -4449,6 +4629,20 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) menu.arrangeAndClear(); } +void LLFolderBridge::addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items) +{ + if ((flags & ITEM_IN_MULTI_SELECTION) == 0) + { + items.push_back(std::string("open_in_new_window")); + items.push_back(std::string("Open Folder Separator")); + items.push_back(std::string("Copy Separator")); + if(isPanelActive("comb_single_folder_inv")) + { + items.push_back(std::string("open_in_current_window")); + } + } +} + bool LLFolderBridge::hasChildren() const { LLInventoryModel* model = getInventoryModel(); @@ -4465,6 +4659,18 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, { LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + static LLPointer drop_cb = NULL; + LLInventoryPanel* panel = mInventoryPanel.get(); + LLToolDragAndDrop* drop_tool = LLToolDragAndDrop::getInstance(); + if (drop + && panel->getRootFolder()->isSingleFolderMode() + && panel->getRootFolderID() == mUUID + && drop_tool->getCargoIndex() == 0) + { + drop_cb = new LLPasteIntoFolderCallback(mInventoryPanel); + } + + //LL_INFOS() << "LLFolderBridge::dragOrDrop()" << LL_ENDL; BOOL accept = FALSE; switch(cargo_type) @@ -4483,7 +4689,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, case DAD_MESH: case DAD_SETTINGS: case DAD_MATERIAL: - accept = dragItemIntoFolder(inv_item, drop, tooltip_msg); + accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, TRUE, drop_cb); break; case DAD_LINK: // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. @@ -4494,12 +4700,12 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); if (linked_category) { - accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE); + accept = dragCategoryIntoFolder((LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE, TRUE, drop_cb); } } else { - accept = dragItemIntoFolder(inv_item, drop, tooltip_msg); + accept = dragItemIntoFolder(inv_item, drop, tooltip_msg, TRUE, drop_cb); } break; case DAD_CATEGORY: @@ -4509,7 +4715,7 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, } else { - accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg); + accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data, drop, tooltip_msg, FALSE, TRUE, drop_cb); } break; case DAD_ROOT_CATEGORY: @@ -4519,6 +4725,11 @@ BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop, LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL; break; } + + if (!drop || drop_tool->getCargoIndex() + 1 == drop_tool->getCargoCount()) + { + drop_cb = NULL; + } return accept; } @@ -4819,138 +5030,56 @@ bool move_task_inventory_callback(const LLSD& notification, const LLSD& response if (move_inv->mCallback) { - move_inv->mCallback(option, move_inv->mUserData); + move_inv->mCallback(option, move_inv->mUserData, move_inv.get()); } move_inv.reset(); //since notification will persist return false; } -// Returns true if the item can be moved to Current Outfit or any outfit folder. -static BOOL can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +void drop_to_favorites_cb(const LLUUID& id, LLPointer cb1, LLPointer cb2) { - LLInventoryType::EType inv_type = inv_item->getInventoryType(); - if ((inv_type != LLInventoryType::IT_WEARABLE) && - (inv_type != LLInventoryType::IT_GESTURE) && - (inv_type != LLInventoryType::IT_ATTACHMENT) && - (inv_type != LLInventoryType::IT_OBJECT) && - (inv_type != LLInventoryType::IT_SNAPSHOT) && - (inv_type != LLInventoryType::IT_TEXTURE)) - { - return FALSE; - } - - U32 flags = inv_item->getFlags(); - if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) - { - return FALSE; - } - - if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT)) - { - return !move_is_into_current_outfit; - } - - if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) - { - return FALSE; - } - - return TRUE; + cb1->fire(id); + cb2->fire(id); } -// Returns true if folder's content can be moved to Current Outfit or any outfit folder. -static bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit) -{ - LLInventoryModel::cat_array_t *cats; - LLInventoryModel::item_array_t *items; - model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items); - - if (items->size() > wear_limit) - { - return false; - } - - if (items->size() == 0) - { - // Nothing to move(create) - return false; - } - - if (cats->size() > 0) - { - // We do not allow subfolders in outfits of "My Outfits" yet - return false; - } - - LLInventoryModel::item_array_t::iterator iter = items->begin(); - LLInventoryModel::item_array_t::iterator end = items->end(); - - while (iter != end) - { - LLViewerInventoryItem *item = *iter; - if (!can_move_to_outfit(item, false)) - { - return false; - } - iter++; - } - - return true; -} - -// Returns TRUE if item is a landmark or a link to a landmark -// and can be moved to Favorites or Landmarks folder. -static BOOL can_move_to_landmarks(LLInventoryItem* inv_item) -{ - // Need to get the linked item to know its type because LLInventoryItem::getType() - // returns actual type AT_LINK for links, not the asset type of a linked item. - if (LLAssetType::AT_LINK == inv_item->getType()) - { - LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); - if (linked_item) - { - return LLAssetType::AT_LANDMARK == linked_item->getType(); - } - } - - return LLAssetType::AT_LANDMARK == inv_item->getType(); -} - -void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item) +void LLFolderBridge::dropToFavorites(LLInventoryItem* inv_item, LLPointer cb) { // use callback to rearrange favorite landmarks after adding // to have new one placed before target (on which it was dropped). See EXT-4312. - LLPointer cb = new AddFavoriteLandmarkCallback(); + LLPointer cb_fav = new AddFavoriteLandmarkCallback(); LLInventoryPanel* panel = mInventoryPanel.get(); LLFolderViewItem* drag_over_item = panel ? panel->getRootFolder()->getDraggingOverItem() : NULL; LLFolderViewModelItemInventory* view_model = drag_over_item ? static_cast(drag_over_item->getViewModelItem()) : NULL; if (view_model) { - cb.get()->setTargetLandmarkId(view_model->getUUID()); + cb_fav.get()->setTargetLandmarkId(view_model->getUUID()); } + LLPointer callback = cb_fav; + if (cb) + { + callback = new LLBoostFuncInventoryCallback(boost::bind(drop_to_favorites_cb, _1, cb, cb_fav)); + } + copy_inventory_item( gAgent.getID(), inv_item->getPermissions().getOwner(), inv_item->getUUID(), mUUID, std::string(), - cb); + callback); } -void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer cb) { if((inv_item->getInventoryType() == LLInventoryType::IT_TEXTURE) || (inv_item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)) { - const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID &my_outifts_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); if(mUUID != my_outifts_id) { - LLFloaterOutfitPhotoPreview* photo_preview = LLFloaterReg::showTypedInstance("outfit_photo_preview", inv_item->getUUID()); - if(photo_preview) - { - photo_preview->setOutfitID(mUUID); - } + // Legacy: prior to thumbnails images in outfits were used for outfit gallery. + LLNotificationsUtil::add("ThumbnailOutfitPhoto"); } return; } @@ -4967,21 +5096,22 @@ void LLFolderBridge::dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_c } } -void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat) +void LLFolderBridge::dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer cb) { // make a folder in the My Outfits directory. const LLUUID dest_id = getInventoryModel()->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); // Note: creation will take time, so passing folder id to callback is slightly unreliable, // but so is collecting and passing descendants' ids - inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1); + inventory_func_type func = boost::bind(&LLFolderBridge::outfitFolderCreatedCallback, this, inv_cat->getUUID(), _1, cb); gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), - func); + func, + inv_cat->getThumbnailUUID()); } -void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id) +void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer cb) { LLInventoryModel::cat_array_t* categories; LLInventoryModel::item_array_t* items; @@ -5012,7 +5142,6 @@ void LLFolderBridge::outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID ca if (!link_array.empty()) { - LLPointer cb = NULL; link_inventory_array(cat_dest_id, link_array, cb); } } @@ -5045,7 +5174,8 @@ void LLFolderBridge::callback_dropCategoryIntoFolder(const LLSD& notification, c BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, - BOOL user_confirm) + BOOL user_confirm, + LLPointer cb) { LLInventoryModel* model = getInventoryModel(); @@ -5059,11 +5189,11 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLInventoryFilter* filter = getInventoryFilter(); if (!filter) return false; - const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, false); - const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE, false); - const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK, false); - const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); - const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); const LLUUID from_folder_uuid = inv_item->getParentUUID(); const BOOL move_is_into_current_outfit = (mUUID == current_outfit_id); @@ -5083,7 +5213,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLViewerObject* object = NULL; if(LLToolDragAndDrop::SOURCE_AGENT == source) { - const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH, false); + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); const BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id); const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); @@ -5225,26 +5355,27 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // (copy the item) else if (move_is_into_favorites) { - dropToFavorites(inv_item); + dropToFavorites(inv_item, cb); } // CURRENT OUTFIT or OUTFIT folder // (link the item) else if (move_is_into_current_outfit || move_is_into_outfit) { - dropToOutfit(inv_item, move_is_into_current_outfit); + dropToOutfit(inv_item, move_is_into_current_outfit, cb); } // MARKETPLACE LISTINGS folder // Move the item else if (move_is_into_marketplacelistings) { move_item_to_marketplacelistings(inv_item, mUUID); + if (cb) cb->fire(inv_item->getUUID()); } // NORMAL or TRASH folder // (move the item, restamp if into trash) else { // set up observer to select item once drag and drop from inbox is complete - if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, false))) + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX))) { set_dad_inbox_object(inv_item->getUUID()); } @@ -5254,6 +5385,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, (LLViewerInventoryItem*)inv_item, mUUID, move_is_into_trash); + if (cb) cb->fire(inv_item->getUUID()); } if (move_is_from_marketplacelistings) @@ -5262,11 +5394,15 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); if (version_folder_id.notNull()) { - LLViewerInventoryCategory* cat = gInventory.getCategory(version_folder_id); - if (!validate_marketplacelistings(cat,NULL)) + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) { - LLMarketplaceData::instance().activateListing(version_folder_id,false); - } + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + }); } } @@ -5334,11 +5470,16 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, if (accept && drop) { + LLUUID item_id = inv_item->getUUID(); boost::shared_ptr move_inv (new LLMoveInv()); move_inv->mObjectID = inv_item->getParentUUID(); - two_uuids_t item_pair(mUUID, inv_item->getUUID()); + two_uuids_t item_pair(mUUID, item_id); move_inv->mMoveList.push_back(item_pair); - move_inv->mCallback = NULL; + if (cb) + { + move_inv->mCallback = [item_id, cb](S32, void*, const LLMoveInv* move_inv) mutable + { cb->fire(item_id); }; + } move_inv->mUserData = NULL; if(is_move) { @@ -5430,13 +5571,13 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, // (copy the item) if (move_is_into_favorites) { - dropToFavorites(inv_item); + dropToFavorites(inv_item, cb); } // CURRENT OUTFIT or OUTFIT folder // (link the item) else if (move_is_into_current_outfit || move_is_into_outfit) { - dropToOutfit(inv_item, move_is_into_current_outfit); + dropToOutfit(inv_item, move_is_into_current_outfit, cb); } else { @@ -5446,7 +5587,7 @@ BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item, inv_item->getUUID(), mUUID, std::string(), - LLPointer(NULL)); + cb); } } } @@ -6001,7 +6142,7 @@ std::string LLCallingCardBridge::getLabelSuffix() const LLViewerInventoryItem* item = getItem(); if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) ) { - return LLItemBridge::getLabelSuffix() + " (online)"; + return LLItemBridge::getLabelSuffix() + " online"; } else { @@ -7618,16 +7759,26 @@ class LLObjectBridgeAction: public LLInvFVBridgeAction public: virtual void doIt() { - /* - LLFloaterReg::showInstance("properties", mUUID); - */ - LLInvFVBridgeAction::doIt(); + attachOrDetach(); } virtual ~LLObjectBridgeAction(){} protected: LLObjectBridgeAction(const LLUUID& id,LLInventoryModel* model) : LLInvFVBridgeAction(id,model) {} + void attachOrDetach(); }; +void LLObjectBridgeAction::attachOrDetach() +{ + if (get_is_item_worn(mUUID)) + { + LLAppearanceMgr::instance().removeItemFromAvatar(mUUID); + } + else + { + LLAppearanceMgr::instance().wearItemOnAvatar(mUUID, true, false); // Don't replace if adding. + } +} + class LLLSLTextBridgeAction: public LLInvFVBridgeAction { friend class LLInvFVBridgeAction; @@ -7686,7 +7837,17 @@ void LLWearableBridgeAction::wearOnAvatar() LLViewerInventoryItem* item = getItem(); if(item) { - LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); + if (get_is_item_worn(mUUID)) + { + if(item->getType() != LLAssetType::AT_BODYPART) + { + LLAppearanceMgr::instance().removeItemFromAvatar(item->getUUID()); + } + } + else + { + LLAppearanceMgr::instance().wearItemOnAvatar(item->getUUID(), true, true); + } } } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index d3cd180174..3cbbd68e51 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -28,7 +28,6 @@ #define LL_LLINVENTORYBRIDGE_H #include "llcallingcard.h" -#include "llfloaterproperties.h" #include "llfolderviewmodel.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" @@ -47,9 +46,11 @@ class LLMenuGL; class LLCallingCardObserver; class LLViewerJointAttachment; class LLFolderView; +struct LLMoveInv; typedef std::vector menuentry_vec_t; - +typedef std::pair two_uuids_t; +typedef std::list two_uuids_list_t; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInvFVBridge // @@ -84,6 +85,7 @@ public: // LLInvFVBridge functionality //-------------------------------------------------------------------- virtual const LLUUID& getUUID() const { return mUUID; } + virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null; } virtual void clearDisplayName() { mDisplayName.clear(); } virtual void restoreItem() {} virtual void restoreToWorld() {} @@ -107,6 +109,7 @@ public: virtual std::string getLabelSuffix() const { return LLStringUtil::null; } virtual void openItem() {} virtual void closeItem() {} + virtual void navigateToFolder(bool new_window = false, bool change_mode = false); virtual void showProperties(); virtual BOOL isItemRenameable() const { return TRUE; } virtual BOOL isMultiPreviewAllowed() { return TRUE; } @@ -114,6 +117,7 @@ public: virtual BOOL isItemRemovable() const; virtual BOOL isItemMovable() const; virtual BOOL isItemInTrash() const; + virtual bool isItemInOutfits() const; virtual BOOL isLink() const; virtual BOOL isLibraryItem() const; //virtual BOOL removeItem() = 0; @@ -251,6 +255,7 @@ public: virtual LLUIImagePtr getIconOverlay() const; LLViewerInventoryItem* getItem() const; + virtual const LLUUID& getThumbnailUUID() const; protected: BOOL confirmRemoveItem(const LLSD& notification, const LLSD& response); @@ -275,8 +280,8 @@ public: mShowDescendantsCount(false) {} - BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE); - BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE); + BOOL dragItemIntoFolder(LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm = TRUE, LLPointer cb = NULL); + BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category, BOOL drop, std::string& tooltip_msg, BOOL is_link = FALSE, BOOL user_confirm = TRUE, LLPointer cb = NULL); void callback_dropItemIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryItem* inv_item); void callback_dropCategoryIntoFolder(const LLSD& notification, const LLSD& response, LLInventoryCategory* inv_category); @@ -296,6 +301,7 @@ public: static LLUIImagePtr getIcon(LLFolderType::EType preferred_type); virtual std::string getLabelSuffix() const; virtual LLFontGL::StyleFlags getLabelStyle() const; + virtual const LLUUID& getThumbnailUUID() const; void setShowDescendantsCount(bool show_count) {mShowDescendantsCount = show_count;} @@ -335,6 +341,7 @@ public: protected: void buildContextMenuOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); void buildContextMenuFolderOptions(U32 flags, menuentry_vec_t& items, menuentry_vec_t& disabled_items); + void addOpenFolderMenuOptions(U32 flags, menuentry_vec_t& items); //-------------------------------------------------------------------- // Menu callbacks @@ -360,9 +367,9 @@ protected: void copyOutfitToClipboard(); void determineFolderType(); - void dropToFavorites(LLInventoryItem* inv_item); - void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); - void dropToMyOutfits(LLInventoryCategory* inv_cat); + void dropToFavorites(LLInventoryItem* inv_item, LLPointer cb = NULL); + void dropToOutfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit, LLPointer cb = NULL); + void dropToMyOutfits(LLInventoryCategory* inv_cat, LLPointer cb = NULL); //-------------------------------------------------------------------- // Messy hacks for handling folder options @@ -372,7 +379,7 @@ public: static void staticFolderOptionsMenu(); protected: - void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id); + void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id, LLPointer cb); void callback_pasteFromClipboard(const LLSD& notification, const LLSD& response); void perform_pasteFromClipboard(); void gatherMessage(std::string& message, S32 depth, LLError::ELevel log_level); @@ -752,7 +759,7 @@ void rez_attachment(LLViewerInventoryItem* item, BOOL move_inv_category_world_to_agent(const LLUUID& object_id, const LLUUID& category_id, BOOL drop, - void (*callback)(S32, void*) = NULL, + std::function callback = NULL, void* user_data = NULL, LLInventoryFilter* filter = NULL); @@ -778,4 +785,16 @@ public: bool canWearSelected(const uuid_vec_t& item_ids) const; }; +struct LLMoveInv +{ + LLUUID mObjectID; + LLUUID mCategoryID; + two_uuids_list_t mMoveList; + std::function mCallback; + void* mUserData; +}; + +void warn_move_inventory(LLViewerObject* object, boost::shared_ptr move_inv); +bool move_task_inventory_callback(const LLSD& notification, const LLSD& response, boost::shared_ptr); + #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 374790b917..7b4283e94d 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -63,6 +63,7 @@ LLInventoryFilter::FilterOps::FilterOps(const Params& p) mFilterTypes(p.types), mFilterUUID(p.uuid), mFilterLinks(p.links), + mFilterThumbnails(p.thumbnails), mSearchVisibility(p.search_visibility) { } @@ -81,7 +82,8 @@ LLInventoryFilter::LLInventoryFilter(const Params& p) mCurrentGeneration(0), mFirstRequiredGeneration(0), mFirstSuccessGeneration(0), - mSearchType(SEARCHTYPE_NAME) + mSearchType(SEARCHTYPE_NAME), + mSingleFolderMode(false) { // copy mFilterOps into mDefaultFilterOps markDefault(); @@ -157,6 +159,8 @@ bool LLInventoryFilter::check(const LLFolderViewModelItem* item) passed = passed && checkAgainstCreator(listener); passed = passed && checkAgainstSearchVisibility(listener); + passed = passed && checkAgainstFilterThumbnails(listener->getUUID()); + return passed; } @@ -194,17 +198,23 @@ bool LLInventoryFilter::checkFolder(const LLUUID& folder_id) const // when applying a filter, matching folders get their contents downloaded first // but make sure we are not interfering with pre-download if (isNotDefault() - && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT) + && LLStartUp::getStartupState() > STATE_WEARABLES_WAIT + && !LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress()) { LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); - if (!cat || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)) + if ((!cat && folder_id.notNull()) || (cat && cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN)) { // At the moment background fetch only cares about VERSION_UNKNOWN, // so do not check isCategoryComplete that compares descendant count - LLInventoryModelBackgroundFetch::instance().start(folder_id); + LLInventoryModelBackgroundFetch::instance().start(folder_id, false); } } + if (!checkAgainstFilterThumbnails(folder_id)) + { + return false; + } + // Marketplace folder filtering const U32 filterTypes = mFilterOps.mFilterTypes; const U32 marketplace_filter = FILTERTYPE_MARKETPLACE_ACTIVE | FILTERTYPE_MARKETPLACE_INACTIVE | @@ -565,6 +575,19 @@ bool LLInventoryFilter::checkAgainstFilterLinks(const LLFolderViewModelItemInven return TRUE; } +bool LLInventoryFilter::checkAgainstFilterThumbnails(const LLUUID& object_id) const +{ + const LLInventoryObject *object = gInventory.getObject(object_id); + if (!object) return true; + + const bool is_thumbnail = object->getThumbnailUUID().notNull(); + if (is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS)) + return false; + if (!is_thumbnail && (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS)) + return false; + return true; +} + bool LLInventoryFilter::checkAgainstCreator(const LLFolderViewModelItemInventory* listener) const { if (!listener) return TRUE; @@ -595,6 +618,9 @@ bool LLInventoryFilter::checkAgainstSearchVisibility(const LLFolderViewModelItem if (is_link && ((mFilterOps.mSearchVisibility & VISIBILITY_LINKS) == 0)) return FALSE; + if (listener->isItemInOutfits() && ((mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS) == 0)) + return FALSE; + if (listener->isItemInTrash() && ((mFilterOps.mSearchVisibility & VISIBILITY_TRASH) == 0)) return FALSE; @@ -733,6 +759,32 @@ void LLInventoryFilter::setFilterSettingsTypes(U64 types) mFilterOps.mFilterTypes |= FILTERTYPE_SETTINGS; } +void LLInventoryFilter::setFilterThumbnails(U64 filter_thumbnails) +{ + if (mFilterOps.mFilterThumbnails != filter_thumbnails) + { + if (mFilterOps.mFilterThumbnails == FILTER_EXCLUDE_THUMBNAILS + && filter_thumbnails == FILTER_ONLY_THUMBNAILS) + { + setModified(FILTER_RESTART); + } + else if (mFilterOps.mFilterThumbnails == FILTER_ONLY_THUMBNAILS + && filter_thumbnails == FILTER_EXCLUDE_THUMBNAILS) + { + setModified(FILTER_RESTART); + } + else if (mFilterOps.mFilterThumbnails == FILTER_INCLUDE_THUMBNAILS) + { + setModified(FILTER_MORE_RESTRICTIVE); + } + else + { + setModified(FILTER_LESS_RESTRICTIVE); + } + } + mFilterOps.mFilterThumbnails = filter_thumbnails; +} + void LLInventoryFilter::setFilterEmptySystemFolders() { mFilterOps.mFilterTypes |= FILTERTYPE_EMPTYFOLDERS; @@ -791,6 +843,24 @@ void LLInventoryFilter::toggleSearchVisibilityLinks() } } +void LLInventoryFilter::toggleSearchVisibilityOutfits() +{ + bool hide_outfits = mFilterOps.mSearchVisibility & VISIBILITY_OUTFITS; + if (hide_outfits) + { + mFilterOps.mSearchVisibility &= ~VISIBILITY_OUTFITS; + } + else + { + mFilterOps.mSearchVisibility |= VISIBILITY_OUTFITS; + } + + if (hasFilterString()) + { + setModified(hide_outfits ? FILTER_MORE_RESTRICTIVE : FILTER_LESS_RESTRICTIVE); + } +} + void LLInventoryFilter::toggleSearchVisibilityTrash() { bool hide_trash = mFilterOps.mSearchVisibility & VISIBILITY_TRASH; @@ -1515,6 +1585,11 @@ U64 LLInventoryFilter::getSearchVisibilityTypes() const return mFilterOps.mSearchVisibility; } +U64 LLInventoryFilter::getFilterThumbnails() const +{ + return mFilterOps.mFilterThumbnails; +} + bool LLInventoryFilter::hasFilterString() const { return mFilterSubString.size() > 0; @@ -1592,9 +1667,9 @@ void LLInventoryFilter::setDefaultEmptyLookupMessage(const std::string& message) mDefaultEmptyLookupMessage = message; } -std::string LLInventoryFilter::getEmptyLookupMessage() const +std::string LLInventoryFilter::getEmptyLookupMessage(bool is_empty_folder) const { - if (isDefault() && !mDefaultEmptyLookupMessage.empty()) + if ((isDefault() || is_empty_folder) && !mDefaultEmptyLookupMessage.empty()) { return LLTrans::getString(mDefaultEmptyLookupMessage); } @@ -1617,7 +1692,7 @@ bool LLInventoryFilter::areDateLimitsSet() bool LLInventoryFilter::showAllResults() const { - return hasFilterString(); + return hasFilterString() && !mSingleFolderMode; } diff --git a/indra/newview/llinventoryfilter.h b/indra/newview/llinventoryfilter.h index 384de3e889..ada1d0f4b4 100644 --- a/indra/newview/llinventoryfilter.h +++ b/indra/newview/llinventoryfilter.h @@ -75,6 +75,13 @@ public: FILTERLINK_ONLY_LINKS // only show links }; + enum EFilterThumbnail + { + FILTER_INCLUDE_THUMBNAILS, + FILTER_EXCLUDE_THUMBNAILS, + FILTER_ONLY_THUMBNAILS + }; + enum ESortOrderType { SO_NAME = 0, // Sort inventory by name @@ -104,7 +111,8 @@ public: VISIBILITY_NONE = 0, VISIBILITY_TRASH = 0x1 << 0, VISIBILITY_LIBRARY = 0x1 << 1, - VISIBILITY_LINKS = 0x1 << 2 + VISIBILITY_LINKS = 0x1 << 2, + VISIBILITY_OUTFITS = 0x1 << 3 }; struct FilterOps @@ -139,12 +147,14 @@ public: Optional show_folder_state; Optional permissions; Optional creator_type; + Optional thumbnails; Params() : types("filter_types", FILTERTYPE_OBJECT), object_types("object_types", 0xffffFFFFffffFFFFULL), wearable_types("wearable_types", 0xffffFFFFffffFFFFULL), settings_types("settings_types", 0xffffFFFFffffFFFFULL), + thumbnails("thumbnails", FILTER_INCLUDE_THUMBNAILS), category_types("category_types", 0xffffFFFFffffFFFFULL), links("links", FILTERLINK_INCLUDE_LINKS), search_visibility("search_visibility", 0xFFFFFFFF), @@ -165,6 +175,7 @@ public: U64 mFilterObjectTypes, // For _OBJECT mFilterWearableTypes, mFilterSettingsTypes, // for _SETTINGS + mFilterThumbnails, mFilterLinks, mFilterCategoryTypes; // For _CATEGORY LLUUID mFilterUUID; // for UUID @@ -207,6 +218,7 @@ public: U64 getFilterWearableTypes() const; U64 getFilterSettingsTypes() const; U64 getSearchVisibilityTypes() const; + U64 getFilterThumbnails() const; bool isFilterObjectTypesWith(LLInventoryType::EType t) const; void setFilterObjectTypes(U64 types); @@ -221,6 +233,7 @@ public: void setFilterMarketplaceUnassociatedFolders(); void setFilterMarketplaceListingFolders(bool select_only_listing_folders); void setFilterNoMarketplaceFolder(); + void setFilterThumbnails(U64 filter_thumbnails); void updateFilterTypes(U64 types, U64& current_types); void setSearchType(ESearchType type); ESearchType getSearchType() { return mSearchType; } @@ -228,6 +241,7 @@ public: void toggleSearchVisibilityLinks(); void toggleSearchVisibilityTrash(); + void toggleSearchVisibilityOutfits(); void toggleSearchVisibilityLibrary(); void setSearchVisibilityTypes(U32 types); void setSearchVisibilityTypes(const Params& params); @@ -237,6 +251,8 @@ public: const std::string& getFilterSubStringOrig() const { return mFilterSubStringOrig; } bool hasFilterString() const; + void setSingleFolderMode(bool is_single_folder) { mSingleFolderMode = is_single_folder; } + void setFilterPermissions(PermissionMask perms); PermissionMask getFilterPermissions() const; @@ -277,7 +293,7 @@ public: void setEmptyLookupMessage(const std::string& message); void setDefaultEmptyLookupMessage(const std::string& message); - std::string getEmptyLookupMessage() const; + std::string getEmptyLookupMessage(bool is_empty_folder = false) const; // +-------------------------------------------------------------------+ // + Status @@ -321,6 +337,8 @@ public: LLInventoryFilter& operator =(const LLInventoryFilter& other); + bool checkAgainstFilterThumbnails(const LLUUID& object_id) const; + private: bool areDateLimitsSet(); bool checkAgainstFilterType(const class LLFolderViewModelItemInventory* listener) const; @@ -359,6 +377,8 @@ private: std::vector mFilterTokens; std::string mExactToken; + + bool mSingleFolderMode; }; #endif diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 67240ac7e7..4aeacae6ed 100644 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -46,13 +46,16 @@ #include "llappearancemgr.h" #include "llappviewer.h" #include "llavataractions.h" +#include "llavatarnamecache.h" #include "llclipboard.h" #include "lldirpicker.h" #include "lldonotdisturbnotificationstorage.h" +#include "llfloatermarketplacelistings.h" #include "llfloatersidepanelcontainer.h" #include "llfocusmgr.h" #include "llfolderview.h" #include "llgesturemgr.h" +#include "llgiveinventory.h" #include "lliconctrl.h" #include "llimview.h" #include "llinventorybridge.h" @@ -93,6 +96,7 @@ BOOL LLInventoryState::sWearNewClothing = FALSE; LLUUID LLInventoryState::sWearNewClothingTransactionID; std::list LLInventoryAction::sMarketplaceFolders; +bool LLInventoryAction::sDeleteConfirmationDisplayed = false; // Helper function : callback to update a folder after inventory action happened in the background void update_folder_cb(const LLUUID& dest_folder) @@ -213,8 +217,7 @@ bool contains_nocopy_items(const LLUUID& id) return false; } -// Generates a string containing the path to the item specified by -// item_id. +// Generates a string containing the path to the item specified by id. void append_path(const LLUUID& id, std::string& path) { std::string temp; @@ -234,6 +237,36 @@ void append_path(const LLUUID& id, std::string& path) path.append(temp); } +// Generates a string containing the path name of the object. +std::string make_path(const LLInventoryObject* object) +{ + std::string path; + append_path(object->getUUID(), path); + return path + "/" + object->getName(); +} + +// Generates a string containing the path name of the object specified by id. +std::string make_inventory_path(const LLUUID& id) +{ + if (LLInventoryObject* object = gInventory.getObject(id)) + return make_path(object); + return ""; +} + +// Generates a string containing the path name and id of the object. +std::string make_info(const LLInventoryObject* object) +{ + return "'" + make_path(object) + "' (" + object->getUUID().asString() + ")"; +} + +// Generates a string containing the path name and id of the object specified by id. +std::string make_inventory_info(const LLUUID& id) +{ + if (LLInventoryObject* object = gInventory.getObject(id)) + return make_info(object); + return " (" + id.asString() + ")"; +} + void update_marketplace_folder_hierarchy(const LLUUID cat_id) { // When changing the marketplace status of a folder, the only thing that needs to happen is @@ -371,7 +404,7 @@ void update_all_marketplace_count(const LLUUID& cat_id) void update_all_marketplace_count() { // Get the marketplace root and launch the recursive exploration - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (!marketplace_listings_uuid.isNull()) { update_all_marketplace_count(marketplace_listings_uuid); @@ -397,14 +430,36 @@ void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::s } void copy_inventory_category(LLInventoryModel* model, - LLViewerInventoryCategory* cat, - const LLUUID& parent_id, - const LLUUID& root_copy_id, - bool move_no_copy_items ) + LLViewerInventoryCategory* cat, + const LLUUID& parent_id, + const LLUUID& root_copy_id, + bool move_no_copy_items) +{ + // Create the initial folder + inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items](const LLUUID& new_id) + { + copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); + }; + gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); +} + +void copy_inventory_category(LLInventoryModel* model, + LLViewerInventoryCategory* cat, + const LLUUID& parent_id, + const LLUUID& root_copy_id, + bool move_no_copy_items, + inventory_func_type callback) { // Create the initial folder - inventory_func_type func = boost::bind(©_inventory_category_content, _1, model, cat, root_copy_id, move_no_copy_items); - gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func); + inventory_func_type func = [model, cat, root_copy_id, move_no_copy_items, callback](const LLUUID &new_id) + { + copy_inventory_category_content(new_id, model, cat, root_copy_id, move_no_copy_items); + if (callback) + { + callback(new_id); + } + }; + gInventory.createNewCategory(parent_id, LLFolderType::FT_NONE, cat->getName(), func, cat->getThumbnailUUID()); } void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items) @@ -529,11 +584,12 @@ BOOL get_is_item_worn(const LLUUID& id) const LLViewerInventoryItem* item = gInventory.getItem(id); if (!item) return FALSE; - + if (item->getIsLinkType() && !gInventory.getItem(item->getLinkedUUID())) { return FALSE; } + // Consider the item as worn if it has links in COF. if (LLAppearanceMgr::instance().isLinkedInCOF(id)) { @@ -768,18 +824,37 @@ BOOL get_is_category_renameable(const LLInventoryModel* model, const LLUUID& id) void show_task_item_profile(const LLUUID& item_uuid, const LLUUID& object_id) { - LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", item_uuid).with("object", object_id)); + LLSD params; + params["id"] = item_uuid; + params["object"] = object_id; + + LLFloaterReg::showInstance("item_properties", params); } void show_item_profile(const LLUUID& item_uuid) { LLUUID linked_uuid = gInventory.getLinkedItemID(item_uuid); - LLFloaterSidePanelContainer::showPanel("inventory", LLSD().with("id", linked_uuid)); + LLFloaterReg::showInstance("item_properties", LLSD().with("id", linked_uuid)); } void show_item_original(const LLUUID& item_uuid) { - LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); + static LLUICachedControl find_original_new_floater("FindOriginalOpenWindow", false); + + //show in a new single-folder window + if(find_original_new_floater) + { + const LLUUID& linked_item_uuid = gInventory.getLinkedItemID(item_uuid); + const LLInventoryObject *obj = gInventory.getObject(linked_item_uuid); + if (obj && obj->getParentUUID().notNull()) + { + LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), linked_item_uuid); + } + } + //show in main Inventory + else + { + LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); if (!floater_inventory) { LL_WARNS() << "Could not find My Inventory floater" << LL_ENDL; @@ -791,6 +866,10 @@ void show_item_original(const LLUUID& item_uuid) LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); if (main_inventory) { + if(main_inventory->isSingleFolderMode()) + { + main_inventory->toggleViewMode(); + } main_inventory->resetAllItemsFilters(); } reset_inventory_filter(); @@ -799,7 +878,6 @@ void show_item_original(const LLUUID& item_uuid) { LLFloaterReg::toggleInstanceOrBringToFront("inventory"); } - sidepanel_inventory->showInventoryPanel(); const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); if (gInventory.isObjectDescendentOf(gInventory.getLinkedItemID(item_uuid), inbox_id)) @@ -819,6 +897,7 @@ void show_item_original(const LLUUID& item_uuid) } } } + } } @@ -840,22 +919,6 @@ void open_marketplace_listings() LLFloaterReg::showInstance("marketplace_listings"); } -// Create a new folder in destFolderId with the same name as the item name and return the uuid of the new folder -// Note: this is used locally in various situation where we need to wrap an item into a special folder -LLUUID create_folder_for_item(LLInventoryItem* item, const LLUUID& destFolderId) -{ - llassert(item); - llassert(destFolderId.notNull()); - - LLUUID created_folder_id = gInventory.createNewCategory(destFolderId, LLFolderType::FT_NONE, item->getName()); - gInventory.notifyObservers(); - - // *TODO : Create different notifications for the various cases - LLNotificationsUtil::add("OutboxFolderCreated"); - - return created_folder_id; -} - ///---------------------------------------------------------------------------- // Marketplace functions // @@ -870,7 +933,7 @@ S32 depth_nesting_in_marketplace(LLUUID cur_uuid) // Todo: findCategoryUUIDForType is somewhat expensive with large // flat root folders yet we use depth_nesting_in_marketplace at // every turn, find a way to correctly cache this id. - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplace_listings_uuid.isNull()) { return -1; @@ -1342,6 +1405,7 @@ bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLIn return accept; } +// Can happen asynhroneously!!! bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy) { // Get the marketplace listings depth of the destination folder, exit with error if not under marketplace @@ -1381,53 +1445,119 @@ bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_fol if (can_move_to_marketplace(inv_item, error_msg, true)) { // When moving an isolated item, we might need to create the folder structure to support it + + LLUUID item_id = inv_item->getUUID(); + std::function callback_create_stock = [copy, item_id](const LLUUID& new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS() << "Failed to create category" << LL_ENDL; + LLSD subs; + subs["[ERROR_CODE]"] = + LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + + // Verify we can have this item in that destination category + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); + LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); + if (!dest_cat || !viewer_inv_item) + { + LL_WARNS() << "Move to marketplace: item or folder do not exist" << LL_ENDL; + + LLSD subs; + subs["[ERROR_CODE]"] = + LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + if (!dest_cat->acceptItem(viewer_inv_item)) + { + LLSD subs; + subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + } + + if (copy) + { + // Copy the item + LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, new_cat_id)); + copy_inventory_item( + gAgent.getID(), + viewer_inv_item->getPermissions().getOwner(), + viewer_inv_item->getUUID(), + new_cat_id, + std::string(), + cb); + } + else + { + // Reparent the item + gInventory.changeItemParent(viewer_inv_item, new_cat_id, true); + } + }; + + std::function callback_dest_create = [item_id, callback_create_stock](const LLUUID& new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS() << "Failed to create category" << LL_ENDL; + LLSD subs; + subs["[ERROR_CODE]"] = + LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); + LLNotificationsUtil::add("MerchantPasteFailed", subs); + return; + } + + LLViewerInventoryCategory* dest_cat = gInventory.getCategory(new_cat_id); + LLViewerInventoryItem * viewer_inv_item = gInventory.getItem(item_id); + if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && + (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) + { + // We need to create a stock folder to move a no copy item + gInventory.createNewCategory(new_cat_id, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName(), callback_create_stock); + } + else + { + callback_create_stock(new_cat_id); + } + }; + if (depth == 0) { // We need a listing folder - dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); - depth++; + gInventory.createNewCategory(dest_folder, + LLFolderType::FT_NONE, + viewer_inv_item->getName(), + [callback_dest_create](const LLUUID &new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS() << "Failed to create listing folder for marketpace" << LL_ENDL; + return; + } + LLViewerInventoryCategory *dest_cat = gInventory.getCategory(new_cat_id); + if (!dest_cat) + { + LL_WARNS() << "Failed to find freshly created listing folder" << LL_ENDL; + return; + } + // version folder + gInventory.createNewCategory(new_cat_id, + LLFolderType::FT_NONE, + dest_cat->getName(), + callback_dest_create); + }); } - if (depth == 1) + else if (depth == 1) { // We need a version folder - dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName()); - depth++; - } - LLViewerInventoryCategory* dest_cat = gInventory.getCategory(dest_folder); - if (!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID()) && - (dest_cat->getPreferredType() != LLFolderType::FT_MARKETPLACE_STOCK)) - { - // We need to create a stock folder to move a no copy item - dest_folder = gInventory.createNewCategory(dest_folder, LLFolderType::FT_MARKETPLACE_STOCK, viewer_inv_item->getName()); - dest_cat = gInventory.getCategory(dest_folder); - depth++; - } - - // Verify we can have this item in that destination category - if (!dest_cat->acceptItem(viewer_inv_item)) - { - LLSD subs; - subs["[ERROR_CODE]"] = LLTrans::getString("Marketplace Error Prefix") + LLTrans::getString("Marketplace Error Not Accepted"); - LLNotificationsUtil::add("MerchantPasteFailed", subs); - return false; - } - - if (copy) - { - // Copy the item - LLPointer cb = new LLBoostFuncInventoryCallback(boost::bind(update_folder_cb, dest_folder)); - copy_inventory_item( - gAgent.getID(), - viewer_inv_item->getPermissions().getOwner(), - viewer_inv_item->getUUID(), - dest_folder, - std::string(), - cb); + gInventory.createNewCategory(dest_folder, LLFolderType::FT_NONE, viewer_inv_item->getName(), callback_dest_create); } else { - // Reparent the item - gInventory.changeItemParent(viewer_inv_item, dest_folder, true); + callback_dest_create(dest_folder); } } else @@ -1472,10 +1602,11 @@ bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUU } else { + LL_INFOS("SLM") << "Move category " << make_info(viewer_inv_cat) << " to '" << make_inventory_path(dest_folder) << "'" << LL_ENDL; // Reparent the folder gInventory.changeCategoryParent(viewer_inv_cat, dest_folder, false); // Check the destination folder recursively for no copy items and promote the including folders if any - validate_marketplacelistings(dest_cat); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(dest_folder); } // Update the modified folders @@ -1500,32 +1631,23 @@ bool sort_alpha(const LLViewerInventoryCategory* cat1, const LLViewerInventoryCa return cat1->getName().compare(cat2->getName()) < 0; } -void dump_trace(std::string& message, S32 depth, LLError::ELevel log_level) -{ - LL_INFOS() << "validate_marketplacelistings : error = "<< log_level << ", depth = " << depth << ", message = " << message << LL_ENDL; -} - // Make all relevant business logic checks on the marketplace listings starting with the folder as argument. // This function does no deletion of listings but a mere audit and raises issues to the user (through the -// optional callback cb). It also returns a boolean, true if things validate, false if issues are raised. +// optional callback cb). // The only inventory changes that are done is to move and sort folders containing no-copy items to stock folders. -bool validate_marketplacelistings( +// @pending_callbacks - how many callbacks we are waiting for, must be inited before use +// @result - true if things validate, false if issues are raised, must be inited before use +typedef boost::function validation_result_callback_t; +void validate_marketplacelistings( LLInventoryCategory* cat, - validation_callback_t cb, + validation_result_callback_t cb_result, + LLMarketplaceValidator::validation_msg_callback_t cb_msg, bool fix_hierarchy, S32 depth, - bool notify_observers) + bool notify_observers, + S32 &pending_callbacks, + bool &result) { -#if 0 - // Used only for debug - if (!cb) - { - cb = boost::bind(&dump_trace, _1, _2, _3); - } -#endif - // Folder is valid unless issue is raised - bool result = true; - // Get the type and the depth of the folder LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (cat); const LLFolderType::EType folder_type = cat->getPreferredType(); @@ -1557,10 +1679,10 @@ bool validate_marketplacelistings( if (!can_move_folder_to_marketplace(cat, cat, cat, message, 0, fix_hierarchy)) { result = false; - if (cb) + if (cb_msg) { message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + message; - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } } } @@ -1570,26 +1692,46 @@ bool validate_marketplacelistings( { if (fix_hierarchy) { - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } + // Nest the stock folder one level deeper in a normal folder and restart from there + pending_callbacks++; LLUUID parent_uuid = cat->getParentUUID(); - LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, LLFolderType::FT_NONE, cat->getName()); - LLInventoryCategory* new_cat = gInventory.getCategory(folder_uuid); - gInventory.changeCategoryParent(viewer_cat, folder_uuid, false); - result &= validate_marketplacelistings(new_cat, cb, fix_hierarchy, depth + 1, notify_observers); - return result; + LLUUID cat_uuid = cat->getUUID(); + gInventory.createNewCategory(parent_uuid, + LLFolderType::FT_NONE, + cat->getName(), + [cat_uuid, cb_result, cb_msg, fix_hierarchy, depth](const LLUUID &new_cat_id) + { + if (new_cat_id.isNull()) + { + cb_result(0, false); + return; + } + LLInventoryCategory * move_cat = gInventory.getCategory(cat_uuid); + LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *)(move_cat); + LLInventoryCategory * new_cat = gInventory.getCategory(new_cat_id); + gInventory.changeCategoryParent(viewer_cat, new_cat_id, false); + S32 pending = 0; + bool result = true; + validate_marketplacelistings(new_cat, cb_result, cb_msg, fix_hierarchy, depth + 1, true, pending, result); + cb_result(pending, result); + } + ); + result = false; + return; } else { result = false; - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error") + " " + LLTrans::getString("Marketplace Validation Warning Stock"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } } } @@ -1620,10 +1762,10 @@ bool validate_marketplacelistings( if (!can_move_to_marketplace(item, error_msg, false)) { has_bad_items = true; - if (cb && fix_hierarchy) + if (cb_msg && fix_hierarchy) { std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } continue; } @@ -1654,35 +1796,35 @@ bool validate_marketplacelistings( if (depth == 2) { // If this is an empty version folder, warn only (listing won't be delivered by AIS, but only AIS should unlist) - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Version"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } } else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2)) { // If this is a legit but empty stock folder, warn only (listing must stay searchable when out of stock) - if (cb) + if (cb_msg) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Empty Stock"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } } - else if (cb) + else if (cb_msg) { // We warn if there's nothing in a regular folder (may be it's an under construction listing) std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Empty"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } } else { // Done with that folder : Print out the folder name unless we already found an error here - if (cb && result && (depth >= 1)) + if (cb_msg && result && (depth >= 1)) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message,depth,LLError::LEVEL_INFO); + cb_msg(message,depth,LLError::LEVEL_INFO); } } } @@ -1690,10 +1832,10 @@ bool validate_marketplacelistings( else if ((count == 1) && !has_bad_items && (((unique_key == default_key) && (depth > 1)) || ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (depth > 2) && (cat_array->size() == 0)))) { // Done with that folder : Print out the folder name unless we already found an error here - if (cb && result && (depth >= 1)) + if (cb_msg && result && (depth >= 1)) { std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message,depth,LLError::LEVEL_INFO); + cb_msg(message,depth,LLError::LEVEL_INFO); } } else @@ -1715,11 +1857,12 @@ bool validate_marketplacelistings( while (items_vector_it != items_vector.end()) { // Create a new folder - LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + const LLUUID parent_uuid = (depth > 2 ? viewer_cat->getParentUUID() : viewer_cat->getUUID()); + const LLUUID origin_uuid = viewer_cat->getUUID(); LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); std::string folder_name = (depth >= 1 ? viewer_cat->getName() : viewer_inv_item->getName()); LLFolderType::EType new_folder_type = (items_vector_it->first == default_key ? LLFolderType::FT_NONE : LLFolderType::FT_MARKETPLACE_STOCK); - if (cb) + if (cb_msg) { std::string message = ""; if (new_folder_type == LLFolderType::FT_MARKETPLACE_STOCK) @@ -1730,30 +1873,71 @@ bool validate_marketplacelistings( { message = indent + folder_name + LLTrans::getString("Marketplace Validation Warning Create Version"); } - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } - LLUUID folder_uuid = gInventory.createNewCategory(parent_uuid, new_folder_type, folder_name); - - // Move each item to the new folder - while (!items_vector_it->second.empty()) + + pending_callbacks++; + std::vector uuid_vector = items_vector_it->second; // needs to be a copy for lambda + gInventory.createNewCategory( + parent_uuid, + new_folder_type, + folder_name, + [uuid_vector, cb_result, cb_msg, depth, parent_uuid, origin_uuid, notify_observers](const LLUUID &new_category_id) { - LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(items_vector_it->second.back()); - if (cb) + // Move each item to the new folder + std::vector::const_reverse_iterator iter = uuid_vector.rbegin(); + while (iter != uuid_vector.rend()) { - std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); - cb(message,depth,LLError::LEVEL_WARN); + LLViewerInventoryItem* viewer_inv_item = gInventory.getItem(*iter); + if (cb_msg) + { + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + std::string message = indent + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Move"); + cb_msg(message, depth, LLError::LEVEL_WARN); + } + gInventory.changeItemParent(viewer_inv_item, new_category_id, true); + iter++; } - gInventory.changeItemParent(viewer_inv_item, folder_uuid, true); - items_vector_it->second.pop_back(); - } - - // Next type - update_marketplace_category(parent_uuid); - update_marketplace_category(folder_uuid); - if (notify_observers) - { - gInventory.notifyObservers(); + + if (origin_uuid != parent_uuid) + { + // We might have moved last item from a folder, check if it needs to be removed + LLViewerInventoryCategory* cat = gInventory.getCategory(origin_uuid); + if (cat->getDescendentCount() == 0) + { + // Remove previous folder if it ends up empty + if (cb_msg) + { + std::string indent; + for (int i = 1; i < depth; i++) + { + indent += " "; + } + std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); + cb_msg(message, depth, LLError::LEVEL_WARN); + } + gInventory.removeCategory(cat->getUUID()); + if (notify_observers) + { + gInventory.notifyObservers(); + } + } + } + + // Next type + update_marketplace_category(parent_uuid); + update_marketplace_category(new_category_id); + if (notify_observers) + { + gInventory.notifyObservers(); + } + cb_result(0, true); } + ); items_vector_it++; } } @@ -1767,11 +1951,11 @@ bool validate_marketplacelistings( { LLViewerInventoryCategory * viewer_cat = (LLViewerInventoryCategory *) (*iter); gInventory.changeCategoryParent(viewer_cat, parent_uuid, false); - result &= validate_marketplacelistings(viewer_cat, cb, fix_hierarchy, depth, false); + validate_marketplacelistings(viewer_cat, cb_result, cb_msg, fix_hierarchy, depth, false, pending_callbacks, result); } } } - else if (cb) + else if (cb_msg) { // We are not fixing the hierarchy but reporting problems, report everything we can find // Print the folder name @@ -1782,20 +1966,20 @@ bool validate_marketplacelistings( // Report if a stock folder contains a mix of items result = false; std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Mixed Stock"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else if ((folder_type == LLFolderType::FT_MARKETPLACE_STOCK) && (cat_array->size() != 0)) { // Report if a stock folder contains subfolders result = false; std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Error Subfolder In Stock"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else { // Simply print the folder name std::string message = indent + cat->getName() + LLTrans::getString("Marketplace Validation Log"); - cb(message,depth,LLError::LEVEL_INFO); + cb_msg(message,depth,LLError::LEVEL_INFO); } } // Scan each item and report if there's a problem @@ -1810,21 +1994,21 @@ bool validate_marketplacelistings( // Report items that shouldn't be there to start with result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error") + " " + error_msg; - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else if ((!viewer_inv_item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID(), gAgent.getGroupID())) && (folder_type != LLFolderType::FT_MARKETPLACE_STOCK)) { // Report stock items that are misplaced result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Error Stock Item"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } else if (depth == 1) { // Report items not wrapped in version folder result = false; std::string message = indent + " " + viewer_inv_item->getName() + LLTrans::getString("Marketplace Validation Warning Unwrapped Item"); - cb(message,depth,LLError::LEVEL_ERROR); + cb_msg(message,depth,LLError::LEVEL_ERROR); } } } @@ -1833,17 +2017,18 @@ bool validate_marketplacelistings( if (viewer_cat->getDescendentCount() == 0) { // Remove the current folder if it ends up empty - if (cb) + if (cb_msg) { std::string message = indent + viewer_cat->getName() + LLTrans::getString("Marketplace Validation Warning Delete"); - cb(message,depth,LLError::LEVEL_WARN); + cb_msg(message,depth,LLError::LEVEL_WARN); } gInventory.removeCategory(cat->getUUID()); if (notify_observers) { gInventory.notifyObservers(); } - return result && !has_bad_items; + result &=!has_bad_items; + return; } } @@ -1856,15 +2041,15 @@ bool validate_marketplacelistings( for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) { LLInventoryCategory* category = *iter; - result &= validate_marketplacelistings(category, cb, fix_hierarchy, depth + 1, false); + validate_marketplacelistings(category, cb_result, cb_msg, fix_hierarchy, depth + 1, false, pending_callbacks, result); } - + update_marketplace_category(cat->getUUID(), true, true); if (notify_observers) { gInventory.notifyObservers(); } - return result && !has_bad_items; + result &= !has_bad_items; } void change_item_parent(const LLUUID& item_id, const LLUUID& new_parent_id) @@ -1964,9 +2149,346 @@ void move_items_to_new_subfolder(const uuid_vec_t& selected_uuids, const std::st inventory_func_type func = boost::bind(&move_items_to_folder, _1, selected_uuids); gInventory.createNewCategory(first_item->getParentUUID(), LLFolderType::FT_NONE, folder_name, func); - } +// Returns true if the item can be moved to Current Outfit or any outfit folder. +bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit) +{ + LLInventoryType::EType inv_type = inv_item->getInventoryType(); + if ((inv_type != LLInventoryType::IT_WEARABLE) && + (inv_type != LLInventoryType::IT_GESTURE) && + (inv_type != LLInventoryType::IT_ATTACHMENT) && + (inv_type != LLInventoryType::IT_OBJECT) && + (inv_type != LLInventoryType::IT_SNAPSHOT) && + (inv_type != LLInventoryType::IT_TEXTURE)) + { + return false; + } + + U32 flags = inv_item->getFlags(); + if(flags & LLInventoryItemFlags::II_FLAGS_OBJECT_HAS_MULTIPLE_ITEMS) + { + return false; + } + + if((inv_type == LLInventoryType::IT_TEXTURE) || (inv_type == LLInventoryType::IT_SNAPSHOT)) + { + return !move_is_into_current_outfit; + } + + if (move_is_into_current_outfit && get_is_item_worn(inv_item->getUUID())) + { + return false; + } + + return true; +} + +// Returns TRUE if item is a landmark or a link to a landmark +// and can be moved to Favorites or Landmarks folder. +bool can_move_to_landmarks(LLInventoryItem* inv_item) +{ + // Need to get the linked item to know its type because LLInventoryItem::getType() + // returns actual type AT_LINK for links, not the asset type of a linked item. + if (LLAssetType::AT_LINK == inv_item->getType()) + { + LLInventoryItem* linked_item = gInventory.getItem(inv_item->getLinkedUUID()); + if (linked_item) + { + return LLAssetType::AT_LANDMARK == linked_item->getType(); + } + } + + return LLAssetType::AT_LANDMARK == inv_item->getType(); +} + +// Returns true if folder's content can be moved to Current Outfit or any outfit folder. +bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit) +{ + LLInventoryModel::cat_array_t *cats; + LLInventoryModel::item_array_t *items; + model->getDirectDescendentsOf(inv_cat->getUUID(), cats, items); + + if (items->size() > wear_limit) + { + return false; + } + + if (items->size() == 0) + { + // Nothing to move(create) + return false; + } + + if (cats->size() > 0) + { + // We do not allow subfolders in outfits of "My Outfits" yet + return false; + } + + LLInventoryModel::item_array_t::iterator iter = items->begin(); + LLInventoryModel::item_array_t::iterator end = items->end(); + + while (iter != end) + { + LLViewerInventoryItem *item = *iter; + if (!can_move_to_outfit(item, false)) + { + return false; + } + iter++; + } + + return true; +} + +std::string get_localized_folder_name(LLUUID cat_uuid) +{ + std::string localized_root_name; + const LLViewerInventoryCategory* cat = gInventory.getCategory(cat_uuid); + if (cat) + { + LLFolderType::EType preferred_type = cat->getPreferredType(); + + // Translation of Accessories folder in Library inventory folder + bool accessories = false; + if(cat->getName() == "Accessories") + { + const LLUUID& parent_folder_id = cat->getParentUUID(); + accessories = (parent_folder_id == gInventory.getLibraryRootFolderID()); + } + + //"Accessories" inventory category has folder type FT_NONE. So, this folder + //can not be detected as protected with LLFolderType::lookupIsProtectedType + localized_root_name.assign(cat->getName()); + if (accessories || LLFolderType::lookupIsProtectedType(preferred_type)) + { + LLTrans::findString(localized_root_name, std::string("InvFolder ") + cat->getName(), LLSD()); + } + } + + return localized_root_name; +} + +void new_folder_window(const LLUUID& folder_id) +{ + LLPanelMainInventory::newFolderWindow(folder_id); +} + +void ungroup_folder_items(const LLUUID& folder_id) +{ + LLInventoryCategory* inv_cat = gInventory.getCategory(folder_id); + if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + return; + } + const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); + LLInventoryModel::cat_array_t cats = *cat_array; + LLInventoryModel::item_array_t items = *item_array; + + for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) + { + LLViewerInventoryCategory* cat = *cat_iter; + if (cat) + { + gInventory.changeCategoryParent(cat, new_cat_uuid, false); + } + } + for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) + { + LLViewerInventoryItem* item = *item_iter; + if(item) + { + gInventory.changeItemParent(item, new_cat_uuid, false); + } + } + gInventory.removeCategory(inv_cat->getUUID()); + gInventory.notifyObservers(); +} + +std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id) +{ + if (model) + { + const LLInventoryItem *item = model->getItem(item_id); + if(item) + { + std::string desc = item->getDescription(); + LLStringUtil::toUpper(desc); + return desc; + } + } + return LLStringUtil::null; +} + +std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id) +{ + if (model) + { + const LLInventoryItem *item = model->getItem(item_id); + if(item) + { + LLAvatarName av_name; + if (LLAvatarNameCache::get(item->getCreatorUUID(), &av_name)) + { + std::string username = av_name.getUserName(); + LLStringUtil::toUpper(username); + return username; + } + } + } + return LLStringUtil::null; +} + +std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id) +{ + if (model) + { + const LLViewerInventoryItem *item = model->getItem(item_id); + if(item && (item->getIsFullPerm() || gAgent.isGodlikeWithoutAdminMenuFakery())) + { + std::string uuid = item->getAssetUUID().asString(); + LLStringUtil::toUpper(uuid); + return uuid; + } + } + return LLStringUtil::null; +} + +bool can_share_item(const LLUUID& item_id) +{ + bool can_share = false; + + if (gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID())) + { + const LLViewerInventoryItem *item = gInventory.getItem(item_id); + if (item) + { + if (LLInventoryCollectFunctor::itemTransferCommonlyAllowed(item)) + { + can_share = LLGiveInventory::isInventoryGiveAcceptable(item); + } + } + else + { + can_share = (gInventory.getCategory(item_id) != NULL); + } + + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if ((item_id == trash_id) || gInventory.isObjectDescendentOf(item_id, trash_id)) + { + can_share = false; + } + } + + return can_share; +} +///---------------------------------------------------------------------------- +/// LLMarketplaceValidator implementations +///---------------------------------------------------------------------------- + + +LLMarketplaceValidator::LLMarketplaceValidator() + : mPendingCallbacks(0) + , mValidationInProgress(false) +{ +} + +LLMarketplaceValidator::~LLMarketplaceValidator() +{ +} + +void LLMarketplaceValidator::validateMarketplaceListings( + const LLUUID &category_id, + LLMarketplaceValidator::validation_done_callback_t cb_done, + LLMarketplaceValidator::validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth) +{ + + mValidationQueue.emplace(category_id, cb_done, cb_msg, fix_hierarchy, depth); + if (!mValidationInProgress) + { + start(); + } +} + +void LLMarketplaceValidator::start() +{ + if (mValidationQueue.empty()) + { + mValidationInProgress = false; + return; + } + mValidationInProgress = true; + + const ValidationRequest &first = mValidationQueue.front(); + LLViewerInventoryCategory* cat = gInventory.getCategory(first.mCategoryId); + if (!cat) + { + LL_WARNS() << "Tried to validate a folder that doesn't exist" << LL_ENDL; + if (first.mCbDone) + { + first.mCbDone(false); + } + mValidationQueue.pop(); + start(); + return; + } + + validation_result_callback_t result_callback = [](S32 pending, bool result) + { + LLMarketplaceValidator* validator = LLMarketplaceValidator::getInstance(); + validator->mPendingCallbacks--; // we just got a callback + validator->mPendingCallbacks += pending; + validator->mPendingResult &= result; + if (validator->mPendingCallbacks <= 0) + { + llassert(validator->mPendingCallbacks == 0); // shouldn't be below 0 + const ValidationRequest &first = validator->mValidationQueue.front(); + if (first.mCbDone) + { + first.mCbDone(validator->mPendingResult); + } + validator->mValidationQueue.pop(); // done; + validator->start(); + } + }; + + mPendingResult = true; + mPendingCallbacks = 1; // do '1' in case something decides to callback immediately + + S32 pending_calbacks = 0; + bool result = true; + validate_marketplacelistings( + cat, + result_callback, + first.mCbMsg, + first.mFixHierarchy, + first.mDepth, + true, + pending_calbacks, + result); + + result_callback(pending_calbacks, result); +} + +LLMarketplaceValidator::ValidationRequest::ValidationRequest( + LLUUID category_id, + validation_done_callback_t cb_done, + validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth) +: mCategoryId(category_id) +, mCbDone(cb_done) +, mCbMsg(cb_msg) +, mFixHierarchy(fix_hierarchy) +, mDepth(depth) +{} + ///---------------------------------------------------------------------------- /// LLInventoryCollectFunctor implementations ///---------------------------------------------------------------------------- @@ -2150,6 +2672,19 @@ bool LLFindCOFValidItems::operator()(LLInventoryCategory* cat, } } +bool LLFindBrokenLinks::operator()(LLInventoryCategory* cat, + LLInventoryItem* item) +{ + // only for broken links getType will be a link + // otherwise it's supposed to have the type of an item + // it is linked too + if (item && LLAssetType::lookupIsLinkType(item->getType())) + { + return TRUE; + } + return FALSE; +} + bool LLFindWearables::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { @@ -2215,6 +2750,11 @@ void LLFindWearablesOfType::setType(LLWearableType::EType type) mWearableType = type; } +bool LLIsTextureType::operator()(LLInventoryCategory* cat, LLInventoryItem* item) +{ + return item && (item->getType() == LLAssetType::AT_TEXTURE); +} + bool LLFindNonRemovableObjects::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { if (item) @@ -2482,8 +3022,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root if ("delete" == action) { - static bool sDisplayedAtSession = false; - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); bool marketplacelistings_item = false; LLAllDescendentsPassedFilter f; for (std::set::iterator it = selected_items.begin(); (it != selected_items.end()) && (f.allDescendentsPassedFilter()); ++it) @@ -2506,10 +3045,10 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } else { - if (!sDisplayedAtSession) // ask for the confirmation at least once per session + if (!sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session { LLNotifications::instance().setIgnored("DeleteItems", false); - sDisplayedAtSession = true; + sDeleteConfirmationDisplayed = true; } LLSD args; @@ -2561,7 +3100,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root LLMultiPreview* multi_previewp = NULL; - LLMultiProperties* multi_propertiesp = NULL; + LLMultiItemProperties* multi_itempropertiesp = nullptr; if (("task_open" == action || "open" == action) && selected_items.size() > 1) { @@ -2595,10 +3134,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root } else if (("task_properties" == action || "properties" == action) && selected_items.size() > 1) { - multi_propertiesp = new LLMultiProperties(); - gFloaterView->addChild(multi_propertiesp); - - LLFloater::setFloaterHost(multi_propertiesp); + multi_itempropertiesp = new LLMultiItemProperties("item_properties"); + gFloaterView->addChild(multi_itempropertiesp); + LLFloater::setFloaterHost(multi_itempropertiesp); } std::set selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); @@ -2608,7 +3146,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root if (action == "wear" || action == "wear_add") { const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); - const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID mp_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); std::copy_if(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids), @@ -2723,36 +3261,7 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { if (ids.size() == 1) { - LLInventoryCategory* inv_cat = gInventory.getCategory(*ids.begin()); - if (!inv_cat || LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) - { - return; - } - const LLUUID &new_cat_uuid = inv_cat->getParentUUID(); - LLInventoryModel::cat_array_t* cat_array; - LLInventoryModel::item_array_t* item_array; - gInventory.getDirectDescendentsOf(inv_cat->getUUID(), cat_array, item_array); - LLInventoryModel::cat_array_t cats = *cat_array; - LLInventoryModel::item_array_t items = *item_array; - - for (LLInventoryModel::cat_array_t::const_iterator cat_iter = cats.begin(); cat_iter != cats.end(); ++cat_iter) - { - LLViewerInventoryCategory* cat = *cat_iter; - if (cat) - { - gInventory.changeCategoryParent(cat, new_cat_uuid, false); - } - } - for (LLInventoryModel::item_array_t::const_iterator item_iter = items.begin(); item_iter != items.end(); ++item_iter) - { - LLViewerInventoryItem* item = *item_iter; - if(item) - { - gInventory.changeItemParent(item, new_cat_uuid, false); - } - } - gInventory.removeCategory(inv_cat->getUUID()); - gInventory.notifyObservers(); + ungroup_folder_items(*ids.begin()); } } else @@ -2766,6 +3275,14 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root if(!bridge) continue; bridge->performAction(model, action); } + if(root->isSingleFolderMode() && selected_items.empty()) + { + LLInvFVBridge* bridge = (LLInvFVBridge*)root->getViewModelItem(); + if(bridge) + { + bridge->performAction(model, action); + } + } } // Update the marketplace listings that have been affected by the operation @@ -2776,9 +3293,9 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root { multi_previewp->openFloater(LLSD()); } - else if (multi_propertiesp) + else if (multi_itempropertiesp) { - multi_propertiesp->openFloater(LLSD()); + multi_itempropertiesp->openFloater(LLSD()); } } @@ -2866,7 +3383,7 @@ void LLInventoryAction::buildMarketplaceFolders(LLFolderView* root) // target listing *and* the original listing. So we need to keep track of both. // Note: do not however put the marketplace listings root itself in this list or the whole marketplace data will be rebuilt. sMarketplaceFolders.clear(); - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.isNull()) { return; diff --git a/indra/newview/llinventoryfunctions.h b/indra/newview/llinventoryfunctions.h index 56ad6f6496..925217dda3 100644 --- a/indra/newview/llinventoryfunctions.h +++ b/indra/newview/llinventoryfunctions.h @@ -75,19 +75,28 @@ void update_all_marketplace_count(); void rename_category(LLInventoryModel* model, const LLUUID& cat_id, const std::string& new_name); void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id = LLUUID::null, bool move_no_copy_items = false); +void copy_inventory_category(LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& parent_id, const LLUUID& root_copy_id, bool move_no_copy_items, inventory_func_type callback); void copy_inventory_category_content(const LLUUID& new_cat_uuid, LLInventoryModel* model, LLViewerInventoryCategory* cat, const LLUUID& root_copy_id, bool move_no_copy_items); -// Generates a string containing the path to the item specified by item_id. +// Generates a string containing the path to the object specified by id (not including the object name). void append_path(const LLUUID& id, std::string& path); -typedef boost::function validation_callback_t; +// Generates a string containing the path name of the object. +std::string make_path(const LLInventoryObject* object); +// Generates a string containing the path name of the object specified by id. +std::string make_inventory_path(const LLUUID& id); + +// Generates a string containing the path name and id of the object. +std::string make_info(const LLInventoryObject* object); +// Generates a string containing the path name and id of the object specified by id. +std::string make_inventory_info(const LLUUID& id); bool can_move_item_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryItem* inv_item, std::string& tooltip_msg, S32 bundle_size = 1, bool from_paste = false); bool can_move_folder_to_marketplace(const LLInventoryCategory* root_folder, LLInventoryCategory* dest_folder, LLInventoryCategory* inv_cat, std::string& tooltip_msg, S32 bundle_size = 1, bool check_items = true, bool from_paste = false); bool move_item_to_marketplacelistings(LLInventoryItem* inv_item, LLUUID dest_folder, bool copy = false); bool move_folder_to_marketplacelistings(LLInventoryCategory* inv_cat, const LLUUID& dest_folder, bool copy = false, bool move_no_copy_items = false); -bool validate_marketplacelistings(LLInventoryCategory* inv_cat, validation_callback_t cb = NULL, bool fix_hierarchy = true, S32 depth = -1, bool notify_observers = true); + S32 depth_nesting_in_marketplace(LLUUID cur_uuid); LLUUID nested_parent_id(LLUUID cur_uuid, S32 depth); S32 compute_stock_count(LLUUID cat_uuid, bool force_count = false); @@ -98,10 +107,65 @@ void move_items_to_folder(const LLUUID& new_cat_uuid, const uuid_vec_t& selected bool is_only_cats_selected(const uuid_vec_t& selected_uuids); bool is_only_items_selected(const uuid_vec_t& selected_uuids); +bool can_move_to_outfit(LLInventoryItem* inv_item, BOOL move_is_into_current_outfit); +bool can_move_to_landmarks(LLInventoryItem* inv_item); +bool can_move_to_my_outfits(LLInventoryModel* model, LLInventoryCategory* inv_cat, U32 wear_limit); +std::string get_localized_folder_name(LLUUID cat_uuid); +void new_folder_window(const LLUUID& folder_id); +void ungroup_folder_items(const LLUUID& folder_id); +std::string get_searchable_description(LLInventoryModel* model, const LLUUID& item_id); +std::string get_searchable_creator_name(LLInventoryModel* model, const LLUUID& item_id); +std::string get_searchable_UUID(LLInventoryModel* model, const LLUUID& item_id); +bool can_share_item(const LLUUID& item_id); + /** Miscellaneous global functions ** ** *******************************************************************************/ +class LLMarketplaceValidator: public LLSingleton +{ + LLSINGLETON(LLMarketplaceValidator); + ~LLMarketplaceValidator(); + LOG_CLASS(LLMarketplaceValidator); +public: + + typedef boost::function validation_msg_callback_t; + typedef boost::function validation_done_callback_t; + + void validateMarketplaceListings( + const LLUUID &category_id, + validation_done_callback_t cb_done = NULL, + validation_msg_callback_t cb_msg = NULL, + bool fix_hierarchy = true, + S32 depth = -1); + +private: + void start(); + + class ValidationRequest + { + public: + ValidationRequest( + LLUUID category_id, + validation_done_callback_t cb_done, + validation_msg_callback_t cb_msg, + bool fix_hierarchy, + S32 depth); + LLUUID mCategoryId; + validation_done_callback_t mCbDone; + validation_msg_callback_t mCbMsg; + bool mFixHierarchy; + S32 mDepth; + }; + + bool mValidationInProgress; + S32 mPendingCallbacks; + bool mPendingResult; + // todo: might be a good idea to memorize requests by id and + // filter out ones that got multiple validation requests + std::queue mValidationQueue; +}; + /******************************************************************************** ** ** ** INVENTORY COLLECTOR FUNCTIONS @@ -317,6 +381,20 @@ public: }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindBrokenLinks +// +// Collects broken links +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindBrokenLinks : public LLInventoryCollectFunctor +{ +public: + LLFindBrokenLinks() {} + virtual ~LLFindBrokenLinks() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLFindByMask //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -415,6 +493,15 @@ private: LLWearableType::EType mWearableType; }; +class LLIsTextureType : public LLInventoryCollectFunctor +{ +public: + LLIsTextureType() {} + virtual ~LLIsTextureType() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + /** Filter out wearables-links */ class LLFindActualWearablesOfType : public LLFindWearablesOfType { @@ -474,7 +561,7 @@ struct LLInventoryAction static void saveMultipleTextures(const std::vector& filenames, std::set selected_items, LLInventoryModel* model); - static const int sConfirmOnDeleteItemsNumber; + static bool sDeleteConfirmationDisplayed; private: static void buildMarketplaceFolders(LLFolderView* root); diff --git a/indra/newview/llinventorygallery.cpp b/indra/newview/llinventorygallery.cpp new file mode 100644 index 0000000000..4838ba7a47 --- /dev/null +++ b/indra/newview/llinventorygallery.cpp @@ -0,0 +1,3824 @@ +/** + * @file llinventorygallery.cpp + * @brief LLInventoryGallery class implementation + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llinventorygallery.h" +#include "llinventorygallerymenu.h" + +#include "llclipboard.h" +#include "llcommonutils.h" +#include "lliconctrl.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventoryicon.h" +#include "llinventorymodel.h" +#include "llinventorymodelbackgroundfetch.h" +#include "llthumbnailctrl.h" +#include "lltextbox.h" +#include "llviewerfoldertype.h" + +#include "llagent.h" +#include "llappearancemgr.h" +#include "llenvironment.h" +#include "llfriendcard.h" +#include "llgesturemgr.h" +#include "llmarketplacefunctions.h" +#include "llnotificationsutil.h" +#include "lloutfitobserver.h" +#include "lltrans.h" +#include "llviewerassettype.h" +#include "llviewermessage.h" +#include "llviewerobjectlist.h" +#include "llvoavatarself.h" + +static LLPanelInjector t_inventory_gallery("inventory_gallery"); + +const S32 GALLERY_ITEMS_PER_ROW_MIN = 2; + +// Helper dnd functions +BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, BOOL drop, std::string& tooltip_msg, BOOL is_link); +BOOL dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm); +void dropToMyOutfits(LLInventoryCategory* inv_cat); + +class LLGalleryPanel: public LLPanel +{ +public: + + BOOL canFocusChildren() const override + { + // Tell Tab to not focus children + return FALSE; + } + +protected: + + LLGalleryPanel(const LLPanel::Params& params): LLPanel(params) + { + }; + + friend class LLUICtrlFactory; +}; + +//----------------------------- +// LLInventoryGallery +//----------------------------- + +LLInventoryGallery::LLInventoryGallery(const LLInventoryGallery::Params& p) + : LLPanel(), + mScrollPanel(NULL), + mGalleryPanel(NULL), + mLastRowPanel(NULL), + mGalleryCreated(false), + mRowCount(0), + mItemsAddedCount(0), + mRowPanelHeight(p.row_panel_height), + mVerticalGap(p.vertical_gap), + mHorizontalGap(p.horizontal_gap), + mItemWidth(p.item_width), + mItemHeight(p.item_height), + mItemHorizontalGap(p.item_horizontal_gap), + mItemsInRow(p.items_in_row), + mRowPanWidthFactor(p.row_panel_width_factor), + mGalleryWidthFactor(p.gallery_width_factor), + mIsInitialized(false), + mRootDirty(false), + mNeedsArrange(false), + mSearchType(LLInventoryFilter::SEARCHTYPE_NAME), + mSortOrder(LLInventoryFilter::SO_DATE) +{ + updateGalleryWidth(); + mFilter = new LLInventoryFilter(); + mCategoriesObserver = new LLInventoryCategoriesObserver(); + mThumbnailsObserver = new LLThumbnailsObserver(); + gInventory.addObserver(mThumbnailsObserver); + + mGestureObserver = new LLGalleryGestureObserver(this); + LLGestureMgr::instance().addObserver(mGestureObserver); + + mUsername = gAgentUsername; + LLStringUtil::toUpper(mUsername); +} + +LLInventoryGallery::Params::Params() + : row_panel_height("row_panel_height", 180), + vertical_gap("vertical_gap", 10), + horizontal_gap("horizontal_gap", 10), + item_width("item_width", 150), + item_height("item_height", 175), + item_horizontal_gap("item_horizontal_gap", 16), + items_in_row("items_in_row", GALLERY_ITEMS_PER_ROW_MIN), + row_panel_width_factor("row_panel_width_factor", 166), + gallery_width_factor("gallery_width_factor", 163) +{ + addSynonym(row_panel_height, "row_height"); +} + +const LLInventoryGallery::Params& LLInventoryGallery::getDefaultParams() +{ + return LLUICtrlFactory::getDefaultParams(); +} + +BOOL LLInventoryGallery::postBuild() +{ + mScrollPanel = getChild("gallery_scroll_panel"); + LLPanel::Params params = LLPanel::getDefaultParams(); + mGalleryPanel = LLUICtrlFactory::create(params); + mMessageTextBox = getChild("empty_txt"); + mInventoryGalleryMenu = new LLInventoryGalleryContextMenu(this); + mRootGalleryMenu = new LLInventoryGalleryContextMenu(this); + mRootGalleryMenu->setRootFolder(true); + return TRUE; +} + +LLInventoryGallery::~LLInventoryGallery() +{ + if (gEditMenuHandler == this) + { + gEditMenuHandler = NULL; + } + + delete mInventoryGalleryMenu; + delete mRootGalleryMenu; + delete mFilter; + + gIdleCallbacks.deleteFunction(onIdle, (void*)this); + + while (!mUnusedRowPanels.empty()) + { + LLPanel* panelp = mUnusedRowPanels.back(); + mUnusedRowPanels.pop_back(); + panelp->die(); + } + while (!mUnusedItemPanels.empty()) + { + LLPanel* panelp = mUnusedItemPanels.back(); + mUnusedItemPanels.pop_back(); + panelp->die(); + } + while (!mHiddenItems.empty()) + { + LLPanel* panelp = mHiddenItems.back(); + mHiddenItems.pop_back(); + panelp->die(); + } + + + if (gInventory.containsObserver(mCategoriesObserver)) + { + gInventory.removeObserver(mCategoriesObserver); + } + delete mCategoriesObserver; + + if (gInventory.containsObserver(mThumbnailsObserver)) + { + gInventory.removeObserver(mThumbnailsObserver); + } + delete mThumbnailsObserver; + + LLGestureMgr::instance().removeObserver(mGestureObserver); + delete mGestureObserver; +} + +void LLInventoryGallery::setRootFolder(const LLUUID cat_id) +{ + LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); + if(!category || (mFolderID == cat_id)) + { + return; + } + if(mFolderID.notNull()) + { + mBackwardFolders.push_back(mFolderID); + } + + gIdleCallbacks.deleteFunction(onIdle, (void*)this); + + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + + mFolderID = cat_id; + mItemsToSelect.clear(); + mSelectedItemIDs.clear(); + mItemBuildQuery.clear(); + mNeedsArrange = false; + dirtyRootFolder(); +} + +void LLInventoryGallery::dirtyRootFolder() +{ + if (getVisible()) + { + updateRootFolder(); + } + else + { + mRootDirty = true; + } +} + +void LLInventoryGallery::updateRootFolder() +{ + llassert(mFolderID.notNull()); + if (mIsInitialized && mFolderID.notNull()) + { + S32 count = mItemsAddedCount; + for (S32 i = count - 1; i >= 0; i--) + { + updateRemovedItem(mItems[i]->getUUID()); + } + S32 hidden_count = mHiddenItems.size(); + for (S32 i = hidden_count - 1; i >= 0; i--) + { + updateRemovedItem(mHiddenItems[i]->getUUID()); + } + mItemBuildQuery.clear(); + + if (gInventory.containsObserver(mCategoriesObserver)) + { + gInventory.removeObserver(mCategoriesObserver); + } + delete mCategoriesObserver; + + mCategoriesObserver = new LLInventoryCategoriesObserver(); + + if (gInventory.containsObserver(mThumbnailsObserver)) + { + gInventory.removeObserver(mThumbnailsObserver); + } + delete mThumbnailsObserver; + mThumbnailsObserver = new LLThumbnailsObserver(); + gInventory.addObserver(mThumbnailsObserver); + } + { + mRootChangedSignal(); + + gInventory.addObserver(mCategoriesObserver); + + // Start observing changes in selected category. + mCategoriesObserver->addCategory(mFolderID, + boost::bind(&LLInventoryGallery::refreshList, this, mFolderID)); + + LLViewerInventoryCategory* category = gInventory.getCategory(mFolderID); + //If not all items are fetched now + // the observer will refresh the list as soon as the new items + // arrive. + category->fetch(); + + //refreshList(cat_id); + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + + gInventory.getDirectDescendentsOf(mFolderID, cat_array, item_array); + + // Creating a vector of newly collected sub-categories UUIDs. + for (LLInventoryModel::cat_array_t::const_iterator iter = cat_array->begin(); + iter != cat_array->end(); + iter++) + { + mItemBuildQuery.insert((*iter)->getUUID()); + } + + for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin(); + iter != item_array->end(); + iter++) + { + mItemBuildQuery.insert((*iter)->getUUID()); + } + mIsInitialized = true; + mRootDirty = false; + + if (mScrollPanel) + { + mScrollPanel->goToTop(); + } + } + + LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLInventoryGallery::onCOFChanged, this)); + + if (!mGalleryCreated) + { + initGallery(); + } + + if (!mItemBuildQuery.empty()) + { + gIdleCallbacks.addFunction(onIdle, (void*)this); + } +} + +void LLInventoryGallery::initGallery() +{ + if (!mGalleryCreated) + { + uuid_vec_t cats; + getCurrentCategories(cats); + int n = cats.size(); + buildGalleryPanel(n); + mScrollPanel->addChild(mGalleryPanel); + for (int i = 0; i < n; i++) + { + addToGallery(mItemMap[cats[i]]); + } + reArrangeRows(); + mGalleryCreated = true; + } +} + +void LLInventoryGallery::draw() +{ + LLPanel::draw(); + if (mGalleryCreated) + { + if(!updateRowsIfNeeded()) + { + handleModifiedFilter(); + } + } +} + +void LLInventoryGallery::onVisibilityChange(BOOL new_visibility) +{ + if (new_visibility) + { + if (mRootDirty) + { + updateRootFolder(); + } + else if (mNeedsArrange) + { + gIdleCallbacks.addFunction(onIdle, (void*)this); + } + } + LLPanel::onVisibilityChange(new_visibility); +} + +bool LLInventoryGallery::updateRowsIfNeeded() +{ + S32 scroll_content_width = mScrollPanel ? mScrollPanel->getVisibleContentRect().getWidth() : getRect().getWidth(); + if(((scroll_content_width - mRowPanelWidth) > mItemWidth) + && mRowCount > 1) + { + reArrangeRows(1); + return true; + } + else if((mRowPanelWidth > (scroll_content_width + mItemHorizontalGap)) + && mItemsInRow > GALLERY_ITEMS_PER_ROW_MIN) + { + reArrangeRows(-1); + return true; + } + return false; +} + +bool compareGalleryItem(LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2, bool sort_by_date, bool sort_folders_by_name) +{ + if (item1->getSortGroup() != item2->getSortGroup()) + { + return (item1->getSortGroup() < item2->getSortGroup()); + } + + if(sort_folders_by_name && (item1->getSortGroup() != LLInventoryGalleryItem::SG_ITEM)) + { + std::string name1 = item1->getItemName(); + std::string name2 = item2->getItemName(); + + return (LLStringUtil::compareDict(name1, name2) < 0); + } + + if(((item1->isDefaultImage() && item2->isDefaultImage()) || (!item1->isDefaultImage() && !item2->isDefaultImage()))) + { + if(sort_by_date) + { + return item1->getCreationDate() > item2->getCreationDate(); + } + else + { + std::string name1 = item1->getItemName(); + std::string name2 = item2->getItemName(); + + return (LLStringUtil::compareDict(name1, name2) < 0); + } + } + else + { + return item2->isDefaultImage(); + } +} + +void LLInventoryGallery::reArrangeRows(S32 row_diff) +{ + std::vector buf_items = mItems; + for (std::vector::const_reverse_iterator it = buf_items.rbegin(); it != buf_items.rend(); ++it) + { + removeFromGalleryLast(*it, false); + } + for (std::vector::const_reverse_iterator it = mHiddenItems.rbegin(); it != mHiddenItems.rend(); ++it) + { + buf_items.push_back(*it); + } + mHiddenItems.clear(); + + mItemsInRow+= row_diff; + updateGalleryWidth(); + + bool sort_by_date = (mSortOrder & LLInventoryFilter::SO_DATE); + bool sort_folders_by_name = (mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME); + std::sort(buf_items.begin(), buf_items.end(), [sort_by_date, sort_folders_by_name](LLInventoryGalleryItem* item1, LLInventoryGalleryItem* item2) + { + return compareGalleryItem(item1, item2, sort_by_date, sort_folders_by_name); + }); + + for (std::vector::const_iterator it = buf_items.begin(); it != buf_items.end(); ++it) + { + (*it)->setHidden(false); + applyFilter(*it, mFilterSubString); + addToGallery(*it); + } + mFilter->clearModified(); + updateMessageVisibility(); +} + +void LLInventoryGallery::updateGalleryWidth() +{ + mRowPanelWidth = mRowPanWidthFactor * mItemsInRow - mItemHorizontalGap; + mGalleryWidth = mGalleryWidthFactor * mItemsInRow - mItemHorizontalGap; +} + +LLPanel* LLInventoryGallery::addLastRow() +{ + mRowCount++; + int row = 0; + int vgap = mVerticalGap * row; + LLPanel* result = buildRowPanel(0, row * mRowPanelHeight + vgap); + mGalleryPanel->addChild(result); + return result; +} + +void LLInventoryGallery::moveRowUp(int row) +{ + moveRow(row, mRowCount - 1 - row + 1); +} + +void LLInventoryGallery::moveRowDown(int row) +{ + moveRow(row, mRowCount - 1 - row - 1); +} + +void LLInventoryGallery::moveRow(int row, int pos) +{ + int vgap = mVerticalGap * pos; + moveRowPanel(mRowPanels[row], 0, pos * mRowPanelHeight + vgap); +} + +void LLInventoryGallery::removeLastRow() +{ + mRowCount--; + mGalleryPanel->removeChild(mLastRowPanel); + mUnusedRowPanels.push_back(mLastRowPanel); + mRowPanels.pop_back(); + if (mRowPanels.size() > 0) + { + // Just removed last row + mLastRowPanel = mRowPanels.back(); + } + else + { + mLastRowPanel = NULL; + } +} + +LLPanel* LLInventoryGallery::addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap) +{ + LLPanel* lpanel = buildItemPanel(pos * mItemWidth + hgap); + lpanel->addChild(item); + row_stack->addChild(lpanel); + mItemPanels.push_back(lpanel); + return lpanel; +} + +void LLInventoryGallery::addToGallery(LLInventoryGalleryItem* item) +{ + if(item->isHidden()) + { + mHiddenItems.push_back(item); + return; + } + mItemIndexMap[item] = mItemsAddedCount; + mIndexToItemMap[mItemsAddedCount] = item; + mItemsAddedCount++; + int n = mItemsAddedCount; + int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; + int n_prev = n - 1; + int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; + + bool add_row = row_count != row_count_prev; + int pos = 0; + if (add_row) + { + for (int i = 0; i < row_count_prev; i++) + { + moveRowUp(i); + } + mLastRowPanel = addLastRow(); + mRowPanels.push_back(mLastRowPanel); + } + pos = (n - 1) % mItemsInRow; + mItems.push_back(item); + addToRow(mLastRowPanel, item, pos, mHorizontalGap * pos); + reshapeGalleryPanel(row_count); +} + + +void LLInventoryGallery::removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape) +{ + if(item->isHidden()) + { + mHiddenItems.pop_back(); + // Note: item still exists!!! + return; + } + int n_prev = mItemsAddedCount; + int n = mItemsAddedCount - 1; + int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; + int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; + mItemsAddedCount--; + mIndexToItemMap.erase(mItemsAddedCount); + + bool remove_row = row_count != row_count_prev; + removeFromLastRow(mItems[mItemsAddedCount]); + mItems.pop_back(); + if (remove_row) + { + for (int i = 0; i < row_count_prev - 1; i++) + { + moveRowDown(i); + } + removeLastRow(); + } + if (needs_reshape) + { + reshapeGalleryPanel(row_count); + } +} + + +void LLInventoryGallery::removeFromGalleryMiddle(LLInventoryGalleryItem* item) +{ + if(item->isHidden()) + { + mHiddenItems.erase(std::remove(mHiddenItems.begin(), mHiddenItems.end(), item), mHiddenItems.end()); + // item still exists and needs to be deleted or used!!! + return; + } + int n = mItemIndexMap[item]; + mItemIndexMap.erase(item); + mIndexToItemMap.erase(n); + std::vector saved; + for (int i = mItemsAddedCount - 1; i > n; i--) + { + saved.push_back(mItems[i]); + removeFromGalleryLast(mItems[i]); + } + removeFromGalleryLast(mItems[n]); + int saved_count = saved.size(); + for (int i = 0; i < saved_count; i++) + { + addToGallery(saved.back()); + saved.pop_back(); + } +} + +void LLInventoryGallery::removeFromLastRow(LLInventoryGalleryItem* item) +{ + mItemPanels.back()->removeChild(item); + mLastRowPanel->removeChild(mItemPanels.back()); + mUnusedItemPanels.push_back(mItemPanels.back()); + mItemPanels.pop_back(); +} + +LLInventoryGalleryItem* LLInventoryGallery::buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn) +{ + LLInventoryGalleryItem::Params giparams; + giparams.visible = true; + giparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + giparams.rect(LLRect(0,mItemHeight, mItemWidth, 0)); + LLInventoryGalleryItem* gitem = LLUICtrlFactory::create(giparams); + gitem->setItemName(name); + gitem->setUUID(item_id); + gitem->setGallery(this); + gitem->setType(type, inventory_type, flags, is_link); + gitem->setThumbnail(thumbnail_id); + gitem->setWorn(is_worn); + gitem->setCreatorName(get_searchable_creator_name(&gInventory, item_id)); + gitem->setDescription(get_searchable_description(&gInventory, item_id)); + gitem->setAssetIDStr(get_searchable_UUID(&gInventory, item_id)); + gitem->setCreationDate(creation_date); + return gitem; +} + +void LLInventoryGallery::buildGalleryPanel(int row_count) +{ + LLPanel::Params params; + params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + params.visible = true; + params.use_bounding_rect = false; + mGalleryPanel = LLUICtrlFactory::create(params); + reshapeGalleryPanel(row_count); +} + +void LLInventoryGallery::reshapeGalleryPanel(int row_count) +{ + int bottom = 0; + int left = 0; + int height = row_count * (mRowPanelHeight + mVerticalGap); + LLRect rect = LLRect(left, bottom + height, left + mGalleryWidth, bottom); + mGalleryPanel->setRect(rect); + mGalleryPanel->reshape(mGalleryWidth, height); +} + +LLPanel* LLInventoryGallery::buildItemPanel(int left) +{ + int top = 0; + LLPanel* lpanel = NULL; + if(mUnusedItemPanels.empty()) + { + LLPanel::Params lpparams; + lpparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + lpparams.visible = true; + lpparams.rect(LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top)); + lpparams.use_bounding_rect = false; + lpparams.focus_root = false; + //lpparams.tab_stop = false; + lpanel = LLUICtrlFactory::create(lpparams); + } + else + { + lpanel = mUnusedItemPanels.back(); + mUnusedItemPanels.pop_back(); + + LLRect rect = LLRect(left, top + mItemHeight, left + mItemWidth + mItemHorizontalGap, top); + lpanel->setShape(rect, false); + } + return lpanel; +} + +LLPanel* LLInventoryGallery::buildRowPanel(int left, int bottom) +{ + LLPanel* stack = NULL; + if(mUnusedRowPanels.empty()) + { + LLPanel::Params sparams; + sparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); + sparams.use_bounding_rect = false; + sparams.visible = true; + sparams.focus_root = false; + //sparams.tab_stop = false; + stack = LLUICtrlFactory::create(sparams); + } + else + { + stack = mUnusedRowPanels.back(); + mUnusedRowPanels.pop_back(); + } + moveRowPanel(stack, left, bottom); + return stack; +} + +void LLInventoryGallery::moveRowPanel(LLPanel* stack, int left, int bottom) +{ + LLRect rect = LLRect(left, bottom + mRowPanelHeight, left + mRowPanelWidth, bottom); + stack->setRect(rect); + stack->reshape(mRowPanelWidth, mRowPanelHeight); +} + +void LLInventoryGallery::setFilterSubString(const std::string& string) +{ + mFilterSubString = string; + mFilter->setFilterSubString(string); + + //reArrangeRows(); +} + +bool LLInventoryGallery::applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring) +{ + if(item) + { + bool visible = checkAgainstFilters(item, filter_substring); + item->setHidden(!visible); + return visible; + } + return false; +} + +bool LLInventoryGallery::checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring) +{ + if (!item) return false; + + if (item->isFolder() && (mFilter->getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)) + { + return true; + } + + if(item->isLink() && ((mFilter->getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) == 0) && !filter_substring.empty()) + { + return false; + } + + bool hidden = false; + + if(mFilter->getFilterCreatorType() == LLInventoryFilter::FILTERCREATOR_SELF) + { + hidden = (item->getCreatorName() == mUsername) || item->isFolder(); + } + else if(mFilter->getFilterCreatorType() == LLInventoryFilter::FILTERCREATOR_OTHERS) + { + hidden = (item->getCreatorName() != mUsername) || item->isFolder(); + } + if(hidden) + { + return false; + } + + if(!mFilter->checkAgainstFilterThumbnails(item->getUUID())) + { + return false; + } + + if(!checkAgainstFilterType(item->getUUID())) + { + return false; + } + + std::string desc; + switch(mSearchType) + { + case LLInventoryFilter::SEARCHTYPE_CREATOR: + desc = item->getCreatorName(); + break; + case LLInventoryFilter::SEARCHTYPE_DESCRIPTION: + desc = item->getDescription(); + break; + case LLInventoryFilter::SEARCHTYPE_UUID: + desc = item->getAssetIDStr(); + break; + case LLInventoryFilter::SEARCHTYPE_NAME: + default: + desc = item->getItemName() + item->getItemNameSuffix(); + break; + } + + LLStringUtil::toUpper(desc); + + std::string cur_filter = filter_substring; + LLStringUtil::toUpper(cur_filter); + + hidden = (std::string::npos == desc.find(cur_filter)); + return !hidden; +} + +void LLInventoryGallery::onIdle(void* userdata) +{ + LLInventoryGallery* self = (LLInventoryGallery*)userdata; + + if (!self->mIsInitialized || !self->mGalleryCreated) + { + self->mNeedsArrange = false; + return; + } + + bool visible = self->getVisible(); // In visible chain? + const F64 MAX_TIME_VISIBLE = 0.020f; + const F64 MAX_TIME_HIDDEN = 0.001f; // take it slow + const F64 max_time = visible ? MAX_TIME_VISIBLE : MAX_TIME_HIDDEN; + F64 curent_time = LLTimer::getTotalSeconds(); + const F64 end_time = curent_time + max_time; + + while (!self->mItemBuildQuery.empty() && end_time > curent_time) + { + uuid_set_t::iterator iter = self->mItemBuildQuery.begin(); + LLUUID item_id = *iter; + self->mNeedsArrange |= self->updateAddedItem(item_id); + self->mItemBuildQuery.erase(iter); + curent_time = LLTimer::getTotalSeconds(); + } + + if (self->mNeedsArrange && visible) + { + self->mNeedsArrange = false; + self->reArrangeRows(); + self->updateMessageVisibility(); + } + + if (!self->mItemsToSelect.empty() && !self->mNeedsArrange) + { + selection_deque selection_list(self->mItemsToSelect); + self->mItemsToSelect.clear(); + for (LLUUID & item_to_select : selection_list) + { + self->addItemSelection(item_to_select, true); + } + } + + if (self->mItemsToSelect.empty() && self->mItemBuildQuery.empty()) + { + gIdleCallbacks.deleteFunction(onIdle, (void*)self); + } +} + +void LLInventoryGallery::setSearchType(LLInventoryFilter::ESearchType type) +{ + if(mSearchType != type) + { + mSearchType = type; + if(!mFilterSubString.empty()) + { + reArrangeRows(); + } + } +} + +void LLInventoryGallery::getCurrentCategories(uuid_vec_t& vcur) +{ + for (gallery_item_map_t::const_iterator iter = mItemMap.begin(); + iter != mItemMap.end(); + iter++) + { + if ((*iter).second != NULL) + { + vcur.push_back((*iter).first); + } + } +} + +bool LLInventoryGallery::updateAddedItem(LLUUID item_id) +{ + LLInventoryObject* obj = gInventory.getObject(item_id); + if (!obj) + { + LL_WARNS("InventoryGallery") << "Failed to find item: " << item_id << LL_ENDL; + return false; + } + + std::string name = obj->getName(); + LLUUID thumbnail_id = obj->getThumbnailUUID();; + LLInventoryType::EType inventory_type(LLInventoryType::IT_CATEGORY); + U32 misc_flags = 0; + bool is_worn = false; + LLInventoryItem* inv_item = gInventory.getItem(item_id); + if (inv_item) + { + inventory_type = inv_item->getInventoryType(); + misc_flags = inv_item->getFlags(); + if (LLAssetType::AT_GESTURE == obj->getType()) + { + is_worn = LLGestureMgr::instance().isGestureActive(item_id); + } + else + { + is_worn = LLAppearanceMgr::instance().isLinkedInCOF(item_id); + } + } + else if (LLAssetType::AT_CATEGORY == obj->getType()) + { + name = get_localized_folder_name(item_id); + if(thumbnail_id.isNull()) + { + thumbnail_id = getOutfitImageID(item_id); + } + } + + bool res = false; + + LLInventoryGalleryItem* item = buildGalleryItem(name, item_id, obj->getType(), thumbnail_id, inventory_type, misc_flags, obj->getCreationDate(), obj->getIsLinkType(), is_worn); + mItemMap.insert(LLInventoryGallery::gallery_item_map_t::value_type(item_id, item)); + if (mGalleryCreated) + { + res = applyFilter(item, mFilterSubString); + addToGallery(item); + } + + mThumbnailsObserver->addItem(item_id, + boost::bind(&LLInventoryGallery::updateItemThumbnail, this, item_id)); + return res; +} + +void LLInventoryGallery::updateRemovedItem(LLUUID item_id) +{ + gallery_item_map_t::iterator item_iter = mItemMap.find(item_id); + if (item_iter != mItemMap.end()) + { + mThumbnailsObserver->removeItem(item_id); + + LLInventoryGalleryItem* item = item_iter->second; + + deselectItem(item_id); + mItemMap.erase(item_iter); + removeFromGalleryMiddle(item); + + // kill removed item + if (item != NULL) + { + // Todo: instead of deleting, store somewhere to reuse later + item->die(); + } + } + + mItemBuildQuery.erase(item_id); +} + +void LLInventoryGallery::updateChangedItemName(LLUUID item_id, std::string name) +{ + gallery_item_map_t::iterator iter = mItemMap.find(item_id); + if (iter != mItemMap.end()) + { + LLInventoryGalleryItem* item = iter->second; + if (item) + { + item->setItemName(name); + } + } +} + +void LLInventoryGallery::updateWornItem(LLUUID item_id, bool is_worn) +{ + gallery_item_map_t::iterator iter = mItemMap.find(item_id); + if (iter != mItemMap.end()) + { + LLInventoryGalleryItem* item = iter->second; + if (item) + { + item->setWorn(is_worn); + } + } +} + +void LLInventoryGallery::updateItemThumbnail(LLUUID item_id) +{ + LLInventoryObject* obj = gInventory.getObject(item_id); + if (!obj) + { + return; + } + LLUUID thumbnail_id = obj->getThumbnailUUID(); + + if ((LLAssetType::AT_CATEGORY == obj->getType()) && thumbnail_id.isNull()) + { + thumbnail_id = getOutfitImageID(item_id); + } + + if (mItemMap[item_id]) + { + mItemMap[item_id]->setThumbnail(thumbnail_id); + + bool passes_filter = checkAgainstFilters(mItemMap[item_id], mFilterSubString); + if((mItemMap[item_id]->isHidden() && passes_filter) + || (!mItemMap[item_id]->isHidden() && !passes_filter)) + { + reArrangeRows(); + } + } +} + +BOOL LLInventoryGallery::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (mSelectedItemIDs.size() > 0) + { + setFocus(true); + } + mLastInteractedUUID = LLUUID::null; + + // Scroll is going to always return true + BOOL res = LLPanel::handleRightMouseDown(x, y, mask); + + if (mLastInteractedUUID.isNull()) // no child were hit + { + clearSelection(); + if (mInventoryGalleryMenu && mFolderID.notNull()) + { + uuid_vec_t selected_uuids; + selected_uuids.push_back(mFolderID); + mRootGalleryMenu->show(this, selected_uuids, x, y); + return TRUE; + } + } + return res; +} + + +BOOL LLInventoryGallery::handleKeyHere(KEY key, MASK mask) +{ + BOOL handled = FALSE; + switch (key) + { + case KEY_RETURN: + // Open selected items if enter key hit on the inventory panel + if (mask == MASK_NONE && mInventoryGalleryMenu && mSelectedItemIDs.size() == 1) + { + selection_deque::iterator iter = mSelectedItemIDs.begin(); + LLViewerInventoryCategory* category = gInventory.getCategory(*iter); + if (category) + { + setRootFolder(*iter); + handled = TRUE; + } + else + { + LLViewerInventoryItem* item = gInventory.getItem(*iter); + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(), *iter, &gInventory); + } + } + } + handled = TRUE; + break; + case KEY_DELETE: +#if LL_DARWIN + case KEY_BACKSPACE: +#endif + // Delete selected items if delete or backspace key hit on the inventory panel + // Note: on Mac laptop keyboards, backspace and delete are one and the same + if (canDeleteSelection()) + { + deleteSelection(); + } + handled = TRUE; + break; + + case KEY_F2: + mFilterSubString.clear(); + if (mInventoryGalleryMenu && mSelectedItemIDs.size() == 1) + { + mInventoryGalleryMenu->rename(mSelectedItemIDs.front()); + } + handled = TRUE; + break; + + case KEY_PAGE_UP: + mFilterSubString.clear(); + if (mScrollPanel) + { + mScrollPanel->pageUp(30); + } + handled = TRUE; + break; + + case KEY_PAGE_DOWN: + mFilterSubString.clear(); + if (mScrollPanel) + { + mScrollPanel->pageDown(30); + } + handled = TRUE; + break; + + case KEY_HOME: + mFilterSubString.clear(); + if (mScrollPanel) + { + mScrollPanel->goToTop(); + } + handled = TRUE; + break; + + case KEY_END: + mFilterSubString.clear(); + if (mScrollPanel) + { + mScrollPanel->goToBottom(); + } + handled = TRUE; + break; + + case KEY_LEFT: + moveLeft(mask); + handled = TRUE; + break; + + case KEY_RIGHT: + moveRight(mask); + handled = TRUE; + break; + + case KEY_UP: + moveUp(mask); + handled = TRUE; + break; + + case KEY_DOWN: + moveDown(mask); + handled = TRUE; + break; + + default: + break; + } + + if (handled) + { + mInventoryGalleryMenu->hide(); + } + + return handled; +} + +void LLInventoryGallery::moveUp(MASK mask) +{ + mFilterSubString.clear(); + + if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) + { + LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; + if (item) + { + if (mask == MASK_NONE || mask == MASK_CONTROL) + { + S32 n = mItemIndexMap[item]; + n -= mItemsInRow; + if (n >= 0) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else + { + changeItemSelection(item_id, true); + } + item->setFocus(TRUE); + claimEditHandler(); + } + } + else if (mask == MASK_SHIFT) + { + S32 n = mItemIndexMap[item]; + S32 target = llmax(0, n - mItemsInRow); + if (target != n) + { + item = mIndexToItemMap[target]; + toggleSelectionRangeFromLast(item->getUUID()); + item->setFocus(TRUE); + claimEditHandler(); + } + } + } + } +} + +void LLInventoryGallery::moveDown(MASK mask) +{ + mFilterSubString.clear(); + + if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) + { + LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; + if (item) + { + if (mask == MASK_NONE || mask == MASK_CONTROL) + { + S32 n = mItemIndexMap[item]; + n += mItemsInRow; + if (n < mItemsAddedCount) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else + { + changeItemSelection(item_id, true); + } + item->setFocus(TRUE); + claimEditHandler(); + } + } + else if (mask == MASK_SHIFT) + { + S32 n = mItemIndexMap[item]; + S32 target = llmin(mItemsAddedCount - 1, n + mItemsInRow); + if (target != n) + { + item = mIndexToItemMap[target]; + toggleSelectionRangeFromLast(item->getUUID()); + item->setFocus(TRUE); + claimEditHandler(); + } + } + } + } +} + +void LLInventoryGallery::moveLeft(MASK mask) +{ + mFilterSubString.clear(); + + if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) + { + LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; + if (mask == MASK_SHIFT) + { + item = mItemMap[mLastInteractedUUID]; + } + if (item) + { + // Might be better to get item from panel + S32 n = mItemIndexMap[item]; + n--; + if (n < 0) + { + n = mItemsAddedCount - 1; + } + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else if (mask == MASK_SHIFT) + { + if (item->isSelected()) + { + toggleItemSelection(mLastInteractedUUID, true); + } + else + { + toggleItemSelection(item_id, true); + } + mLastInteractedUUID = item_id; + } + else + { + changeItemSelection(item_id, true); + } + item->setFocus(TRUE); + claimEditHandler(); + } + } +} + +void LLInventoryGallery::moveRight(MASK mask) +{ + mFilterSubString.clear(); + + if (mInventoryGalleryMenu && mSelectedItemIDs.size() > 0 && mItemsAddedCount > 1) + { + LLInventoryGalleryItem* item = mItemMap[mLastInteractedUUID]; + if (item) + { + S32 n = mItemIndexMap[item]; + n++; + if (n == mItemsAddedCount) + { + n = 0; + } + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + if (mask == MASK_CONTROL) + { + addItemSelection(item_id, true); + } + else if (mask == MASK_SHIFT) + { + if (item->isSelected()) + { + toggleItemSelection(mLastInteractedUUID, true); + } + else + { + toggleItemSelection(item_id, true); + } + mLastInteractedUUID = item_id; + } + else + { + changeItemSelection(item_id, true); + } + item->setFocus(TRUE); + claimEditHandler(); + } + } +} + +void LLInventoryGallery::toggleSelectionRange(S32 start_idx, S32 end_idx) +{ + LLInventoryGalleryItem* item = NULL; + if (end_idx > start_idx) + { + for (S32 i = start_idx; i <= end_idx; i++) + { + item = mIndexToItemMap[i]; + LLUUID item_id = item->getUUID(); + toggleItemSelection(item_id, true); + } + } + else + { + for (S32 i = start_idx; i >= end_idx; i--) + { + item = mIndexToItemMap[i]; + LLUUID item_id = item->getUUID(); + toggleItemSelection(item_id, true); + } + } +} + +void LLInventoryGallery::toggleSelectionRangeFromLast(const LLUUID target) +{ + if (mLastInteractedUUID == target) + { + return; + } + LLInventoryGalleryItem* last_item = mItemMap[mLastInteractedUUID]; + LLInventoryGalleryItem* next_item = mItemMap[target]; + if (last_item && next_item) + { + S32 last_idx = mItemIndexMap[last_item]; + S32 next_idx = mItemIndexMap[next_item]; + if (next_item->isSelected()) + { + if (last_idx < next_idx) + { + toggleSelectionRange(last_idx, next_idx - 1); + } + else + { + toggleSelectionRange(last_idx, next_idx + 1); + } + } + else + { + if (last_idx < next_idx) + { + toggleSelectionRange(last_idx + 1, next_idx); + } + else + { + toggleSelectionRange(last_idx - 1, next_idx); + } + } + } + mLastInteractedUUID = next_item->getUUID(); +} + +void LLInventoryGallery::onFocusLost() +{ + // inventory no longer handles cut/copy/paste/delete + if (gEditMenuHandler == this) + { + gEditMenuHandler = NULL; + } + + LLPanel::onFocusLost(); + + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(false); + } + } +} + +void LLInventoryGallery::onFocusReceived() +{ + // inventory now handles cut/copy/paste/delete + gEditMenuHandler = this; + + // Tab support, when tabbing into this view, select first item + if (mSelectedItemIDs.size() > 0) + { + LLInventoryGalleryItem* focus_item = NULL; + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + focus_item = mItemMap[id]; + focus_item->setSelected(true); + } + } + if (focus_item) + { + focus_item->setFocus(TRUE); + } + } + else if (mIndexToItemMap.size() > 0 && mItemsToSelect.empty()) + { + // choose any items from visible rect + S32 vert_offset = mScrollPanel->getDocPosVertical(); + S32 panel_size = mVerticalGap + mRowPanelHeight; + S32 n = llclamp((S32)(vert_offset / panel_size) * mItemsInRow, 0, (S32)(mIndexToItemMap.size() - 1) ); + + LLInventoryGalleryItem* focus_item = mIndexToItemMap[n]; + changeItemSelection(focus_item->getUUID(), true); + focus_item->setFocus(TRUE); + } + + LLPanel::onFocusReceived(); +} + +void LLInventoryGallery::showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id) +{ + if (mInventoryGalleryMenu && item_id.notNull()) + { + if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) == mSelectedItemIDs.end()) + { + changeItemSelection(item_id, false); + } + uuid_vec_t selected_uuids(mSelectedItemIDs.begin(), mSelectedItemIDs.end()); + mInventoryGalleryMenu->show(ctrl, selected_uuids, x, y); + } +} + +void LLInventoryGallery::changeItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + mSelectedItemIDs.clear(); + mItemsToSelect.clear(); + + if ((mItemMap.count(item_id) == 0) || mNeedsArrange) + { + mItemsToSelect.push_back(item_id); + return; + } + if (mSelectedItemIDs.size() == 1 + && std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end()) + { + // Already selected + mLastInteractedUUID = item_id; + return; + } + + if (mItemMap[item_id]) + { + mItemMap[item_id]->setSelected(TRUE); + } + mSelectedItemIDs.push_back(item_id); + signalSelectionItemID(item_id); + mLastInteractedUUID = item_id; + + if (scroll_to_selection) + { + scrollToShowItem(item_id); + } +} + +void LLInventoryGallery::addItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ + if ((mItemMap.count(item_id) == 0) || mNeedsArrange) + { + mItemsToSelect.push_back(item_id); + return; + } + if (std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id) != mSelectedItemIDs.end()) + { + // Already selected + mLastInteractedUUID = item_id; + return; + } + + if (mItemMap[item_id]) + { + mItemMap[item_id]->setSelected(TRUE); + } + mSelectedItemIDs.push_back(item_id); + signalSelectionItemID(item_id); + mLastInteractedUUID = item_id; + + if (scroll_to_selection) + { + scrollToShowItem(item_id); + } +} + +bool LLInventoryGallery::toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection) +{ + bool result = false; + if ((mItemMap.count(item_id) == 0) || mNeedsArrange) + { + mItemsToSelect.push_back(item_id); + return result; + } + selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), item_id); + if (found != mSelectedItemIDs.end()) + { + if (mItemMap[item_id]) + { + mItemMap[item_id]->setSelected(FALSE); + } + mSelectedItemIDs.erase(found); + result = false; + } + else + { + if (mItemMap[item_id]) + { + mItemMap[item_id]->setSelected(TRUE); + } + mSelectedItemIDs.push_back(item_id); + signalSelectionItemID(item_id); + result = true; + } + mLastInteractedUUID = item_id; + + if (scroll_to_selection) + { + scrollToShowItem(item_id); + } + return result; +} + +void LLInventoryGallery::scrollToShowItem(const LLUUID& item_id) +{ + LLInventoryGalleryItem* item = mItemMap[item_id]; + if(item) + { + const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect(); + + LLRect item_rect; + item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel); + LLRect overlap_rect(item_rect); + overlap_rect.intersectWith(visible_content_rect); + + //Scroll when the selected item is outside the visible area + if (overlap_rect.getHeight() + 5 < item->getRect().getHeight()) + { + LLRect content_rect = mScrollPanel->getContentWindowRect(); + LLRect constraint_rect; + constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + + LLRect item_doc_rect; + item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel); + + mScrollPanel->scrollToShowRect( item_doc_rect, constraint_rect ); + } + } +} + +LLInventoryGalleryItem* LLInventoryGallery::getFirstSelectedItem() +{ + if (mSelectedItemIDs.size() > 0) + { + selection_deque::iterator iter = mSelectedItemIDs.begin(); + return mItemMap[*iter]; + } + return NULL; +} + +void LLInventoryGallery::copy() +{ + if (!getVisible() || !getEnabled()) + { + return; + } + + LLClipboard::instance().reset(); + + for (const LLUUID& id : mSelectedItemIDs) + { + LLClipboard::instance().addToClipboard(id); + } + mFilterSubString.clear(); +} + +BOOL LLInventoryGallery::canCopy() const +{ + if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty()) + { + return FALSE; + } + + for (const LLUUID& id : mSelectedItemIDs) + { + if (!isItemCopyable(id)) + { + return FALSE; + } + } + + return TRUE; +} + +void LLInventoryGallery::cut() +{ + if (!getVisible() || !getEnabled()) + { + return; + } + + // clear the inventory clipboard + LLClipboard::instance().reset(); + LLClipboard::instance().setCutMode(true); + for (const LLUUID& id : mSelectedItemIDs) + { + // todo: fade out selected item + LLClipboard::instance().addToClipboard(id); + } + + mFilterSubString.clear(); +} + +BOOL LLInventoryGallery::canCut() const +{ + if (!getVisible() || !getEnabled() || mSelectedItemIDs.empty()) + { + return FALSE; + } + + for (const LLUUID& id : mSelectedItemIDs) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(id); + if (cat) + { + if (!get_is_category_removable(&gInventory, id)) + { + return FALSE; + } + } + else if (!get_is_item_removable(&gInventory, id)) + { + return FALSE; + } + } + + return TRUE; +} + +void LLInventoryGallery::paste() +{ + if (!LLClipboard::instance().hasContents()) + { + return; + } + + const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + if (mSelectedItemIDs.size() == 1 && gInventory.isObjectDescendentOf(*mSelectedItemIDs.begin(), marketplacelistings_id)) + { + return; + } + + bool is_cut_mode = LLClipboard::instance().isCutMode(); + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + + bool paste_into_root = mSelectedItemIDs.empty(); + for (LLUUID& dest : mSelectedItemIDs) + { + LLInventoryObject* obj = gInventory.getObject(dest); + if (!obj || (obj->getType() != LLAssetType::AT_CATEGORY)) + { + paste_into_root = true; + continue; + } + + paste(dest, objects, is_cut_mode, marketplacelistings_id); + is_cut_mode = false; + } + + if (paste_into_root) + { + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + mSelectedItemIDs.clear(); + + paste(mFolderID, objects, is_cut_mode, marketplacelistings_id); + } + + LLClipboard::instance().setCutMode(false); +} + +void LLInventoryGallery::paste(const LLUUID& dest, + std::vector& objects, + bool is_cut_mode, + const LLUUID& marketplacelistings_id) +{ + LLHandle handle = getHandle(); + std::function on_copy_callback = NULL; + LLPointer cb = NULL; + if (dest == mFolderID) + { + on_copy_callback = [handle](const LLUUID& inv_item) + { + LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); + if (panel) + { + // Scroll to pasted item and highlight it + // Should it only highlight the last one? + panel->addItemSelection(inv_item, true); + } + }; + cb = new LLBoostFuncInventoryCallback(on_copy_callback); + } + + for (std::vector::const_iterator iter = objects.begin(); iter != objects.end(); ++iter) + { + const LLUUID& item_id = (*iter); + if (gInventory.isObjectDescendentOf(item_id, marketplacelistings_id) && (LLMarketplaceData::instance().isInActiveFolder(item_id) || + LLMarketplaceData::instance().isListedAndActive(item_id))) + { + return; + } + LLViewerInventoryCategory* cat = gInventory.getCategory(item_id); + if (cat) + { + if (is_cut_mode) + { + gInventory.changeCategoryParent(cat, dest, false); + if (dest == mFolderID) + { + // Don't select immediately, wait for item to arrive + mItemsToSelect.push_back(item_id); + } + } + else + { + copy_inventory_category(&gInventory, cat, dest, LLUUID::null, false, on_copy_callback); + } + } + else + { + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (item) + { + if (is_cut_mode) + { + gInventory.changeItemParent(item, dest, false); + if (dest == mFolderID) + { + // Don't select immediately, wait for item to arrive + mItemsToSelect.push_back(item_id); + } + } + else + { + if (item->getIsLinkType()) + { + link_inventory_object(dest, item_id, cb); + } + else + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + dest, + std::string(), + cb); + } + } + } + } + } + + LLClipboard::instance().setCutMode(false); +} + +BOOL LLInventoryGallery::canPaste() const +{ + // Return FALSE on degenerated cases: empty clipboard, no inventory, no agent + if (!LLClipboard::instance().hasContents()) + { + return FALSE; + } + + // In cut mode, whatever is on the clipboard is always pastable + if (LLClipboard::instance().isCutMode()) + { + return TRUE; + } + + // In normal mode, we need to check each element of the clipboard to know if we can paste or not + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + S32 count = objects.size(); + for (S32 i = 0; i < count; i++) + { + const LLUUID& item_id = objects.at(i); + + // Each item must be copyable to be pastable + if (!isItemCopyable(item_id)) + { + return FALSE; + } + } + return TRUE; +} + +void LLInventoryGallery::onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + for (const LLUUID& id : selected_ids) + { + LLInventoryObject* obj = gInventory.getObject(id); + if (!obj) + { + return; + } + if (obj->getType() == LLAssetType::AT_CATEGORY) + { + if (get_is_category_removable(&gInventory, id)) + { + gInventory.removeCategory(id); + } + } + else + { + if (get_is_item_removable(&gInventory, id)) + { + gInventory.removeItem(id); + } + } + } + } +} + +void LLInventoryGallery::deleteSelection() +{ + if (!LLInventoryAction::sDeleteConfirmationDisplayed) // ask for the confirmation at least once per session + { + LLNotifications::instance().setIgnored("DeleteItems", false); + LLInventoryAction::sDeleteConfirmationDisplayed = true; + } + + LLSD args; + args["QUESTION"] = LLTrans::getString("DeleteItem"); + LLNotificationsUtil::add("DeleteItems", args, LLSD(), boost::bind(&LLInventoryGallery::onDelete, _1, _2, mSelectedItemIDs)); +} + +bool LLInventoryGallery::canDeleteSelection() +{ + if (mSelectedItemIDs.empty()) + { + return false; + } + + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + if (mFolderID == trash_id || gInventory.isObjectDescendentOf(mFolderID, trash_id)) + { + return false; + } + + for (const LLUUID& id : mSelectedItemIDs) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(id); + if (cat) + { + if (!get_is_category_removable(&gInventory, id)) + { + return false; + } + } + else if (!get_is_item_removable(&gInventory, id)) + { + return false; + } + } + + return true; +} + +void LLInventoryGallery::pasteAsLink() +{ + if (!LLClipboard::instance().hasContents()) + { + return; + } + + const LLUUID& current_outfit_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID& marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID& my_outifts_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + std::vector objects; + LLClipboard::instance().pasteFromClipboard(objects); + + bool paste_into_root = mSelectedItemIDs.empty(); + for (LLUUID& dest : mSelectedItemIDs) + { + LLInventoryObject* obj = gInventory.getObject(dest); + if (!obj || obj->getType() != LLAssetType::AT_CATEGORY) + { + paste_into_root = true; + continue; + } + + pasteAsLink(dest, objects, current_outfit_id, marketplacelistings_id, my_outifts_id); + } + + if (paste_into_root) + { + for (const LLUUID& id : mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + mSelectedItemIDs.clear(); + + pasteAsLink(mFolderID, objects, current_outfit_id, marketplacelistings_id, my_outifts_id); + } + + LLClipboard::instance().setCutMode(false); +} + +void LLInventoryGallery::pasteAsLink(const LLUUID& dest, + std::vector& objects, + const LLUUID& current_outfit_id, + const LLUUID& marketplacelistings_id, + const LLUUID& my_outifts_id) +{ + const BOOL move_is_into_current_outfit = (dest == current_outfit_id); + const BOOL move_is_into_my_outfits = (dest == my_outifts_id) || gInventory.isObjectDescendentOf(dest, my_outifts_id); + const BOOL move_is_into_marketplacelistings = gInventory.isObjectDescendentOf(dest, marketplacelistings_id); + + if (move_is_into_marketplacelistings || move_is_into_current_outfit || move_is_into_my_outfits) + { + return; + } + + LLPointer cb = NULL; + if (dest == mFolderID) + { + LLHandle handle = getHandle(); + std::function on_link_callback = [handle](const LLUUID& inv_item) + { + LLInventoryGallery* panel = (LLInventoryGallery*)handle.get(); + if (panel) + { + // Scroll to pasted item and highlight it + // Should it only highlight the last one? + panel->addItemSelection(inv_item, true); + } + }; + cb = new LLBoostFuncInventoryCallback(on_link_callback); + } + + for (std::vector::const_iterator iter = objects.begin(); + iter != objects.end(); + ++iter) + { + const LLUUID& object_id = (*iter); + if (LLConstPointer link_obj = gInventory.getObject(object_id)) + { + link_inventory_object(dest, link_obj, cb); + } + } +} + +void LLInventoryGallery::claimEditHandler() +{ + gEditMenuHandler = this; +} + +void LLInventoryGallery::resetEditHandler() +{ + if (gEditMenuHandler == this) + { + gEditMenuHandler = NULL; + } +} + +bool LLInventoryGallery::isItemCopyable(const LLUUID & item_id) +{ + const LLInventoryCategory* cat = gInventory.getCategory(item_id); + if (cat) + { + // Folders are copyable if items in them are, recursively, copyable. + // Get the content of the folder + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(item_id, cat_array, item_array); + + // Check the items + LLInventoryModel::item_array_t item_array_copy = *item_array; + for (LLInventoryModel::item_array_t::iterator iter = item_array_copy.begin(); iter != item_array_copy.end(); iter++) + { + LLInventoryItem* item = *iter; + if (!isItemCopyable(item->getUUID())) + { + return false; + } + } + + // Check the folders + LLInventoryModel::cat_array_t cat_array_copy = *cat_array; + for (LLInventoryModel::cat_array_t::iterator iter = cat_array_copy.begin(); iter != cat_array_copy.end(); iter++) + { + LLViewerInventoryCategory* category = *iter; + if (!isItemCopyable(category->getUUID())) + { + return false; + } + } + + return true; + } + + LLViewerInventoryItem* item = gInventory.getItem(item_id); + if (item) + { + // Can't copy worn objects. + // Worn objects are tied to their inworld conterparts + // Copy of modified worn object will return object with obsolete asset and inventory + if (get_is_item_worn(item_id)) + { + return false; + } + + static LLCachedControl inventory_linking(gSavedSettings, "InventoryLinking", true); + return (item->getIsLinkType() && inventory_linking) + || item->getPermissions().allowCopyBy(gAgent.getID()); + } + + return false; +} + +void LLInventoryGallery::updateMessageVisibility() +{ + + mMessageTextBox->setVisible(mItems.empty()); + if(mItems.empty()) + { + mMessageTextBox->setText(hasDescendents(mFolderID) ? LLTrans::getString("InventorySingleFolderEmpty") : LLTrans::getString("InventorySingleFolderNoMatches")); + } + + mScrollPanel->setVisible(!mItems.empty()); +} + +void LLInventoryGallery::refreshList(const LLUUID& category_id) +{ + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + + gInventory.getDirectDescendentsOf(category_id, cat_array, item_array); + uuid_vec_t vadded; + uuid_vec_t vremoved; + + // Create added and removed items vectors. + computeDifference(*cat_array, *item_array, vadded, vremoved); + + // Handle added tabs. + for (uuid_vec_t::const_iterator iter = vadded.begin(); + iter != vadded.end(); + ++iter) + { + const LLUUID cat_id = (*iter); + updateAddedItem(cat_id); + mNeedsArrange = true; + } + + // Handle removed tabs. + for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) + { + const LLUUID cat_id = (*iter); + updateRemovedItem(cat_id); + } + + const LLInventoryModel::changed_items_t& changed_items = gInventory.getChangedIDs(); + for (LLInventoryModel::changed_items_t::const_iterator items_iter = changed_items.begin(); + items_iter != changed_items.end(); + ++items_iter) + { + LLInventoryObject* obj = gInventory.getObject(*items_iter); + if(!obj) + { + return; + } + + updateChangedItemName(*items_iter, obj->getName()); + mNeedsArrange = true; + } + + if(mNeedsArrange || !mItemsToSelect.empty()) + { + // Don't scroll to target/arrange immediately + // since more updates might be pending + gIdleCallbacks.addFunction(onIdle, (void*)this); + } + updateMessageVisibility(); +} + +void LLInventoryGallery::computeDifference( + const LLInventoryModel::cat_array_t vcats, + const LLInventoryModel::item_array_t vitems, + uuid_vec_t& vadded, + uuid_vec_t& vremoved) +{ + uuid_vec_t vnew; + // Creating a vector of newly collected UUIDs. + for (LLInventoryModel::cat_array_t::const_iterator iter = vcats.begin(); + iter != vcats.end(); + iter++) + { + vnew.push_back((*iter)->getUUID()); + } + for (LLInventoryModel::item_array_t::const_iterator iter = vitems.begin(); + iter != vitems.end(); + iter++) + { + vnew.push_back((*iter)->getUUID()); + } + + uuid_vec_t vcur; + getCurrentCategories(vcur); + std::copy(mItemBuildQuery.begin(), mItemBuildQuery.end(), std::back_inserter(vcur)); + + LLCommonUtils::computeDifference(vnew, vcur, vadded, vremoved); +} + +void LLInventoryGallery::onCOFChanged() +{ + LLInventoryModel::cat_array_t cat_array; + LLInventoryModel::item_array_t item_array; + + gInventory.collectDescendents( + LLAppearanceMgr::instance().getCOF(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH); + + uuid_vec_t vnew; + uuid_vec_t vadded; + uuid_vec_t vremoved; + + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + ++iter) + { + vnew.push_back((*iter)->getLinkedUUID()); + } + + // We need to update only items that were added or removed from COF. + LLCommonUtils::computeDifference(vnew, mCOFLinkedItems, vadded, vremoved); + + mCOFLinkedItems = vnew; + + for (uuid_vec_t::const_iterator iter = vadded.begin(); + iter != vadded.end(); + ++iter) + { + updateWornItem(*iter, true); + } + + for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) + { + updateWornItem(*iter, false); + } +} + +void LLInventoryGallery::onGesturesChanged() +{ + uuid_vec_t vnew; + uuid_vec_t vadded; + uuid_vec_t vremoved; + + const LLGestureMgr::item_map_t& active_gestures = LLGestureMgr::instance().getActiveGestures(); + for (LLGestureMgr::item_map_t::const_iterator iter = active_gestures.begin(); + iter != active_gestures.end(); + ++iter) + { + vnew.push_back(iter->first); + } + + LLCommonUtils::computeDifference(vnew, mActiveGestures, vadded, vremoved); + + mActiveGestures = vnew; + + for (uuid_vec_t::const_iterator iter = vadded.begin(); + iter != vadded.end(); + ++iter) + { + updateWornItem(*iter, true); + } + + for (uuid_vec_t::const_iterator iter = vremoved.begin(); iter != vremoved.end(); ++iter) + { + updateWornItem(*iter, false); + } +} + +void LLInventoryGallery::deselectItem(const LLUUID& category_id) +{ + // Reset selection if the item is selected. + LLInventoryGalleryItem* item = mItemMap[category_id]; + if (item && item->isSelected()) + { + mItemMap[category_id]->setSelected(FALSE); + setFocus(true); + // Todo: support multiselect + // signalSelectionItemID(LLUUID::null); + } + + selection_deque::iterator found = std::find(mSelectedItemIDs.begin(), mSelectedItemIDs.end(), category_id); + if (found != mSelectedItemIDs.end()) + { + mSelectedItemIDs.erase(found); + } +} + +void LLInventoryGallery::clearSelection() +{ + for (const LLUUID& id: mSelectedItemIDs) + { + if (mItemMap[id]) + { + mItemMap[id]->setSelected(FALSE); + } + } + if (!mSelectedItemIDs.empty()) + { + mSelectedItemIDs.clear(); + // BUG: wrong, item can be null + signalSelectionItemID(LLUUID::null); + } +} + +void LLInventoryGallery::signalSelectionItemID(const LLUUID& category_id) +{ + mSelectionChangeSignal(category_id); +} + +boost::signals2::connection LLInventoryGallery::setSelectionChangeCallback(selection_change_callback_t cb) +{ + return mSelectionChangeSignal.connect(cb); +} + +LLUUID LLInventoryGallery::getFirstSelectedItemID() +{ + if (mSelectedItemIDs.size() > 0) + { + return *mSelectedItemIDs.begin(); + } + return LLUUID::null; +} + +LLUUID LLInventoryGallery::getOutfitImageID(LLUUID outfit_id) +{ + LLUUID thumbnail_id; + LLViewerInventoryCategory* cat = gInventory.getCategory(outfit_id); + if (cat && cat->getPreferredType() == LLFolderType::FT_OUTFIT) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + // Not LLIsOfAssetType, because we allow links + LLIsTextureType f; + gInventory.getDirectDescendentsOf(outfit_id, cats, items, f); + + // Exactly one texture found => show the texture as thumbnail + if (1 == items.size()) + { + LLViewerInventoryItem* item = items.front(); + if (item && item->getIsLinkType()) + { + item = item->getLinkedItem(); + } + if (item) + { + thumbnail_id = item->getAssetUUID(); + } + } + } + return thumbnail_id; +} + +boost::signals2::connection LLInventoryGallery::setRootChangedCallback(callback_t cb) +{ + return mRootChangedSignal.connect(cb); +} + +void LLInventoryGallery::onForwardFolder() +{ + if(isForwardAvailable()) + { + mBackwardFolders.push_back(mFolderID); + mFolderID = mForwardFolders.back(); + mForwardFolders.pop_back(); + dirtyRootFolder(); + } +} + +void LLInventoryGallery::onBackwardFolder() +{ + if(isBackwardAvailable()) + { + mForwardFolders.push_back(mFolderID); + mFolderID = mBackwardFolders.back(); + mBackwardFolders.pop_back(); + dirtyRootFolder(); + } +} + +void LLInventoryGallery::clearNavigationHistory() +{ + mForwardFolders.clear(); + mBackwardFolders.clear(); +} + +bool LLInventoryGallery::isBackwardAvailable() +{ + return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back())); +} + +bool LLInventoryGallery::isForwardAvailable() +{ + return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back())); +} + +BOOL LLInventoryGallery::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, void* cargo_data, + EAcceptance* accept, std::string& tooltip_msg) +{ + // have children handle it first + BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, + accept, tooltip_msg); + + // when drop is not handled by child, it should be handled by the root folder . + if (!handled || (*accept == ACCEPT_NO)) + { + handled = baseHandleDragAndDrop(mFolderID, drop, cargo_type, cargo_data, accept, tooltip_msg); + } + + return handled; +} + +void LLInventoryGallery::startDrag() +{ + std::vector types; + uuid_vec_t ids; + LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_AGENT; + for (LLUUID& selected_id : mSelectedItemIDs) + { + const LLInventoryItem* item = gInventory.getItem(selected_id); + if (item) + { + if (item->getPermissions().getOwner() == ALEXANDRIA_LINDEN_ID) + { + src = LLToolDragAndDrop::SOURCE_LIBRARY; + } + + EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(item->getType()); + types.push_back(type); + ids.push_back(selected_id); + } + + const LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); + if (cat && gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID()) + && !LLFolderType::lookupIsProtectedType((cat)->getPreferredType())) + { + if (cat->getOwnerID() == ALEXANDRIA_LINDEN_ID) + { + src = LLToolDragAndDrop::SOURCE_LIBRARY; + } + + EDragAndDropType type = LLViewerAssetType::lookupDragAndDropType(cat->getType()); + types.push_back(type); + ids.push_back(selected_id); + } + } + LLToolDragAndDrop::getInstance()->beginMultiDrag(types, ids, LLToolDragAndDrop::SOURCE_AGENT); +} + +bool LLInventoryGallery::areViewsInitialized() +{ + return mGalleryCreated && mItemBuildQuery.empty(); +} + +bool LLInventoryGallery::hasDescendents(const LLUUID& cat_id) +{ + LLInventoryModel::cat_array_t* cats; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat_id, cats, items); + + return (cats->empty() && items->empty()); +} + +bool LLInventoryGallery::checkAgainstFilterType(const LLUUID& object_id) +{ + const LLInventoryObject *object = gInventory.getObject(object_id); + if(!object) return false; + + LLInventoryType::EType object_type = LLInventoryType::IT_CATEGORY; + LLInventoryItem* inv_item = gInventory.getItem(object_id); + if (inv_item) + { + object_type = inv_item->getInventoryType(); + } + const U32 filterTypes = mFilter->getFilterTypes(); + + if ((filterTypes & LLInventoryFilter::FILTERTYPE_OBJECT) && inv_item) + { + switch (object_type) + { + case LLInventoryType::IT_NONE: + // If it has no type, pass it, unless it's a link. + if (object && object->getIsLinkType()) + { + return false; + } + break; + case LLInventoryType::IT_UNKNOWN: + { + // Unknows are only shown when we show every type. + // Unknows are 255 and won't fit in 64 bits. + if (mFilter->getFilterObjectTypes() != 0xffffffffffffffffULL) + { + return false; + } + break; + } + default: + if ((1LL << object_type & mFilter->getFilterObjectTypes()) == U64(0)) + { + return false; + } + break; + } + } + + if (filterTypes & LLInventoryFilter::FILTERTYPE_DATE) + { + const U16 HOURS_TO_SECONDS = 3600; + time_t earliest = time_corrected() - mFilter->getHoursAgo() * HOURS_TO_SECONDS; + + if (mFilter->getMinDate() > time_min() && mFilter->getMinDate() < earliest) + { + earliest = mFilter->getMinDate(); + } + else if (!mFilter->getHoursAgo()) + { + earliest = 0; + } + + if (LLInventoryFilter::FILTERDATEDIRECTION_NEWER == mFilter->getDateSearchDirection() || mFilter->isSinceLogoff()) + { + if (object->getCreationDate() < earliest || + object->getCreationDate() > mFilter->getMaxDate()) + return false; + } + else + { + if (object->getCreationDate() > earliest || + object->getCreationDate() > mFilter->getMaxDate()) + return false; + } + } + return true; +} + +bool LLInventoryGallery::hasVisibleItems() +{ + return mItemsAddedCount > 0; +} + +void LLInventoryGallery::handleModifiedFilter() +{ + if(mFilter->isModified()) + { + reArrangeRows(); + } +} + +void LLInventoryGallery::setSortOrder(U32 order, bool update) +{ + bool dirty = (mSortOrder != order); + + mSortOrder = order; + if(update && dirty) + { + mNeedsArrange = true; + gIdleCallbacks.addFunction(onIdle, (void*)this); + } +} +//----------------------------- +// LLInventoryGalleryItem +//----------------------------- + +static LLDefaultChildRegistry::Register r("inventory_gallery_item"); + +LLInventoryGalleryItem::LLInventoryGalleryItem(const Params& p) + : LLPanel(p), + mSelected(false), + mDefaultImage(true), + mItemName(""), + mWornSuffix(""), + mPermSuffix(""), + mUUID(LLUUID()), + mIsFolder(true), + mIsLink(false), + mHidden(false), + mGallery(NULL), + mType(LLAssetType::AT_NONE), + mSortGroup(SG_ITEM), + mCutGeneration(0), + mSelectedForCut(false) +{ + buildFromFile("panel_inventory_gallery_item.xml"); +} + +LLInventoryGalleryItem::~LLInventoryGalleryItem() +{ +} + +BOOL LLInventoryGalleryItem::postBuild() +{ + mNameText = getChild("item_name"); + mTextBgPanel = getChild("text_bg_panel"); + + return TRUE; +} + +void LLInventoryGalleryItem::setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link) +{ + mType = type; + mIsFolder = (mType == LLAssetType::AT_CATEGORY); + mIsLink = is_link; + + std::string icon_name = LLInventoryIcon::getIconName(mType, inventory_type, flags); + if(mIsFolder) + { + mSortGroup = SG_NORMAL_FOLDER; + LLUUID folder_id = mUUID; + if(mIsLink) + { + LLInventoryObject* obj = gInventory.getObject(mUUID); + if (obj) + { + folder_id = obj->getLinkedUUID(); + } + } + LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); + if (cat) + { + LLFolderType::EType preferred_type = cat->getPreferredType(); + icon_name = LLViewerFolderType::lookupIconName(preferred_type); + + if (preferred_type == LLFolderType::FT_TRASH) + { + mSortGroup = SG_TRASH_FOLDER; + } + else if(LLFolderType::lookupIsProtectedType(cat->getPreferredType())) + { + mSortGroup = SG_SYSTEM_FOLDER; + } + } + } + else + { + const LLInventoryItem *item = gInventory.getItem(mUUID); + if(item && (LLAssetType::AT_CALLINGCARD != item->getType()) && !mIsLink) + { + std::string delim(" --"); + bool copy = item->getPermissions().allowCopyBy(gAgent.getID()); + if (!copy) + { + mPermSuffix += delim; + mPermSuffix += LLTrans::getString("no_copy_lbl"); + } + bool mod = item->getPermissions().allowModifyBy(gAgent.getID()); + if (!mod) + { + mPermSuffix += mPermSuffix.empty() ? delim : ","; + mPermSuffix += LLTrans::getString("no_modify_lbl"); + } + bool xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()); + if (!xfer) + { + mPermSuffix += mPermSuffix.empty() ? delim : ","; + mPermSuffix += LLTrans::getString("no_transfer_lbl"); + } + } + } + + getChild("item_type")->setValue(icon_name); + getChild("link_overlay")->setVisible(is_link); +} + +void LLInventoryGalleryItem::setThumbnail(LLUUID id) +{ + mDefaultImage = id.isNull(); + if(mDefaultImage) + { + getChild("preview_thumbnail")->clearTexture(); + } + else + { + getChild("preview_thumbnail")->setValue(id); + } +} + +void LLInventoryGalleryItem::draw() +{ + if (isFadeItem()) + { + // Fade out to indicate it's being cut + LLViewDrawContext context(0.5f); + LLPanel::draw(); + } + else + { + LLPanel::draw(); + + // Draw border + LLUIColor border_color = LLUIColorTable::instance().getColor(mSelected ? "MenuItemHighlightBgColor" : "TextFgTentativeColor", LLColor4::white); + LLRect border = getChildView("preview_thumbnail")->getRect(); + border.mRight = border.mRight + 1; + border.mTop = border.mTop + 1; + gl_rect_2d(border, border_color.get(), FALSE); + } +} + +void LLInventoryGalleryItem::setItemName(std::string name) +{ + mItemName = name; + updateNameText(); +} + +void LLInventoryGalleryItem::setSelected(bool value) +{ + mSelected = value; + mTextBgPanel->setBackgroundVisible(value); + + if(mSelected) + { + LLViewerInventoryItem* item = gInventory.getItem(mUUID); + if(item && !item->isFinished()) + { + LLInventoryModelBackgroundFetch::instance().start(mUUID, false); + } + } +} + +BOOL LLInventoryGalleryItem::handleMouseDown(S32 x, S32 y, MASK mask) +{ + // call changeItemSelection directly, before setFocus + // to avoid autoscroll from LLInventoryGallery::onFocusReceived() + if (mask == MASK_CONTROL) + { + mGallery->addItemSelection(mUUID, false); + } + else if (mask == MASK_SHIFT) + { + mGallery->toggleSelectionRangeFromLast(mUUID); + } + else + { + mGallery->changeItemSelection(mUUID, false); + } + setFocus(TRUE); + mGallery->claimEditHandler(); + + gFocusMgr.setMouseCapture(this); + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); + LLToolDragAndDrop::getInstance()->setDragStart(screen_x, screen_y); + return TRUE; +} + +BOOL LLInventoryGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + if (!isSelected()) + { + mGallery->changeItemSelection(mUUID, false); + } + else + { + // refresh last interacted + mGallery->addItemSelection(mUUID, false); + } + setFocus(TRUE); + mGallery->claimEditHandler(); + mGallery->showContextMenu(this, x, y, mUUID); + + LLUICtrl::handleRightMouseDown(x, y, mask); + return TRUE; +} + +BOOL LLInventoryGalleryItem::handleMouseUp(S32 x, S32 y, MASK mask) +{ + if(hasMouseCapture()) + { + gFocusMgr.setMouseCapture(NULL); + return TRUE; + } + return LLPanel::handleMouseUp(x, y, mask); +} + +BOOL LLInventoryGalleryItem::handleHover(S32 x, S32 y, MASK mask) +{ + if(hasMouseCapture()) + { + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); + + if(LLToolDragAndDrop::getInstance()->isOverThreshold(screen_x, screen_y) && mGallery) + { + mGallery->startDrag(); + return LLToolDragAndDrop::getInstance()->handleHover(x, y, mask); + } + } + return LLUICtrl::handleHover(x,y,mask); +} + +BOOL LLInventoryGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + if (mIsFolder && mGallery) + { + // setRootFolder can destroy this item. + // Delay it until handleDoubleClick processing is complete + // or make gallery handle doubleclicks. + LLHandle handle = mGallery->getHandle(); + LLUUID navigate_to = mUUID; + doOnIdleOneTime([handle, navigate_to]() + { + LLInventoryGallery* gallery = (LLInventoryGallery*)handle.get(); + if (gallery) + { + gallery->setRootFolder(navigate_to); + } + }); + } + else + { + LLInvFVBridgeAction::doAction(mUUID, &gInventory); + } + + return TRUE; +} + +BOOL LLInventoryGalleryItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + if (!mIsFolder) + { + return FALSE; + } + return mGallery->baseHandleDragAndDrop(mUUID, drop, cargo_type, cargo_data, accept, tooltip_msg); +} + +BOOL LLInventoryGalleryItem::handleKeyHere(KEY key, MASK mask) +{ + if (!mGallery) + { + return FALSE; + } + + BOOL handled = FALSE; + switch (key) + { + case KEY_LEFT: + mGallery->moveLeft(mask); + handled = true; + break; + + case KEY_RIGHT: + mGallery->moveRight(mask); + handled = true; + break; + + case KEY_UP: + mGallery->moveUp(mask); + handled = true; + break; + + case KEY_DOWN: + mGallery->moveDown(mask); + handled = true; + break; + + default: + break; + } + return handled; +} + +void LLInventoryGalleryItem::onFocusLost() +{ + // inventory no longer handles cut/copy/paste/delete + mGallery->resetEditHandler(); + + LLPanel::onFocusLost(); +} + +void LLInventoryGalleryItem::onFocusReceived() +{ + // inventory now handles cut/copy/paste/delete + mGallery->claimEditHandler(); + + LLPanel::onFocusReceived(); +} + +void LLInventoryGalleryItem::setWorn(bool value) +{ + mWorn = value; + + if(mWorn) + { + mWornSuffix = (mType == LLAssetType::AT_GESTURE) ? LLTrans::getString("active") : LLTrans::getString("worn"); + } + else + { + mWornSuffix = ""; + } + + updateNameText(); +} + +LLFontGL* LLInventoryGalleryItem::getTextFont() +{ + if(mWorn) + { + return LLFontGL::getFontSansSerifSmallBold(); + } + return mIsLink ? LLFontGL::getFontSansSerifSmallItalic() : LLFontGL::getFontSansSerifSmall(); +} + +void LLInventoryGalleryItem::updateNameText() +{ + mNameText->setFont(getTextFont()); + mNameText->setText(mItemName + mPermSuffix + mWornSuffix); + mNameText->setToolTip(mItemName + mPermSuffix + mWornSuffix); + getChild("preview_thumbnail")->setToolTip(mItemName + mPermSuffix + mWornSuffix); +} + +bool LLInventoryGalleryItem::isFadeItem() +{ + LLClipboard& clipboard = LLClipboard::instance(); + if (mCutGeneration == clipboard.getGeneration()) + { + return mSelectedForCut; + } + + mCutGeneration = clipboard.getGeneration(); + mSelectedForCut = clipboard.isCutMode() && clipboard.isOnClipboard(mUUID); + return mSelectedForCut; +} + +//----------------------------- +// LLThumbnailsObserver +//----------------------------- + +void LLThumbnailsObserver::changed(U32 mask) +{ + std::vector deleted_ids; + for (item_map_t::iterator iter = mItemMap.begin(); + iter != mItemMap.end(); + ++iter) + { + const LLUUID& obj_id = (*iter).first; + LLItemData& data = (*iter).second; + + LLInventoryObject* obj = gInventory.getObject(obj_id); + if (!obj) + { + deleted_ids.push_back(obj_id); + continue; + } + + const LLUUID thumbnail_id = obj->getThumbnailUUID(); + if (data.mThumbnailID != thumbnail_id) + { + data.mThumbnailID = thumbnail_id; + data.mCallback(); + } + } + + // Remove deleted items from the list + for (std::vector::iterator deleted_id = deleted_ids.begin(); deleted_id != deleted_ids.end(); ++deleted_id) + { + removeItem(*deleted_id); + } +} + +bool LLThumbnailsObserver::addItem(const LLUUID& obj_id, callback_t cb) +{ + LLInventoryObject* obj = gInventory.getObject(obj_id); + if (obj) + { + mItemMap.insert(item_map_value_t(obj_id, LLItemData(obj_id, obj->getThumbnailUUID(), cb))); + return true; + } + return false; +} + +void LLThumbnailsObserver::removeItem(const LLUUID& obj_id) +{ + mItemMap.erase(obj_id); +} + +//----------------------------- +// Helper drag&drop functions +//----------------------------- + +BOOL LLInventoryGallery::baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg) +{ + LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data; + + if (drop && LLToolDragAndDrop::getInstance()->getCargoIndex() == 0) + { + clearSelection(); + } + + BOOL accepted = FALSE; + switch(cargo_type) + { + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_CALLINGCARD: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: + case DAD_MESH: + case DAD_SETTINGS: + accepted = dragItemIntoFolder(dest_id, inv_item, drop, tooltip_msg, true); + if (accepted && drop) + { + // Don't select immediately, wait for item to arrive + mItemsToSelect.push_back(inv_item->getUUID()); + } + break; + case DAD_LINK: + // DAD_LINK type might mean one of two asset types: AT_LINK or AT_LINK_FOLDER. + // If we have an item of AT_LINK_FOLDER type we should process the linked + // category being dragged or dropped into folder. + if (inv_item && LLAssetType::AT_LINK_FOLDER == inv_item->getActualType()) + { + LLInventoryCategory* linked_category = gInventory.getCategory(inv_item->getLinkedUUID()); + if (linked_category) + { + accepted = dragCategoryIntoFolder(dest_id, (LLInventoryCategory*)linked_category, drop, tooltip_msg, TRUE); + } + } + else + { + accepted = dragItemIntoFolder(dest_id, inv_item, drop, tooltip_msg, TRUE); + } + if (accepted && drop && inv_item) + { + mItemsToSelect.push_back(inv_item->getUUID()); + } + break; + case DAD_CATEGORY: + if (LLFriendCardsManager::instance().isAnyFriendCategory(dest_id)) + { + accepted = FALSE; + } + else + { + LLInventoryCategory* cat_ptr = (LLInventoryCategory*)cargo_data; + accepted = dragCategoryIntoFolder(dest_id, cat_ptr, drop, tooltip_msg, FALSE); + if (accepted && drop) + { + mItemsToSelect.push_back(cat_ptr->getUUID()); + } + } + break; + case DAD_ROOT_CATEGORY: + case DAD_NONE: + break; + default: + LL_WARNS() << "Unhandled cargo type for drag&drop " << cargo_type << LL_ENDL; + break; + } + if (accepted) + { + *accept = ACCEPT_YES_MULTI; + } + else + { + *accept = ACCEPT_NO; + } + return accepted; +} + +// copy of LLFolderBridge::dragItemIntoFolder +BOOL dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, BOOL drop, std::string& tooltip_msg, BOOL user_confirm) +{ + LLViewerInventoryCategory * cat = gInventory.getCategory(folder_id); + if (!cat) + { + return FALSE; + } + LLInventoryModel* model = &gInventory; + + if (!model || !inv_item) return FALSE; + + // cannot drag into library + if((gInventory.getRootFolderID() != folder_id) && !model->isObjectDescendentOf(folder_id, gInventory.getRootFolderID())) + { + return FALSE; + } + if (!isAgentAvatarValid()) return FALSE; + + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &favorites_id = model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + const BOOL move_is_into_current_outfit = (folder_id == current_outfit_id); + const BOOL move_is_into_favorites = (folder_id == favorites_id); + const BOOL move_is_into_my_outfits = (folder_id == my_outifts_id) || model->isObjectDescendentOf(folder_id, my_outifts_id); + const BOOL move_is_into_outfit = move_is_into_my_outfits || (cat && cat->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_landmarks = (folder_id == landmarks_id) || model->isObjectDescendentOf(folder_id, landmarks_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(folder_id, marketplacelistings_id); + const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(inv_item->getUUID(), marketplacelistings_id); + + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + BOOL accept = FALSE; + LLViewerObject* object = NULL; + if(LLToolDragAndDrop::SOURCE_AGENT == source) + { + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + + const BOOL move_is_into_trash = (folder_id == trash_id) || model->isObjectDescendentOf(folder_id, trash_id); + const BOOL move_is_outof_current_outfit = LLAppearanceMgr::instance().getIsInCOF(inv_item->getUUID()); + + //-------------------------------------------------------------------------------- + // Determine if item can be moved. + // + + BOOL is_movable = TRUE; + + switch (inv_item->getActualType()) + { + case LLAssetType::AT_CATEGORY: + is_movable = !LLFolderType::lookupIsProtectedType(((LLInventoryCategory*)inv_item)->getPreferredType()); + break; + default: + break; + } + // Can't explicitly drag things out of the COF. + if (move_is_outof_current_outfit) + { + is_movable = FALSE; + } + if (move_is_into_trash) + { + is_movable &= inv_item->getIsLinkType() || !get_is_item_worn(inv_item->getUUID()); + } + if (is_movable) + { + // Don't allow creating duplicates in the Calling Card/Friends + // subfolders, see bug EXT-1599. Check is item direct descendent + // of target folder and forbid item's movement if it so. + // Note: isItemDirectDescendentOfCategory checks if + // passed category is in the Calling Card/Friends folder + is_movable &= !LLFriendCardsManager::instance().isObjDirectDescendentOfCategory(inv_item, cat); + } + + // + //-------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------------- + // Determine if item can be moved & dropped + // Note: if user_confirm is false, we already went through those accept logic test and can skip them + + accept = TRUE; + + if (user_confirm && !is_movable) + { + accept = FALSE; + } + else if (user_confirm && (folder_id == inv_item->getParentUUID()) && !move_is_into_favorites) + { + accept = FALSE; + } + else if (user_confirm && (move_is_into_current_outfit || move_is_into_outfit)) + { + accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); + } + else if (user_confirm && (move_is_into_favorites || move_is_into_landmarks)) + { + accept = can_move_to_landmarks(inv_item); + } + else if (user_confirm && move_is_into_marketplacelistings) + { + //disable dropping in or out of marketplace for now + return FALSE; + + /*const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, folder_id); + LLViewerInventoryCategory * dest_folder = cat; + accept = can_move_item_to_marketplace(master_folder, dest_folder, inv_item, tooltip_msg, LLToolDragAndDrop::instance().getCargoCount() - LLToolDragAndDrop::instance().getCargoIndex());*/ + } + + // Check that the folder can accept this item based on folder/item type compatibility (e.g. stock folder compatibility) + if (user_confirm && accept) + { + LLViewerInventoryCategory * dest_folder = cat; + accept = dest_folder->acceptItem(inv_item); + } + + LLInventoryPanel* active_panel = LLInventoryPanel::getActiveInventoryPanel(FALSE); + + if (accept && drop) + { + if (inv_item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash) + { + LLGestureMgr::instance().deactivateGesture(inv_item->getUUID()); + } + // If an item is being dragged between windows, unselect everything in the active window + // so that we don't follow the selection to its new location (which is very annoying). + // RN: a better solution would be to deselect automatically when an item is moved + // and then select any item that is dropped only in the panel that it is dropped in + if (active_panel) + { + active_panel->unSelectAll(); + } + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + //disable dropping in or out of marketplace for now + return FALSE; + } + + //-------------------------------------------------------------------------------- + // Destination folder logic + // + + // FAVORITES folder + // (copy the item) + else if (move_is_into_favorites) + { + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + folder_id, + std::string(), + LLPointer(NULL)); + } + // CURRENT OUTFIT or OUTFIT folder + // (link the item) + else if (move_is_into_current_outfit || move_is_into_outfit) + { + if (move_is_into_current_outfit) + { + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true); + } + else + { + LLPointer cb = NULL; + link_inventory_object(folder_id, LLConstPointer(inv_item), cb); + } + } + // MARKETPLACE LISTINGS folder + // Move the item + else if (move_is_into_marketplacelistings) + { + //move_item_to_marketplacelistings(inv_item, mUUID); + return FALSE; + } + // NORMAL or TRASH folder + // (move the item, restamp if into trash) + else + { + // set up observer to select item once drag and drop from inbox is complete + if (gInventory.isObjectDescendentOf(inv_item->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX))) + { + set_dad_inbox_object(inv_item->getUUID()); + } + + gInventory.changeItemParent((LLViewerInventoryItem*)inv_item, folder_id, move_is_into_trash); + } + + if (move_is_from_marketplacelistings) + { + // If we move from an active (listed) listing, checks that it's still valid, if not, unlist + /*LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) + { + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + }); + }*/ + return FALSE; + } + + // + //-------------------------------------------------------------------------------- + } + } + else if (LLToolDragAndDrop::SOURCE_WORLD == source) + { + // Make sure the object exists. If we allowed dragging from + // anonymous objects, it would be possible to bypass + // permissions. + object = gObjectList.findObject(inv_item->getParentUUID()); + if (!object) + { + LL_INFOS() << "Object not found for drop." << LL_ENDL; + return FALSE; + } + + // coming from a task. Need to figure out if the person can + // move/copy this item. + LLPermissions perm(inv_item->getPermissions()); + bool is_move = false; + if ((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID()) + && perm.allowTransferTo(gAgent.getID()))) + // || gAgent.isGodlike()) + { + accept = TRUE; + } + else if(object->permYouOwner()) + { + // If the object cannot be copied, but the object the + // inventory is owned by the agent, then the item can be + // moved from the task to agent inventory. + is_move = true; + accept = TRUE; + } + + // Don't allow placing an original item into Current Outfit or an outfit folder + // because they must contain only links to wearable items. + if (move_is_into_current_outfit || move_is_into_outfit) + { + accept = FALSE; + } + // Don't allow to move a single item to Favorites or Landmarks + // if it is not a landmark or a link to a landmark. + else if ((move_is_into_favorites || move_is_into_landmarks) + && !can_move_to_landmarks(inv_item)) + { + accept = FALSE; + } + else if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + + if (accept && drop) + { + boost::shared_ptr move_inv (new LLMoveInv()); + move_inv->mObjectID = inv_item->getParentUUID(); + std::pair item_pair(folder_id, inv_item->getUUID()); + move_inv->mMoveList.push_back(item_pair); + move_inv->mCallback = NULL; + move_inv->mUserData = NULL; + if(is_move) + { + warn_move_inventory(object, move_inv); + } + else + { + // store dad inventory item to select added one later. See EXT-4347 + set_dad_inventory_item(inv_item, folder_id); + + LLNotification::Params params("MoveInventoryFromObject"); + params.functor.function(boost::bind(move_task_inventory_callback, _1, _2, move_inv)); + LLNotifications::instance().forceResponse(params, 0); + } + } + } + else if(LLToolDragAndDrop::SOURCE_NOTECARD == source) + { + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else if ((inv_item->getActualType() == LLAssetType::AT_SETTINGS) && !LLEnvironment::instance().isInventoryEnabled()) + { + tooltip_msg = LLTrans::getString("NoEnvironmentSettings"); + accept = FALSE; + } + else + { + // Don't allow placing an original item from a notecard to Current Outfit or an outfit folder + // because they must contain only links to wearable items. + accept = !(move_is_into_current_outfit || move_is_into_outfit); + } + + if (accept && drop) + { + copy_inventory_from_notecard(folder_id, // Drop to the chosen destination folder + LLToolDragAndDrop::getInstance()->getObjectID(), + LLToolDragAndDrop::getInstance()->getSourceID(), + inv_item); + } + } + else if(LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item; + if(item && item->isFinished()) + { + accept = TRUE; + + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else if (move_is_into_current_outfit || move_is_into_outfit) + { + accept = can_move_to_outfit(inv_item, move_is_into_current_outfit); + } + // Don't allow to move a single item to Favorites or Landmarks + // if it is not a landmark or a link to a landmark. + else if (move_is_into_favorites || move_is_into_landmarks) + { + accept = can_move_to_landmarks(inv_item); + } + + if (accept && drop) + { + // FAVORITES folder + // (copy the item) + if (move_is_into_favorites) + { + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + folder_id, + std::string(), + LLPointer(NULL)); + } + // CURRENT OUTFIT or OUTFIT folder + // (link the item) + else if (move_is_into_current_outfit || move_is_into_outfit) + { + if (move_is_into_current_outfit) + { + LLAppearanceMgr::instance().wearItemOnAvatar(inv_item->getUUID(), true, true); + } + else + { + LLPointer cb = NULL; + link_inventory_object(folder_id, LLConstPointer(inv_item), cb); + } + } + else + { + copy_inventory_item( + gAgent.getID(), + inv_item->getPermissions().getOwner(), + inv_item->getUUID(), + folder_id, + std::string(), + LLPointer(NULL)); + } + } + } + } + else + { + LL_WARNS() << "unhandled drag source" << LL_ENDL; + } + return accept; +} + +// copy of LLFolderBridge::dragCategoryIntoFolder +BOOL dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat, + BOOL drop, std::string& tooltip_msg, BOOL is_link) +{ + BOOL user_confirm = TRUE; + LLInventoryModel* model = &gInventory; + LLViewerInventoryCategory * dest_cat = gInventory.getCategory(dest_id); + if (!dest_cat) + { + return FALSE; + } + + if (!inv_cat) return FALSE; // shouldn't happen, but in case item is incorrectly parented in which case inv_cat will be NULL + + if (!isAgentAvatarValid()) return FALSE; + // cannot drag into library + if((gInventory.getRootFolderID() != dest_id) && !model->isObjectDescendentOf(dest_id, gInventory.getRootFolderID())) + { + return FALSE; + } + + const LLUUID &cat_id = inv_cat->getUUID(); + const LLUUID ¤t_outfit_id = model->findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + const LLUUID &marketplacelistings_id = model->findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); + //const LLUUID from_folder_uuid = inv_cat->getParentUUID(); + + const BOOL move_is_into_current_outfit = (dest_id == current_outfit_id); + const BOOL move_is_into_marketplacelistings = model->isObjectDescendentOf(dest_id, marketplacelistings_id); + const BOOL move_is_from_marketplacelistings = model->isObjectDescendentOf(cat_id, marketplacelistings_id); + + // check to make sure source is agent inventory, and is represented there. + LLToolDragAndDrop::ESource source = LLToolDragAndDrop::getInstance()->getSource(); + const BOOL is_agent_inventory = (model->getCategory(cat_id) != NULL) + && (LLToolDragAndDrop::SOURCE_AGENT == source); + + BOOL accept = FALSE; + + if (is_agent_inventory) + { + const LLUUID &trash_id = model->findCategoryUUIDForType(LLFolderType::FT_TRASH); + const LLUUID &landmarks_id = model->findCategoryUUIDForType(LLFolderType::FT_LANDMARK); + const LLUUID &my_outifts_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + const LLUUID &lost_and_found_id = model->findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND); + + const BOOL move_is_into_trash = (dest_id == trash_id) || model->isObjectDescendentOf(dest_id, trash_id); + const BOOL move_is_into_my_outfits = (dest_id == my_outifts_id) || model->isObjectDescendentOf(dest_id, my_outifts_id); + const BOOL move_is_into_outfit = move_is_into_my_outfits || (dest_cat && dest_cat->getPreferredType()==LLFolderType::FT_OUTFIT); + const BOOL move_is_into_current_outfit = (dest_cat && dest_cat->getPreferredType()==LLFolderType::FT_CURRENT_OUTFIT); + const BOOL move_is_into_landmarks = (dest_id == landmarks_id) || model->isObjectDescendentOf(dest_id, landmarks_id); + const BOOL move_is_into_lost_and_found = model->isObjectDescendentOf(dest_id, lost_and_found_id); + + //-------------------------------------------------------------------------------- + // Determine if folder can be moved. + // + + BOOL is_movable = TRUE; + + if (is_movable && (marketplacelistings_id == cat_id)) + { + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipOutboxCannotMoveRoot"); + } + if (is_movable && move_is_from_marketplacelistings) + //&& LLMarketplaceData::instance().getActivationState(cat_id)) + { + // If the incoming folder is listed and active (and is therefore either the listing or the version folder), + // then moving is *not* allowed + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipOutboxDragActive"); + } + if (is_movable && (dest_id == cat_id)) + { + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipDragOntoSelf"); + } + if (is_movable && (model->isObjectDescendentOf(dest_id, cat_id))) + { + is_movable = FALSE; + tooltip_msg = LLTrans::getString("TooltipDragOntoOwnChild"); + } + if (is_movable && LLFolderType::lookupIsProtectedType(inv_cat->getPreferredType())) + { + is_movable = FALSE; + // tooltip? + } + + U32 max_items_to_wear = gSavedSettings.getU32("WearFolderLimit"); + if (is_movable && move_is_into_outfit) + { + if (dest_id == my_outifts_id) + { + if (source != LLToolDragAndDrop::SOURCE_AGENT || move_is_from_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutfitNotInInventory"); + is_movable = false; + } + else if (can_move_to_my_outfits(model, inv_cat, max_items_to_wear)) + { + is_movable = true; + } + else + { + tooltip_msg = LLTrans::getString("TooltipCantCreateOutfit"); + is_movable = false; + } + } + else if(dest_cat && dest_cat->getPreferredType() == LLFolderType::FT_NONE) + { + is_movable = ((inv_cat->getPreferredType() == LLFolderType::FT_NONE) || (inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)); + } + else + { + is_movable = false; + } + } + if(is_movable && move_is_into_current_outfit && is_link) + { + is_movable = FALSE; + } + if (is_movable && move_is_into_lost_and_found) + { + is_movable = FALSE; + } + if (is_movable && (dest_id == model->findCategoryUUIDForType(LLFolderType::FT_FAVORITE))) + { + is_movable = FALSE; + // tooltip? + } + if (is_movable && (dest_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_STOCK)) + { + // One cannot move a folder into a stock folder + is_movable = FALSE; + // tooltip? + } + + LLInventoryModel::cat_array_t descendent_categories; + LLInventoryModel::item_array_t descendent_items; + if (is_movable) + { + model->collectDescendents(cat_id, descendent_categories, descendent_items, FALSE); + for (S32 i=0; i < descendent_categories.size(); ++i) + { + LLInventoryCategory* category = descendent_categories[i]; + if(LLFolderType::lookupIsProtectedType(category->getPreferredType())) + { + // Can't move "special folders" (e.g. Textures Folder). + is_movable = FALSE; + break; + } + } + } + if (is_movable + && move_is_into_current_outfit + && descendent_items.size() > max_items_to_wear) + { + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFindWearablesEx not_worn(/*is_worn=*/ false, /*include_body_parts=*/ false); + gInventory.collectDescendentsIf(cat_id, + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + not_worn); + + if (items.size() > max_items_to_wear) + { + // Can't move 'large' folders into current outfit: MAINT-4086 + is_movable = FALSE; + LLStringUtil::format_map_t args; + args["AMOUNT"] = llformat("%d", max_items_to_wear); + tooltip_msg = LLTrans::getString("TooltipTooManyWearables",args); + } + } + if (is_movable && move_is_into_trash) + { + for (S32 i=0; i < descendent_items.size(); ++i) + { + LLInventoryItem* item = descendent_items[i]; + if (get_is_item_worn(item->getUUID())) + { + is_movable = FALSE; + break; // It's generally movable, but not into the trash. + } + } + } + if (is_movable && move_is_into_landmarks) + { + for (S32 i=0; i < descendent_items.size(); ++i) + { + LLViewerInventoryItem* item = descendent_items[i]; + + // Don't move anything except landmarks and categories into Landmarks folder. + // We use getType() instead of getActua;Type() to allow links to landmarks and folders. + if (LLAssetType::AT_LANDMARK != item->getType() && LLAssetType::AT_CATEGORY != item->getType()) + { + is_movable = FALSE; + break; // It's generally movable, but not into Landmarks. + } + } + } + + if (is_movable && move_is_into_marketplacelistings) + { + const LLViewerInventoryCategory * master_folder = model->getFirstDescendantOf(marketplacelistings_id, dest_id); + LLViewerInventoryCategory * dest_folder = dest_cat; + S32 bundle_size = (drop ? 1 : LLToolDragAndDrop::instance().getCargoCount()); + is_movable = can_move_folder_to_marketplace(master_folder, dest_folder, inv_cat, tooltip_msg, bundle_size); + } + + // + //-------------------------------------------------------------------------------- + + accept = is_movable; + + if (accept && drop) + { + // Dropping in or out of marketplace needs (sometimes) confirmation + if (user_confirm && (move_is_from_marketplacelistings || move_is_into_marketplacelistings)) + { + //disable dropping in or out of marketplace for now + return FALSE; + } + // Look for any gestures and deactivate them + if (move_is_into_trash) + { + for (S32 i=0; i < descendent_items.size(); i++) + { + LLInventoryItem* item = descendent_items[i]; + if (item->getType() == LLAssetType::AT_GESTURE + && LLGestureMgr::instance().isGestureActive(item->getUUID())) + { + LLGestureMgr::instance().deactivateGesture(item->getUUID()); + } + } + } + + if (dest_id == my_outifts_id) + { + // Category can contains objects, + // create a new folder and populate it with links to original objects + dropToMyOutfits(inv_cat); + } + // if target is current outfit folder we use link + else if (move_is_into_current_outfit && + (inv_cat->getPreferredType() == LLFolderType::FT_NONE || + inv_cat->getPreferredType() == LLFolderType::FT_OUTFIT)) + { + // traverse category and add all contents to currently worn. + BOOL append = true; + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, false, append); + } + else if (move_is_into_marketplacelistings) + { + //move_folder_to_marketplacelistings(inv_cat, dest_id); + } + else + { + if (model->isObjectDescendentOf(cat_id, model->findCategoryUUIDForType(LLFolderType::FT_INBOX))) + { + set_dad_inbox_object(cat_id); + } + + // Reparent the folder and restamp children if it's moving + // into trash. + gInventory.changeCategoryParent( + (LLViewerInventoryCategory*)inv_cat, + dest_id, + move_is_into_trash); + } + if (move_is_from_marketplacelistings) + { + //disable dropping in or out of marketplace for now + return FALSE; + + // If we are moving a folder at the listing folder level (i.e. its parent is the marketplace listings folder) + /*if (from_folder_uuid == marketplacelistings_id) + { + // Clear the folder from the marketplace in case it is a listing folder + if (LLMarketplaceData::instance().isListed(cat_id)) + { + LLMarketplaceData::instance().clearListing(cat_id); + } + } + else + { + // If we move from within an active (listed) listing, checks that it's still valid, if not, unlist + LLUUID version_folder_id = LLMarketplaceData::instance().getActiveFolder(from_folder_uuid); + if (version_folder_id.notNull()) + { + LLMarketplaceValidator::getInstance()->validateMarketplaceListings( + version_folder_id, + [version_folder_id](bool result) + { + if (!result) + { + LLMarketplaceData::instance().activateListing(version_folder_id, false); + } + } + ); + } + // In all cases, update the listing we moved from so suffix are updated + update_marketplace_category(from_folder_uuid); + }*/ + } + } + } + else if (LLToolDragAndDrop::SOURCE_WORLD == source) + { + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else + { + accept = move_inv_category_world_to_agent(cat_id, dest_id, drop); + } + } + else if (LLToolDragAndDrop::SOURCE_LIBRARY == source) + { + if (move_is_into_marketplacelistings) + { + tooltip_msg = LLTrans::getString("TooltipOutboxNotInInventory"); + accept = FALSE; + } + else + { + // Accept folders that contain complete outfits. + accept = move_is_into_current_outfit && LLAppearanceMgr::instance().getCanMakeFolderIntoOutfit(cat_id); + } + + if (accept && drop) + { + LLAppearanceMgr::instance().wearInventoryCategory(inv_cat, true, false); + } + } + + return accept; +} + +void outfitFolderCreatedCallback(LLUUID cat_source_id, LLUUID cat_dest_id) +{ + LLInventoryModel::cat_array_t* categories; + LLInventoryModel::item_array_t* items; + gInventory.getDirectDescendentsOf(cat_source_id, categories, items); + + LLInventoryObject::const_object_list_t link_array; + + + LLInventoryModel::item_array_t::iterator iter = items->begin(); + LLInventoryModel::item_array_t::iterator end = items->end(); + while (iter!=end) + { + const LLViewerInventoryItem* item = (*iter); + // By this point everything is supposed to be filtered, + // but there was a delay to create folder so something could have changed + LLInventoryType::EType inv_type = item->getInventoryType(); + if ((inv_type == LLInventoryType::IT_WEARABLE) || + (inv_type == LLInventoryType::IT_GESTURE) || + (inv_type == LLInventoryType::IT_ATTACHMENT) || + (inv_type == LLInventoryType::IT_OBJECT) || + (inv_type == LLInventoryType::IT_SNAPSHOT) || + (inv_type == LLInventoryType::IT_TEXTURE)) + { + link_array.push_back(LLConstPointer(item)); + } + iter++; + } + + if (!link_array.empty()) + { + LLPointer cb = NULL; + link_inventory_array(cat_dest_id, link_array, cb); + } +} + +void dropToMyOutfits(LLInventoryCategory* inv_cat) +{ + // make a folder in the My Outfits directory. + const LLUUID dest_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); + + // Note: creation will take time, so passing folder id to callback is slightly unreliable, + // but so is collecting and passing descendants' ids + inventory_func_type func = boost::bind(&outfitFolderCreatedCallback, inv_cat->getUUID(), _1); + gInventory.createNewCategory(dest_id, LLFolderType::FT_OUTFIT, inv_cat->getName(), func, inv_cat->getThumbnailUUID()); +} + diff --git a/indra/newview/llinventorygallery.h b/indra/newview/llinventorygallery.h new file mode 100644 index 0000000000..9b3f12701f --- /dev/null +++ b/indra/newview/llinventorygallery.h @@ -0,0 +1,419 @@ +/** + * @file llinventorygallery.h + * @brief LLInventoryGallery class definition + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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_LLINVENTORYGALLERY_H +#define LL_LLINVENTORYGALLERY_H + +#include "llgesturemgr.h" +#include "lllistcontextmenu.h" +#include "llpanel.h" +#include "llinventoryfilter.h" +#include "llinventoryobserver.h" +#include "llinventorymodel.h" + +class LLInventoryCategoriesObserver; +class LLInventoryGalleryItem; +class LLScrollContainer; +class LLTextBox; +class LLThumbnailsObserver; +class LLGalleryGestureObserver; + +class LLInventoryGalleryContextMenu; + +typedef boost::function callback_t; + +class LLInventoryGallery : public LLPanel, public LLEditMenuHandler +{ +public: + + typedef boost::signals2::signal selection_change_signal_t; + typedef boost::function selection_change_callback_t; + typedef std::deque selection_deque; + + struct Params + : public LLInitParam::Block + { + Optional row_panel_height; + Optional row_panel_width_factor; + Optional gallery_width_factor; + Optional vertical_gap; + Optional horizontal_gap; + Optional item_width; + Optional item_height; + Optional item_horizontal_gap; + Optional items_in_row; + + Params(); + }; + + static const LLInventoryGallery::Params& getDefaultParams(); + + LLInventoryGallery(const LLInventoryGallery::Params& params = getDefaultParams()); + ~LLInventoryGallery(); + + BOOL postBuild() override; + void initGallery(); + void draw() override; + void onVisibilityChange(BOOL new_visibility) override; + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, + void* cargo_data, EAcceptance* accept, std::string& tooltip_msg) override; + void startDrag(); + BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) override; + BOOL handleKeyHere(KEY key, MASK mask) override; + void moveUp(MASK mask); + void moveDown(MASK mask); + void moveLeft(MASK mask); + void moveRight(MASK mask); + void toggleSelectionRange(S32 start_idx, S32 end_idx); + void toggleSelectionRangeFromLast(const LLUUID target); + + void onFocusLost() override; + void onFocusReceived() override; + + void setFilterSubString(const std::string& string); + std::string getFilterSubString() { return mFilterSubString; } + LLInventoryFilter& getFilter() const { return *mFilter; } + bool checkAgainstFilterType(const LLUUID& object_id); + + void getCurrentCategories(uuid_vec_t& vcur); + bool updateAddedItem(LLUUID item_id); // returns true if added item is visible + void updateRemovedItem(LLUUID item_id); + void updateChangedItemName(LLUUID item_id, std::string name); + void updateItemThumbnail(LLUUID item_id); + void updateWornItem(LLUUID item_id, bool is_worn); + + void updateMessageVisibility(); + + void setRootFolder(const LLUUID cat_id); + void updateRootFolder(); + LLUUID getRootFolder() { return mFolderID; } + bool isRootDirty() { return mRootDirty; } + boost::signals2::connection setRootChangedCallback(callback_t cb); + void onForwardFolder(); + void onBackwardFolder(); + void clearNavigationHistory(); + bool isBackwardAvailable(); + bool isForwardAvailable(); + + void setNavBackwardList(std::list backward_list) { mBackwardFolders = backward_list; } + void setNavForwardList(std::list forward_list) { mForwardFolders = forward_list; } + std::list getNavBackwardList() { return mBackwardFolders; } + std::list getNavForwardList() { return mForwardFolders; } + + LLUUID getOutfitImageID(LLUUID outfit_id); + + void refreshList(const LLUUID& category_id); + void onCOFChanged(); + void onGesturesChanged(); + void computeDifference(const LLInventoryModel::cat_array_t vcats, const LLInventoryModel::item_array_t vitems, uuid_vec_t& vadded, uuid_vec_t& vremoved); + + void deselectItem(const LLUUID& category_id); + void clearSelection(); + void changeItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); + void addItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); + bool toggleItemSelection(const LLUUID& item_id, bool scroll_to_selection = false); + void scrollToShowItem(const LLUUID& item_id); + void signalSelectionItemID(const LLUUID& category_id); + boost::signals2::connection setSelectionChangeCallback(selection_change_callback_t cb); + LLUUID getFirstSelectedItemID(); + + void setSearchType(LLInventoryFilter::ESearchType type); + LLInventoryFilter::ESearchType getSearchType() { return mSearchType; } + + bool areViewsInitialized(); + bool hasDescendents(const LLUUID& cat_id); + bool hasVisibleItems(); + void handleModifiedFilter(); + LLScrollContainer* getScrollableContainer() { return mScrollPanel; } + LLInventoryGalleryItem* getFirstSelectedItem(); + + // Copy & paste (LLEditMenuHandler) + void copy() override; + BOOL canCopy() const override; + + void cut() override; + BOOL canCut() const override; + + void paste() override; + BOOL canPaste() const override; + + // Copy & paste & delete + static void onDelete(const LLSD& notification, const LLSD& response, const selection_deque selected_ids); + void deleteSelection(); + bool canDeleteSelection(); + void pasteAsLink(); + + void setSortOrder(U32 order, bool update = false); + U32 getSortOrder() { return mSortOrder; }; + + void claimEditHandler(); + void resetEditHandler(); + static bool isItemCopyable(const LLUUID & item_id); + + BOOL baseHandleDragAndDrop(LLUUID dest_id, BOOL drop, EDragAndDropType cargo_type, + void* cargo_data, EAcceptance* accept, std::string& tooltip_msg); + + void showContextMenu(LLUICtrl* ctrl, S32 x, S32 y, const LLUUID& item_id); + +protected: + void paste(const LLUUID& dest, + std::vector& objects, + bool is_cut_mode, + const LLUUID& marketplacelistings_id); + void pasteAsLink(const LLUUID& dest, + std::vector& objects, + const LLUUID& current_outfit_id, + const LLUUID& marketplacelistings_id, + const LLUUID& my_outifts_id); + + bool applyFilter(LLInventoryGalleryItem* item, const std::string& filter_substring); + bool checkAgainstFilters(LLInventoryGalleryItem* item, const std::string& filter_substring); + static void onIdle(void* userdata); + void dirtyRootFolder(); + + LLInventoryCategoriesObserver* mCategoriesObserver; + LLThumbnailsObserver* mThumbnailsObserver; + LLGalleryGestureObserver* mGestureObserver; + LLInventoryObserver* mInventoryObserver; + selection_deque mSelectedItemIDs; + selection_deque mItemsToSelect; + LLUUID mLastInteractedUUID; + bool mIsInitialized; + bool mRootDirty; + + selection_change_signal_t mSelectionChangeSignal; + boost::signals2::signal mRootChangedSignal; + LLUUID mFolderID; + std::list mBackwardFolders; + std::list mForwardFolders; + +private: + void addToGallery(LLInventoryGalleryItem* item); + void removeFromGalleryLast(LLInventoryGalleryItem* item, bool needs_reshape = true); + void removeFromGalleryMiddle(LLInventoryGalleryItem* item); + LLPanel* addLastRow(); + void removeLastRow(); + void moveRowUp(int row); + void moveRowDown(int row); + void moveRow(int row, int pos); + LLPanel* addToRow(LLPanel* row_stack, LLInventoryGalleryItem* item, int pos, int hgap); + void removeFromLastRow(LLInventoryGalleryItem* item); + void reArrangeRows(S32 row_diff = 0); + bool updateRowsIfNeeded(); + void updateGalleryWidth(); + + LLInventoryGalleryItem* buildGalleryItem(std::string name, LLUUID item_id, LLAssetType::EType type, LLUUID thumbnail_id, LLInventoryType::EType inventory_type, U32 flags, time_t creation_date, bool is_link, bool is_worn); + + void buildGalleryPanel(int row_count); + void reshapeGalleryPanel(int row_count); + LLPanel* buildItemPanel(int left); + LLPanel* buildRowPanel(int left, int bottom); + void moveRowPanel(LLPanel* stack, int left, int bottom); + + std::vector mRowPanels; + std::vector mItemPanels; + std::vector mUnusedRowPanels; + std::vector mUnusedItemPanels; + std::vector mItems; + std::vector mHiddenItems; + LLScrollContainer* mScrollPanel; + LLPanel* mGalleryPanel; + LLPanel* mLastRowPanel; + LLTextBox* mMessageTextBox; + int mRowCount; + int mItemsAddedCount; + bool mGalleryCreated; + bool mNeedsArrange; + + /* Params */ + int mRowPanelHeight; + int mVerticalGap; + int mHorizontalGap; + int mItemWidth; + int mItemHeight; + int mItemHorizontalGap; + int mItemsInRow; + int mRowPanelWidth; + int mGalleryWidth; + int mRowPanWidthFactor; + int mGalleryWidthFactor; + + LLInventoryGalleryContextMenu* mInventoryGalleryMenu; + LLInventoryGalleryContextMenu* mRootGalleryMenu; + std::string mFilterSubString; + LLInventoryFilter* mFilter; + U32 mSortOrder; + + typedef std::map gallery_item_map_t; + gallery_item_map_t mItemMap; + uuid_vec_t mCOFLinkedItems; + uuid_vec_t mActiveGestures; + uuid_set_t mItemBuildQuery; + std::map mItemIndexMap; + std::map mIndexToItemMap; + + LLInventoryFilter::ESearchType mSearchType; + std::string mUsername; +}; + +class LLInventoryGalleryItem : public LLPanel +{ +public: + struct Params : public LLInitParam::Block + {}; + + enum EInventorySortGroup + { + SG_SYSTEM_FOLDER, + SG_TRASH_FOLDER, + SG_NORMAL_FOLDER, + SG_ITEM + }; + + LLInventoryGalleryItem(const Params& p); + virtual ~LLInventoryGalleryItem(); + + BOOL postBuild(); + void draw(); + BOOL handleMouseDown(S32 x, S32 y, MASK mask); + BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + BOOL handleMouseUp(S32 x, S32 y, MASK mask); + BOOL handleHover(S32 x, S32 y, MASK mask); + BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, + EDragAndDropType cargo_type, + void* cargo_data, + EAcceptance* accept, + std::string& tooltip_msg); + BOOL handleKeyHere(KEY key, MASK mask); + + void onFocusLost(); + void onFocusReceived(); + + LLFontGL* getTextFont(); + + void setItemName(std::string name); + bool isSelected() { return mSelected; } + void setSelected(bool value); + void setWorn(bool value); + void setUUID(LLUUID id) {mUUID = id;} + LLUUID getUUID() { return mUUID;} + + void setAssetIDStr(std::string asset_id) {mAssetIDStr = asset_id;} + std::string getAssetIDStr() { return mAssetIDStr;} + void setDescription(std::string desc) {mDesc = desc;} + std::string getDescription() { return mDesc;} + void setCreatorName(std::string name) {mCreatorName = name;} + std::string getCreatorName() { return mCreatorName;} + void setCreationDate(time_t date) {mCreationDate = date;} + time_t getCreationDate() { return mCreationDate;} + + std::string getItemName() {return mItemName;} + std::string getItemNameSuffix() {return mPermSuffix + mWornSuffix;} + bool isDefaultImage() {return mDefaultImage;} + + bool isHidden() {return mHidden;} + void setHidden(bool hidden) {mHidden = hidden;} + + void setType(LLAssetType::EType type, LLInventoryType::EType inventory_type, U32 flags, bool is_link); + LLAssetType::EType getAssetType() { return mType; } + void setThumbnail(LLUUID id); + void setGallery(LLInventoryGallery* gallery) { mGallery = gallery; } + bool isFolder() { return mIsFolder; } + bool isLink() { return mIsLink; } + EInventorySortGroup getSortGroup() { return mSortGroup; } + + void updateNameText(); + +private: + bool isFadeItem(); + + LLUUID mUUID; + LLTextBox* mNameText; + LLPanel* mTextBgPanel; + bool mSelected; + bool mWorn; + bool mDefaultImage; + bool mHidden; + bool mIsFolder; + bool mIsLink; + S32 mCutGeneration; + bool mSelectedForCut; + + std::string mAssetIDStr; + std::string mDesc; + std::string mCreatorName; + time_t mCreationDate; + + EInventorySortGroup mSortGroup; + LLAssetType::EType mType; + std::string mItemName; + std::string mWornSuffix; + std::string mPermSuffix; + LLInventoryGallery* mGallery; +}; + +class LLThumbnailsObserver : public LLInventoryObserver +{ +public: + LLThumbnailsObserver(){}; + + virtual void changed(U32 mask); + bool addItem(const LLUUID& obj_id, callback_t cb); + void removeItem(const LLUUID& obj_id); + +protected: + + struct LLItemData + { + LLItemData(const LLUUID& obj_id, const LLUUID& thumbnail_id, callback_t cb) + : mItemID(obj_id) + , mCallback(cb) + , mThumbnailID(thumbnail_id) + {} + + callback_t mCallback; + LLUUID mItemID; + LLUUID mThumbnailID; + }; + + typedef std::map item_map_t; + typedef item_map_t::value_type item_map_value_t; + item_map_t mItemMap; +}; + +class LLGalleryGestureObserver : public LLGestureManagerObserver +{ +public: + LLGalleryGestureObserver(LLInventoryGallery* gallery) : mGallery(gallery) {} + virtual ~LLGalleryGestureObserver() {} + virtual void changed() { mGallery->onGesturesChanged(); } + +private: + LLInventoryGallery* mGallery; +}; + +#endif diff --git a/indra/newview/llinventorygallerymenu.cpp b/indra/newview/llinventorygallerymenu.cpp new file mode 100644 index 0000000000..5f4b816b99 --- /dev/null +++ b/indra/newview/llinventorygallerymenu.cpp @@ -0,0 +1,714 @@ +/** + * @file llinventorygallerymenu.cpp + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llinventorygallery.h" +#include "llinventorygallerymenu.h" + +#include "llagent.h" +#include "llappearancemgr.h" +#include "llavataractions.h" +#include "llclipboard.h" +#include "llfloaterreg.h" +#include "llfloatersidepanelcontainer.h" +#include "llfloaterworldmap.h" +#include "llinventorybridge.h" +#include "llinventoryfunctions.h" +#include "llinventorymodel.h" +#include "lllandmarkactions.h" +#include "llmarketplacefunctions.h" +#include "llmenugl.h" +#include "llnotificationsutil.h" +#include "llpreviewtexture.h" +#include "lltrans.h" +#include "llviewerfoldertype.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" + +LLContextMenu* LLInventoryGalleryContextMenu::createMenu() +{ + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; + + registrar.add("Inventory.DoToSelected", boost::bind(&LLInventoryGalleryContextMenu::doToSelected, this, _2)); + registrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::fileUploadLocation, this, _2)); + registrar.add("Inventory.EmptyTrash", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyTrash", LLFolderType::FT_TRASH)); + registrar.add("Inventory.EmptyLostAndFound", boost::bind(&LLInventoryModel::emptyFolderType, &gInventory, "ConfirmEmptyLostAndFound", LLFolderType::FT_LOST_AND_FOUND)); + + std::set uuids(mUUIDs.begin(), mUUIDs.end()); + registrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, uuids, gFloaterView->getParentFloater(mGallery))); + + enable_registrar.add("Inventory.CanSetUploadLocation", boost::bind(&LLInventoryGalleryContextMenu::canSetUploadLocation, this, _2)); + + LLContextMenu* menu = createFromFile("menu_gallery_inventory.xml"); + + updateMenuItemsVisibility(menu); + + return menu; +} + +void LLInventoryGalleryContextMenu::doToSelected(const LLSD& userdata) +{ + std::string action = userdata.asString(); + LLInventoryObject* obj = gInventory.getObject(mUUIDs.front()); + if(!obj) return; + + if ("open_selected_folder" == action) + { + mGallery->setRootFolder(mUUIDs.front()); + } + else if ("open_in_new_window" == action) + { + new_folder_window(mUUIDs.front()); + } + else if ("properties" == action) + { + show_item_profile(mUUIDs.front()); + } + else if ("restore" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + LLViewerInventoryCategory* cat = gInventory.getCategory(selected_id); + if (cat) + { + const LLUUID new_parent = gInventory.findCategoryUUIDForType(LLFolderType::assetTypeToFolderType(cat->getType())); + // do not restamp children on restore + gInventory.changeCategoryParent(cat, new_parent, false); + } + else + { + LLViewerInventoryItem* item = gInventory.getItem(selected_id); + if (item) + { + bool is_snapshot = (item->getInventoryType() == LLInventoryType::IT_SNAPSHOT); + + const LLUUID new_parent = gInventory.findCategoryUUIDForType(is_snapshot ? LLFolderType::FT_SNAPSHOT_CATEGORY : LLFolderType::assetTypeToFolderType(item->getType())); + // do not restamp children on restore + gInventory.changeItemParent(item, new_parent, false); + } + } + } + } + else if ("copy_uuid" == action) + { + LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front()); + if(item) + { + LLUUID asset_id = item->getProtectedAssetUUID(); + std::string buffer; + asset_id.toString(buffer); + + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(buffer)); + } + } + else if ("purge" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + remove_inventory_object(selected_id, NULL); + } + } + else if ("goto" == action) + { + show_item_original(mUUIDs.front()); + } + else if ("thumbnail" == action) + { + LLSD data(mUUIDs.front()); + LLFloaterReg::showInstance("change_item_thumbnail", data); + } + else if ("cut" == action) + { + if (mGallery->canCut()) + { + mGallery->cut(); + } + } + else if ("paste" == action) + { + if (mGallery->canPaste()) + { + mGallery->paste(); + } + } + else if ("delete" == action) + { + mGallery->deleteSelection(); + } + else if ("copy" == action) + { + if (mGallery->canCopy()) + { + mGallery->copy(); + } + } + else if ("paste_link" == action) + { + mGallery->pasteAsLink(); + } + else if ("rename" == action) + { + rename(mUUIDs.front()); + } + else if ("open" == action || "open_original" == action) + { + LLViewerInventoryItem* item = gInventory.getItem(mUUIDs.front()); + if (item) + { + LLInvFVBridgeAction::doAction(item->getType(), mUUIDs.front(), &gInventory); + } + } + else if ("ungroup_folder_items" == action) + { + ungroup_folder_items(mUUIDs.front()); + } + else if ("take_off" == action || "detach" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + LLAppearanceMgr::instance().removeItemFromAvatar(selected_id); + } + } + else if ("wear_add" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, false); // Don't replace if adding. + } + } + else if ("wear" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + LLAppearanceMgr::instance().wearItemOnAvatar(selected_id, true, true); + } + } + else if ("activate" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + LLGestureMgr::instance().activateGesture(selected_id); + + LLViewerInventoryItem* item = gInventory.getItem(selected_id); + if (!item) return; + + gInventory.updateItem(item); + } + gInventory.notifyObservers(); + } + else if ("deactivate" == action) + { + for (LLUUID& selected_id : mUUIDs) + { + LLGestureMgr::instance().deactivateGesture(selected_id); + + LLViewerInventoryItem* item = gInventory.getItem(selected_id); + if (!item) return; + + gInventory.updateItem(item); + } + gInventory.notifyObservers(); + } + else if ("replace_links" == action) + { + LLFloaterReg::showInstance("linkreplace", LLSD(mUUIDs.front())); + } + else if ("copy_slurl" == action) + { + boost::function copy_slurl_cb = [](LLLandmark* landmark) + { + LLVector3d global_pos; + landmark->getGlobalPos(global_pos); + boost::function copy_slurl_to_clipboard_cb = [](const std::string& slurl) + { + gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + LLSD args; + args["SLURL"] = slurl; + LLNotificationsUtil::add("CopySLURL", args); + }; + LLLandmarkActions::getSLURLfromPosGlobal(global_pos, copy_slurl_to_clipboard_cb, true); + }; + LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), copy_slurl_cb); + if (landmark) + { + copy_slurl_cb(landmark); + } + } + else if ("about" == action) + { + LLSD key; + key["type"] = "landmark"; + key["id"] = mUUIDs.front(); + LLFloaterSidePanelContainer::showPanel("places", key); + } + else if ("show_on_map" == action) + { + boost::function show_on_map_cb = [](LLLandmark* landmark) + { + LLVector3d landmark_global_pos; + if (landmark->getGlobalPos(landmark_global_pos)) + { + LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); + if (!landmark_global_pos.isExactlyZero() && worldmap_instance) + { + worldmap_instance->trackLocation(landmark_global_pos); + LLFloaterReg::showInstance("world_map", "center"); + } + } + }; + LLLandmark* landmark = LLLandmarkActions::getLandmark(mUUIDs.front(), show_on_map_cb); + if(landmark) + { + show_on_map_cb(landmark); + } + } + else if ("save_as" == action) + { + LLPreviewTexture* preview_texture = LLFloaterReg::getTypedInstance("preview_texture", mUUIDs.front()); + if (preview_texture) + { + preview_texture->openToSave(); + preview_texture->saveAs(); + } + } +} + +void LLInventoryGalleryContextMenu::rename(const LLUUID& item_id) +{ + LLInventoryObject* obj = gInventory.getObject(item_id); + if (!obj) return; + + LLSD args; + args["NAME"] = obj->getName(); + + LLSD payload; + payload["id"] = mUUIDs.front(); + + LLNotificationsUtil::add("RenameItem", args, payload, boost::bind(onRename, _1, _2)); +} + +void LLInventoryGalleryContextMenu::onRename(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + std::string new_name = response["new_name"].asString(); + LLStringUtil::trim(new_name); + if (!new_name.empty()) + { + LLUUID id = notification["payload"]["id"].asUUID(); + + LLViewerInventoryCategory* cat = gInventory.getCategory(id); + if(cat && (cat->getName() != new_name)) + { + LLSD updates; + updates["name"] = new_name; + update_inventory_category(cat->getUUID(),updates, NULL); + return; + } + + LLViewerInventoryItem* item = gInventory.getItem(id); + if(item && (item->getName() != new_name)) + { + LLSD updates; + updates["name"] = new_name; + update_inventory_item(item->getUUID(),updates, NULL); + } + } +} + +void LLInventoryGalleryContextMenu::fileUploadLocation(const LLSD& userdata) +{ + const std::string param = userdata.asString(); + if (param == "model") + { + gSavedPerAccountSettings.setString("ModelUploadFolder", mUUIDs.front().asString()); + } + else if (param == "texture") + { + gSavedPerAccountSettings.setString("TextureUploadFolder", mUUIDs.front().asString()); + } + else if (param == "sound") + { + gSavedPerAccountSettings.setString("SoundUploadFolder", mUUIDs.front().asString()); + } + else if (param == "animation") + { + gSavedPerAccountSettings.setString("AnimationUploadFolder", mUUIDs.front().asString()); + } +} + +bool LLInventoryGalleryContextMenu::canSetUploadLocation(const LLSD& userdata) +{ + if (mUUIDs.size() != 1) + { + return false; + } + LLInventoryCategory* cat = gInventory.getCategory(mUUIDs.front()); + if (!cat) + { + return false; + } + return true; +} + +bool is_inbox_folder(LLUUID item_id) +{ + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); + + if (inbox_id.isNull()) + { + return false; + } + + return gInventory.isObjectDescendentOf(item_id, inbox_id); +} + +void LLInventoryGalleryContextMenu::updateMenuItemsVisibility(LLContextMenu* menu) +{ + LLUUID selected_id = mUUIDs.front(); + LLInventoryObject* obj = gInventory.getObject(selected_id); + if (!obj) + { + return; + } + + std::vector items; + std::vector disabled_items; + + bool is_agent_inventory = gInventory.isObjectDescendentOf(selected_id, gInventory.getRootFolderID()); + bool is_link = obj->getIsLinkType(); + bool is_folder = (obj->getType() == LLAssetType::AT_CATEGORY); + bool is_cof = LLAppearanceMgr::instance().getIsInCOF(selected_id); + bool is_inbox = is_inbox_folder(selected_id); + bool is_trash = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)); + bool is_in_trash = gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH)); + bool is_lost_and_found = (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_LOST_AND_FOUND)); + bool is_outfits= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS)); + //bool is_favorites= (selected_id == gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE)); + + bool is_system_folder = false; + LLFolderType::EType folder_type(LLFolderType::FT_NONE); + bool has_children = false; + bool is_full_perm_item = false; + bool is_copyable = false; + LLViewerInventoryItem* selected_item = gInventory.getItem(selected_id); + + if(is_folder) + { + LLInventoryCategory* category = gInventory.getCategory(selected_id); + if (category) + { + folder_type = category->getPreferredType(); + is_system_folder = LLFolderType::lookupIsProtectedType(folder_type); + has_children = (gInventory.categoryHasChildren(selected_id) != LLInventoryModel::CHILDREN_NO); + } + } + else + { + if (selected_item) + { + is_full_perm_item = selected_item->getIsFullPerm(); + is_copyable = selected_item->getPermissions().allowCopyBy(gAgent.getID()); + } + } + + if(!is_link) + { + items.push_back(std::string("thumbnail")); + if (!is_agent_inventory || (is_in_trash && !is_trash)) + { + disabled_items.push_back(std::string("thumbnail")); + } + } + + if (is_folder) + { + if(!isRootFolder()) + { + items.push_back(std::string("Copy Separator")); + + items.push_back(std::string("open_in_current_window")); + items.push_back(std::string("open_in_new_window")); + items.push_back(std::string("Open Folder Separator")); + } + } + else + { + if (is_agent_inventory && (obj->getType() != LLAssetType::AT_LINK_FOLDER)) + { + items.push_back(std::string("Replace Links")); + } + if (obj->getType() == LLAssetType::AT_LANDMARK) + { + items.push_back(std::string("Landmark Separator")); + items.push_back(std::string("url_copy")); + items.push_back(std::string("About Landmark")); + items.push_back(std::string("show_on_map")); + } + } + + if(is_trash) + { + items.push_back(std::string("Empty Trash")); + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array); + if (0 == cat_array->size() && 0 == item_array->size()) + { + disabled_items.push_back(std::string("Empty Trash")); + } + } + else if(is_in_trash) + { + if (is_link) + { + items.push_back(std::string("Find Original")); + if (LLAssetType::lookupIsLinkType(obj->getType())) + { + disabled_items.push_back(std::string("Find Original")); + } + } + items.push_back(std::string("Purge Item")); + if (is_folder && !get_is_category_removable(&gInventory, selected_id)) + { + disabled_items.push_back(std::string("Purge Item")); + } + items.push_back(std::string("Restore Item")); + } + else + { + if(can_share_item(selected_id)) + { + items.push_back(std::string("Share")); + } + if (LLClipboard::instance().hasContents() && is_agent_inventory && !is_cof && !is_inbox_folder(selected_id)) + { + items.push_back(std::string("Paste")); + + static LLCachedControl inventory_linking(gSavedSettings, "InventoryLinking", true); + if (inventory_linking) + { + items.push_back(std::string("Paste As Link")); + } + } + if (is_folder && is_agent_inventory) + { + if (!is_cof && (folder_type != LLFolderType::FT_OUTFIT) && !is_outfits && !is_inbox_folder(selected_id)) + { + if (!gInventory.isObjectDescendentOf(selected_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD)) && !isRootFolder()) + { + items.push_back(std::string("New Folder")); + } + items.push_back(std::string("upload_def")); + } + + if(is_outfits && !isRootFolder()) + { + items.push_back(std::string("New Outfit")); + } + + items.push_back(std::string("Subfolder Separator")); + if (!is_system_folder && !isRootFolder()) + { + if(has_children && (folder_type != LLFolderType::FT_OUTFIT)) + { + items.push_back(std::string("Ungroup folder items")); + } + items.push_back(std::string("Cut")); + items.push_back(std::string("Delete")); + if(!get_is_category_removable(&gInventory, selected_id)) + { + disabled_items.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Cut")); + } + + if(!is_inbox) + { + items.push_back(std::string("Rename")); + } + } + if(!is_system_folder) + { + items.push_back(std::string("Copy")); + } + } + else if(!is_folder) + { + items.push_back(std::string("Properties")); + items.push_back(std::string("Copy Asset UUID")); + items.push_back(std::string("Copy Separator")); + + bool is_asset_knowable = is_asset_knowable = LLAssetType::lookupIsAssetIDKnowable(obj->getType()); + if ( !is_asset_knowable // disable menu item for Inventory items with unknown asset. EXT-5308 + || (! ( is_full_perm_item || gAgent.isGodlike()))) + { + disabled_items.push_back(std::string("Copy Asset UUID")); + } + if(is_agent_inventory) + { + items.push_back(std::string("Cut")); + if (!is_link || !is_cof || !get_is_item_worn(selected_id)) + { + items.push_back(std::string("Delete")); + } + if(!get_is_item_removable(&gInventory, selected_id)) + { + disabled_items.push_back(std::string("Delete")); + disabled_items.push_back(std::string("Cut")); + } + + if (selected_item && (selected_item->getInventoryType() != LLInventoryType::IT_CALLINGCARD) && !is_inbox && selected_item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) + { + items.push_back(std::string("Rename")); + } + } + items.push_back(std::string("Copy")); + if (!is_copyable) + { + disabled_items.push_back(std::string("Copy")); + } + } + if((obj->getType() == LLAssetType::AT_SETTINGS) + || ((obj->getType() <= LLAssetType::AT_GESTURE) + && obj->getType() != LLAssetType::AT_OBJECT + && obj->getType() != LLAssetType::AT_CLOTHING + && obj->getType() != LLAssetType::AT_CATEGORY + && obj->getType() != LLAssetType::AT_LANDMARK + && obj->getType() != LLAssetType::AT_BODYPART)) + { + bool can_open = !LLAssetType::lookupIsLinkType(obj->getType()); + + if (can_open) + { + if (is_link) + items.push_back(std::string("Open Original")); + else + items.push_back(std::string("Open")); + } + else + { + disabled_items.push_back(std::string("Open")); + disabled_items.push_back(std::string("Open Original")); + } + + if(LLAssetType::AT_GESTURE == obj->getType()) + { + items.push_back(std::string("Gesture Separator")); + if(!LLGestureMgr::instance().isGestureActive(selected_id)) + { + items.push_back(std::string("Activate")); + } + else + { + items.push_back(std::string("Deactivate")); + } + } + } + else if(LLAssetType::AT_LANDMARK == obj->getType()) + { + items.push_back(std::string("Landmark Open")); + } + else if (obj->getType() == LLAssetType::AT_OBJECT || obj->getType() == LLAssetType::AT_CLOTHING || obj->getType() == LLAssetType::AT_BODYPART) + { + items.push_back(std::string("Wearable And Object Separator")); + if(obj->getType() == LLAssetType::AT_CLOTHING) + { + items.push_back(std::string("Take Off")); + } + if(get_is_item_worn(selected_id)) + { + if(obj->getType() == LLAssetType::AT_OBJECT) + { + items.push_back(std::string("Detach From Yourself")); + } + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); + } + else + { + if(obj->getType() == LLAssetType::AT_OBJECT) + { + items.push_back(std::string("Wearable Add")); + } + items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Take Off")); + } + + if (!gAgentAvatarp->canAttachMoreObjects() && (obj->getType() == LLAssetType::AT_OBJECT)) + { + disabled_items.push_back(std::string("Wearable And Object Wear")); + disabled_items.push_back(std::string("Wearable Add")); + } + if (selected_item && (obj->getType() != LLAssetType::AT_OBJECT) && LLWearableType::getInstance()->getAllowMultiwear(selected_item->getWearableType())) + { + items.push_back(std::string("Wearable Add")); + if (!gAgentWearables.canAddWearable(selected_item->getWearableType())) + { + disabled_items.push_back(std::string("Wearable Add")); + } + } + } + if(obj->getType() == LLAssetType::AT_TEXTURE) + { + items.push_back(std::string("Save As")); + bool can_copy = selected_item && selected_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); + if (!can_copy) + { + disabled_items.push_back(std::string("Save As")); + } + } + if (is_link) + { + items.push_back(std::string("Find Original")); + if (LLAssetType::lookupIsLinkType(obj->getType())) + { + disabled_items.push_back(std::string("Find Original")); + } + } + if (is_lost_and_found) + { + items.push_back(std::string("Empty Lost And Found")); + + LLInventoryModel::cat_array_t* cat_array; + LLInventoryModel::item_array_t* item_array; + gInventory.getDirectDescendentsOf(selected_id, cat_array, item_array); + // Enable Empty menu item only when there is something to act upon. + if (0 == cat_array->size() && 0 == item_array->size()) + { + disabled_items.push_back(std::string("Empty Lost And Found")); + } + + disabled_items.push_back(std::string("New Folder")); + disabled_items.push_back(std::string("upload_def")); + } + } + + hide_context_entries(*menu, items, disabled_items); +} + diff --git a/indra/newview/llinventorygallerymenu.h b/indra/newview/llinventorygallerymenu.h new file mode 100644 index 0000000000..7c3545432b --- /dev/null +++ b/indra/newview/llinventorygallerymenu.h @@ -0,0 +1,62 @@ +/** + * @file llinventorygallerymenu.h + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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_LLINVENTORYGALLERYMENU_H +#define LL_LLINVENTORYGALLERYMENU_H + +#include "lllistcontextmenu.h" + +class LLInventoryGalleryContextMenu : public LLListContextMenu +{ +public: + LLInventoryGalleryContextMenu(LLInventoryGallery* gallery) + : LLListContextMenu(), + mGallery(gallery), + mRootFolder(false){} + /*virtual*/ LLContextMenu* createMenu(); + + bool isRootFolder() { return mRootFolder; } + void setRootFolder(bool is_root) { mRootFolder = is_root; } + void doToSelected(const LLSD& userdata); + void rename(const LLUUID& item_id); + +protected: + //virtual void buildContextMenu(class LLMenuGL& menu, U32 flags); + void updateMenuItemsVisibility(LLContextMenu* menu); + + void fileUploadLocation(const LLSD& userdata); + bool canSetUploadLocation(const LLSD& userdata); + + static void onRename(const LLSD& notification, const LLSD& response); + +private: + bool enableContextMenuItem(const LLSD& userdata); + bool checkContextMenuItem(const LLSD& userdata); + + LLInventoryGallery* mGallery; + bool mRootFolder; +}; + +#endif diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 7ccb8b807d..f7c327c699 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -37,9 +37,11 @@ #include "llappearancemgr.h" #include "llavatarnamecache.h" #include "llclipboard.h" +#include "lldispatcher.h" #include "llinventorypanel.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventoryobserver.h" #include "llinventorypanel.h" #include "llfloaterpreviewtrash.h" @@ -49,6 +51,7 @@ #include "llviewercontrol.h" #include "llviewernetwork.h" #include "llpreview.h" +#include "llviewergenericmessage.h" #include "llviewermessage.h" #include "llviewerfoldertype.h" #include "llviewerwindow.h" @@ -62,6 +65,7 @@ #include "bufferarray.h" #include "bufferstream.h" #include "llcorehttputil.h" +#include "hbxxh.h" //#define DIFF_INVENTORY_FILES #ifdef DIFF_INVENTORY_FILES @@ -73,9 +77,11 @@ // Increment this if the inventory contents change in a non-backwards-compatible way. // For viewer 2, the addition of link items makes a pre-viewer-2 cache incorrect. -const S32 LLInventoryModel::sCurrentInvCacheVersion = 2; +const S32 LLInventoryModel::sCurrentInvCacheVersion = 3; BOOL LLInventoryModel::sFirstTimeInViewer2 = TRUE; +S32 LLInventoryModel::sPendingSystemFolders = 0; + ///---------------------------------------------------------------------------- /// Local function declarations, constants, enums, and typedefs ///---------------------------------------------------------------------------- @@ -132,6 +138,222 @@ bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item) return rv; } +struct InventoryCallbackInfo +{ + InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) : + mCallback(callback), mInvID(inv_id) {} + U32 mCallback; + LLUUID mInvID; +}; + +///---------------------------------------------------------------------------- +/// Class LLDispatchClassifiedClickThrough +///---------------------------------------------------------------------------- + +class LLDispatchBulkUpdateInventory : public LLDispatchHandler +{ +public: + virtual bool operator()( + const LLDispatcher* dispatcher, + const std::string& key, + const LLUUID& invoice, + const sparam_t& strings) + { + LLSD message; + + // Expect single string parameter in the form of a notation serialized LLSD. + sparam_t::const_iterator it = strings.begin(); + if (it != strings.end()) { + const std::string& llsdRaw = *it++; + std::istringstream llsdData(llsdRaw); + if (!LLSDSerialize::deserialize(message, llsdData, llsdRaw.length())) + { + LL_WARNS() << "LLDispatchBulkUpdateInventory: Attempted to read parameter data into LLSD but failed:" << llsdRaw << LL_ENDL; + } + } + + LLInventoryModel::update_map_t update; + LLInventoryModel::cat_array_t folders; + LLInventoryModel::item_array_t items; + std::list cblist; + uuid_vec_t wearable_ids; + + LLSD item_data = message["item_data"]; + if (item_data.isArray()) + { + for (LLSD::array_iterator itd = item_data.beginArray(); itd != item_data.endArray(); ++itd) + { + const LLSD &item(*itd); + + // Agent id probably should be in the root of the message + LLUUID agent_id = item["agent_id"].asUUID(); + if (agent_id != gAgent.getID()) + { + LL_WARNS() << "Got a BulkUpdateInventory for the wrong agent." << LL_ENDL; + return false; + } + + LLPointer titem = new LLViewerInventoryItem; + titem->unpackMessage(item); + LL_DEBUGS("Inventory") << "unpacked item '" << titem->getName() << "' in " + << titem->getParentUUID() << LL_ENDL; + // callback id might be no longer supported + U32 callback_id = item["callback_id"].asInteger(); + + if (titem->getUUID().notNull()) + { + items.push_back(titem); + cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID())); + if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE) + { + wearable_ids.push_back(titem->getUUID()); + } + + // examine update for changes. + LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID()); + if (itemp) + { + if (titem->getParentUUID() == itemp->getParentUUID()) + { + update[titem->getParentUUID()]; + } + else + { + ++update[titem->getParentUUID()]; + --update[itemp->getParentUUID()]; + } + } + else + { + LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID()); + if (folderp) + { + ++update[titem->getParentUUID()]; + } + } + } + else + { + cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null)); + } + } + } + + LLSD folder_data = message["folder_data"]; + if (folder_data.isArray()) + { + for (LLSD::array_iterator itd = folder_data.beginArray(); itd != folder_data.endArray(); ++itd) + { + const LLSD &folder(*itd); + + LLPointer tfolder = new LLViewerInventoryCategory(gAgent.getID()); + tfolder->unpackMessage(folder); + + LL_DEBUGS("Inventory") << "unpacked folder '" << tfolder->getName() << "' (" + << tfolder->getUUID() << ") in " << tfolder->getParentUUID() + << LL_ENDL; + + // If the folder is a listing or a version folder, all we need to do is update the SLM data + int depth_folder = depth_nesting_in_marketplace(tfolder->getUUID()); + if ((depth_folder == 1) || (depth_folder == 2)) + { + // Trigger an SLM listing update + LLUUID listing_uuid = (depth_folder == 1 ? tfolder->getUUID() : tfolder->getParentUUID()); + S32 listing_id = LLMarketplaceData::instance().getListingID(listing_uuid); + LLMarketplaceData::instance().getListing(listing_id); + // In that case, there is no item to update so no callback -> we skip the rest of the update + } + else if (tfolder->getUUID().notNull()) + { + folders.push_back(tfolder); + LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); + if (folderp) + { + if (tfolder->getParentUUID() == folderp->getParentUUID()) + { + update[tfolder->getParentUUID()]; + } + else + { + ++update[tfolder->getParentUUID()]; + --update[folderp->getParentUUID()]; + } + } + else + { + // we could not find the folder, so it is probably + // new. However, we only want to attempt accounting + // for the parent if we can find the parent. + folderp = gInventory.getCategory(tfolder->getParentUUID()); + if (folderp) + { + ++update[tfolder->getParentUUID()]; + } + } + } + } + } + + gInventory.accountForUpdate(update); + + for (LLInventoryModel::cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit) + { + gInventory.updateCategory(*cit); + } + for (LLInventoryModel::item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit) + { + gInventory.updateItem(*iit); + } + gInventory.notifyObservers(); + + /* + Transaction id not included? + + // The incoming inventory could span more than one BulkInventoryUpdate packet, + // so record the transaction ID for this purchase, then wear all clothing + // that comes in as part of that transaction ID. JC + if (LLInventoryState::sWearNewClothing) + { + LLInventoryState::sWearNewClothingTransactionID = tid; + LLInventoryState::sWearNewClothing = FALSE; + } + + if (tid.notNull() && tid == LLInventoryState::sWearNewClothingTransactionID) + { + count = wearable_ids.size(); + for (i = 0; i < count; ++i) + { + LLViewerInventoryItem* wearable_item; + wearable_item = gInventory.getItem(wearable_ids[i]); + LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); + } + } + */ + + if (LLInventoryState::sWearNewClothing && wearable_ids.size() > 0) + { + LLInventoryState::sWearNewClothing = FALSE; + + size_t count = wearable_ids.size(); + for (S32 i = 0; i < count; ++i) + { + LLViewerInventoryItem* wearable_item; + wearable_item = gInventory.getItem(wearable_ids[i]); + LLAppearanceMgr::instance().wearItemOnAvatar(wearable_item->getUUID(), true, true); + } + } + + std::list::iterator inv_it; + for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it) + { + InventoryCallbackInfo cbinfo = (*inv_it); + gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID); + } + return true; + } +}; +static LLDispatchBulkUpdateInventory sBulkUpdateInventory; + ///---------------------------------------------------------------------------- /// Class LLInventoryValidationInfo ///---------------------------------------------------------------------------- @@ -221,6 +443,7 @@ LLInventoryModel::LLInventoryModel() mIsNotifyObservers(FALSE), mModifyMask(LLInventoryObserver::ALL), mChangedItemIDs(), + mBulkFecthCallbackSlot(), mObservers(), mHttpRequestFG(NULL), mHttpRequestBG(NULL), @@ -250,6 +473,11 @@ void LLInventoryModel::cleanupInventory() mObservers.erase(iter); delete observer; } + + if (mBulkFecthCallbackSlot.connected()) + { + mBulkFecthCallbackSlot.disconnect(); + } mObservers.clear(); // Run down HTTP transport @@ -449,17 +677,41 @@ void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, items = get_ptr_in_map(mParentChildItemTree, cat_id); } -LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const +void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const +{ + if (cat_array_t* categoriesp = get_ptr_in_map(mParentChildCategoryTree, cat_id)) + { + for (LLViewerInventoryCategory* pFolder : *categoriesp) + { + if (f(pFolder, nullptr)) + { + categories.push_back(pFolder); + } + } + } + + if (item_array_t* itemsp = get_ptr_in_map(mParentChildItemTree, cat_id)) + { + for (LLViewerInventoryItem* pItem : *itemsp) + { + if (f(nullptr, pItem)) + { + items.push_back(pItem); + } + } + } +} + +LLInventoryModel::digest_t LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const { LLInventoryModel::cat_array_t* cat_array; LLInventoryModel::item_array_t* item_array; getDirectDescendentsOf(cat_id,cat_array,item_array); - LLMD5 item_name_hash; if (!item_array) { - item_name_hash.finalize(); - return item_name_hash; + return LLUUID::null; } + HBXXH128 item_name_hash; for (LLInventoryModel::item_array_t::const_iterator iter = item_array->begin(); iter != item_array->end(); iter++) @@ -469,8 +721,7 @@ LLMD5 LLInventoryModel::hashDirectDescendentNames(const LLUUID& cat_id) const continue; item_name_hash.update(item->getName()); } - item_name_hash.finalize(); - return item_name_hash; + return item_name_hash.digest(); } // SJB: Added version to lock the arrays to catch potential logic bugs @@ -560,10 +811,77 @@ void LLInventoryModel::consolidateForType(const LLUUID& main_id, LLFolderType::E } } +void LLInventoryModel::ensureCategoryForTypeExists(LLFolderType::EType preferred_type) +{ + LLUUID rv = LLUUID::null; + LLUUID root_id = gInventory.getRootFolderID(); + if (LLFolderType::FT_ROOT_INVENTORY == preferred_type) + { + rv = root_id; + } + else if (root_id.notNull()) + { + cat_array_t* cats = NULL; + cats = get_ptr_in_map(mParentChildCategoryTree, root_id); + if (cats) + { + S32 count = cats->size(); + for (S32 i = 0; i < count; ++i) + { + LLViewerInventoryCategory* p_cat = cats->at(i); + if (p_cat && p_cat->getPreferredType() == preferred_type) + { + const LLUUID& folder_id = cats->at(i)->getUUID(); + if (rv.isNull() || folder_id < rv) + { + rv = folder_id; + } + } + } + } + } + + if (rv.isNull() && root_id.notNull()) + { + + if (isInventoryUsable()) + { + createNewCategory( + root_id, + preferred_type, + LLStringUtil::null, + [preferred_type](const LLUUID &new_cat_id) + { + if (new_cat_id.isNull()) + { + LL_WARNS("Inventory") + << "Failed to create folder of type " << preferred_type + << LL_ENDL; + } + else + { + LL_WARNS("Inventory") << "Created category: " << new_cat_id + << " for type: " << preferred_type << LL_ENDL; + sPendingSystemFolders--; + } + } + ); + } + else + { + LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type + << " because inventory is not usable" << LL_ENDL; + } + } + else + { + sPendingSystemFolders--; + } +} + const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( LLFolderType::EType preferred_type, - bool create_folder, - const LLUUID& root_id) + const LLUUID& root_id) const { LLUUID rv = LLUUID::null; if(LLFolderType::FT_ROOT_INVENTORY == preferred_type) @@ -592,18 +910,17 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( } } - if(rv.isNull() && create_folder && root_id.notNull()) + if(rv.isNull() + && root_id.notNull() + && preferred_type != LLFolderType::FT_MARKETPLACE_LISTINGS + && preferred_type != LLFolderType::FT_OUTBOX) { - - if (isInventoryUsable()) - { - return createNewCategory(root_id, preferred_type, LLStringUtil::null); - } - else - { - LL_WARNS("Inventory") << "Can't create requested folder, type " << preferred_type - << " because inventory is not usable" << LL_ENDL; - } + // if it does not exists, it should either be added + // to createCommonSystemCategories or server should + // have set it + llassert(!isInventoryUsable()); + LL_WARNS("Inventory") << "Tried to find folder, type " << preferred_type + << " but category does not exist" << LL_ENDL; } return rv; } @@ -612,12 +929,12 @@ const LLUUID LLInventoryModel::findCategoryUUIDForTypeInRoot( // specifies 'type' as what it defaults to containing. The category is // not necessarily only for that type. *NOTE: This will create a new // inventory category on the fly if one does not exist. -const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +const LLUUID LLInventoryModel::findCategoryUUIDForType(LLFolderType::EType preferred_type) const { - return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getRootFolderID()); + return findCategoryUUIDForTypeInRoot(preferred_type, gInventory.getRootFolderID()); } -const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) +const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const { LLUUID cat_id; switch (preferred_type) @@ -653,39 +970,46 @@ const LLUUID LLInventoryModel::findUserDefinedCategoryUUIDForType(LLFolderType:: if (cat_id.isNull() || !getCategory(cat_id)) { - cat_id = findCategoryUUIDForTypeInRoot(preferred_type, true, getRootFolderID()); + cat_id = findCategoryUUIDForTypeInRoot(preferred_type, getRootFolderID()); } return cat_id; } -const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, bool create_folder) +const LLUUID LLInventoryModel::findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const { - return findCategoryUUIDForTypeInRoot(preferred_type, create_folder, gInventory.getLibraryRootFolderID()); + return findCategoryUUIDForTypeInRoot(preferred_type, gInventory.getLibraryRootFolderID()); } // Convenience function to create a new category. You could call // updateCategory() with a newly generated UUID category, but this // version will take care of details like what the name should be -// based on preferred type. Returns the UUID of the new category. -LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, +// based on preferred type. +void LLInventoryModel::createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& pname, - inventory_func_type callback) + inventory_func_type callback, + const LLUUID& thumbnail_id) { - LLUUID id; + LL_DEBUGS(LOG_INV) << "Create '" << pname << "' in '" << make_inventory_path(parent_id) << "'" << LL_ENDL; if (!isInventoryUsable()) { LL_WARNS(LOG_INV) << "Inventory is not usable; can't create requested category of type " << preferred_type << LL_ENDL; - // FIXME failing but still returning an id? - return id; + if (callback) + { + callback(LLUUID::null); + } + return; } if(LLFolderType::lookup(preferred_type) == LLFolderType::badLookup()) { LL_DEBUGS(LOG_INV) << "Attempt to create undefined category." << LL_ENDL; - // FIXME failing but still returning an id? - return id; + if (callback) + { + callback(LLUUID::null); + } + return; } if (preferred_type != LLFolderType::FT_NONE) @@ -696,26 +1020,72 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, LL_WARNS(LOG_INV) << "Creating new system folder, type " << preferred_type << LL_ENDL; } - id.generate(); std::string name = pname; - if(!pname.empty()) - { - name.assign(pname); - } - else + if (pname.empty()) { name.assign(LLViewerFolderType::lookupNewCategoryName(preferred_type)); } - + + if (AISAPI::isAvailable()) + { + LLSD new_inventory = LLSD::emptyMap(); + new_inventory["categories"] = LLSD::emptyArray(); + LLViewerInventoryCategory cat(LLUUID::null, parent_id, preferred_type, name, gAgent.getID()); + cat.setThumbnailUUID(thumbnail_id); + LLSD cat_sd = cat.asAISCreateCatLLSD(); + new_inventory["categories"].append(cat_sd); + AISAPI::CreateInventory( + parent_id, + new_inventory, + [this, callback, parent_id, preferred_type, name] (const LLUUID& new_category) + { + if (new_category.isNull()) + { + if (callback && !callback.empty()) + { + callback(new_category); + } + return; + } + + // todo: not needed since AIS does the accounting? + LLViewerInventoryCategory* folderp = gInventory.getCategory(new_category); + if (!folderp) + { + // Add the category to the internal representation + LLPointer cat = new LLViewerInventoryCategory( + new_category, + parent_id, + preferred_type, + name, + gAgent.getID()); + + LLInventoryModel::LLCategoryUpdate update(cat->getParentUUID(), 1); + accountForUpdate(update); + + cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 + cat->setDescendentCount(0); + updateCategory(cat); + } + + if (callback && !callback.empty()) + { + callback(new_category); + } + }); + return; + } + LLViewerRegion* viewer_region = gAgent.getRegion(); std::string url; if ( viewer_region ) url = viewer_region->getCapability("CreateInventoryCategory"); - if (!url.empty() && callback) + if (!url.empty()) { //Let's use the new capability. - + LLUUID id; + id.generate(); LLSD request, body; body["folder_id"] = id; body["parent_id"] = parent_id; @@ -728,43 +1098,13 @@ LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id, LL_DEBUGS(LOG_INV) << "Creating category via request: " << ll_pretty_print_sd(request) << LL_ENDL; LLCoros::instance().launch("LLInventoryModel::createNewCategoryCoro", boost::bind(&LLInventoryModel::createNewCategoryCoro, this, url, body, callback)); - - return LLUUID::null; + return; } - if (!gMessageSystem) - { - return LLUUID::null; - } - - // FIXME this UDP code path needs to be removed. Requires - // reworking many of the callers to use callbacks rather than - // assuming instant success. - - // Add the category to the internal representation - LLPointer cat = - new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID()); - cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL - 1); // accountForUpdate() will icrease version by 1 - cat->setDescendentCount(0); - LLCategoryUpdate update(cat->getParentUUID(), 1); - accountForUpdate(update); - updateCategory(cat); - - LL_DEBUGS(LOG_INV) << "Creating category via UDP message CreateInventoryFolder, type " << preferred_type << LL_ENDL; - - // Create the category on the server. We do this to prevent people - // from munging their protected folders. - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("CreateInventoryFolder"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlock("FolderData"); - cat->packMessage(msg); - gAgent.sendReliableMessage(); - - // return the folder id of the newly created folder - return id; + if (callback) + { + callback(LLUUID::null); // Notify about failure + } } void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inventory_func_type callback) @@ -788,12 +1128,20 @@ void LLInventoryModel::createNewCategoryCoro(std::string url, LLSD postData, inv if (!status) { LL_WARNS() << "HTTP failure attempting to create category." << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } if (!result.has("folder_id")) { LL_WARNS() << "Malformed response contents" << ll_pretty_print_sd(result) << LL_ENDL; + if (callback) + { + callback(LLUUID::null); + } return; } @@ -1298,7 +1646,7 @@ void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat, U32 mask |= LLInventoryObserver::LABEL; } // Under marketplace, category labels are quite complex and need extra upate - const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_id = findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplace_id.notNull() && isObjectDescendentOf(cat->getUUID(), marketplace_id)) { mask |= LLInventoryObserver::LABEL; @@ -1351,6 +1699,7 @@ void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id) LLPointer cat = getCategory(object_id); if(cat && (cat->getParentUUID() != cat_id)) { + LL_DEBUGS(LOG_INV) << "Move category '" << make_path(cat) << "' to '" << make_inventory_path(cat_id) << "'" << LL_ENDL; cat_array_t* cat_array; cat_array = getUnlockedCatArray(cat->getParentUUID()); if(cat_array) vector_replace_with_last(*cat_array, cat); @@ -1363,6 +1712,7 @@ void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id) LLPointer item = getItem(object_id); if(item && (item->getParentUUID() != cat_id)) { + LL_DEBUGS(LOG_INV) << "Move item '" << make_path(item) << "' to '" << make_inventory_path(cat_id) << "'" << LL_ENDL; item_array_t* item_array; item_array = getUnlockedItemArray(item->getParentUUID()); if(item_array) vector_replace_with_last(*item_array, item); @@ -1381,14 +1731,14 @@ void LLInventoryModel::changeItemParent(LLViewerInventoryItem* item, { if (item->getParentUUID() == new_parent_id) { - LL_DEBUGS(LOG_INV) << "'" << item->getName() << "' (" << item->getUUID() - << ") is already in folder " << new_parent_id << LL_ENDL; + LL_DEBUGS(LOG_INV) << make_info(item) << " is already in folder " << make_inventory_info(new_parent_id) << LL_ENDL; } else { - LL_INFOS(LOG_INV) << "Moving '" << item->getName() << "' (" << item->getUUID() - << ") from " << item->getParentUUID() << " to folder " - << new_parent_id << LL_ENDL; + LL_INFOS(LOG_INV) << "Move item " << make_info(item) + << " from " << make_inventory_info(item->getParentUUID()) + << " to " << make_inventory_info(new_parent_id) << LL_ENDL; + LLInventoryModel::update_list_t update; LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1); update.push_back(old_folder); @@ -1420,6 +1770,10 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, return; } + LL_INFOS(LOG_INV) << "Move category " << make_info(cat) + << " from " << make_inventory_info(cat->getParentUUID()) + << " to " << make_inventory_info(new_parent_id) << LL_ENDL; + LLInventoryModel::update_list_t update; LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1); update.push_back(old_folder); @@ -1434,17 +1788,25 @@ void LLInventoryModel::changeCategoryParent(LLViewerInventoryCategory* cat, notifyObservers(); } -void LLInventoryModel::onAISUpdateReceived(const std::string& context, const LLSD& update) +void LLInventoryModel::rebuildBrockenLinks() { - LLTimer timer; - if (gSavedSettings.getBOOL("DebugAvatarAppearanceMessage")) - { - dump_sequential_xml(gAgentAvatarp->getFullname() + "_ais_update", update); - } + // make sure we aren't adding expensive Rebuild to anything else. + notifyObservers(); - AISUpdate ais_update(update); // parse update llsd into stuff to do. - ais_update.doUpdate(); // execute the updates in the appropriate order. - LL_INFOS(LOG_INV) << "elapsed: " << timer.getElapsedTimeF32() << LL_ENDL; + for (const broken_links_t::value_type &link_list : mPossiblyBrockenLinks) + { + for (const LLUUID& link_id : link_list.second) + { + addChangedMask(LLInventoryObserver::REBUILD , link_id); + } + } + for (const LLUUID& link_id : mLinksRebuildList) + { + addChangedMask(LLInventoryObserver::REBUILD , link_id); + } + mPossiblyBrockenLinks.clear(); + mLinksRebuildList.clear(); + notifyObservers(); } // Does not appear to be used currently. @@ -1750,6 +2112,20 @@ void LLInventoryModel::idleNotifyObservers() { // *FIX: Think I want this conditional or moved elsewhere... handleResponses(true); + + if (mLinksRebuildList.size() > 0) + { + if (mModifyMask != LLInventoryObserver::NONE || (mChangedItemIDs.size() != 0)) + { + notifyObservers(); + } + for (const LLUUID& link_id : mLinksRebuildList) + { + addChangedMask(LLInventoryObserver::REBUILD , link_id); + } + mLinksRebuildList.clear(); + notifyObservers(); + } if (mModifyMask == LLInventoryObserver::NONE && (mChangedItemIDs.size() == 0)) { @@ -2069,10 +2445,52 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) // The item will show up as a broken link. if (item->getIsBrokenLink()) { - LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() - << " itemID: " << item->getUUID() - << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + if (item->getAssetUUID().notNull() + && LLInventoryModelBackgroundFetch::getInstance()->folderFetchActive()) + { + // Schedule this link for a recheck as inventory gets loaded + // Todo: expand to cover not just an initial fetch + mPossiblyBrockenLinks[item->getAssetUUID()].insert(item->getUUID()); + + // Do a blank rebuild of links once fetch is done + if (!mBulkFecthCallbackSlot.connected()) + { + // Links might take a while to update this way, and there + // might be a lot of them. A better option might be to check + // links periodically with final check on fetch completion. + mBulkFecthCallbackSlot = + LLInventoryModelBackgroundFetch::getInstance()->setFetchCompletionCallback( + [this]() + { + // rebuild is just in case, primary purpose is to wipe + // the list since we won't be getting anything 'new' + // see mLinksRebuildList + rebuildBrockenLinks(); + mBulkFecthCallbackSlot.disconnect(); + }); + } + LL_DEBUGS(LOG_INV) << "Scheduling a link to be rebuilt later [ name: " << item->getName() + << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + + } + else + { + LL_INFOS(LOG_INV) << "Adding broken link [ name: " << item->getName() + << " itemID: " << item->getUUID() + << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << LL_ENDL; + } } + if (!mPossiblyBrockenLinks.empty()) + { + // check if we are waiting for this item + broken_links_t::iterator iter = mPossiblyBrockenLinks.find(item->getUUID()); + if (iter != mPossiblyBrockenLinks.end()) + { + mLinksRebuildList.insert(iter->second.begin() , iter->second.end()); + mPossiblyBrockenLinks.erase(iter); + } + } if (item->getIsLinkType()) { // Add back-link from linked-to UUID. @@ -2340,6 +2758,10 @@ bool LLInventoryModel::loadSkeleton( else { cached_ids.insert(tcat->getUUID()); + + // At the moment download does not provide a thumbnail + // uuid, use the one from cache + tcat->setThumbnailUUID(cat->getThumbnailUUID()); } } @@ -2627,7 +3049,7 @@ void LLInventoryModel::buildParentChildMap() } } - const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, FALSE) != LLUUID::null); + const BOOL COF_exists = (findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT) != LLUUID::null); sFirstTimeInViewer2 = !COF_exists || gAgent.isFirstLogin(); @@ -2790,6 +3212,11 @@ void LLInventoryModel::initHttpRequest() mHttpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_LLSD_XML); mHttpPolicyClass = app_core_http.getPolicy(LLAppCoreHttp::AP_INVENTORY); } + + if (!gGenericDispatcher.isHandlerPresent("BulkUpdateInventory")) + { + gGenericDispatcher.addHandler("BulkUpdateInventory", &sBulkUpdateInventory); + } } void LLInventoryModel::handleResponses(bool foreground) @@ -2841,15 +3268,18 @@ LLCore::HttpHandle LLInventoryModel::requestPost(bool foreground, void LLInventoryModel::createCommonSystemCategories() { - gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_FAVORITE,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_CALLINGCARD,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS,true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT, true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_LANDMARK, true); // folder should exist before user tries to 'landmark this' - gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS, true); - gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL, true); // probably should be server created - gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, true); + //amount of System Folder we should wait for + sPendingSystemFolders = 9; + + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_TRASH); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_FAVORITE); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CALLINGCARD); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MY_OUTFITS); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_LANDMARK); // folder should exist before user tries to 'landmark this' + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_SETTINGS); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MATERIAL); // probably should be server created + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_INBOX); } struct LLUUIDAndName @@ -3074,9 +3504,6 @@ void LLInventoryModel::registerCallbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem, processRemoveInventoryItem, NULL); - msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder, - processUpdateInventoryFolder, - NULL); msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder, processRemoveInventoryFolder, NULL); @@ -3105,6 +3532,10 @@ void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, vo msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id); gInventoryCallbacks.fire(callback_id, item_id); + + // todo: instead of unpacking message fully, + // grab only an item_id, then fetch + LLInventoryModelBackgroundFetch::instance().scheduleItemFetch(item_id, true); } } @@ -3219,66 +3650,6 @@ void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**) gInventory.notifyObservers(); } -// static -void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg, - void**) -{ - LL_DEBUGS(LOG_INV) << "LLInventoryModel::processUpdateInventoryFolder()" << LL_ENDL; - LLUUID agent_id, folder_id, parent_id; - //char name[DB_INV_ITEM_NAME_BUF_SIZE]; - msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id); - if(agent_id != gAgent.getID()) - { - LL_WARNS(LOG_INV) << "Got an UpdateInventoryFolder for the wrong agent." - << LL_ENDL; - return; - } - LLPointer lastfolder; // hack - cat_array_t folders; - update_map_t update; - S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData); - for(S32 i = 0; i < count; ++i) - { - LLPointer tfolder = new LLViewerInventoryCategory(gAgent.getID()); - lastfolder = tfolder; - tfolder->unpackMessage(msg, _PREHASH_FolderData, i); - // make sure it's not a protected folder - tfolder->setPreferredType(LLFolderType::FT_NONE); - folders.push_back(tfolder); - // examine update for changes. - LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); - if(folderp) - { - if(tfolder->getParentUUID() == folderp->getParentUUID()) - { - update[tfolder->getParentUUID()]; - } - else - { - ++update[tfolder->getParentUUID()]; - --update[folderp->getParentUUID()]; - } - } - else - { - ++update[tfolder->getParentUUID()]; - } - } - gInventory.accountForUpdate(update); - for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it) - { - gInventory.updateCategory(*it); - } - gInventory.notifyObservers(); - - // *HACK: Do the 'show' logic for a new item in the inventory. - LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); - if (active_panel) - { - active_panel->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO); - } -} - // static void LLInventoryModel::removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg) @@ -3381,14 +3752,6 @@ void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg, } } -struct InventoryCallbackInfo -{ - InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) : - mCallback(callback), mInvID(inv_id) {} - U32 mCallback; - LLUUID mInvID; -}; - // static void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) { @@ -3434,15 +3797,22 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID()); if(folderp) { - if(tfolder->getParentUUID() == folderp->getParentUUID()) - { - update[tfolder->getParentUUID()]; - } - else - { - ++update[tfolder->getParentUUID()]; - --update[folderp->getParentUUID()]; - } + if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) + { + if (tfolder->getParentUUID() == folderp->getParentUUID()) + { + update[tfolder->getParentUUID()]; + } + else + { + ++update[tfolder->getParentUUID()]; + --update[folderp->getParentUUID()]; + } + } + else + { + folderp->fetch(); + } } else { @@ -3452,7 +3822,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) folderp = gInventory.getCategory(tfolder->getParentUUID()); if(folderp) { - ++update[tfolder->getParentUUID()]; + if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) + { + ++update[tfolder->getParentUUID()]; + } + else + { + folderp->fetch(); + } } } } @@ -3498,7 +3875,14 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID()); if(folderp) { - ++update[titem->getParentUUID()]; + if (folderp->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN) + { + ++update[titem->getParentUUID()]; + } + else + { + folderp->fetch(); + } } } } @@ -3512,10 +3896,20 @@ void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**) for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit) { gInventory.updateCategory(*cit); + + // Temporary workaround: just fetch the item using AIS to get missing fields. + // If this works fine we might want to extract ids only from the message + // then use AIS as a primary fetcher + LLInventoryModelBackgroundFetch::instance().scheduleFolderFetch((*cit)->getUUID(), true /*force, since it has changes*/); } for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit) { gInventory.updateItem(*iit); + + // Temporary workaround: just fetch the item using AIS to get missing fields. + // If this works fine we might want to extract ids only from the message + // then use AIS as a primary fetcher + LLInventoryModelBackgroundFetch::instance().scheduleItemFetch((*iit)->getUUID(), true); } gInventory.notifyObservers(); @@ -4340,7 +4734,6 @@ LLPointer LLInventoryModel::validate() const } else if (count_under_root > 1) { - LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL; validation_info->mDuplicateRequiredSystemFolders.insert(folder_type); if (!is_automatic && folder_type != LLFolderType::FT_SETTINGS @@ -4352,6 +4745,7 @@ LLPointer LLInventoryModel::validate() const // outfits, trash and other non-automatic folders. validation_info->mFatalSystemDuplicate++; fatal_errs++; + LL_WARNS("Inventory") << "Fatal inventory corruption: system folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL; } else { @@ -4360,6 +4754,7 @@ LLPointer LLInventoryModel::validate() const // Exception: FT_SETTINGS is not automatic, but only deserves a warning. validation_info->mWarnings["non_fatal_system_duplicate_under_root"]++; warning_count++; + LL_WARNS("Inventory") << "System folder type has excess copies under root, type " << ft << " count " << count_under_root << LL_ENDL; } } if (count_elsewhere > 0) diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index af9a020788..69d987cabd 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -39,7 +39,6 @@ #include "llpermissionsflags.h" #include "llviewerinventory.h" #include "llstring.h" -#include "llmd5.h" #include "httpcommon.h" #include "httprequest.h" #include "httpoptions.h" @@ -227,10 +226,14 @@ private: //-------------------------------------------------------------------- public: static BOOL getIsFirstTimeInViewer2(); + static bool isSysFoldersReady() { return (sPendingSystemFolders == 0); } + private: static BOOL sFirstTimeInViewer2; const static S32 sCurrentInvCacheVersion; // expected inventory cache version + static S32 sPendingSystemFolders; + /** Initialization/Setup ** ** *******************************************************************************/ @@ -256,9 +259,11 @@ public: void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t*& categories, item_array_t*& items) const; + void getDirectDescendentsOf(const LLUUID& cat_id, cat_array_t& categories, item_array_t& items, LLInventoryCollectFunctor& f) const; + typedef LLUUID digest_t; // To clarify the actual usage of this "UUID" // Compute a hash of direct descendant names (for detecting child name changes) - LLMD5 hashDirectDescendentNames(const LLUUID& cat_id) const; + digest_t hashDirectDescendentNames(const LLUUID& cat_id) const; // Starting with the object specified, add its descendants to the // array provided, but do not add the inventory object specified @@ -302,24 +307,25 @@ public: // Find //-------------------------------------------------------------------- public: + + // Checks if category exists ("My Inventory" only), if it does not, creates it + void ensureCategoryForTypeExists(LLFolderType::EType preferred_type); + const LLUUID findCategoryUUIDForTypeInRoot( LLFolderType::EType preferred_type, - bool create_folder, - const LLUUID& root_id); + const LLUUID& root_id) const; // Returns the uuid of the category that specifies 'type' as what it // defaults to containing. The category is not necessarily only for that type. // NOTE: If create_folder is true, this will create a new inventory category // on the fly if one does not exist. *NOTE: if find_in_library is true it // will search in the user's library folder instead of "My Inventory" - const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder = true); + const LLUUID findCategoryUUIDForType(LLFolderType::EType preferred_type) const; // will search in the user's library folder instead of "My Inventory" - const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type, - bool create_folder = true); + const LLUUID findLibraryCategoryUUIDForType(LLFolderType::EType preferred_type) const; // Returns user specified category for uploads, returns default id if there are no // user specified one or it does not exist, creates default category if it is missing. - const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type); + const LLUUID findUserDefinedCategoryUUIDForType(LLFolderType::EType preferred_type) const; // Get whatever special folder this object is a child of, if any. const LLViewerInventoryCategory *getFirstNondefaultParent(const LLUUID& obj_id) const; @@ -405,13 +411,15 @@ public: const LLUUID& new_parent_id, BOOL restamp); + // Marks links from a "possibly" broken list for a rebuild + // clears the list + void rebuildBrockenLinks(); + bool hasPosiblyBrockenLinks() const { return mPossiblyBrockenLinks.size() > 0; } + //-------------------------------------------------------------------- // Delete //-------------------------------------------------------------------- public: - - // Update model after an AISv3 update received for any operation. - void onAISUpdateReceived(const std::string& context, const LLSD& update); // Update model after an item is confirmed as removed from // server. Works for categories or items. @@ -475,10 +483,11 @@ public: public: // Returns the UUID of the new category. If you want to use the default // name based on type, pass in a NULL to the 'name' parameter. - LLUUID createNewCategory(const LLUUID& parent_id, + void createNewCategory(const LLUUID& parent_id, LLFolderType::EType preferred_type, const std::string& name, - inventory_func_type callback = NULL); + inventory_func_type callback = NULL, + const LLUUID& thumbnail_id = LLUUID::null); protected: // Internal methods that add inventory and make sure that all of // the internal data structures are consistent. These methods @@ -575,6 +584,10 @@ private: U32 mModifyMaskBacklog; changed_items_t mChangedItemIDsBacklog; changed_items_t mAddedItemIDsBacklog; + typedef std::map broken_links_t; + broken_links_t mPossiblyBrockenLinks; // there can be multiple links per item + changed_items_t mLinksRebuildList; + boost::signals2::connection mBulkFecthCallbackSlot; //-------------------------------------------------------------------- @@ -661,7 +674,6 @@ public: static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**); static void removeInventoryItem(LLUUID agent_id, LLMessageSystem* msg, const char* msg_label); static void processRemoveInventoryItem(LLMessageSystem* msg, void**); - static void processUpdateInventoryFolder(LLMessageSystem* msg, void**); static void removeInventoryFolder(LLUUID agent_id, LLMessageSystem* msg); static void processRemoveInventoryFolder(LLMessageSystem* msg, void**); static void processRemoveInventoryObjects(LLMessageSystem* msg, void**); diff --git a/indra/newview/llinventorymodelbackgroundfetch.cpp b/indra/newview/llinventorymodelbackgroundfetch.cpp index 4a9b471a47..91adef8047 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.cpp +++ b/indra/newview/llinventorymodelbackgroundfetch.cpp @@ -27,11 +27,14 @@ #include "llviewerprecompiledheaders.h" #include "llinventorymodelbackgroundfetch.h" +#include "llaisapi.h" #include "llagent.h" #include "llappviewer.h" #include "llcallbacklist.h" -#include "llinventorypanel.h" #include "llinventorymodel.h" +#include "llinventorypanel.h" +#include "llnotificationsutil.h" +#include "llstartup.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewermessage.h" @@ -184,12 +187,14 @@ const char * const LOG_INV("Inventory"); ///---------------------------------------------------------------------------- LLInventoryModelBackgroundFetch::LLInventoryModelBackgroundFetch(): - mBackgroundFetchActive(FALSE), + mBackgroundFetchActive(false), mFolderFetchActive(false), mFetchCount(0), - mAllFoldersFetched(FALSE), - mRecursiveInventoryFetchStarted(FALSE), - mRecursiveLibraryFetchStarted(FALSE), + mLastFetchCount(0), + mFetchFolderCount(0), + mAllRecursiveFoldersFetched(false), + mRecursiveInventoryFetchStarted(false), + mRecursiveLibraryFetchStarted(false), mMinTimeBetweenFetches(0.3f) {} @@ -198,7 +203,12 @@ LLInventoryModelBackgroundFetch::~LLInventoryModelBackgroundFetch() bool LLInventoryModelBackgroundFetch::isBulkFetchProcessingComplete() const { - return mFetchQueue.empty() && mFetchCount <= 0; + return mFetchFolderQueue.empty() && mFetchItemQueue.empty() && mFetchCount <= 0; +} + +bool LLInventoryModelBackgroundFetch::isFolderFetchProcessingComplete() const +{ + return mFetchFolderQueue.empty() && mFetchFolderCount <= 0; } bool LLInventoryModelBackgroundFetch::libraryFetchStarted() const @@ -233,7 +243,7 @@ bool LLInventoryModelBackgroundFetch::inventoryFetchInProgress() const bool LLInventoryModelBackgroundFetch::isEverythingFetched() const { - return mAllFoldersFetched; + return mAllRecursiveFoldersFetched; } BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const @@ -241,17 +251,33 @@ BOOL LLInventoryModelBackgroundFetch::folderFetchActive() const return mFolderFetchActive; } -void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, BOOL recursive, bool is_category) +void LLInventoryModelBackgroundFetch::addRequestAtFront(const LLUUID & id, bool recursive, bool is_category) { - mFetchQueue.push_front(FetchQueueInfo(id, recursive, is_category)); + EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT; + if (is_category) + { + mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type, is_category)); + } + else + { + mFetchItemQueue.push_front(FetchQueueInfo(id, recursion_type, is_category)); + } } -void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, BOOL recursive, bool is_category) +void LLInventoryModelBackgroundFetch::addRequestAtBack(const LLUUID & id, bool recursive, bool is_category) { - mFetchQueue.push_back(FetchQueueInfo(id, recursive, is_category)); + EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT; + if (is_category) + { + mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type, is_category)); + } + else + { + mFetchItemQueue.push_back(FetchQueueInfo(id, recursion_type, is_category)); + } } -void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) +void LLInventoryModelBackgroundFetch::start(const LLUUID& id, bool recursive) { LLViewerInventoryCategory * cat(gInventory.getCategory(id)); @@ -260,31 +286,53 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) // it's a folder, do a bulk fetch LL_DEBUGS(LOG_INV) << "Start fetching category: " << id << ", recursive: " << recursive << LL_ENDL; - mBackgroundFetchActive = TRUE; + mBackgroundFetchActive = true; mFolderFetchActive = true; + EFetchType recursion_type = recursive ? FT_RECURSIVE : FT_DEFAULT; if (id.isNull()) { if (! mRecursiveInventoryFetchStarted) { mRecursiveInventoryFetchStarted |= recursive; - mFetchQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursive)); + if (recursive && AISAPI::isAvailable()) + { + // Not only root folder can be massive, but + // most system folders will be requested independently + // so request root folder and content separately + mFetchFolderQueue.push_front(FetchQueueInfo(gInventory.getRootFolderID(), FT_FOLDER_AND_CONTENT)); + } + else + { + mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getRootFolderID(), recursion_type)); + } gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } if (! mRecursiveLibraryFetchStarted) { mRecursiveLibraryFetchStarted |= recursive; - mFetchQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursive)); + mFetchFolderQueue.push_back(FetchQueueInfo(gInventory.getLibraryRootFolderID(), recursion_type)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } } else { - // Specific folder requests go to front of queue. - if (mFetchQueue.empty() || mFetchQueue.front().mUUID != id) - { - mFetchQueue.push_front(FetchQueueInfo(id, recursive)); - gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); - } + if (AISAPI::isAvailable()) + { + if (mFetchFolderQueue.empty() || mFetchFolderQueue.back().mUUID != id) + { + // On AIS make sure root goes to the top and follow up recursive + // fetches, not individual requests + mFetchFolderQueue.push_back(FetchQueueInfo(id, recursion_type)); + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + } + else if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != id) + { + // Specific folder requests go to front of queue. + mFetchFolderQueue.push_front(FetchQueueInfo(id, recursion_type)); + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + if (id == gInventory.getLibraryRootFolderID()) { mRecursiveLibraryFetchStarted |= recursive; @@ -297,21 +345,41 @@ void LLInventoryModelBackgroundFetch::start(const LLUUID& id, BOOL recursive) } else if (LLViewerInventoryItem * itemp = gInventory.getItem(id)) { - if (! itemp->mIsComplete && (mFetchQueue.empty() || mFetchQueue.front().mUUID != id)) + if (! itemp->mIsComplete) { - mBackgroundFetchActive = TRUE; - - mFetchQueue.push_front(FetchQueueInfo(id, false, false)); - gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + scheduleItemFetch(id); } } } +void LLInventoryModelBackgroundFetch::scheduleFolderFetch(const LLUUID& cat_id, bool forced) +{ + if (mFetchFolderQueue.empty() || mFetchFolderQueue.front().mUUID != cat_id) + { + mBackgroundFetchActive = true; + + // Specific folder requests go to front of queue. + mFetchFolderQueue.push_front(FetchQueueInfo(cat_id, forced ? FT_FORCED : FT_DEFAULT)); + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } +} + +void LLInventoryModelBackgroundFetch::scheduleItemFetch(const LLUUID& item_id, bool forced) +{ + if (mFetchItemQueue.empty() || mFetchItemQueue.front().mUUID != item_id) + { + mBackgroundFetchActive = true; + + mFetchItemQueue.push_front(FetchQueueInfo(item_id, forced ? FT_FORCED : FT_DEFAULT, false)); + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } +} + void LLInventoryModelBackgroundFetch::findLostItems() { - mBackgroundFetchActive = TRUE; - mFolderFetchActive = true; - mFetchQueue.push_back(FetchQueueInfo(LLUUID::null, TRUE)); + mBackgroundFetchActive = true; + mFolderFetchActive = true; + mFetchFolderQueue.push_back(FetchQueueInfo(LLUUID::null, FT_RECURSIVE)); gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); } @@ -320,15 +388,28 @@ void LLInventoryModelBackgroundFetch::setAllFoldersFetched() if (mRecursiveInventoryFetchStarted && mRecursiveLibraryFetchStarted) { - mAllFoldersFetched = TRUE; + mAllRecursiveFoldersFetched = true; //LL_INFOS(LOG_INV) << "All folders fetched, validating" << LL_ENDL; //gInventory.validate(); } + mFolderFetchActive = false; - mBackgroundFetchActive = false; + if (isBulkFetchProcessingComplete()) + { + mBackgroundFetchActive = false; + } + + // For now only informs about initial fetch being done + mFoldersFetchedSignal(); + LL_INFOS(LOG_INV) << "Inventory background fetch completed" << LL_ENDL; } +boost::signals2::connection LLInventoryModelBackgroundFetch::setFetchCompletionCallback(folders_fetched_callback_t cb) +{ + return mFoldersFetchedSignal.connect(cb); +} + void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) { LLInventoryModelBackgroundFetch::instance().backgroundFetch(); @@ -336,10 +417,17 @@ void LLInventoryModelBackgroundFetch::backgroundFetchCB(void *) void LLInventoryModelBackgroundFetch::backgroundFetch() { - if (mBackgroundFetchActive && gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived()) + if (mBackgroundFetchActive) { - // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. - bulkFetch(); + if (AISAPI::isAvailable()) + { + bulkFetchViaAis(); + } + else if (gAgent.getRegion() && gAgent.getRegion()->capabilitiesReceived()) + { + // If we'll be using the capability, we'll be sending batches and the background thing isn't as important. + bulkFetch(); + } } } @@ -352,9 +440,476 @@ void LLInventoryModelBackgroundFetch::incrFetchCount(S32 fetching) mFetchCount = 0; } } +void LLInventoryModelBackgroundFetch::incrFetchFolderCount(S32 fetching) +{ + incrFetchCount(fetching); + mFetchFolderCount += fetching; + if (mFetchCount < 0) + { + LL_WARNS_ONCE(LOG_INV) << "Inventory fetch count fell below zero (0)." << LL_ENDL; + mFetchFolderCount = 0; + } +} + +void ais_simple_item_callback(const LLUUID& inv_id) +{ + LL_DEBUGS(LOG_INV , "AIS3") << "Response for " << inv_id << LL_ENDL; + LLInventoryModelBackgroundFetch::instance().incrFetchCount(-1); +} + +void LLInventoryModelBackgroundFetch::onAISContentCalback( + const LLUUID& request_id, + const uuid_vec_t& content_ids, + const LLUUID& response_id, + EFetchType fetch_type) +{ + // Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis + incrFetchFolderCount(-1); + + uuid_vec_t::const_iterator folder_iter = content_ids.begin(); + uuid_vec_t::const_iterator folder_end = content_ids.end(); + while (folder_iter != folder_end) + { + std::list::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), *folder_iter); + if (found != mExpectedFolderIds.end()) + { + mExpectedFolderIds.erase(found); + } + + LLViewerInventoryCategory* cat(gInventory.getCategory(*folder_iter)); + if (cat) + { + cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); + } + if (response_id.isNull()) + { + // Failed to fetch, get it individually + mFetchFolderQueue.push_back(FetchQueueInfo(*folder_iter, FT_RECURSIVE)); + } + else + { + // push descendant back to verify they are fetched fully (ex: didn't encounter depth limit) + LLInventoryModel::cat_array_t* categories(NULL); + LLInventoryModel::item_array_t* items(NULL); + gInventory.getDirectDescendentsOf(*folder_iter, categories, items); + if (categories) + { + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE)); + } + } + } + + folder_iter++; + } + + if (!mFetchFolderQueue.empty()) + { + mBackgroundFetchActive = true; + mFolderFetchActive = true; + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } +} +void LLInventoryModelBackgroundFetch::onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type) +{ + // Don't push_front on failure - there is a chance it was fired from inside bulkFetchViaAis + incrFetchFolderCount(-1); + std::list::const_iterator found = std::find(mExpectedFolderIds.begin(), mExpectedFolderIds.end(), request_id); + if (found != mExpectedFolderIds.end()) + { + mExpectedFolderIds.erase(found); + } + else + { + // ais shouldn't respond twice + llassert(false); + LL_WARNS() << "Unexpected folder response for " << request_id << LL_ENDL; + } + + if (request_id.isNull()) + { + // orhans, no other actions needed + return; + } + + bool request_descendants = false; + if (response_id.isNull()) // Failure + { + LL_DEBUGS(LOG_INV , "AIS3") << "Failure response for folder " << request_id << LL_ENDL; + if (fetch_type == FT_RECURSIVE) + { + // A full recursive request failed. + // Try requesting folder and nested content separately + mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_FOLDER_AND_CONTENT)); + } + else if (fetch_type == FT_FOLDER_AND_CONTENT) + { + LL_WARNS() << "Failed to download folder: " << request_id << " Requesting known content separately" << LL_ENDL; + mFetchFolderQueue.push_back(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE)); + + // set folder's version to prevent viewer from trying to request folder indefinetely + LLViewerInventoryCategory* cat(gInventory.getCategory(request_id)); + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + cat->setVersion(0); + } + } + } + else + { + if (fetch_type == FT_RECURSIVE) + { + // Got the folder and content, now verify content + // Request content even for FT_RECURSIVE in case of changes, failures + // or if depth limit gets imlemented. + // This shouldn't redownload folders if they already have version + request_descendants = true; + LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << ". Requesting content" << LL_ENDL; + } + else if (fetch_type == FT_FOLDER_AND_CONTENT) + { + // readd folder for content request + mFetchFolderQueue.push_front(FetchQueueInfo(request_id, FT_CONTENT_RECURSIVE)); + } + else + { + LL_DEBUGS(LOG_INV, "AIS3") << "Got folder " << request_id << "." << LL_ENDL; + } + + } + + if (request_descendants) + { + LLInventoryModel::cat_array_t* categories(NULL); + LLInventoryModel::item_array_t* items(NULL); + gInventory.getDirectDescendentsOf(request_id, categories, items); + if (categories) + { + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE)); + } + } + } + + if (!mFetchFolderQueue.empty()) + { + mBackgroundFetchActive = true; + mFolderFetchActive = true; + gIdleCallbacks.addFunction(&LLInventoryModelBackgroundFetch::backgroundFetchCB, NULL); + } + + // done + LLViewerInventoryCategory * cat(gInventory.getCategory(request_id)); + if (cat) + { + cat->setFetching(LLViewerInventoryCategory::FETCH_NONE); + } +} static LLTrace::BlockTimerStatHandle FTM_BULK_FETCH("Bulk Fetch"); +void LLInventoryModelBackgroundFetch::bulkFetchViaAis() +{ + LL_RECORD_BLOCK_TIME(FTM_BULK_FETCH); + //Background fetch is called from gIdleCallbacks in a loop until background fetch is stopped. + if (gDisconnected) + { + return; + } + + static LLCachedControl ais_pool(gSavedSettings, "PoolSizeAIS", 20); + // Don't have too many requests at once, AIS throttles + // Reserve one request for actions outside of fetch (like renames) + const U32 max_concurrent_fetches = llclamp(ais_pool - 1, 1, 50); + + if (mFetchCount >= max_concurrent_fetches) + { + return; + } + + // Don't loop for too long (in case of large, fully loaded inventory) + F64 curent_time = LLTimer::getTotalSeconds(); + const F64 max_time = LLStartUp::getStartupState() > STATE_WEARABLES_WAIT + ? 0.006f // 6 ms + : 1.f; + const F64 end_time = curent_time + max_time; + S32 last_fetch_count = mFetchCount; + + while (!mFetchFolderQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time) + { + const FetchQueueInfo & fetch_info(mFetchFolderQueue.front()); + bulkFetchViaAis(fetch_info); + mFetchFolderQueue.pop_front(); + curent_time = LLTimer::getTotalSeconds(); + } + + // Ideally we shouldn't fetch items if recursive fetch isn't done, + // but there is a chance some request will start timeouting and recursive + // fetch will get stuck on a signle folder, don't block item fetch in such case + while (!mFetchItemQueue.empty() && mFetchCount < max_concurrent_fetches && curent_time < end_time) + { + const FetchQueueInfo& fetch_info(mFetchItemQueue.front()); + bulkFetchViaAis(fetch_info); + mFetchItemQueue.pop_front(); + curent_time = LLTimer::getTotalSeconds(); + } + + if (last_fetch_count != mFetchCount // if anything was added + || mLastFetchCount != mFetchCount) // if anything was substracted + { + LL_DEBUGS(LOG_INV , "AIS3") << "Total active fetches: " << mLastFetchCount << "->" << last_fetch_count << "->" << mFetchCount + << ", scheduled folder fetches: " << (S32)mFetchFolderQueue.size() + << ", scheduled item fetches: " << (S32)mFetchItemQueue.size() + << LL_ENDL; + mLastFetchCount = mFetchCount; + + if (!mExpectedFolderIds.empty()) + { + // A folder seem to be stack fetching on QA account, print oldest folder out + LL_DEBUGS(LOG_INV , "AIS3") << "Oldest expected folder: "; + std::list::const_iterator iter = mExpectedFolderIds.begin(); + LL_CONT << *iter; + if ((*iter).notNull()) + { + LLViewerInventoryCategory* cat(gInventory.getCategory(*iter)); + if (cat) + { + LL_CONT << " Folder name: " << cat->getName() << " Parent: " << cat->getParentUUID(); + } + else + { + LL_CONT << " This folder doesn't exist"; + } + } + else + { + LL_CONT << " Orphans request"; + } + LL_CONT << LL_ENDL; + } + } + + if (isFolderFetchProcessingComplete() && mFolderFetchActive) + { + setAllFoldersFetched(); + } + + if (isBulkFetchProcessingComplete()) + { + mBackgroundFetchActive = false; + } +} + +void LLInventoryModelBackgroundFetch::bulkFetchViaAis(const FetchQueueInfo& fetch_info) +{ + if (fetch_info.mIsCategory) + { + const LLUUID & cat_id(fetch_info.mUUID); + if (cat_id.isNull()) + { + incrFetchFolderCount(1); + mExpectedFolderIds.push_back(cat_id); + // Lost and found + // Should it actually be recursive? + AISAPI::FetchOrphans([](const LLUUID& response_id) + { + LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(LLUUID::null, + response_id, + FT_DEFAULT); + }); + } + else + { + LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); + if (cat) + { + if (fetch_info.mFetchType == FT_CONTENT_RECURSIVE) + { + // fetch content only, ignore cat itself + uuid_vec_t children; + LLInventoryModel::cat_array_t* categories(NULL); + LLInventoryModel::item_array_t* items(NULL); + gInventory.getDirectDescendentsOf(cat_id, categories, items); + + LLViewerInventoryCategory::EFetchType target_state = LLViewerInventoryCategory::FETCH_RECURSIVE; + bool content_done = true; + + // Top limit is 'as many as you can put into url' + static LLCachedControl ais_batch(gSavedSettings, "BatchSizeAIS3", 20); + S32 batch_limit = llclamp(ais_batch(), 1, 40); + + for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); + it != categories->end(); + ++it) + { + LLViewerInventoryCategory* child_cat = (*it); + if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion() + || child_cat->getFetching() >= target_state) + { + continue; + } + + if (child_cat->getPreferredType() == LLFolderType::FT_MARKETPLACE_LISTINGS) + { + // special case + content_done = false; + if (children.empty()) + { + // fetch marketplace alone + // Should it actually be fetched as FT_FOLDER_AND_CONTENT? + children.push_back(child_cat->getUUID()); + mExpectedFolderIds.push_back(child_cat->getUUID()); + child_cat->setFetching(target_state); + break; + } + else + { + // fetch marketplace alone next run + continue; + } + } + + children.push_back(child_cat->getUUID()); + mExpectedFolderIds.push_back(child_cat->getUUID()); + child_cat->setFetching(target_state); + + if (children.size() >= batch_limit) + { + content_done = false; + break; + } + } + + if (!children.empty()) + { + // increment before call in case of immediate callback + incrFetchFolderCount(1); + + EFetchType type = fetch_info.mFetchType; + LLUUID cat_id = cat->getUUID(); // need a copy for lambda + AISAPI::completion_t cb = [cat_id, children, type](const LLUUID& response_id) + { + LLInventoryModelBackgroundFetch::instance().onAISContentCalback(cat_id, children, response_id, type); + }; + + AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY; + if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + { + item_type = AISAPI::LIBRARY; + } + + AISAPI::FetchCategorySubset(cat_id, children, item_type, true, cb, 0); + } + + if (content_done) + { + // This will have a bit of overlap with onAISContentCalback, + // but something else might have dowloaded folders, so verify + // every child that is complete has it's children done as well + for (LLInventoryModel::cat_array_t::iterator it = categories->begin(); + it != categories->end(); + ++it) + { + LLViewerInventoryCategory* child_cat = (*it); + if (LLViewerInventoryCategory::VERSION_UNKNOWN != child_cat->getVersion()) + { + mFetchFolderQueue.push_back(FetchQueueInfo(child_cat->getUUID(), FT_RECURSIVE)); + } + } + } + else + { + // send it back to get the rest + mFetchFolderQueue.push_back(FetchQueueInfo(cat_id, FT_CONTENT_RECURSIVE)); + } + } + else if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion() + || fetch_info.mFetchType == FT_FORCED) + { + LLViewerInventoryCategory::EFetchType target_state = + fetch_info.mFetchType > FT_CONTENT_RECURSIVE + ? LLViewerInventoryCategory::FETCH_RECURSIVE + : LLViewerInventoryCategory::FETCH_NORMAL; + // start again if we did a non-recursive fetch before + // to get all children in a single request + if (cat->getFetching() < target_state) + { + // increment before call in case of immediate callback + incrFetchFolderCount(1); + cat->setFetching(target_state); + mExpectedFolderIds.push_back(cat_id); + + EFetchType type = fetch_info.mFetchType; + LLUUID cat_id = cat->getUUID(); + AISAPI::completion_t cb = [cat_id , type](const LLUUID& response_id) + { + LLInventoryModelBackgroundFetch::instance().onAISFolderCalback(cat_id , response_id , type); + }; + + AISAPI::ITEM_TYPE item_type = AISAPI::INVENTORY; + if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + { + item_type = AISAPI::LIBRARY; + } + + AISAPI::FetchCategoryChildren(cat_id , item_type , type == FT_RECURSIVE , cb, 0); + } + } + else + { + // Already fetched, check if anything inside needs fetching + if (fetch_info.mFetchType == FT_RECURSIVE + || fetch_info.mFetchType == FT_FOLDER_AND_CONTENT) + { + LLInventoryModel::cat_array_t * categories(NULL); + LLInventoryModel::item_array_t * items(NULL); + gInventory.getDirectDescendentsOf(cat_id, categories, items); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + // not push_front to not cause an infinite loop + mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), FT_RECURSIVE)); + } + } + } + } // else try to fetch folder either way? + } + } + else + { + LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); + + if (itemp) + { + if (!itemp->isFinished() || fetch_info.mFetchType == FT_FORCED) + { + mFetchCount++; + if (itemp->getPermissions().getOwner() == gAgent.getID()) + { + AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback); + } + else + { + AISAPI::FetchItem(fetch_info.mUUID, AISAPI::LIBRARY, ais_simple_item_callback); + } + } + } + else // We don't know it, assume incomplete + { + // Assume agent's inventory, library wouldn't have gotten here + mFetchCount++; + AISAPI::FetchItem(fetch_info.mUUID, AISAPI::INVENTORY, ais_simple_item_callback); + } + } +} + // Bundle up a bunch of requests to send all at once. void LLInventoryModelBackgroundFetch::bulkFetch() { @@ -374,13 +929,6 @@ void LLInventoryModelBackgroundFetch::bulkFetch() // inventory more quickly. static const U32 max_batch_size(10); static const S32 max_concurrent_fetches(12); // Outstanding requests, not connections - static const F32 new_min_time(0.05f); // *HACK: Clean this up when old code goes away entirely. - - mMinTimeBetweenFetches = new_min_time; - if (mMinTimeBetweenFetches < new_min_time) - { - mMinTimeBetweenFetches = new_min_time; // *HACK: See above. - } if (mFetchCount) { @@ -394,8 +942,7 @@ void LLInventoryModelBackgroundFetch::bulkFetch() gInventory.notifyObservers(); } - if ((mFetchCount > max_concurrent_fetches) || - (mFetchTimer.getElapsedTimeF32() < mMinTimeBetweenFetches)) + if (mFetchCount > max_concurrent_fetches) { return; } @@ -408,99 +955,112 @@ void LLInventoryModelBackgroundFetch::bulkFetch() // *TODO: Think I'd like to get a shared pointer to this and share it // among all the folder requests. uuid_vec_t recursive_cats; + uuid_vec_t all_cats; // dupplicate avoidance LLSD folder_request_body; LLSD folder_request_body_lib; LLSD item_request_body; LLSD item_request_body_lib; - while (! mFetchQueue.empty() + while (! mFetchFolderQueue.empty() && (item_count + folder_count) < max_batch_size) { - const FetchQueueInfo & fetch_info(mFetchQueue.front()); + const FetchQueueInfo & fetch_info(mFetchFolderQueue.front()); if (fetch_info.mIsCategory) { const LLUUID & cat_id(fetch_info.mUUID); - if (cat_id.isNull()) //DEV-17797 + if (cat_id.isNull()) //DEV-17797 Lost and found { LLSD folder_sd; folder_sd["folder_id"] = LLUUID::null.asString(); folder_sd["owner_id"] = gAgent.getID(); folder_sd["sort_order"] = LLSD::Integer(sort_order); - folder_sd["fetch_folders"] = LLSD::Boolean(FALSE); - folder_sd["fetch_items"] = LLSD::Boolean(TRUE); + folder_sd["fetch_folders"] = LLSD::Boolean(false); + folder_sd["fetch_items"] = LLSD::Boolean(true); folder_request_body["folders"].append(folder_sd); folder_count++; } else { - const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); - - if (cat) - { - if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) - { - LLSD folder_sd; - folder_sd["folder_id"] = cat->getUUID(); - folder_sd["owner_id"] = cat->getOwnerID(); - folder_sd["sort_order"] = LLSD::Integer(sort_order); - folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted; - folder_sd["fetch_items"] = LLSD::Boolean(TRUE); - - if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) - { - folder_request_body_lib["folders"].append(folder_sd); - } - else - { - folder_request_body["folders"].append(folder_sd); - } - folder_count++; - } + const LLViewerInventoryCategory * cat(gInventory.getCategory(cat_id)); + if (cat) + { + if (LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion()) + { + if (std::find(all_cats.begin(), all_cats.end(), cat_id) == all_cats.end()) + { + LLSD folder_sd; + folder_sd["folder_id"] = cat->getUUID(); + folder_sd["owner_id"] = cat->getOwnerID(); + folder_sd["sort_order"] = LLSD::Integer(sort_order); + folder_sd["fetch_folders"] = LLSD::Boolean(TRUE); //(LLSD::Boolean)sFullFetchStarted; + folder_sd["fetch_items"] = LLSD::Boolean(TRUE); - // May already have this folder, but append child folders to list. - if (fetch_info.mRecursive) - { - LLInventoryModel::cat_array_t * categories(NULL); - LLInventoryModel::item_array_t * items(NULL); - gInventory.getDirectDescendentsOf(cat->getUUID(), categories, items); - for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); - it != categories->end(); - ++it) - { - mFetchQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mRecursive)); - } - } - } + if (ALEXANDRIA_LINDEN_ID == cat->getOwnerID()) + { + folder_request_body_lib["folders"].append(folder_sd); + } + else + { + folder_request_body["folders"].append(folder_sd); + } + folder_count++; + } + } + else + { + // May already have this folder, but append child folders to list. + if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE) + { + LLInventoryModel::cat_array_t * categories(NULL); + LLInventoryModel::item_array_t * items(NULL); + gInventory.getDirectDescendentsOf(cat_id, categories, items); + for (LLInventoryModel::cat_array_t::const_iterator it = categories->begin(); + it != categories->end(); + ++it) + { + mFetchFolderQueue.push_back(FetchQueueInfo((*it)->getUUID(), fetch_info.mFetchType)); + } + } + } + } } - if (fetch_info.mRecursive) + if (fetch_info.mFetchType >= FT_CONTENT_RECURSIVE) { recursive_cats.push_back(cat_id); } - } - else - { - LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); - - if (itemp) - { - LLSD item_sd; - item_sd["owner_id"] = itemp->getPermissions().getOwner(); - item_sd["item_id"] = itemp->getUUID(); - if (itemp->getPermissions().getOwner() == gAgent.getID()) - { - item_request_body.append(item_sd); - } - else - { - item_request_body_lib.append(item_sd); - } - //itemp->fetchFromServer(); - item_count++; - } + all_cats.push_back(cat_id); } - mFetchQueue.pop_front(); + mFetchFolderQueue.pop_front(); + } + + + while (!mFetchItemQueue.empty() + && (item_count + folder_count) < max_batch_size) + { + const FetchQueueInfo & fetch_info(mFetchItemQueue.front()); + + LLViewerInventoryItem * itemp(gInventory.getItem(fetch_info.mUUID)); + + if (itemp) + { + LLSD item_sd; + item_sd["owner_id"] = itemp->getPermissions().getOwner(); + item_sd["item_id"] = itemp->getUUID(); + if (itemp->getPermissions().getOwner() == gAgent.getID()) + { + item_request_body.append(item_sd); + } + else + { + item_request_body_lib.append(item_sd); + } + //itemp->fetchFromServer(); + item_count++; + } + + mFetchItemQueue.pop_front(); } // Issue HTTP POST requests to fetch folders and items @@ -571,14 +1131,22 @@ void LLInventoryModelBackgroundFetch::bulkFetch() bool LLInventoryModelBackgroundFetch::fetchQueueContainsNoDescendentsOf(const LLUUID & cat_id) const { - for (fetch_queue_t::const_iterator it = mFetchQueue.begin(); - it != mFetchQueue.end(); + for (fetch_queue_t::const_iterator it = mFetchFolderQueue.begin(); + it != mFetchFolderQueue.end(); ++it) { const LLUUID & fetch_id = (*it).mUUID; if (gInventory.isObjectDescendentOf(fetch_id, cat_id)) return false; } + for (fetch_queue_t::const_iterator it = mFetchItemQueue.begin(); + it != mFetchItemQueue.end(); + ++it) + { + const LLUUID & fetch_id = (*it).mUUID; + if (gInventory.isObjectDescendentOf(fetch_id, cat_id)) + return false; + } return true; } @@ -796,6 +1364,63 @@ void BGFolderHttpHandler::processFailure(LLCore::HttpStatus status, LLCore::Http << LLCoreHttpUtil::responseToString(response) << "]" << LL_ENDL; // Could use a 404 test here to try to detect revoked caps... + + if(status == LLCore::HttpStatus(HTTP_FORBIDDEN)) + { + // Too large, split into two if possible + if (gDisconnected || LLApp::isExiting()) + { + return; + } + + const std::string url(gAgent.getRegionCapability("FetchInventoryDescendents2")); + if (url.empty()) + { + LL_WARNS(LOG_INV) << "Failed to get AIS2 cap" << LL_ENDL; + return; + } + + S32 size = mRequestSD["folders"].size(); + + if (size > 1) + { + // Can split, assume that this isn't the library + LLSD folders; + uuid_vec_t recursive_cats; + LLSD::array_iterator iter = mRequestSD["folders"].beginArray(); + LLSD::array_iterator end = mRequestSD["folders"].endArray(); + while (iter != end) + { + folders.append(*iter); + LLUUID folder_id = iter->get("folder_id").asUUID(); + if (std::find(mRecursiveCatUUIDs.begin(), mRecursiveCatUUIDs.end(), folder_id) != mRecursiveCatUUIDs.end()) + { + recursive_cats.push_back(folder_id); + } + if (folders.size() == (S32)(size / 2)) + { + LLSD request_body; + request_body["folders"] = folders; + LLCore::HttpHandler::ptr_t handler(new BGFolderHttpHandler(request_body, recursive_cats)); + gInventory.requestPost(false, url, request_body, handler, "Inventory Folder"); + recursive_cats.clear(); + folders.clear(); + } + iter++; + } + + LLSD request_body; + request_body["folders"] = folders; + LLCore::HttpHandler::ptr_t handler(new BGFolderHttpHandler(request_body, recursive_cats)); + gInventory.requestPost(false, url, request_body, handler, "Inventory Folder"); + return; + } + else + { + // Can't split + LLNotificationsUtil::add("InventoryLimitReachedAIS"); + } + } // This was originally the request retry logic for the inventory // request which tested on HTTP_INTERNAL_ERROR status. This diff --git a/indra/newview/llinventorymodelbackgroundfetch.h b/indra/newview/llinventorymodelbackgroundfetch.h index 00d2908c1b..e7be265a3d 100644 --- a/indra/newview/llinventorymodelbackgroundfetch.h +++ b/indra/newview/llinventorymodelbackgroundfetch.h @@ -47,9 +47,11 @@ class LLInventoryModelBackgroundFetch : public LLSingleton folders_fetched_callback_t; + boost::signals2::connection setFetchCompletionCallback(folders_fetched_callback_t cb); + + void addRequestAtFront(const LLUUID & id, bool recursive, bool is_category); + void addRequestAtBack(const LLUUID & id, bool recursive, bool is_category); protected: + + typedef enum { + FT_DEFAULT = 0, + FT_FORCED, // request non-recursively even if already loaded + FT_CONTENT_RECURSIVE, // request content recursively + FT_FOLDER_AND_CONTENT, // request folder, then content recursively + FT_RECURSIVE, // request everything recursively + } EFetchType; + struct FetchQueueInfo + { + FetchQueueInfo(const LLUUID& id, EFetchType recursive, bool is_category = true) + : mUUID(id), + mIsCategory(is_category), + mFetchType(recursive) + {} + + LLUUID mUUID; + bool mIsCategory; + EFetchType mFetchType; + }; + typedef std::deque fetch_queue_t; + + void onAISContentCalback(const LLUUID& request_id, const uuid_vec_t &content_ids, const LLUUID& response_id, EFetchType fetch_type); + void onAISFolderCalback(const LLUUID &request_id, const LLUUID &response_id, EFetchType fetch_type); + void bulkFetchViaAis(); + void bulkFetchViaAis(const FetchQueueInfo& fetch_info); void bulkFetch(); void backgroundFetch(); @@ -80,31 +113,23 @@ protected: bool fetchQueueContainsNoDescendentsOf(const LLUUID& cat_id) const; private: - BOOL mRecursiveInventoryFetchStarted; - BOOL mRecursiveLibraryFetchStarted; - BOOL mAllFoldersFetched; + bool mRecursiveInventoryFetchStarted; + bool mRecursiveLibraryFetchStarted; + bool mAllRecursiveFoldersFetched; + typedef boost::signals2::signal folders_fetched_signal_t; + folders_fetched_signal_t mFoldersFetchedSignal; - BOOL mBackgroundFetchActive; + bool mBackgroundFetchActive; bool mFolderFetchActive; S32 mFetchCount; + S32 mLastFetchCount; // for debug + S32 mFetchFolderCount; LLFrameTimer mFetchTimer; F32 mMinTimeBetweenFetches; - - struct FetchQueueInfo - { - FetchQueueInfo(const LLUUID& id, BOOL recursive, bool is_category = true) - : mUUID(id), - mIsCategory(is_category), - mRecursive(recursive) - {} - - LLUUID mUUID; - bool mIsCategory; - BOOL mRecursive; - }; - typedef std::deque fetch_queue_t; - fetch_queue_t mFetchQueue; + fetch_queue_t mFetchFolderQueue; + fetch_queue_t mFetchItemQueue; + std::list mExpectedFolderIds; // for debug, should this track time? }; #endif // LL_LLINVENTORYMODELBACKGROUNDFETCH_H diff --git a/indra/newview/llinventoryobserver.cpp b/indra/newview/llinventoryobserver.cpp index 26d7a7a28a..281a8bc789 100644 --- a/indra/newview/llinventoryobserver.cpp +++ b/indra/newview/llinventoryobserver.cpp @@ -37,8 +37,10 @@ #include "llagent.h" #include "llagentwearables.h" +#include "llaisapi.h" #include "llfloater.h" #include "llfocusmgr.h" +#include "llinventorymodelbackgroundfetch.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -56,6 +58,7 @@ #include "llsdutil.h" #include +const S32 LLInventoryFetchItemsObserver::MAX_INDIVIDUAL_ITEM_REQUESTS = 7; const F32 LLInventoryFetchItemsObserver::FETCH_TIMER_EXPIRY = 60.0f; @@ -149,7 +152,7 @@ LLInventoryFetchItemsObserver::LLInventoryFetchItemsObserver(const uuid_vec_t& i void LLInventoryFetchItemsObserver::changed(U32 mask) { - LL_DEBUGS() << this << " remaining incomplete " << mIncomplete.size() + LL_DEBUGS("InventoryFetch") << this << " remaining incomplete " << mIncomplete.size() << " complete " << mComplete.size() << " wait period " << mFetchingPeriod.getRemainingTimeF32() << LL_ENDL; @@ -158,6 +161,15 @@ void LLInventoryFetchItemsObserver::changed(U32 mask) // appropriate. if (!mIncomplete.empty()) { + if (!LLInventoryModelBackgroundFetch::getInstance()->isEverythingFetched()) + { + // Folders have a priority over items and they download items as well + // Wait untill initial folder fetch is done + LL_DEBUGS("InventoryFetch") << "Folder fetch in progress, resetting fetch timer" << LL_ENDL; + + mFetchingPeriod.reset(); + mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY); + } // Have we exceeded max wait time? bool timeout_expired = mFetchingPeriod.hasExpired(); @@ -176,7 +188,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask) if (timeout_expired) { // Just concede that this item hasn't arrived in reasonable time and continue on. - LL_WARNS() << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL; + LL_WARNS("InventoryFetch") << "Fetcher timed out when fetching inventory item UUID: " << item_id << LL_ENDL; it = mIncomplete.erase(it); } else @@ -191,7 +203,7 @@ void LLInventoryFetchItemsObserver::changed(U32 mask) if (mIncomplete.empty()) { - LL_DEBUGS() << this << " done at remaining incomplete " + LL_DEBUGS("InventoryFetch") << this << " done at remaining incomplete " << mIncomplete.size() << " complete " << mComplete.size() << LL_ENDL; done(); } @@ -251,29 +263,21 @@ void fetch_items_from_llsd(const LLSD& items_llsd) void LLInventoryFetchItemsObserver::startFetch() { - LLUUID owner_id; + bool aisv3 = AISAPI::isAvailable(); + LLSD items_llsd; + + typedef std::map requests_by_folders_t; + requests_by_folders_t requests; for (uuid_vec_t::const_iterator it = mIDs.begin(); it < mIDs.end(); ++it) { - LLViewerInventoryItem* item = gInventory.getItem(*it); - if (item) - { - if (item->isFinished()) - { - // It's complete, so put it on the complete container. - mComplete.push_back(*it); - continue; - } - else - { - owner_id = item->getPermissions().getOwner(); - } - } - else - { - // assume it's agent inventory. - owner_id = gAgent.getID(); - } + LLViewerInventoryItem* item = gInventory.getItem(*it); + if (item && item->isFinished()) + { + // It's complete, so put it on the complete container. + mComplete.push_back(*it); + continue; + } // Ignore categories since they're not items. We // could also just add this to mComplete but not sure what the @@ -294,17 +298,98 @@ void LLInventoryFetchItemsObserver::startFetch() // pack this on the message. mIncomplete.push_back(*it); - // Prepare the data to fetch - LLSD item_entry; - item_entry["owner_id"] = owner_id; - item_entry["item_id"] = (*it); - items_llsd.append(item_entry); + if (aisv3) + { + if (item) + { + LLUUID parent_id = item->getParentUUID(); + requests[parent_id].push_back(*it); + } + else + { + // Can happen for gestures and calling cards if server notified us before they fetched + // Request by id without checking for an item. + LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(*it); + } + } + else + { + // Prepare the data to fetch + LLSD item_entry; + if (item) + { + item_entry["owner_id"] = item->getPermissions().getOwner(); + } + else + { + // assume it's agent inventory. + item_entry["owner_id"] = gAgent.getID(); + } + item_entry["item_id"] = (*it); + items_llsd.append(item_entry); + } } mFetchingPeriod.reset(); mFetchingPeriod.setTimerExpirySec(FETCH_TIMER_EXPIRY); - fetch_items_from_llsd(items_llsd); + if (aisv3) + { + for (requests_by_folders_t::value_type &folder : requests) + { + if (folder.second.size() > MAX_INDIVIDUAL_ITEM_REQUESTS) + { + // requesting one by one will take a while + // do whole folder + LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true); + } + else + { + LLViewerInventoryCategory* cat = gInventory.getCategory(folder.first); + if (cat) + { + if (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + // start fetching whole folder since it's not ready either way + cat->fetch(); + } + else if (cat->getViewerDescendentCount() <= folder.second.size() + || cat->getDescendentCount() <= folder.second.size()) + { + // Start fetching whole folder since we need all items + LLInventoryModelBackgroundFetch::getInstance()->scheduleFolderFetch(folder.first, true); + + } + else + { + // get items one by one + for (LLUUID &item_id : folder.second) + { + LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id); + } + } + } + else + { + // Isn't supposed to happen? We should have all folders + // and if item exists, folder is supposed to exist as well. + llassert(false); + LL_WARNS("Inventory") << "Missing folder: " << folder.first << " fetching items individually" << LL_ENDL; + + // get items one by one + for (LLUUID &item_id : folder.second) + { + LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(item_id); + } + } + } + } + } + else + { + fetch_items_from_llsd(items_llsd); + } + } LLInventoryFetchDescendentsObserver::LLInventoryFetchDescendentsObserver(const LLUUID& cat_id) : @@ -640,7 +725,7 @@ void LLInventoryCategoriesObserver::changed(U32 mask) // computed, or (b) a name has changed. if (!cat_data.mIsNameHashInitialized || (mask & LLInventoryObserver::LABEL)) { - LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cat_id); + digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id); if (cat_data.mItemNameHash != item_name_hash) { cat_data.mIsNameHashInitialized = true; @@ -649,6 +734,13 @@ void LLInventoryCategoriesObserver::changed(U32 mask) } } + const LLUUID thumbnail_id = category->getThumbnailUUID(); + if (cat_data.mThumbnailId != thumbnail_id) + { + cat_data.mThumbnailId = thumbnail_id; + cat_changed = true; + } + // If anything has changed above, fire the callback. if (cat_changed) cat_data.mCallback(); @@ -666,6 +758,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t S32 version = LLViewerInventoryCategory::VERSION_UNKNOWN; S32 current_num_known_descendents = LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN; bool can_be_added = true; + LLUUID thumbnail_id; LLViewerInventoryCategory* category = gInventory.getCategory(cat_id); // If category could not be retrieved it might mean that @@ -677,6 +770,7 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t // Inventory category version is used to find out if some changes // to a category have been made. version = category->getVersion(); + thumbnail_id = category->getThumbnailUUID(); LLInventoryModel::cat_array_t* cats; LLInventoryModel::item_array_t* items; @@ -701,12 +795,12 @@ bool LLInventoryCategoriesObserver::addCategory(const LLUUID& cat_id, callback_t { if(init_name_hash) { - LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cat_id); - mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents,item_name_hash))); + digest_t item_name_hash = gInventory.hashDirectDescendentNames(cat_id); + mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents,item_name_hash))); } else { - mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, cb, version, current_num_known_descendents))); + mCategoryMap.insert(category_map_value_t(cat_id,LLCategoryData(cat_id, thumbnail_id, cb, version, current_num_known_descendents))); } } @@ -719,24 +813,25 @@ void LLInventoryCategoriesObserver::removeCategory(const LLUUID& cat_id) } LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( - const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents) + const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents) : mCatID(cat_id) , mCallback(cb) , mVersion(version) , mDescendentsCount(num_descendents) + , mThumbnailId(thumbnail_id) , mIsNameHashInitialized(false) { - mItemNameHash.finalize(); } LLInventoryCategoriesObserver::LLCategoryData::LLCategoryData( - const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, LLMD5 name_hash) + const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash) : mCatID(cat_id) , mCallback(cb) , mVersion(version) , mDescendentsCount(num_descendents) + , mThumbnailId(thumbnail_id) , mIsNameHashInitialized(true) , mItemNameHash(name_hash) { diff --git a/indra/newview/llinventoryobserver.h b/indra/newview/llinventoryobserver.h index 36d8ee3f59..bec08d2cdf 100644 --- a/indra/newview/llinventoryobserver.h +++ b/indra/newview/llinventoryobserver.h @@ -28,7 +28,6 @@ #define LL_LLINVENTORYOBSERVERS_H #include "lluuid.h" -#include "llmd5.h" #include #include @@ -105,6 +104,9 @@ public: /*virtual*/ void startFetch(); /*virtual*/ void changed(U32 mask); + + // For attempts to group requests if too many items are requested + static const S32 MAX_INDIVIDUAL_ITEM_REQUESTS; private: LLTimer mFetchingPeriod; @@ -126,7 +128,7 @@ public: LLInventoryFetchDescendentsObserver(const LLUUID& cat_id = LLUUID::null); LLInventoryFetchDescendentsObserver(const uuid_vec_t& cat_ids); - /*virtual*/ void startFetch(); + virtual void startFetch(); /*virtual*/ void changed(U32 mask); protected: BOOL isCategoryComplete(const LLViewerInventoryCategory* cat) const; @@ -271,16 +273,18 @@ public: void removeCategory(const LLUUID& cat_id); protected: + typedef LLUUID digest_t; // To clarify the actual usage of this "UUID" struct LLCategoryData { - LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents); - LLCategoryData(const LLUUID& cat_id, callback_t cb, S32 version, S32 num_descendents, LLMD5 name_hash); + LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents); + LLCategoryData(const LLUUID& cat_id, const LLUUID& thumbnail_id, callback_t cb, S32 version, S32 num_descendents, const digest_t& name_hash); callback_t mCallback; S32 mVersion; S32 mDescendentsCount; - LLMD5 mItemNameHash; + digest_t mItemNameHash; bool mIsNameHashInitialized; LLUUID mCatID; + LLUUID mThumbnailId; }; typedef std::map category_map_t; diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 2c9ed26a39..154d8e2e18 100644 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -40,6 +40,7 @@ #include "llfolderviewitem.h" #include "llfloaterimcontainer.h" #include "llimview.h" +#include "llinspecttexture.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" #include "llinventorymodelbackgroundfetch.h" @@ -160,13 +161,15 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mInvFVBridgeBuilder(NULL), mInventoryViewModel(p.name), mGroupedItemBridge(new LLFolderViewGroupedItemBridge), - mFocusSelection(false) + mFocusSelection(false), + mBuildChildrenViews(true), + mRootInited(false) { mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; if (!sColorSetInitialized) { - sDefaultColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); + sDefaultColor = LLUIColorTable::instance().getColor("InventoryItemColor", DEFAULT_WHITE); sDefaultHighlightColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); sLibraryColor = LLUIColorTable::instance().getColor("InventoryItemLibraryColor", DEFAULT_WHITE); sLinkColor = LLUIColorTable::instance().getColor("InventoryItemLinkColor", DEFAULT_WHITE); @@ -182,6 +185,7 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) : mCommitCallbackRegistrar.add("Inventory.BeginIMSession", boost::bind(&LLInventoryPanel::beginIMSession, this)); mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); mCommitCallbackRegistrar.add("Inventory.FileUploadLocation", boost::bind(&LLInventoryPanel::fileUploadLocation, this, _2)); + mCommitCallbackRegistrar.add("Inventory.OpenNewFolderWindow", boost::bind(&LLInventoryPanel::openSingleViewInventory, this, LLUUID())); } LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id ) @@ -248,94 +252,9 @@ void LLInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { // save off copy of params mParams = params; - // Clear up the root view - // Note: This needs to be done *before* we build the new folder view - LLUUID root_id = getRootFolderID(); - if (mFolderRoot.get()) - { - removeItemID(root_id); - mFolderRoot.get()->destroyView(); - } - mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves - { - // Determine the root folder in case specified, and - // build the views starting with that folder. - LLFolderView* folder_view = createFolderRoot(root_id); - mFolderRoot = folder_view->getHandle(); - - addItemID(root_id, mFolderRoot.get()); - } - mCommitCallbackRegistrar.popScope(); - mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar); - mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar); - - // Scroller - LLRect scroller_view_rect = getRect(); - scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); - LLScrollContainer::Params scroller_params(mParams.scroll()); - scroller_params.rect(scroller_view_rect); - mScroller = LLUICtrlFactory::create(scroller_params); - addChild(mScroller); - mScroller->addChild(mFolderRoot.get()); - mFolderRoot.get()->setScrollContainer(mScroller); - mFolderRoot.get()->setFollowsAll(); - mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox); + initFolderRoot(); - // Set up the callbacks from the inventory we're viewing, and then build everything. - mInventoryObserver = new LLInventoryPanelObserver(this); - mInventory->addObserver(mInventoryObserver); - - mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); - mInventory->addObserver(mCompletionObserver); - - if (mBuildViewsOnInit && mViewsInitialized == VIEWS_UNINITIALIZED) - { - // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle. - // Initializing views takes a while so always do it onIdle if viewer already loaded. - if (mInventory->isInventoryUsable() - && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) - { - // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect - const F64 max_time = 20.f; - initializeViews(max_time); - } - else - { - mViewsInitialized = VIEWS_INITIALIZING; - gIdleCallbacks.addFunction(onIdle, (void*)this); - } - } - - if (mSortOrderSetting != INHERIT_SORT_ORDER) - { - setSortOrder(gSavedSettings.getU32(mSortOrderSetting)); - } - else - { - setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); - } - - // hide inbox - if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible")) - { - getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); - } - // hide marketplace listing box, unless we are a marketplace panel - if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders) - { - getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); - } - - // set the filter for the empty folder if the debug setting is on - if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) - { - getFilter().setFilterEmptySystemFolders(); - } - - // keep track of the clipboard state so that we avoid filtering too much - mClipboardState = LLClipboard::instance().getGeneration(); - // Initialize base class params. LLPanel::initFromParams(mParams); } @@ -351,13 +270,122 @@ LLInventoryPanel::~LLInventoryPanel() clearFolderRoot(); } +void LLInventoryPanel::initFolderRoot() +{ + // Clear up the root view + // Note: This needs to be done *before* we build the new folder view + LLUUID root_id = getRootFolderID(); + if (mFolderRoot.get()) + { + removeItemID(root_id); + mFolderRoot.get()->destroyView(); + } + + mCommitCallbackRegistrar.pushScope(); // registered as a widget; need to push callback scope ourselves + { + // Determine the root folder in case specified, and + // build the views starting with that folder. + LLFolderView* folder_view = createFolderRoot(root_id); + mFolderRoot = folder_view->getHandle(); + mRootInited = true; + + addItemID(root_id, mFolderRoot.get()); + } + mCommitCallbackRegistrar.popScope(); + mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar); + mFolderRoot.get()->setEnableRegistrar(&mEnableCallbackRegistrar); + + // Scroller + LLRect scroller_view_rect = getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(mParams.scroll()); + scroller_params.rect(scroller_view_rect); + mScroller = LLUICtrlFactory::create(scroller_params); + addChild(mScroller); + mScroller->addChild(mFolderRoot.get()); + mFolderRoot.get()->setScrollContainer(mScroller); + mFolderRoot.get()->setFollowsAll(); + mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox); + + if (mSelectionCallback) + { + mFolderRoot.get()->setSelectCallback(mSelectionCallback); + } + + // Set up the callbacks from the inventory we're viewing, and then build everything. + mInventoryObserver = new LLInventoryPanelObserver(this); + mInventory->addObserver(mInventoryObserver); + + mCompletionObserver = new LLInvPanelComplObserver(boost::bind(&LLInventoryPanel::onItemsCompletion, this)); + mInventory->addObserver(mCompletionObserver); + + if (mBuildViewsOnInit) + { + initializeViewBuilding(); + } + + if (mSortOrderSetting != INHERIT_SORT_ORDER) + { + setSortOrder(gSavedSettings.getU32(mSortOrderSetting)); + } + else + { + setSortOrder(gSavedSettings.getU32(DEFAULT_SORT_ORDER)); + } + + // hide inbox + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible")) + { + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_INBOX)); + } + // hide marketplace listing box, unless we are a marketplace panel + if (!gSavedSettings.getBOOL("InventoryOutboxMakeVisible") && !mParams.use_marketplace_folders) + { + getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << LLFolderType::FT_MARKETPLACE_LISTINGS)); + } + + // set the filter for the empty folder if the debug setting is on + if (gSavedSettings.getBOOL("DebugHideEmptySystemFolders")) + { + getFilter().setFilterEmptySystemFolders(); + } + + // keep track of the clipboard state so that we avoid filtering too much + mClipboardState = LLClipboard::instance().getGeneration(); +} + +void LLInventoryPanel::initializeViewBuilding() +{ + if (mViewsInitialized == VIEWS_UNINITIALIZED) + { + LL_DEBUGS("Inventory") << "Setting views for " << getName() << " to initialize" << LL_ENDL; + // Build view of inventory if we need default full hierarchy and inventory is ready, otherwise do in onIdle. + // Initializing views takes a while so always do it onIdle if viewer already loaded. + if (mInventory->isInventoryUsable() + && LLStartUp::getStartupState() <= STATE_WEARABLES_WAIT) + { + // Usually this happens on login, so we have less time constraits, but too long and we can cause a disconnect + const F64 max_time = 20.f; + initializeViews(max_time); + } + else + { + mViewsInitialized = VIEWS_INITIALIZING; + gIdleCallbacks.addFunction(onIdle, (void*)this); + } + } +} + /*virtual*/ void LLInventoryPanel::onVisibilityChange(BOOL new_visibility) { if (new_visibility && mViewsInitialized == VIEWS_UNINITIALIZED) { - mViewsInitialized = VIEWS_INITIALIZING; - gIdleCallbacks.addFunction(onIdle, (void*)this); + // first call can be from tab initialization + if (gFloaterView->getParentFloater(this) != NULL) + { + initializeViewBuilding(); + } } LLPanel::onVisibilityChange(new_visibility); } @@ -743,7 +771,7 @@ LLUUID LLInventoryPanel::getRootFolderID() LLStringExplicit label(mParams.start_folder.name()); setLabel(label); - root_id = gInventory.findCategoryUUIDForType(preferred_type, false); + root_id = gInventory.findCategoryUUIDForType(preferred_type); if (root_id.isNull()) { LL_WARNS() << "Could not find folder of type " << preferred_type << LL_ENDL; @@ -878,6 +906,7 @@ void LLInventoryPanel::idle(void* user_data) void LLInventoryPanel::initializeViews(F64 max_time) { if (!gInventory.isInventoryUsable()) return; + if (!mRootInited) return; mViewsInitialized = VIEWS_BUILDING; @@ -905,7 +934,10 @@ void LLInventoryPanel::initializeViews(F64 max_time) gIdleCallbacks.addFunction(idle, this); - openStartFolderOrMyInventory(); + if(mParams.open_first_folder) + { + openStartFolderOrMyInventory(); + } // Special case for new user login if (gAgent.isFirstLogin()) @@ -937,8 +969,8 @@ LLFolderViewFolder * LLInventoryPanel::createFolderViewFolder(LLInvFVBridge * br params.tool_tip = params.name; params.allow_drop = allow_drop; - params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor)); - params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor)); + params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor); + params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor); return LLUICtrlFactory::create(params); } @@ -954,8 +986,8 @@ LLFolderViewItem * LLInventoryPanel::createFolderViewItem(LLInvFVBridge * bridge params.rect = LLRect (0, 0, 0, 0); params.tool_tip = params.name; - params.font_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultColor)); - params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : (bridge->isLink() ? sLinkColor : sDefaultHighlightColor)); + params.font_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultColor); + params.font_highlight_color = (bridge->isLibraryItem() ? sLibraryColor : sDefaultHighlightColor); return LLUICtrlFactory::create(params); } @@ -1011,8 +1043,11 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, LLInventoryObject const* objectp, LLFolderViewItem *folder_view_item, LLFolderViewFolder *parent_folder, - const EBuildModes &mode) + const EBuildModes &mode, + S32 depth) { + depth++; + // Force the creation of an extra root level folder item if required by the inventory panel (default is "false") bool allow_drop = true; bool create_root = false; @@ -1042,7 +1077,7 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, if (objectp->getType() >= LLAssetType::AT_COUNT) { // Example: Happens when we add assets of new, not yet supported type to library - LL_DEBUGS() << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : " + LL_DEBUGS("Inventory") << "LLInventoryPanel::buildViewsTree called with unknown objectp->mType : " << ((S32) objectp->getType()) << " name " << objectp->getName() << " UUID " << objectp->getUUID() << LL_ENDL; @@ -1112,7 +1147,8 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, } } - bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY; + bool create_children = folder_view_item && objectp->getType() == LLAssetType::AT_CATEGORY + && (mBuildChildrenViews || depth == 0); if (create_children) { @@ -1132,12 +1168,15 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, { create_children = false; // run it again for the sake of creating children - mBuildViewsQueue.push_back(id); + if (mBuildChildrenViews || depth == 0) + { + mBuildViewsQueue.push_back(id); + } } else { create_children = true; - folder_view_item->setChildrenInited(true); + folder_view_item->setChildrenInited(mBuildChildrenViews); } break; } @@ -1145,7 +1184,10 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, { create_children = false; // run it to create children, current caller is only interested in current view - mBuildViewsQueue.push_back(id); + if (mBuildChildrenViews || depth == 0) + { + mBuildViewsQueue.push_back(id); + } break; } case BUILD_ONE_FOLDER: @@ -1175,7 +1217,13 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, LLViewerInventoryItem::item_array_t* items; mInventory->lockDirectDescendentArrays(id, categories, items); + // Make sure panel won't lock in a loop over existing items if + // folder is enormous and at least some work gets done + const S32 MIN_ITEMS_PER_CALL = 500; + const S32 starting_item_count = mItemMap.size(); + LLFolderViewFolder *parentp = dynamic_cast(folder_view_item); + bool done = true; if(categories) { @@ -1193,11 +1241,28 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, // each time, especially since content is growing, we can just // iter over copy of mItemMap in some way LLFolderViewItem* view_itemp = getItemByID(cat->getUUID()); - buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode)); + buildViewsTree(cat->getUUID(), id, cat, view_itemp, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth); } else { - buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode)); + buildViewsTree(cat->getUUID(), id, cat, NULL, parentp, (mode == BUILD_ONE_FOLDER ? BUILD_NO_CHILDREN : mode), depth); + } + } + + if (!mBuildChildrenViews + && mode == BUILD_TIMELIMIT + && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size()) + { + // Single folder view, check if we still have time + // + // Todo: make sure this causes no dupplciates, breaks nothing, + // especially filters and arrange + F64 curent_time = LLTimer::getTotalSeconds(); + if (mBuildViewsEndTime < curent_time) + { + mBuildViewsQueue.push_back(id); + done = false; + break; } } } @@ -1217,10 +1282,33 @@ LLFolderViewItem* LLInventoryPanel::buildViewsTree(const LLUUID& id, // each time, especially since content is growing, we can just // iter over copy of mItemMap in some way LLFolderViewItem* view_itemp = getItemByID(item->getUUID()); - buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode); + buildViewsTree(item->getUUID(), id, item, view_itemp, parentp, mode, depth); + } + + if (!mBuildChildrenViews + && mode == BUILD_TIMELIMIT + && MIN_ITEMS_PER_CALL + starting_item_count < mItemMap.size()) + { + // Single folder view, check if we still have time + // + // Todo: make sure this causes no dupplciates, breaks nothing, + // especially filters and arrange + F64 curent_time = LLTimer::getTotalSeconds(); + if (mBuildViewsEndTime < curent_time) + { + mBuildViewsQueue.push_back(id); + done = false; + break; + } } } } + + if (!mBuildChildrenViews && done) + { + // flat list is done initializing folder + folder_view_item->setChildrenInited(true); + } mInventory->unlockDirectDescendentArrays(id); } @@ -1268,8 +1356,10 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = LLView::handleHover(x, y, mask); if(handled) - { - ECursorType cursor = getWindow()->getCursor(); + { + // getCursor gets current cursor, setCursor sets next cursor + // check that children didn't set own 'next' cursor + ECursorType cursor = getWindow()->getNextCursor(); if (LLInventoryModelBackgroundFetch::instance().folderFetchActive() && cursor == UI_CURSOR_ARROW) { // replace arrow cursor with arrow and hourglass cursor @@ -1283,6 +1373,37 @@ BOOL LLInventoryPanel::handleHover(S32 x, S32 y, MASK mask) return TRUE; } +BOOL LLInventoryPanel::handleToolTip(S32 x, S32 y, MASK mask) +{ + if (const LLFolderViewItem* hover_item_p = (!mFolderRoot.isDead()) ? mFolderRoot.get()->getHoveredItem() : nullptr) + { + if (const LLFolderViewModelItemInventory* vm_item_p = static_cast(hover_item_p->getViewModelItem())) + { + LLSD params; + params["inv_type"] = vm_item_p->getInventoryType(); + params["thumbnail_id"] = vm_item_p->getThumbnailUUID(); + params["item_id"] = vm_item_p->getUUID(); + + // tooltip should only show over folder, but screen + // rect includes items under folder as well + LLRect actionable_rect = hover_item_p->calcScreenRect(); + if (hover_item_p->isOpen() && hover_item_p->hasVisibleChildren()) + { + actionable_rect.mBottom = actionable_rect.mTop - hover_item_p->getItemHeight(); + } + + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(hover_item_p->getToolTip()) + .sticky_rect(actionable_rect) + .delay_time(LLView::getTooltipTimeout()) + .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) + .create_params(params)); + return TRUE; + } + } + return LLPanel::handleToolTip(x, y, mask); +} + BOOL LLInventoryPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, @@ -1378,6 +1499,7 @@ void LLInventoryPanel::setSelectCallback(const boost::functionsetSelectCallback(cb); } + mSelectionCallback = cb; } void LLInventoryPanel::clearSelection() @@ -1424,6 +1546,10 @@ void LLInventoryPanel::onSelectionChange(const std::deque& it { fv->startRenamingSelectedItem(); } + else + { + LL_DEBUGS("Inventory") << "Failed to start renemr, no items selected" << LL_ENDL; + } } std::set selected_items = mFolderRoot.get()->getSelectionList(); @@ -1623,6 +1749,11 @@ void LLInventoryPanel::fileUploadLocation(const LLSD& userdata) } } +void LLInventoryPanel::openSingleViewInventory(LLUUID folder_id) +{ + LLPanelMainInventory::newFolderWindow(folder_id.isNull() ? LLFolderBridge::sSelf.get()->getUUID() : folder_id); +} + void LLInventoryPanel::purgeSelectedItems() { if (!mFolderRoot.get()) return; @@ -1756,22 +1887,49 @@ LLInventoryPanel* LLInventoryPanel::getActiveInventoryPanel(BOOL auto_open) } //static -void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL main_panel, BOOL take_keyboard_focus, BOOL reset_filter) +void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, BOOL use_main_panel, BOOL take_keyboard_focus, BOOL reset_filter) { LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); sidepanel_inventory->showInventoryPanel(); bool in_inbox = (gInventory.isObjectDescendentOf(obj_id, gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX))); - if (!in_inbox && (main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected())) + if (!in_inbox && (use_main_panel || !sidepanel_inventory->getMainInventoryPanel()->isRecentItemsPanelSelected())) { sidepanel_inventory->selectAllItemsPanel(); } + + LLFloater* inventory_floater = LLFloaterSidePanelContainer::getTopmostInventoryFloater(); + if(!auto_open && inventory_floater && inventory_floater->getVisible()) + { + LLSidepanelInventory *inventory_panel = inventory_floater->findChild("main_panel"); + LLPanelMainInventory* main_panel = inventory_panel->getMainInventoryPanel(); + if(main_panel->isSingleFolderMode() && main_panel->isGalleryViewMode()) + { + LL_DEBUGS("Inventory") << "Opening gallery panel for item" << obj_id << LL_ENDL; + main_panel->setGallerySelection(obj_id); + return; + } + } + + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory && main_inventory->isSingleFolderMode() + && use_main_panel) + { + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (obj) + { + LL_DEBUGS("Inventory") << "Opening main inventory panel for item" << obj_id << LL_ENDL; + main_inventory->setSingleFolderViewRoot(obj->getParentUUID(), false); + main_inventory->setGallerySelection(obj_id); + return; + } + } LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(auto_open); if (active_panel) { - LL_DEBUGS("Messaging") << "Highlighting" << obj_id << LL_ENDL; + LL_DEBUGS("Messaging", "Inventory") << "Highlighting" << obj_id << LL_ENDL; if (reset_filter) { @@ -1790,7 +1948,7 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L inventory_panel->setSelection(obj_id, take_keyboard_focus); } } - else + else if (auto_open) { LLFloater* floater_inventory = LLFloaterReg::getInstance("inventory"); if (floater_inventory) @@ -1799,9 +1957,33 @@ void LLInventoryPanel::openInventoryPanelAndSetSelection(BOOL auto_open, const L } active_panel->setSelection(obj_id, take_keyboard_focus); } + else + { + // Created items are going to receive proper focus from callbacks + active_panel->setSelection(obj_id, take_keyboard_focus); + } } } +void LLInventoryPanel::setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id) +{ + + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end(); ++iter) + { + LLFloaterSidePanelContainer* inventory_floater = dynamic_cast(*iter); + LLSidepanelInventory* sidepanel_inventory = inventory_floater->findChild("main_panel"); + + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory && panel->hasAncestor(main_inventory) && !main_inventory->isSingleFolderMode()) + { + main_inventory->initSingleFolderRoot(folder_id); + main_inventory->toggleViewMode(); + main_inventory->setSingleFolderViewRoot(folder_id, false); + } + } +} + void LLInventoryPanel::addHideFolderType(LLFolderType::EType folder_type) { getFilter().setFilterCategoryTypes(getFilter().getFilterCategoryTypes() & ~(1ULL << folder_type)); @@ -2010,10 +2192,251 @@ LLInventoryRecentItemsPanel::LLInventoryRecentItemsPanel( const Params& params) mInvFVBridgeBuilder = &RECENT_ITEMS_BUILDER; } +static LLDefaultChildRegistry::Register t_single_folder_inventory_panel("single_folder_inventory_panel"); + +LLInventorySingleFolderPanel::LLInventorySingleFolderPanel(const Params& params) + : LLInventoryPanel(params) +{ + mBuildChildrenViews = false; + getFilter().setSingleFolderMode(true); + getFilter().setEmptyLookupMessage("InventorySingleFolderNoMatches"); + getFilter().setDefaultEmptyLookupMessage("InventorySingleFolderEmpty"); + + mCommitCallbackRegistrar.replace("Inventory.DoToSelected", boost::bind(&LLInventorySingleFolderPanel::doToSelected, this, _2)); + mCommitCallbackRegistrar.replace("Inventory.DoCreate", boost::bind(&LLInventorySingleFolderPanel::doCreate, this, _2)); + mCommitCallbackRegistrar.replace("Inventory.Share", boost::bind(&LLInventorySingleFolderPanel::doShare, this)); +} + +LLInventorySingleFolderPanel::~LLInventorySingleFolderPanel() +{ +} + +void LLInventorySingleFolderPanel::initFromParams(const Params& p) +{ + mFolderID = gInventory.getRootFolderID(); + + mParams = p; + LLPanel::initFromParams(mParams); +} + +void LLInventorySingleFolderPanel::onFocusReceived() +{ + // Tab support, when tabbing into this view, select first item + // (ideally needs to account for scroll) + bool select_first = mSelectThisID.isNull() && mFolderRoot.get() && mFolderRoot.get()->getSelectedCount() == 0; + + if (select_first) + { + LLFolderViewFolder::folders_t::const_iterator folders_it = mFolderRoot.get()->getFoldersBegin(); + LLFolderViewFolder::folders_t::const_iterator folders_end = mFolderRoot.get()->getFoldersEnd(); + + for (; folders_it != folders_end; ++folders_it) + { + const LLFolderViewFolder* folder_view = *folders_it; + if (folder_view->getVisible()) + { + const LLFolderViewModelItemInventory* modelp = static_cast(folder_view->getViewModelItem()); + setSelectionByID(modelp->getUUID(), TRUE); + // quick and dirty fix: don't scroll on switching focus + // todo: better 'tab' support, one that would work for LLInventoryPanel + mFolderRoot.get()->stopAutoScollining(); + select_first = false; + break; + } + } + } + + if (select_first) + { + LLFolderViewFolder::items_t::const_iterator items_it = mFolderRoot.get()->getItemsBegin(); + LLFolderViewFolder::items_t::const_iterator items_end = mFolderRoot.get()->getItemsEnd(); + + for (; items_it != items_end; ++items_it) + { + const LLFolderViewItem* item_view = *items_it; + if (item_view->getVisible()) + { + const LLFolderViewModelItemInventory* modelp = static_cast(item_view->getViewModelItem()); + setSelectionByID(modelp->getUUID(), TRUE); + mFolderRoot.get()->stopAutoScollining(); + break; + } + } + } + LLInventoryPanel::onFocusReceived(); +} + +void LLInventorySingleFolderPanel::initFolderRoot(const LLUUID& start_folder_id) +{ + if(mRootInited) return; + + mRootInited = true; + if(start_folder_id.notNull()) + { + mFolderID = start_folder_id; + } + + mParams.open_first_folder = false; + mParams.start_folder.id = mFolderID; + + LLInventoryPanel::initFolderRoot(); + mFolderRoot.get()->setSingleFolderMode(true); +} + +void LLInventorySingleFolderPanel::changeFolderRoot(const LLUUID& new_id) +{ + if (mFolderID != new_id) + { + if(mFolderID.notNull()) + { + mBackwardFolders.push_back(mFolderID); + } + mFolderID = new_id; + updateSingleFolderRoot(); + } +} + +void LLInventorySingleFolderPanel::onForwardFolder() +{ + if(isForwardAvailable()) + { + mBackwardFolders.push_back(mFolderID); + mFolderID = mForwardFolders.back(); + mForwardFolders.pop_back(); + updateSingleFolderRoot(); + } +} + +void LLInventorySingleFolderPanel::onBackwardFolder() +{ + if(isBackwardAvailable()) + { + mForwardFolders.push_back(mFolderID); + mFolderID = mBackwardFolders.back(); + mBackwardFolders.pop_back(); + updateSingleFolderRoot(); + } +} + +void LLInventorySingleFolderPanel::clearNavigationHistory() +{ + mForwardFolders.clear(); + mBackwardFolders.clear(); +} + +bool LLInventorySingleFolderPanel::isBackwardAvailable() +{ + return (!mBackwardFolders.empty() && (mFolderID != mBackwardFolders.back())); +} + +bool LLInventorySingleFolderPanel::isForwardAvailable() +{ + return (!mForwardFolders.empty() && (mFolderID != mForwardFolders.back())); +} + +boost::signals2::connection LLInventorySingleFolderPanel::setRootChangedCallback(root_changed_callback_t cb) +{ + return mRootChangedSignal.connect(cb); +} + +void LLInventorySingleFolderPanel::updateSingleFolderRoot() +{ + if (mFolderID != getRootFolderID()) + { + mRootChangedSignal(); + + LLUUID root_id = mFolderID; + if (mFolderRoot.get()) + { + mItemMap.clear(); + mFolderRoot.get()->destroyRoot(); + } + + mCommitCallbackRegistrar.pushScope(); + { + LLFolderView* folder_view = createFolderRoot(root_id); + folder_view->setChildrenInited(false); + mFolderRoot = folder_view->getHandle(); + mFolderRoot.get()->setSingleFolderMode(true); + addItemID(root_id, mFolderRoot.get()); + + LLRect scroller_view_rect = getRect(); + scroller_view_rect.translate(-scroller_view_rect.mLeft, -scroller_view_rect.mBottom); + LLScrollContainer::Params scroller_params(mParams.scroll()); + scroller_params.rect(scroller_view_rect); + + if (mScroller) + { + removeChild(mScroller); + delete mScroller; + mScroller = NULL; + } + mScroller = LLUICtrlFactory::create(scroller_params); + addChild(mScroller); + mScroller->addChild(mFolderRoot.get()); + mFolderRoot.get()->setScrollContainer(mScroller); + mFolderRoot.get()->setFollowsAll(); + mFolderRoot.get()->addChild(mFolderRoot.get()->mStatusTextBox); + + if (!mSelectionCallback.empty()) + { + mFolderRoot.get()->setSelectCallback(mSelectionCallback); + } + } + mCommitCallbackRegistrar.popScope(); + mFolderRoot.get()->setCallbackRegistrar(&mCommitCallbackRegistrar); + + buildNewViews(mFolderID); + + LLFloater* root_floater = gFloaterView->getParentFloater(this); + if(root_floater) + { + root_floater->setFocus(true); + } + } +} + +bool LLInventorySingleFolderPanel::hasVisibleItems() +{ + return mFolderRoot.get()->hasVisibleChildren(); +} + +void LLInventorySingleFolderPanel::doCreate(const LLSD& userdata) +{ + std::string type_name = userdata.asString(); + LLUUID dest_id = LLFolderBridge::sSelf.get()->getUUID(); + if (("category" == type_name) || ("outfit" == type_name)) + { + changeFolderRoot(dest_id); + } + reset_inventory_filter(); + menu_create_inventory_item(this, dest_id, userdata); +} + +void LLInventorySingleFolderPanel::doToSelected(const LLSD& userdata) +{ + if (("open_in_current_window" == userdata.asString())) + { + changeFolderRoot(LLFolderBridge::sSelf.get()->getUUID()); + return; + } + LLInventoryPanel::doToSelected(userdata); +} + +void LLInventorySingleFolderPanel::doShare() +{ + LLAvatarActions::shareWithAvatars(this); +} /************************************************************************/ /* Asset Pre-Filtered Inventory Panel related class */ /************************************************************************/ +LLAssetFilteredInventoryPanel::LLAssetFilteredInventoryPanel(const Params& p) + : LLInventoryPanel(p) +{ +} + + void LLAssetFilteredInventoryPanel::initFromParams(const Params& p) { // Init asset types diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index 45e71539d1..b68433bab0 100644 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -107,6 +107,7 @@ public: Optional folder_view; Optional folder; Optional item; + Optional open_first_folder; // All item and folder views will be initialized on init if true (default) // Will initialize on visibility change otherwise. @@ -126,6 +127,7 @@ public: show_root_folder("show_root_folder", false), allow_drop_on_root("allow_drop_on_root", true), use_marketplace_folders("use_marketplace_folders", false), + open_first_folder("open_first_folder", true), scroll("scroll"), accepts_drag_and_drop("accepts_drag_and_drop"), folder_view("folder_view"), @@ -159,22 +161,23 @@ public: LLFolderViewModelInventory& getRootViewModel() { return mInventoryViewModel; } // LLView methods - /*virtual*/ void onVisibilityChange(BOOL new_visibility); - void draw(); - /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); - BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ void onVisibilityChange(BOOL new_visibility) override; + void draw() override; + /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ) override; + BOOL handleHover(S32 x, S32 y, MASK mask) override; /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, - std::string& tooltip_msg); + std::string& tooltip_msg) override; + BOOL handleToolTip(S32 x, S32 y, MASK mask) override; // LLUICtrl methods - /*virtual*/ void onFocusLost(); - /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost() override; + /*virtual*/ void onFocusReceived() override; void onFolderOpening(const LLUUID &id); // LLBadgeHolder methods - bool addBadge(LLBadge * badge); + bool addBadge(LLBadge * badge) override; // Call this method to set the selection. void openAllFolders(); @@ -211,6 +214,7 @@ public: LLUUID getRootFolderID(); LLScrollContainer* getScrollableContainer() { return mScroller; } bool getAllowDropOnRoot() { return mParams.allow_drop_on_root; } + bool areViewsInitialized() { return mViewsInitialized == VIEWS_INITIALIZED && mFolderRoot.get() && !mFolderRoot.get()->needsArrange(); } void onSelectionChange(const std::deque &items, BOOL user_action); @@ -221,6 +225,7 @@ public: void doCreate(const LLSD& userdata); bool beginIMSession(); void fileUploadLocation(const LLSD& userdata); + void openSingleViewInventory(LLUUID folder_id = LLUUID()); void purgeSelectedItems(); bool attachObject(const LLSD& userdata); static void idle(void* user_data); @@ -241,10 +246,10 @@ public: static void openInventoryPanelAndSetSelection(BOOL auto_open, const LLUUID& obj_id, - BOOL main_panel = FALSE, + BOOL use_main_panel = FALSE, BOOL take_keyboard_focus = TAKE_FOCUS_YES, BOOL reset_filter = FALSE); - + static void setSFViewAndOpenFolder(const LLInventoryPanel* panel, const LLUUID& folder_id); void addItemID(const LLUUID& id, LLFolderViewItem* itemp); void removeItemID(const LLUUID& id); LLFolderViewItem* getItemByID(const LLUUID& id); @@ -262,6 +267,10 @@ public: static void callbackPurgeSelectedItems(const LLSD& notification, const LLSD& response, const std::vector inventory_selected); + void changeFolderRoot(const LLUUID& new_id) {}; + void initFolderRoot(); + void initializeViewBuilding(); + protected: void openStartFolderOrMyInventory(); // open the first level of inventory void onItemsCompletion(); // called when selected items are complete @@ -298,6 +307,9 @@ protected: */ const LLInventoryFolderViewModelBuilder* mInvFVBridgeBuilder; + bool mBuildChildrenViews; // build root and children + bool mRootInited; + //-------------------------------------------------------------------- // Sorting @@ -357,6 +369,8 @@ protected: virtual LLFolderView * createFolderRoot(LLUUID root_id ); virtual LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop); virtual LLFolderViewItem* createFolderViewItem(LLInvFVBridge * bridge); + + boost::function& items, BOOL user_action)> mSelectionCallback; private: // buildViewsTree does not include some checks and is meant // for recursive use, use buildNewViews() for first call @@ -365,7 +379,8 @@ private: LLInventoryObject const* objectp, LLFolderViewItem *target_view, LLFolderViewFolder *parent_folder_view, - const EBuildModes &mode); + const EBuildModes &mode, + S32 depth = -1); typedef enum e_views_initialization_state { @@ -381,6 +396,57 @@ private: std::deque mBuildViewsQueue; }; + +class LLInventorySingleFolderPanel : public LLInventoryPanel +{ +public: + struct Params : public LLInitParam::Block + {}; + + void initFromParams(const Params& p); + void onFocusReceived() override; + + bool isSelectionRemovable() { return false; } + + void initFolderRoot(const LLUUID& start_folder_id = LLUUID::null); + + void changeFolderRoot(const LLUUID& new_id); + void onForwardFolder(); + void onBackwardFolder(); + void clearNavigationHistory(); + LLUUID getSingleFolderRoot() { return mFolderID; } + + void doCreate(const LLSD& userdata); + void doToSelected(const LLSD& userdata); + void doShare(); + + bool isBackwardAvailable(); + bool isForwardAvailable(); + + bool hasVisibleItems(); + + void setNavBackwardList(std::list backward_list) { mBackwardFolders = backward_list; } + void setNavForwardList(std::list forward_list) { mForwardFolders = forward_list; } + std::list getNavBackwardList() { return mBackwardFolders; } + std::list getNavForwardList() { return mForwardFolders; } + + typedef boost::function root_changed_callback_t; + boost::signals2::connection setRootChangedCallback(root_changed_callback_t cb); + +protected: + LLInventorySingleFolderPanel(const Params& params); + ~LLInventorySingleFolderPanel(); + void updateSingleFolderRoot(); + + friend class LLUICtrlFactory; + + LLUUID mFolderID; + std::list mBackwardFolders; + std::list mForwardFolders; + + boost::signals2::signal mRootChangedSignal; +}; + /************************************************************************/ /* Asset Pre-Filtered Inventory Panel related class */ /* Exchanges filter's flexibility for speed of generation and */ @@ -400,7 +466,7 @@ public: void initFromParams(const Params& p); protected: - LLAssetFilteredInventoryPanel(const Params& p) : LLInventoryPanel(p) {} + LLAssetFilteredInventoryPanel(const Params& p); friend class LLUICtrlFactory; public: ~LLAssetFilteredInventoryPanel() {} diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp index ba82ff0b0f..07825c1b0c 100644 --- a/indra/newview/lllogchat.cpp +++ b/indra/newview/lllogchat.cpp @@ -59,7 +59,7 @@ #include #include -const S32 LOG_RECALL_SIZE = 2048; +const S32 LOG_RECALL_SIZE = 20480; const std::string LL_IM_TIME("time"); const std::string LL_IM_DATE_TIME("datetime"); diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 22cedf450e..92e341ce93 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -87,6 +87,7 @@ void LLLoginHandler::parse(const LLSD& queryMap) bool LLLoginHandler::handle(const LLSD& tokens, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web) { // do nothing if we are already logged in diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index 1f2eacd094..2579341dbf 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -35,7 +35,7 @@ class LLLoginHandler : public LLCommandHandler public: // allow from external browsers LLLoginHandler() : LLCommandHandler("login", UNTRUSTED_ALLOW) { } - /*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web); + /*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web); // Fill in our internal fields from a SLURL like // secondlife:///app/login?first=Bob&last=Dobbs diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index dd8c9b2dde..01496fa7ce 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -87,6 +87,7 @@ LLLoginInstance::LLLoginInstance() : mLoginModule(new LLLogin()), mNotifications(NULL), mLoginState("offline"), + mSaveMFA(true), mAttemptComplete(false), mTransferRate(0.0f), mDispatcher("LLLoginInstance", "change") @@ -449,10 +450,7 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) gViewerWindow->setShowProgress(FALSE); } - LLSD args(llsd::map( "MESSAGE", LLTrans::getString(response["message_id"]) )); - LLSD payload; - LLNotificationsUtil::add("PromptMFAToken", args, payload, - boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); + showMFAChallange(LLTrans::getString(response["message_id"])); } else if( reason_response == "key" || reason_response == "presence" @@ -540,10 +538,7 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) { // SL-18511 this TOS failure happened while we are in the middle of an MFA challenge/response. // the previously entered token is very likely expired, so prompt again - LLSD args(llsd::map( "MESSAGE", LLTrans::getString("LoginFailedAuthenticationMFARequired") )); - LLSD payload; - LLNotificationsUtil::add("PromptMFAToken", args, payload, - boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); + showMFAChallange(LLTrans::getString("LoginFailedAuthenticationMFARequired")); } else { @@ -561,6 +556,22 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key) return true; } +void LLLoginInstance::showMFAChallange(const std::string& message) +{ + LLSD args(llsd::map("MESSAGE", message)); + LLSD payload; + if (gSavedSettings.getBOOL("RememberUser")) + { + LLNotificationsUtil::add("PromptMFATokenWithSave", args, payload, + boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); + } + else + { + LLNotificationsUtil::add("PromptMFAToken", args, payload, + boost::bind(&LLLoginInstance::handleMFAChallenge, this, _1, _2)); + } +} + bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & response) { bool continue_clicked = response["continue"].asBoolean(); @@ -576,6 +587,7 @@ bool LLLoginInstance::handleMFAChallenge(LLSD const & notif, LLSD const & respon // Set the request data to true and retry login. mRequestData["params"]["token"] = token; + mSaveMFA = response.has("ignore") ? response["ignore"].asBoolean() : false; reconnect(); } else { LL_INFOS("LLLogin") << "PromptMFAToken: no token, attemptComplete" << LL_ENDL; diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index ee3ef0e4b1..2e9aab7c00 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -56,6 +56,7 @@ public: bool authSuccess() { return mAttemptComplete && mLoginState == "online"; } const std::string& getLoginState() { return mLoginState; } + bool saveMFA() const { return mSaveMFA; } LLSD getResponse(const std::string& key) { return getResponse()[key]; } LLSD getResponse(); @@ -84,6 +85,7 @@ private: void syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response); bool handleTOSResponse(bool v, const std::string& key); + void showMFAChallange(const std::string& message); bool handleMFAChallenge(LLSD const & notif, LLSD const & response); void attemptComplete() { mAttemptComplete = true; } // In the future an event? @@ -95,6 +97,7 @@ private: LLSD mRequestData; LLSD mResponseData; bool mAttemptComplete; + bool mSaveMFA; F64 mTransferRate; std::string mSerialNumber; int mLastExecEvent; diff --git a/indra/newview/llmarketplacefunctions.cpp b/indra/newview/llmarketplacefunctions.cpp index 6f3d40bb3a..8784f403cb 100644 --- a/indra/newview/llmarketplacefunctions.cpp +++ b/indra/newview/llmarketplacefunctions.cpp @@ -700,10 +700,9 @@ void LLMarketplaceInventoryObserver::onIdleProcessQueue(void *userdata) // If it's a folder known to the marketplace, let's check it's in proper shape if (LLMarketplaceData::instance().isListed(*id_it) || LLMarketplaceData::instance().isVersionFolder(*id_it)) { - LLInventoryCategory* cat = (LLInventoryCategory*)(obj); // can trigger notifyObservers // can cause more structural changes - validate_marketplacelistings(cat); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(obj->getUUID()); } } else @@ -897,7 +896,7 @@ void LLMarketplaceData::setDataFetchedSignal(const status_updated_signal_t::slot // Get/Post/Put requests to the SLM Server using the SLM API void LLMarketplaceData::getSLMListings() { - const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplaceFolderId = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); setUpdating(marketplaceFolderId, true); LLCoros::instance().launch("getSLMListings", @@ -1804,7 +1803,7 @@ bool LLMarketplaceData::isUpdating(const LLUUID& folder_id, S32 depth) } else { - const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplace_listings_uuid = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); std::set::iterator it = mPendingUpdateSet.find(marketplace_listings_uuid); if (it != mPendingUpdateSet.end()) { @@ -1848,8 +1847,7 @@ void LLMarketplaceData::decrementValidationWaiting(const LLUUID& folder_id, S32 if (found->second <= 0) { mValidationWaitingList.erase(found); - LLInventoryCategory *cat = gInventory.getCategory(folder_id); - validate_marketplacelistings(cat); + LLMarketplaceValidator::getInstance()->validateMarketplaceListings(folder_id); update_marketplace_category(folder_id); gInventory.notifyObservers(); } diff --git a/indra/newview/llmaterialeditor.cpp b/indra/newview/llmaterialeditor.cpp index 609d8ea5d7..be6f8d72e5 100644 --- a/indra/newview/llmaterialeditor.cpp +++ b/indra/newview/llmaterialeditor.cpp @@ -37,6 +37,7 @@ #include "llfilesystem.h" #include "llgltfmateriallist.h" #include "llinventorymodel.h" +#include "llinventoryobserver.h" #include "lllocalgltfmaterials.h" #include "llnotificationsutil.h" #include "lltexturectrl.h" @@ -237,7 +238,9 @@ struct LLSelectedTEGetMatData : public LLSelectedTEFunctor LLUUID mTexEmissiveId; LLUUID mTexNormalId; LLUUID mObjectId; + LLViewerObject* mObject = nullptr; S32 mObjectTE; + LLUUID mMaterialId; LLPointer mMaterial; LLPointer mLocalMaterial; }; @@ -259,6 +262,7 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index) return false; } LLUUID mat_id = objectp->getRenderMaterialID(te_index); + mMaterialId = mat_id; bool can_use = mIsOverride ? objectp->permModify() : objectp->permCopy(); LLTextureEntry *tep = objectp->getTE(te_index); // We might want to disable this entirely if at least @@ -290,6 +294,7 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index) mTexEmissiveId = tex_emissive_id; mTexNormalId = tex_normal_id; mObjectTE = te_index; + mObject = objectp; mObjectId = objectp->getID(); mFirst = false; } @@ -318,6 +323,8 @@ bool LLSelectedTEGetMatData::apply(LLViewerObject* objectp, S32 te_index) LLGLTFMaterial *mat = tep->getGLTFMaterial(); LLLocalGLTFMaterial *local_mat = dynamic_cast(mat); + mObject = objectp; + mObjectId = objectp->getID(); if (local_mat) { mLocalMaterial = local_mat; @@ -405,9 +412,6 @@ BOOL LLMaterialEditor::postBuild() if (mIsOverride) { - // Material override change success callback - LLGLTFMaterialList::addSelectionUpdateCallback(&LLMaterialEditor::updateLive); - // Live editing needs a recovery mechanism on cancel mBaseColorTextureCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_BASE_COLOR_TEX_DIRTY)); mMetallicTextureCtrl->setOnCancelCallback(boost::bind(&LLMaterialEditor::onCancelCtrl, this, _1, _2, MATERIAL_METALLIC_ROUGHTNESS_TEX_DIRTY)); @@ -535,12 +539,6 @@ void LLMaterialEditor::draw() { if (mIsOverride) { - bool selection_empty = LLSelectMgr::getInstance()->getSelection()->isEmpty(); - if (selection_empty && mHasSelection) - { - mSelectionNeedsUpdate = true; - } - if (mSelectionNeedsUpdate) { mSelectionNeedsUpdate = false; @@ -1190,9 +1188,17 @@ bool LLMaterialEditor::saveIfNeeded() } else { - //make a new inventory item + // Make a new inventory item and set upload permissions + LLPermissions local_permissions; + local_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + + U32 everyone_perm = LLFloaterPerms::getEveryonePerms("Materials"); + U32 group_perm = LLFloaterPerms::getGroupPerms("Materials"); + U32 next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Materials"); + local_permissions.initMasks(PERM_ALL, PERM_ALL, everyone_perm, group_perm, next_owner_perm); + std::string res_desc = buildMaterialDescription(); - createInventoryItem(buffer, mMaterialName, res_desc); + createInventoryItem(buffer, mMaterialName, res_desc, local_permissions); // We do not update floater with uploaded asset yet, so just close it. closeFloater(); @@ -1286,36 +1292,37 @@ bool LLMaterialEditor::updateInventoryItem(const std::string &buffer, const LLUU return true; } -void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc) +// Callback intended for when a material is saved from an object and needs to +// be modified to reflect the new asset/name. +class LLObjectsMaterialItemCallback : public LLInventoryCallback { - // gen a new uuid for this asset - LLTransactionID tid; - tid.generate(); // timestamp-based randomization + uniquification - U32 next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Materials"); - LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL); - const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ? +public: + LLObjectsMaterialItemCallback(const LLPermissions& permissions, const std::string& asset_data, const std::string& new_name) + : mPermissions(permissions), + mAssetData(asset_data), + mNewName(new_name) + { + } - create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, name, desc, - LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, subtype, next_owner_perm, - new LLBoostFuncInventoryCallback([output = buffer](LLUUID const& inv_item_id) + void fire(const LLUUID& inv_item_id) override { LLViewerInventoryItem* item = gInventory.getItem(inv_item_id); - if (item) + if (!item) { - // create_inventory_item doesn't allow presetting some permissions, fix it now - LLPermissions perm = item->getPermissions(); - if (perm.getMaskEveryone() != LLFloaterPerms::getEveryonePerms("Materials") - || perm.getMaskGroup() != LLFloaterPerms::getGroupPerms("Materials")) - { - perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Materials")); - perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Materials")); + return; + } - item->setPermissions(perm); + // create_inventory_item/copy_inventory_item don't allow presetting some permissions, fix it now + item->setPermissions(mPermissions); + item->updateServer(FALSE); + gInventory.updateItem(item); + gInventory.notifyObservers(); - item->updateServer(FALSE); - gInventory.updateItem(item); - gInventory.notifyObservers(); - } + if (item->getName() != mNewName) + { + LLSD updates; + updates["name"] = mNewName; + update_inventory_item(inv_item_id, updates, NULL); } // from reference in LLSettingsVOBase::createInventoryItem()/updateInventoryItem() @@ -1323,13 +1330,11 @@ void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std: std::make_shared( inv_item_id, LLAssetType::AT_MATERIAL, - output, + mAssetData, [](LLUUID item_id, LLUUID new_asset_id, LLUUID new_item_id, LLSD response) { // done callback - LL_INFOS("Material") << "inventory item uploaded. item: " << item_id << " asset: " << new_asset_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL; - LLSD params = llsd::map("ASSET_ID", new_asset_id); - LLNotificationsUtil::add("MaterialCreated", params); + LL_INFOS("Material") << "inventory item uploaded. item: " << item_id << " new_item_id: " << new_item_id << " response: " << response << LL_ENDL; }, nullptr // failure callback, floater already closed ); @@ -1344,8 +1349,25 @@ void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std: } LLViewerAssetUpload::EnqueueInventoryUpload(agent_url, uploadInfo); } - }) - ); + } +private: + LLPermissions mPermissions; + std::string mAssetData; + std::string mNewName; +}; + +void LLMaterialEditor::createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions) +{ + // gen a new uuid for this asset + LLTransactionID tid; + tid.generate(); // timestamp-based randomization + uniquification + LLUUID parent = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_MATERIAL); + const U8 subtype = NO_INV_SUBTYPE; // TODO maybe use AT_SETTINGS and LLSettingsType::ST_MATERIAL ? + + LLPointer cb = new LLObjectsMaterialItemCallback(permissions, buffer, name); + create_inventory_item(gAgent.getID(), gAgent.getSessionID(), parent, tid, name, desc, + LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, subtype, permissions.getMaskNextOwner(), + cb); } void LLMaterialEditor::finishInventoryUpload(LLUUID itemId, LLUUID newAssetId, LLUUID newItemId) @@ -1508,7 +1530,7 @@ void LLMaterialEditor::onSaveAsMsgCallback(const LLSD& notification, const LLSD& } if (item) { - const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID &marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); LLUUID parent_id = item->getParentUUID(); if (mObjectUUID.notNull() || marketplacelistings_id == parent_id || gInventory.isObjectDescendentOf(item->getUUID(), gInventory.getLibraryRootFolderID())) { @@ -1759,22 +1781,6 @@ void LLMaterialEditor::updateLive() mOverrideInProgress = false; } -void LLMaterialEditor::updateLive(const LLUUID &object_id, S32 te) -{ - if (mOverrideObjectId != object_id - || mOverrideObjectTE != te) - { - // Ignore if waiting for override, - // if not waiting, mark selection dirty - mSelectionNeedsUpdate |= !mOverrideInProgress; - return; - } - - // update for currently displayed object and face - mSelectionNeedsUpdate = true; - mOverrideInProgress = false; -} - void LLMaterialEditor::loadLive() { LLMaterialEditor* me = (LLMaterialEditor*)LLFloaterReg::getInstance("live_material_editor"); @@ -1794,55 +1800,177 @@ void LLMaterialEditor::loadLive() } } -void LLMaterialEditor::saveObjectsMaterialAs() +// *NOTE: permissions_out includes user preferences for new item creation (LLFloaterPerms) +bool can_use_objects_material(LLSelectedTEGetMatData& func, const std::vector& ops, LLPermissions& permissions_out, LLViewerInventoryItem*& item_out) { - LLSelectedTEGetMatData func(false); - LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func, true /*first applicable*/); - saveMaterialAs(func.mMaterial, func.mLocalMaterial); -} -void LLMaterialEditor::savePickedMaterialAs() -{ - LLPickInfo pick = LLToolPie::getInstance()->getPick(); - if (pick.mPickType != LLPickInfo::PICK_OBJECT || !pick.getObject()) + if (!LLMaterialEditor::capabilitiesAvailable()) { - return; + return false; } - LLPointer render_material; - LLPointer local_material; + // func.mIsOverride=true is used for the singleton material editor floater + // associated with the build floater. This flag also excludes objects from + // the selection that do not satisfy PERM_MODIFY. + llassert(func.mIsOverride); + LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func, true /*first applicable*/); - LLViewerObject *objectp = pick.getObject(); - LLUUID mat_id = objectp->getRenderMaterialID(pick.mObjectFace); - if (mat_id.notNull() && objectp->permCopy()) + LLViewerObject* selected_object = func.mObject; + if (!selected_object) { - // Try a face user picked first - // (likely the only method we need, but in such case - // enable_object_save_gltf_material will need to check this) - LLTextureEntry *tep = objectp->getTE(pick.mObjectFace); - LLGLTFMaterial *mat = tep->getGLTFMaterial(); - LLLocalGLTFMaterial *local_mat = dynamic_cast(mat); - - if (local_mat) + // LLSelectedTEGetMatData can fail if there are no selected faces + // with materials, but we expect at least some object is selected. + llassert(LLSelectMgr::getInstance()->getSelection()->getFirstObject()); + return false; + } + if (selected_object->isInventoryPending()) + { + return false; + } + for (PermissionBit op : ops) + { + if (op == PERM_MODIFY && selected_object->isPermanentEnforced()) { - local_material = local_mat; + return false; + } + } + + item_out = selected_object->getInventoryItemByAsset(func.mMaterialId); + + LLPermissions item_permissions; + if (item_out) + { + item_permissions.set(item_out->getPermissions()); + for (PermissionBit op : ops) + { + if (!gAgent.allowOperation(op, item_permissions, GP_OBJECT_MANIPULATE)) + { + return false; + } + } + // Update flags for new owner + if (!item_permissions.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true)) + { + llassert(false); + return false; } - render_material = tep->getGLTFRenderMaterial(); } else { - // Find an applicable material. - // Do this before showing message, because - // message is going to drop selection. - LLSelectedTEGetMatData func(false); - LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func, true /*first applicable*/); - local_material = func.mLocalMaterial; - render_material = func.mMaterial; + item_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); } - saveMaterialAs(render_material, local_material); + // Use root object for permissions checking + LLViewerObject* root_object = selected_object->getRootEdit(); + LLPermissions* object_permissions_p = LLSelectMgr::getInstance()->findObjectPermissions(root_object); + LLPermissions object_permissions; + if (object_permissions_p) + { + object_permissions.set(*object_permissions_p); + for (PermissionBit op : ops) + { + if (!gAgent.allowOperation(op, object_permissions, GP_OBJECT_MANIPULATE)) + { + return false; + } + } + // Update flags for new owner + if (!object_permissions.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true)) + { + llassert(false); + return false; + } + } + else + { + object_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + } + + LLPermissions floater_perm; + floater_perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + floater_perm.setMaskEveryone(LLFloaterPerms::getEveryonePerms("Materials")); + floater_perm.setMaskGroup(LLFloaterPerms::getGroupPerms("Materials")); + floater_perm.setMaskNext(LLFloaterPerms::getNextOwnerPerms("Materials")); + + // *NOTE: A close inspection of LLPermissions::accumulate shows that + // conflicting UUIDs will be unset. This is acceptable behavior for now. + // The server will populate creator info based on the item creation method + // used. + // *NOTE: As far as I'm aware, there is currently no good way to preserve + // creation history when there's no material item present. In that case, + // the agent who saved the material will be considered the creator. + // -Cosmic,2023-08-07 + if (item_out) + { + permissions_out.set(item_permissions); + } + else + { + permissions_out.set(object_permissions); + } + permissions_out.accumulate(floater_perm); + + return true; } -void LLMaterialEditor::saveMaterialAs(const LLGLTFMaterial* render_material, const LLLocalGLTFMaterial *local_material) +bool LLMaterialEditor::canModifyObjectsMaterial() +{ + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item_out; + return can_use_objects_material(func, std::vector({PERM_MODIFY}), permissions, item_out); +} + +bool LLMaterialEditor::canSaveObjectsMaterial() +{ + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item_out; + return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), permissions, item_out); +} + +bool LLMaterialEditor::canClipboardObjectsMaterial() +{ + if (LLSelectMgr::getInstance()->getSelection()->getObjectCount() != 1) + { + return false; + } + + struct LLSelectedTEGetNullMat : public LLSelectedTEFunctor + { + bool apply(LLViewerObject* objectp, S32 te_index) + { + return objectp->getRenderMaterialID(te_index).isNull(); + } + } null_func; + + if (LLSelectMgr::getInstance()->getSelection()->applyToTEs(&null_func)) + { + return true; + } + + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item_out; + return can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY, PERM_TRANSFER}), permissions, item_out); +} + +void LLMaterialEditor::saveObjectsMaterialAs() +{ + LLSelectedTEGetMatData func(true); + LLPermissions permissions; + LLViewerInventoryItem* item = nullptr; + bool allowed = can_use_objects_material(func, std::vector({PERM_COPY, PERM_MODIFY}), permissions, item); + if (!allowed) + { + LL_WARNS("MaterialEditor") << "Failed to save GLTF material from object" << LL_ENDL; + return; + } + const LLUUID item_id = item ? item->getUUID() : LLUUID::null; + saveObjectsMaterialAs(func.mMaterial, func.mLocalMaterial, permissions, func.mObjectId, item_id); +} + + +void LLMaterialEditor::saveObjectsMaterialAs(const LLGLTFMaterial* render_material, const LLLocalGLTFMaterial *local_material, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id) { if (local_material) { @@ -1918,26 +2046,98 @@ void LLMaterialEditor::saveMaterialAs(const LLGLTFMaterial* render_material, con LLSD args; args["DESC"] = LLTrans::getString("New Material"); - LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2)); + if (local_material) + { + LLPermissions local_permissions; + local_permissions.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null); + LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2, local_permissions)); + } + else + { + if (item_id.notNull()) + { + // Copy existing item from object inventory, and create new composite asset on top of it + LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onCopyObjectsMaterialAsMsgCallback, _1, _2, permissions, object_id, item_id)); + } + else + { + LLNotificationsUtil::add("SaveMaterialAs", args, payload, boost::bind(&LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback, _1, _2, permissions)); + } + } } -void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response) +// static +void LLMaterialEditor::onCopyObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (0 == option) + if (0 != option) { - LLSD asset; - asset["version"] = LLGLTFMaterial::ASSET_VERSION; - asset["type"] = LLGLTFMaterial::ASSET_TYPE; - // This is the string serialized from LLGLTFMaterial::asJSON - asset["data"] = notification["payload"]["data"]; - - std::ostringstream str; - LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY); - - std::string new_name = response["message"].asString(); - createInventoryItem(str.str(), new_name, std::string()); + return; } + + LLSD asset; + asset["version"] = LLGLTFMaterial::ASSET_VERSION; + asset["type"] = LLGLTFMaterial::ASSET_TYPE; + // This is the string serialized from LLGLTFMaterial::asJSON + asset["data"] = notification["payload"]["data"]; + + std::ostringstream str; + LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY); + + LLViewerObject* object = gObjectList.findObject(object_id); + if (!object) + { + return; + } + const LLInventoryItem* item = object->getInventoryItem(item_id); + if (!item) + { + return; + } + + std::string new_name = response["message"].asString(); + LLInventoryObject::correctInventoryName(new_name); + if (new_name.empty()) + { + return; + } + + const LLUUID destination_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL); + + LLPointer cb = new LLObjectsMaterialItemCallback(permissions, str.str(), new_name); + // NOTE: This should be an item copy. Saving a material to an inventory should be disabled when the associated material is no-copy. + move_or_copy_inventory_from_object(destination_id, + object_id, + item_id, + cb); +} + +// static +void LLMaterialEditor::onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 != option) + { + return; + } + + LLSD asset; + asset["version"] = LLGLTFMaterial::ASSET_VERSION; + asset["type"] = LLGLTFMaterial::ASSET_TYPE; + // This is the string serialized from LLGLTFMaterial::asJSON + asset["data"] = notification["payload"]["data"]; + + std::ostringstream str; + LLSDSerialize::serialize(asset, str, LLSDSerialize::LLSD_BINARY); + + std::string new_name = response["message"].asString(); + LLInventoryObject::correctInventoryName(new_name); + if (new_name.empty()) + { + return; + } + + createInventoryItem(str.str(), new_name, std::string(), permissions); } const void upload_bulk(const std::vector& filenames, LLFilePicker::ELoadFilter type); @@ -2591,7 +2791,7 @@ public: // something went wrong update selection LLMaterialEditor::updateLive(); } - // else we will get updateLive(obj, id) from applied overrides + // else we will get updateLive() from panel face } bool getResult() { return mSuccess; } @@ -2710,7 +2910,10 @@ bool LLMaterialEditor::setFromSelection() if (func.mMaterial.notNull()) { setFromGLTFMaterial(func.mMaterial); - setEnableEditing(true); + LLViewerObject* selected_object = func.mObject; + const LLViewerInventoryItem* item = selected_object->getInventoryItemByAsset(func.mMaterialId); + const bool allow_modify = !item || canModify(selected_object, item); + setEnableEditing(allow_modify); } else { diff --git a/indra/newview/llmaterialeditor.h b/indra/newview/llmaterialeditor.h index 6f674a4170..1c40fcc348 100644 --- a/indra/newview/llmaterialeditor.h +++ b/indra/newview/llmaterialeditor.h @@ -38,6 +38,7 @@ class LLGLTFMaterial; class LLLocalGLTFMaterial; class LLTextureCtrl; class LLTextBox; +class LLViewerInventoryItem; namespace tinygltf { @@ -109,12 +110,14 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener void onSelectionChanged(); // live overrides selection changes static void updateLive(); - static void updateLive(const LLUUID &object_id, S32 te); static void loadLive(); + static bool canModifyObjectsMaterial(); + static bool canSaveObjectsMaterial(); + static bool canClipboardObjectsMaterial(); static void saveObjectsMaterialAs(); - static void savePickedMaterialAs(); - static void onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response); + static void onCopyObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions, const LLUUID& object_id, const LLUUID& item_id); + static void onSaveObjectsMaterialAsMsgCallback(const LLSD& notification, const LLSD& response, const LLPermissions& permissions); static void onLoadComplete(const LLUUID& asset_uuid, LLAssetType::EType type, void* user_data, S32 status, LLExtStat ext_status); @@ -229,10 +232,10 @@ class LLMaterialEditor : public LLPreview, public LLVOInventoryListener static bool capabilitiesAvailable(); private: - static void saveMaterialAs(const LLGLTFMaterial *render_material, const LLLocalGLTFMaterial *local_material); + static void saveObjectsMaterialAs(const LLGLTFMaterial *render_material, const LLLocalGLTFMaterial *local_material, const LLPermissions& permissions, const LLUUID& object_id /* = LLUUID::null */, const LLUUID& item /* = LLUUID::null */); static bool updateInventoryItem(const std::string &buffer, const LLUUID &item_id, const LLUUID &task_id); - static void createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc); + static void createInventoryItem(const std::string &buffer, const std::string &name, const std::string &desc, const LLPermissions& permissions); void setFromGLTFMaterial(LLGLTFMaterial* mat); bool setFromSelection(); diff --git a/indra/newview/llmaterialmgr.h b/indra/newview/llmaterialmgr.h index f76cc27db1..6e574219ae 100644 --- a/indra/newview/llmaterialmgr.h +++ b/indra/newview/llmaterialmgr.h @@ -93,7 +93,11 @@ private: bool operator==(const TEMaterialPair& b) const { return (materialID == b.materialID) && (te == b.te); } }; - + + // definitions follow class + friend std::hash; + friend size_t hash_value(const TEMaterialPair&) noexcept; + friend inline bool operator<( const LLMaterialMgr::TEMaterialPair& lhs, const LLMaterialMgr::TEMaterialPair& rhs) @@ -102,13 +106,6 @@ private: (lhs.materialID < rhs.materialID); } - struct TEMaterialPairHasher - { - enum { bucket_size = 8 }; - size_t operator()(const TEMaterialPair& key_value) const { return *((size_t*)key_value.materialID.get()); } // cheesy, but effective - bool operator()(const TEMaterialPair& left, const TEMaterialPair& right) const { return left < right; } - }; - typedef std::set material_queue_t; typedef std::map get_queue_t; typedef std::pair pending_material_t; @@ -116,7 +113,7 @@ private: typedef std::map get_callback_map_t; - typedef boost::unordered_map get_callback_te_map_t; + typedef boost::unordered_map get_callback_te_map_t; typedef std::set getall_queue_t; typedef std::map getall_pending_map_t; typedef std::map getall_callback_map_t; @@ -145,5 +142,23 @@ private: U32 getMaxEntries(const LLViewerRegion* regionp); }; +// std::hash implementation for TEMaterialPair +namespace std +{ + template<> struct hash + { + inline size_t operator()(const LLMaterialMgr::TEMaterialPair& p) const noexcept + { + return size_t((p.te + 1) * p.materialID.getDigest64()); + } + }; +} + +// For use with boost containers. +inline size_t hash_value(const LLMaterialMgr::TEMaterialPair& p) noexcept +{ + return size_t((p.te + 1) * p.materialID.getDigest64()); +} + #endif // LL_LLMATERIALMGR_H diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 57ac111fdf..01d6469010 100644 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -56,6 +56,7 @@ #include "llviewermessage.h" #include "llviewerobjectlist.h" #include "llviewerregion.h" +#include "llviewerstatsrecorder.h" #include "llviewertexturelist.h" #include "llvolume.h" #include "llvolumemgr.h" @@ -1861,11 +1862,11 @@ EMeshProcessingResult LLMeshRepoThread::headerReceived(const LLVolumeParams& mes LLSD header_data; LLMeshHeader header; - - U32 header_size = 0; + + llssize header_size = 0; if (data_size > 0) { - U32 dsize = data_size; + llssize dsize = data_size; char* result_ptr = strip_deprecated_header((char*)data, dsize, &header_size); data_size = dsize; @@ -3021,7 +3022,7 @@ S32 LLMeshRepository::getActualMeshLOD(LLMeshHeader& header, S32 lod) } //search up to find then ext available higher lod - for (S32 i = lod+1; i < 4; ++i) + for (S32 i = lod+1; i < LLVolumeLODGroup::NUM_LODS; ++i) { if (header.mLodSize[i] > 0) { @@ -3183,7 +3184,7 @@ void LLMeshHeaderHandler::processFailure(LLCore::HttpStatus status) // Can't get the header so none of the LODs will be available LLMutexLock lock(gMeshRepo.mThread->mMutex); - for (int i(0); i < 4; ++i) + for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) { gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); } @@ -3212,7 +3213,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // Can't get the header so none of the LODs will be available LLMutexLock lock(gMeshRepo.mThread->mMutex); - for (int i(0); i < 4; ++i) + for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) { gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); } @@ -3293,7 +3294,7 @@ void LLMeshHeaderHandler::processData(LLCore::BufferArray * /* body */, S32 /* b // headerReceived() parsed header, but header's data is invalid so none of the LODs will be available LLMutexLock lock(gMeshRepo.mThread->mMutex); - for (int i(0); i < 4; ++i) + for (int i(0); i < LLVolumeLODGroup::NUM_LODS; ++i) { gMeshRepo.mThread->mUnavailableQ.push_back(LLMeshRepoThread::LODRequest(mMeshParams, i)); } @@ -3654,7 +3655,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para // Manage time-to-load metrics for mesh download operations. metricsProgress(1); - if (detail < 0 || detail >= 4) + if (detail < 0 || detail >= LLVolumeLODGroup::NUM_LODS) { return detail; } @@ -3717,7 +3718,7 @@ S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_para } //no lower LOD is a available, is a higher lod available? - for (S32 i = detail+1; i < 4; ++i) + for (S32 i = detail+1; i < LLVolumeLODGroup::NUM_LODS; ++i) { LLVolume* lod = group->refLOD(i); if (lod && lod->isMeshAssetLoaded() && lod->getNumVolumeFaces() > 0) @@ -3918,7 +3919,7 @@ void LLMeshRepository::notifyLoadedMeshes() //create score map std::map score_map; - for (U32 i = 0; i < 4; ++i) + for (U32 i = 0; i < LLVolumeLODGroup::NUM_LODS; ++i) { for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) { @@ -4087,6 +4088,8 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol } mLoadingMeshes[detail].erase(obj_iter); + + LLViewerStatsRecorder::instance().meshLoaded(); } } @@ -4099,10 +4102,11 @@ void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, { F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); - LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail); + LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, lod); if (sys_volume) { sys_volume->setMeshAssetUnavaliable(true); + LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); } for (LLVOVolume* vobj : obj_iter->second) @@ -4480,7 +4484,7 @@ F32 LLMeshRepository::getStreamingCostLegacy(LLUUID mesh_id, F32 radius, S32* by { LL_WARNS() << mesh_id << "bytes mismatch " << *bytes << " " << data.getSizeTotal() << LL_ENDL; } - if (bytes_visible && (lod >=0) && (lod < 4) && (*bytes_visible != data.getSizeByLOD(lod))) + if (bytes_visible && (lod >=0) && (lod < LLVolumeLODGroup::NUM_LODS) && (*bytes_visible != data.getSizeByLOD(lod))) { LL_WARNS() << mesh_id << "bytes_visible mismatch " << *bytes_visible << " " << data.getSizeByLOD(lod) << LL_ENDL; } @@ -4640,7 +4644,7 @@ bool LLMeshCostData::init(const LLMeshHeader& header) static LLCachedControl minimum_size(gSavedSettings, "MeshMinimumByteSize", 16); //make sure nothing is "free" static LLCachedControl bytes_per_triangle(gSavedSettings, "MeshBytesPerTriangle", 16); - for (S32 i=0; i<4; i++) + for (S32 i=0; i 4) @@ -2887,8 +2884,6 @@ void LLModelPreview::genBuffers(S32 lod, bool include_skin_weights) vb->unmapBuffer(); mVertexBuffer[lod][mdl].push_back(vb); - - ++mesh_count; } } } diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 2c45014321..14840f1b2e 100644 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -99,6 +99,7 @@ public: BOOL isMuted(const LLUUID& id, const std::string& name = LLStringUtil::null, U32 flags = 0) const; // Workaround for username-based mute search, a lot of string conversions so use cautiously + // Expects lower case username BOOL isMuted(const std::string& username, U32 flags = 0) const; // Alternate (convenience) form for places we don't need to pass the name, but do need flags diff --git a/indra/newview/lloutfitgallery.cpp b/indra/newview/lloutfitgallery.cpp index 92a742466e..de988555c5 100644 --- a/indra/newview/lloutfitgallery.cpp +++ b/indra/newview/lloutfitgallery.cpp @@ -36,12 +36,11 @@ #include "llaccordionctrltab.h" #include "llappearancemgr.h" -#include "llagentbenefits.h" #include "llerror.h" #include "llfilepicker.h" #include "llfloaterperms.h" #include "llfloaterreg.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h" #include "llimagedimensionsinfo.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -65,7 +64,6 @@ const S32 GALLERY_ITEMS_PER_ROW_MIN = 2; LLOutfitGallery::LLOutfitGallery(const LLOutfitGallery::Params& p) : LLOutfitListBase(), - mTexturesObserver(NULL), mOutfitsObserver(NULL), mScrollPanel(NULL), mGalleryPanel(NULL), @@ -125,7 +123,6 @@ void LLOutfitGallery::onOpen(const LLSD& info) LLOutfitListBase::onOpen(info); if (!mGalleryCreated) { - loadPhotos(); uuid_vec_t cats; getCurrentCategories(cats); int n = cats.size(); @@ -149,6 +146,264 @@ void LLOutfitGallery::draw() } } +BOOL LLOutfitGallery::handleKeyHere(KEY key, MASK mask) +{ + BOOL handled = FALSE; + switch (key) + { + case KEY_RETURN: + // Open selected items if enter key hit on the inventory panel + if (mask == MASK_NONE && mSelectedOutfitUUID.notNull()) + { + // Or should it wearSelectedOutfit? + getSelectedItem()->openOutfitsContent(); + } + handled = TRUE; + break; + case KEY_DELETE: +#if LL_DARWIN + case KEY_BACKSPACE: +#endif + // Delete selected items if delete or backspace key hit on the inventory panel + // Note: on Mac laptop keyboards, backspace and delete are one and the same + if (mSelectedOutfitUUID.notNull()) + { + onRemoveOutfit(mSelectedOutfitUUID); + } + handled = TRUE; + break; + + case KEY_F2: + LLAppearanceMgr::instance().renameOutfit(mSelectedOutfitUUID); + handled = TRUE; + break; + + case KEY_PAGE_UP: + if (mScrollPanel) + { + mScrollPanel->pageUp(30); + } + handled = TRUE; + break; + + case KEY_PAGE_DOWN: + if (mScrollPanel) + { + mScrollPanel->pageDown(30); + } + handled = TRUE; + break; + + case KEY_HOME: + if (mScrollPanel) + { + mScrollPanel->goToTop(); + } + handled = TRUE; + break; + + case KEY_END: + if (mScrollPanel) + { + mScrollPanel->goToBottom(); + } + handled = TRUE; + break; + + case KEY_LEFT: + moveLeft(); + handled = TRUE; + break; + + case KEY_RIGHT: + moveRight(); + handled = TRUE; + break; + + case KEY_UP: + moveUp(); + handled = TRUE; + break; + + case KEY_DOWN: + moveDown(); + handled = TRUE; + break; + + default: + break; + } + + if (handled) + { + mOutfitGalleryMenu->hide(); + } + + return handled; +} + +void LLOutfitGallery::moveUp() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + S32 n = mItemIndexMap[item]; + n -= mItemsInRow; + if (n >= 0) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } + } +} + +void LLOutfitGallery::moveDown() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + S32 n = mItemIndexMap[item]; + n += mItemsInRow; + if (n < mItemsAddedCount) + { + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } + } +} + +void LLOutfitGallery::moveLeft() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + // Might be better to get item from panel + S32 n = mItemIndexMap[item]; + n--; + if (n < 0) + { + n = mItemsAddedCount - 1; + } + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } +} + +void LLOutfitGallery::moveRight() +{ + if (mSelectedOutfitUUID.notNull() && mItemsAddedCount > 1) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + S32 n = mItemIndexMap[item]; + n++; + if (n == mItemsAddedCount) + { + n = 0; + } + item = mIndexToItemMap[n]; + LLUUID item_id = item->getUUID(); + ChangeOutfitSelection(nullptr, item_id); + item->setFocus(TRUE); + + scrollToShowItem(mSelectedOutfitUUID); + } + } +} + +void LLOutfitGallery::onFocusLost() +{ + LLOutfitListBase::onFocusLost(); + + if (mSelectedOutfitUUID.notNull()) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + item->setSelected(false); + } + } +} + +void LLOutfitGallery::onFocusReceived() +{ + LLOutfitListBase::onFocusReceived(); + + if (mSelectedOutfitUUID.notNull()) + { + LLOutfitGalleryItem* item = getSelectedItem(); + if (item) + { + item->setSelected(true); + } + } +} + +void LLOutfitGallery::onRemoveOutfit(const LLUUID& outfit_cat_id) +{ + LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(onOutfitsRemovalConfirmation, _1, _2, outfit_cat_id)); +} + +void LLOutfitGallery::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option != 0) return; // canceled + + if (outfit_cat_id.notNull()) + { + gInventory.removeCategory(outfit_cat_id); + } +} + +void LLOutfitGallery::scrollToShowItem(const LLUUID& item_id) +{ + LLOutfitGalleryItem* item = mOutfitMap[item_id]; + if (item) + { + const LLRect visible_content_rect = mScrollPanel->getVisibleContentRect(); + + LLRect item_rect; + item->localRectToOtherView(item->getLocalRect(), &item_rect, mScrollPanel); + LLRect overlap_rect(item_rect); + overlap_rect.intersectWith(visible_content_rect); + + //Scroll when the selected item is outside the visible area + if (overlap_rect.getHeight() + 5 < item->getRect().getHeight()) + { + LLRect content_rect = mScrollPanel->getContentWindowRect(); + LLRect constraint_rect; + constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + + LLRect item_doc_rect; + item->localRectToOtherView(item->getLocalRect(), &item_doc_rect, mGalleryPanel); + + mScrollPanel->scrollToShowRect(item_doc_rect, constraint_rect); + } + } +} + void LLOutfitGallery::updateRowsIfNeeded() { if(((getRect().getWidth() - mRowPanelWidth) > mItemWidth) && mRowCount > 1) @@ -269,8 +524,9 @@ void LLOutfitGallery::addToGallery(LLOutfitGalleryItem* item) mHiddenItems.push_back(item); return; } + mItemIndexMap[item] = mItemsAddedCount; + mIndexToItemMap[mItemsAddedCount] = item; mItemsAddedCount++; - mItemIndexMap[item] = mItemsAddedCount - 1; int n = mItemsAddedCount; int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; int n_prev = n - 1; @@ -306,6 +562,7 @@ void LLOutfitGallery::removeFromGalleryLast(LLOutfitGalleryItem* item) int row_count = (n % mItemsInRow) == 0 ? n / mItemsInRow : n / mItemsInRow + 1; int row_count_prev = (n_prev % mItemsInRow) == 0 ? n_prev / mItemsInRow : n_prev / mItemsInRow + 1; mItemsAddedCount--; + mIndexToItemMap.erase(mItemsAddedCount); bool remove_row = row_count != row_count_prev; removeFromLastRow(mItems[mItemsAddedCount]); @@ -331,6 +588,7 @@ void LLOutfitGallery::removeFromGalleryMiddle(LLOutfitGalleryItem* item) } int n = mItemIndexMap[item]; mItemIndexMap.erase(item); + mIndexToItemMap.erase(n); std::vector saved; for (int i = mItemsAddedCount - 1; i > n; i--) { @@ -364,9 +622,15 @@ LLOutfitGalleryItem* LLOutfitGallery::buildGalleryItem(std::string name, LLUUID gitem->setFollowsTop(); gitem->setOutfitName(name); gitem->setUUID(outfit_id); + gitem->setGallery(this); return gitem; } +LLOutfitGalleryItem* LLOutfitGallery::getSelectedItem() +{ + return mOutfitMap[mSelectedOutfitUUID]; +} + void LLOutfitGallery::buildGalleryPanel(int row_count) { LLPanel::Params params; @@ -440,12 +704,6 @@ void LLOutfitGallery::moveRowPanel(LLPanel* stack, int left, int bottom) LLOutfitGallery::~LLOutfitGallery() { delete mOutfitGalleryMenu; - - if (gInventory.containsObserver(mTexturesObserver)) - { - gInventory.removeObserver(mTexturesObserver); - } - delete mTexturesObserver; if (gInventory.containsObserver(mOutfitsObserver)) { @@ -617,6 +875,7 @@ void LLOutfitGallery::onChangeOutfitSelection(LLWearableItemsList* list, const L { mOutfitMap[category_id]->setSelected(TRUE); } + // mSelectedOutfitUUID will be set in LLOutfitListBase::ChangeOutfitSelection } void LLOutfitGallery::wearSelectedOutfit() @@ -668,7 +927,8 @@ static LLDefaultChildRegistry::Register r("outfit_gallery_i LLOutfitGalleryItem::LLOutfitGalleryItem(const Params& p) : LLPanel(p), - mTexturep(NULL), + mGallery(nullptr), + mTexturep(nullptr), mSelected(false), mWorn(false), mDefaultImage(true), @@ -743,10 +1003,13 @@ void LLOutfitGalleryItem::setOutfitWorn(bool value) mWorn = value; LLStringUtil::format_map_t worn_string_args; std::string worn_string = getString("worn_string", worn_string_args); - LLUIColor text_color = LLUIColorTable::instance().getColor(mSelected ? "White" : (mWorn ? "OutfitGalleryItemWorn" : "White"), LLColor4::white); + LLUIColor text_color = LLUIColorTable::instance().getColor("White", LLColor4::white); mOutfitWornText->setReadOnlyColor(text_color.get()); mOutfitNameText->setReadOnlyColor(text_color.get()); + mOutfitWornText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall()); + mOutfitNameText->setFont(value ? LLFontGL::getFontSansSerifBold() : LLFontGL::getFontSansSerifSmall()); mOutfitWornText->setValue(value ? worn_string : ""); + mOutfitNameText->setText(mOutfitName); // refresh LLTextViewModel to pick up font changes } void LLOutfitGalleryItem::setSelected(bool value) @@ -769,9 +1032,64 @@ BOOL LLOutfitGalleryItem::handleRightMouseDown(S32 x, S32 y, MASK mask) } BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask) +{ + return openOutfitsContent() || LLPanel::handleDoubleClick(x, y, mask); +} + +BOOL LLOutfitGalleryItem::handleKeyHere(KEY key, MASK mask) +{ + if (!mGallery) + { + return FALSE; + } + + BOOL handled = FALSE; + switch (key) + { + case KEY_LEFT: + mGallery->moveLeft(); + handled = true; + break; + + case KEY_RIGHT: + mGallery->moveRight(); + handled = true; + break; + + case KEY_UP: + mGallery->moveUp(); + handled = true; + break; + + case KEY_DOWN: + mGallery->moveDown(); + handled = true; + break; + + default: + break; + } + return handled; +} + +void LLOutfitGalleryItem::onFocusLost() +{ + setSelected(false); + + LLPanel::onFocusLost(); +} + +void LLOutfitGalleryItem::onFocusReceived() +{ + setSelected(true); + + LLPanel::onFocusReceived(); +} + +bool LLOutfitGalleryItem::openOutfitsContent() { LLTabContainer* appearence_tabs = LLPanelOutfitsInventory::findInstance()->getChild("appearance_tabs"); - if (appearence_tabs && (mUUID != LLUUID())) + if (appearence_tabs && mUUID.notNull()) { appearence_tabs->selectTabByName("outfitslist_tab"); LLPanel* panel = appearence_tabs->getCurrentPanel(); @@ -784,12 +1102,11 @@ BOOL LLOutfitGalleryItem::handleDoubleClick(S32 x, S32 y, MASK mask) outfit_list->setSelectedOutfitByUUID(mUUID); LLAccordionCtrlTab* tab = accordion->getSelectedTab(); tab->showAndFocusHeader(); - return TRUE; + return true; } } } - - return LLPanel::handleDoubleClick(x, y, mask); + return false; } bool LLOutfitGalleryItem::setImageAssetId(LLUUID image_asset_id) @@ -835,68 +1152,22 @@ LLContextMenu* LLOutfitGalleryContextMenu::createMenu() boost::bind(&LLAppearanceMgr::takeOffOutfit, &LLAppearanceMgr::instance(), selected_id)); registrar.add("Outfit.Edit", boost::bind(editOutfit)); registrar.add("Outfit.Rename", boost::bind(renameOutfit, selected_id)); - registrar.add("Outfit.Delete", boost::bind(&LLOutfitGalleryContextMenu::onRemoveOutfit, this, selected_id)); + registrar.add("Outfit.Delete", boost::bind(LLOutfitGallery::onRemoveOutfit, selected_id)); registrar.add("Outfit.Create", boost::bind(&LLOutfitGalleryContextMenu::onCreate, this, _2)); - registrar.add("Outfit.UploadPhoto", boost::bind(&LLOutfitGalleryContextMenu::onUploadPhoto, this, selected_id)); - registrar.add("Outfit.SelectPhoto", boost::bind(&LLOutfitGalleryContextMenu::onSelectPhoto, this, selected_id)); - registrar.add("Outfit.TakeSnapshot", boost::bind(&LLOutfitGalleryContextMenu::onTakeSnapshot, this, selected_id)); - registrar.add("Outfit.RemovePhoto", boost::bind(&LLOutfitGalleryContextMenu::onRemovePhoto, this, selected_id)); + registrar.add("Outfit.Thumbnail", boost::bind(&LLOutfitGalleryContextMenu::onThumbnail, this, selected_id)); enable_registrar.add("Outfit.OnEnable", boost::bind(&LLOutfitGalleryContextMenu::onEnable, this, _2)); enable_registrar.add("Outfit.OnVisible", boost::bind(&LLOutfitGalleryContextMenu::onVisible, this, _2)); return createFromFile("menu_gallery_outfit_tab.xml"); } -void LLOutfitGalleryContextMenu::onUploadPhoto(const LLUUID& outfit_cat_id) +void LLOutfitGalleryContextMenu::onThumbnail(const LLUUID& outfit_cat_id) { LLOutfitGallery* gallery = dynamic_cast(mOutfitList); if (gallery && outfit_cat_id.notNull()) { - gallery->uploadPhoto(outfit_cat_id); - } -} - -void LLOutfitGalleryContextMenu::onSelectPhoto(const LLUUID& outfit_cat_id) -{ - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - if (gallery && outfit_cat_id.notNull()) - { - gallery->onSelectPhoto(outfit_cat_id); - } -} - -void LLOutfitGalleryContextMenu::onRemovePhoto(const LLUUID& outfit_cat_id) -{ - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - if (gallery && outfit_cat_id.notNull()) - { - gallery->checkRemovePhoto(outfit_cat_id); - gallery->refreshOutfit(outfit_cat_id); - } -} - -void LLOutfitGalleryContextMenu::onTakeSnapshot(const LLUUID& outfit_cat_id) -{ - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - if (gallery && outfit_cat_id.notNull()) - { - gallery->onTakeSnapshot(outfit_cat_id); - } -} - -void LLOutfitGalleryContextMenu::onRemoveOutfit(const LLUUID& outfit_cat_id) -{ - LLNotificationsUtil::add("DeleteOutfits", LLSD(), LLSD(), boost::bind(&LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation, this, _1, _2, outfit_cat_id)); -} - -void LLOutfitGalleryContextMenu::onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - if (option != 0) return; // canceled - - if (outfit_cat_id.notNull()) - { - gInventory.removeCategory(outfit_cat_id); + LLSD data(outfit_cat_id); + LLFloaterReg::showInstance("change_item_thumbnail", data); } } @@ -919,16 +1190,6 @@ bool LLOutfitGalleryContextMenu::onEnable(LLSD::String param) bool LLOutfitGalleryContextMenu::onVisible(LLSD::String param) { - mMenuHandle.get()->getChild("upload_photo")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost())); - if ("remove_photo" == param) - { - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - LLUUID selected_id = mUUIDs.front(); - if (gallery && selected_id.notNull()) - { - return !gallery->hasDefaultImage(selected_id); - } - } return LLOutfitContextMenu::onVisible(param); } @@ -943,56 +1204,12 @@ void LLOutfitGalleryGearMenu::onUpdateItemsVisibility() bool have_selection = getSelectedOutfitID().notNull(); mMenu->setItemVisible("expand", FALSE); mMenu->setItemVisible("collapse", FALSE); - mMenu->setItemVisible("upload_photo", have_selection); - mMenu->setItemVisible("select_photo", have_selection); - mMenu->setItemVisible("take_snapshot", have_selection); - mMenu->setItemVisible("remove_photo", !hasDefaultImage()); + mMenu->setItemVisible("thumbnail", have_selection); mMenu->setItemVisible("sepatator3", TRUE); mMenu->setItemVisible("sort_folders_by_name", TRUE); LLOutfitListGearMenuBase::onUpdateItemsVisibility(); } -void LLOutfitGalleryGearMenu::onUploadFoto() -{ - LLUUID selected_outfit_id = getSelectedOutfitID(); - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - if (gallery && selected_outfit_id.notNull()) - { - gallery->uploadPhoto(selected_outfit_id); - } -} - -void LLOutfitGalleryGearMenu::onSelectPhoto() -{ - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - LLUUID selected_outfit_id = getSelectedOutfitID(); - if (gallery && !selected_outfit_id.isNull()) - { - gallery->onSelectPhoto(selected_outfit_id); - } -} - -void LLOutfitGalleryGearMenu::onRemovePhoto() -{ - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - LLUUID selected_outfit_id = getSelectedOutfitID(); - if (gallery && !selected_outfit_id.isNull()) - { - gallery->checkRemovePhoto(selected_outfit_id); - gallery->refreshOutfit(selected_outfit_id); - } -} - -void LLOutfitGalleryGearMenu::onTakeSnapshot() -{ - LLOutfitGallery* gallery = dynamic_cast(mOutfitList); - LLUUID selected_outfit_id = getSelectedOutfitID(); - if (gallery && !selected_outfit_id.isNull()) - { - gallery->onTakeSnapshot(selected_outfit_id); - } -} - void LLOutfitGalleryGearMenu::onChangeSortOrder() { bool sort_by_name = !gSavedSettings.getBOOL("OutfitGallerySortByName"); @@ -1019,104 +1236,82 @@ void LLOutfitGallery::onTextureSelectionChanged(LLInventoryItem* itemp) { } -void LLOutfitGallery::loadPhotos() -{ - //Iterate over inventory - mSnapshotFolderID = gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE); - LLViewerInventoryCategory* textures_category = gInventory.getCategory(mSnapshotFolderID); - if (!textures_category) - return; - if (mTexturesObserver == NULL) - { - mTexturesObserver = new LLInventoryCategoriesObserver(); - gInventory.addObserver(mTexturesObserver); - } - - // Start observing changes in "Textures" category. - mTexturesObserver->addCategory(mSnapshotFolderID, - boost::bind(&LLOutfitGallery::refreshTextures, this, mSnapshotFolderID)); - - textures_category->fetch(); -} - -void LLOutfitGallery::updateSnapshotFolderObserver() -{ - if(mSnapshotFolderID != gInventory.findUserDefinedCategoryUUIDForType(LLFolderType::FT_TEXTURE)) - { - if (gInventory.containsObserver(mTexturesObserver)) - { - gInventory.removeObserver(mTexturesObserver); - } - delete mTexturesObserver; - mTexturesObserver = NULL; - loadPhotos(); - } -} - void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) { LLViewerInventoryCategory* category = gInventory.getCategory(category_id); if (category) { bool photo_loaded = false; - LLInventoryModel::cat_array_t sub_cat_array; - LLInventoryModel::item_array_t outfit_item_array; - // Collect all sub-categories of a given category. - gInventory.collectDescendents( - category->getUUID(), - sub_cat_array, - outfit_item_array, - LLInventoryModel::EXCLUDE_TRASH); - BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array) + LLUUID asset_id = category->getThumbnailUUID(); + if (asset_id.isNull()) { - LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem(); - LLUUID asset_id, inv_id; - std::string item_name; - if (linked_item != NULL) + LLInventoryModel::cat_array_t sub_cat_array; + LLInventoryModel::item_array_t outfit_item_array; + // Collect all sub-categories of a given category. + gInventory.collectDescendents( + category->getUUID(), + sub_cat_array, + outfit_item_array, + LLInventoryModel::EXCLUDE_TRASH); + BOOST_FOREACH(LLViewerInventoryItem* outfit_item, outfit_item_array) { - if (linked_item->getActualType() == LLAssetType::AT_TEXTURE) + LLViewerInventoryItem* linked_item = outfit_item->getLinkedItem(); + LLUUID asset_id, inv_id; + std::string item_name; + if (linked_item != NULL) { - asset_id = linked_item->getAssetUUID(); - inv_id = linked_item->getUUID(); - item_name = linked_item->getName(); - } - } - else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE) - { - asset_id = outfit_item->getAssetUUID(); - inv_id = outfit_item->getUUID(); - item_name = outfit_item->getName(); - } - if (asset_id.notNull()) - { - photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id); - // Rename links - if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name) - { - LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending); - LLStringUtil::format_map_t photo_string_args; - photo_string_args["OUTFIT_NAME"] = outfit_cat->getName(); - std::string new_name = getString("outfit_photo_string", photo_string_args); - LLSD updates; - updates["name"] = new_name; - update_inventory_item(inv_id, updates, NULL); - mOutfitRenamePending.setNull(); - LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance"); - if (appearance_floater) + if (linked_item->getActualType() == LLAssetType::AT_TEXTURE) { - appearance_floater->setFocus(TRUE); + asset_id = linked_item->getAssetUUID(); + inv_id = linked_item->getUUID(); + item_name = linked_item->getName(); } } - if (item_name == LLAppearanceMgr::sExpectedTextureName) + else if (outfit_item->getActualType() == LLAssetType::AT_TEXTURE) { - // Images with "appropriate" name take priority - break; + asset_id = outfit_item->getAssetUUID(); + inv_id = outfit_item->getUUID(); + item_name = outfit_item->getName(); + } + if (category->getThumbnailUUID().notNull()) + { + asset_id = category->getThumbnailUUID(); + } + if (asset_id.notNull()) + { + photo_loaded |= mOutfitMap[category_id]->setImageAssetId(asset_id); + // Rename links + if (!mOutfitRenamePending.isNull() && mOutfitRenamePending.asString() == item_name) + { + LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(mOutfitRenamePending); + LLStringUtil::format_map_t photo_string_args; + photo_string_args["OUTFIT_NAME"] = outfit_cat->getName(); + std::string new_name = getString("outfit_photo_string", photo_string_args); + LLSD updates; + updates["name"] = new_name; + update_inventory_item(inv_id, updates, NULL); + mOutfitRenamePending.setNull(); + LLFloater* appearance_floater = LLFloaterReg::getInstance("appearance"); + if (appearance_floater) + { + appearance_floater->setFocus(TRUE); + } + } + if (item_name == LLAppearanceMgr::sExpectedTextureName) + { + // Images with "appropriate" name take priority + break; + } + } + if (!photo_loaded) + { + mOutfitMap[category_id]->setDefaultImage(); } } - if (!photo_loaded) - { - mOutfitMap[category_id]->setDefaultImage(); - } + } + else + { + mOutfitMap[category_id]->setImageAssetId(asset_id); } } @@ -1126,135 +1321,6 @@ void LLOutfitGallery::refreshOutfit(const LLUUID& category_id) } } -// Refresh linked textures from "textures" uploads folder -void LLOutfitGallery::refreshTextures(const LLUUID& category_id) -{ - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - - // Collect all sub-categories of a given category. - LLIsType is_texture(LLAssetType::AT_TEXTURE); - gInventory.collectDescendentsIf( - category_id, - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH, - is_texture); - - //Find texture which contain pending outfit ID string in name - LLViewerInventoryItem* photo_upload_item = NULL; - BOOST_FOREACH(LLViewerInventoryItem* item, item_array) - { - std::string name = item->getName(); - if (!mOutfitLinkPending.isNull() && name == mOutfitLinkPending.asString()) - { - photo_upload_item = item; - break; - } - } - - if (photo_upload_item != NULL) - { - LLUUID photo_item_id = photo_upload_item->getUUID(); - LLInventoryObject* upload_object = gInventory.getObject(photo_item_id); - if (!upload_object) - { - LL_WARNS() << "LLOutfitGallery::refreshTextures added_object is null!" << LL_ENDL; - } - else - { - linkPhotoToOutfit(photo_item_id, mOutfitLinkPending); - mOutfitRenamePending = mOutfitLinkPending; - mOutfitLinkPending.setNull(); - } - } -} - -void LLOutfitGallery::uploadPhoto(LLUUID outfit_id) -{ - outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id); - if (outfit_it == mOutfitMap.end() || outfit_it->first.isNull()) - { - return; - } - LLFilePickerReplyThread::startPicker(boost::bind(&LLOutfitGallery::uploadOutfitImage, this, _1, outfit_id), LLFilePicker::FFLOAD_IMAGE, false); -} - -void LLOutfitGallery::uploadOutfitImage(const std::vector& filenames, LLUUID outfit_id) -{ - std::string filename = filenames[0]; - LLLocalBitmap* unit = new LLLocalBitmap(filename); - if (unit->getValid()) - { - std::string exten = gDirUtilp->getExtension(filename); - U32 codec = LLImageBase::getCodecFromExtension(exten); - - LLImageDimensionsInfo image_info; - std::string image_load_error; - if (!image_info.load(filename, codec)) - { - image_load_error = image_info.getLastError(); - } - - S32 max_width = MAX_OUTFIT_PHOTO_WIDTH; - S32 max_height = MAX_OUTFIT_PHOTO_HEIGHT; - - if ((image_info.getWidth() > max_width) || (image_info.getHeight() > max_height)) - { - LLStringUtil::format_map_t args; - args["WIDTH"] = llformat("%d", max_width); - args["HEIGHT"] = llformat("%d", max_height); - - image_load_error = LLTrans::getString("outfit_photo_load_dimensions_error", args); - } - - if (!image_load_error.empty()) - { - LLSD subst; - subst["REASON"] = image_load_error; - LLNotificationsUtil::add("OutfitPhotoLoadError", subst); - return; - } - - S32 expected_upload_cost = LLAgentBenefitsMgr::current().getTextureUploadCost(); - void *nruserdata = NULL; - nruserdata = (void *)&outfit_id; - - LLViewerInventoryCategory *outfit_cat = gInventory.getCategory(outfit_id); - if (!outfit_cat) return; - updateSnapshotFolderObserver(); - checkRemovePhoto(outfit_id); - std::string upload_pending_name = outfit_id.asString(); - std::string upload_pending_desc = ""; - upload_new_resource(filename, // file - upload_pending_name, - upload_pending_desc, - 0, LLFolderType::FT_NONE, LLInventoryType::IT_NONE, - LLFloaterPerms::getNextOwnerPerms("Uploads"), - LLFloaterPerms::getGroupPerms("Uploads"), - LLFloaterPerms::getEveryonePerms("Uploads"), - upload_pending_name, LLAssetStorage::LLStoreAssetCallback(), expected_upload_cost, nruserdata, false); - mOutfitLinkPending = outfit_id; - } - delete unit; -} - -void LLOutfitGallery::linkPhotoToOutfit(LLUUID photo_id, LLUUID outfit_id) -{ - LLPointer cb = new LLUpdateGalleryOnPhotoLinked(); - link_inventory_object(outfit_id, photo_id, cb); -} - -bool LLOutfitGallery::checkRemovePhoto(LLUUID outfit_id) -{ - LLAppearanceMgr::instance().removeOutfitPhoto(outfit_id); - return true; -} - -void LLUpdateGalleryOnPhotoLinked::fire(const LLUUID& inv_item_id) -{ -} - LLUUID LLOutfitGallery::getPhotoAssetId(const LLUUID& outfit_id) { outfit_map_t::iterator outfit_it = mOutfitMap.find(outfit_id); @@ -1270,150 +1336,3 @@ LLUUID LLOutfitGallery::getDefaultPhoto() return LLUUID(); } -void LLOutfitGallery::onTexturePickerCommit(LLTextureCtrl::ETexturePickOp op, LLUUID id) -{ - LLUUID selected_outfit_id = getSelectedOutfitUUID(); - - if (selected_outfit_id.isNull()) - { - return; - } - - LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); - - if (floaterp && op == LLTextureCtrl::TEXTURE_SELECT) - { - LLUUID image_item_id; - if (id.notNull()) - { - image_item_id = id; - } - else - { - image_item_id = floaterp->findItemID(floaterp->getAssetID(), FALSE, TRUE); - if (image_item_id.isNull()) - { - LL_WARNS() << "id or image_item_id is NULL!" << LL_ENDL; - return; - } - } - - std::string image_load_error; - S32 max_width = MAX_OUTFIT_PHOTO_WIDTH; - S32 max_height = MAX_OUTFIT_PHOTO_HEIGHT; - if (mTextureSelected.isNull() || - mTextureSelected->getFullWidth() == 0 || - mTextureSelected->getFullHeight() == 0) - { - image_load_error = LLTrans::getString("outfit_photo_verify_dimensions_error"); - LL_WARNS() << "Cannot verify selected texture dimensions" << LL_ENDL; - return; - } - S32 width = mTextureSelected->getFullWidth(); - S32 height = mTextureSelected->getFullHeight(); - if ((width > max_width) || (height > max_height)) - { - LLStringUtil::format_map_t args; - args["WIDTH"] = llformat("%d", max_width); - args["HEIGHT"] = llformat("%d", max_height); - - image_load_error = LLTrans::getString("outfit_photo_select_dimensions_error", args); - } - - if (!image_load_error.empty()) - { - LLSD subst; - subst["REASON"] = image_load_error; - LLNotificationsUtil::add("OutfitPhotoLoadError", subst); - return; - } - - checkRemovePhoto(selected_outfit_id); - linkPhotoToOutfit(image_item_id, selected_outfit_id); - } -} - -void LLOutfitGallery::onSelectPhoto(LLUUID selected_outfit_id) -{ - if (selected_outfit_id.notNull()) - { - - // show hourglass cursor when loading inventory window - // because inventory construction is slooow - getWindow()->setCursor(UI_CURSOR_WAIT); - LLFloater* floaterp = mFloaterHandle.get(); - - // Show the dialog - if (floaterp) - { - floaterp->openFloater(); - } - else - { - floaterp = new LLFloaterTexturePicker( - this, - getPhotoAssetId(selected_outfit_id), - getPhotoAssetId(selected_outfit_id), - getPhotoAssetId(selected_outfit_id), - FALSE, - TRUE, - "SELECT PHOTO", - PERM_NONE, - PERM_NONE, - FALSE, - NULL); - - mFloaterHandle = floaterp->getHandle(); - mTextureSelected = NULL; - - LLFloaterTexturePicker* texture_floaterp = dynamic_cast(floaterp); - if (texture_floaterp) - { - texture_floaterp->setTextureSelectedCallback(boost::bind(&LLOutfitGallery::onTextureSelectionChanged, this, _1)); - 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); - } - - floaterp->openFloater(); - } - floaterp->setFocus(TRUE); - } -} - -void LLOutfitGallery::onTakeSnapshot(LLUUID selected_outfit_id) -{ - LLFloaterReg::toggleInstanceOrBringToFront("simple_outfit_snapshot"); - LLFloaterSimpleOutfitSnapshot* snapshot_floater = LLFloaterSimpleOutfitSnapshot::getInstance(); - if (snapshot_floater) - { - snapshot_floater->setOutfitID(selected_outfit_id); - snapshot_floater->getInstance()->setGallery(this); - } -} - -void LLOutfitGallery::onBeforeOutfitSnapshotSave() -{ - LLUUID selected_outfit_id = getSelectedOutfitUUID(); - if (!selected_outfit_id.isNull()) - { - checkRemovePhoto(selected_outfit_id); - updateSnapshotFolderObserver(); - } -} - -void LLOutfitGallery::onAfterOutfitSnapshotSave() -{ - LLUUID selected_outfit_id = getSelectedOutfitUUID(); - if (!selected_outfit_id.isNull()) - { - mOutfitLinkPending = selected_outfit_id; - } -} - -void LLOutfitGallery::onTexturePickerUpdateImageStats(LLPointer texture) -{ - mTextureSelected = texture; -} diff --git a/indra/newview/lloutfitgallery.h b/indra/newview/lloutfitgallery.h index ce5c090134..9915752962 100644 --- a/indra/newview/lloutfitgallery.h +++ b/indra/newview/lloutfitgallery.h @@ -33,7 +33,6 @@ #include "lllayoutstack.h" #include "lloutfitslist.h" #include "llpanelappearancetab.h" -#include "lltexturectrl.h" #include "llviewertexture.h" #include @@ -44,15 +43,6 @@ class LLOutfitListGearMenuBase; class LLOutfitGalleryGearMenu; class LLOutfitGalleryContextMenu; -class LLUpdateGalleryOnPhotoLinked : public LLInventoryCallback -{ -public: - LLUpdateGalleryOnPhotoLinked(){} - virtual ~LLUpdateGalleryOnPhotoLinked(){} - /* virtual */ void fire(const LLUUID& inv_item_id); -private: -}; - class LLOutfitGallery : public LLOutfitListBase { public: @@ -83,10 +73,19 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& info); - /*virtual*/ void draw(); - - void onSelectPhoto(LLUUID selected_outfit_id); - void onTakeSnapshot(LLUUID selected_outfit_id); + /*virtual*/ void draw(); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + void moveUp(); + void moveDown(); + void moveLeft(); + void moveRight(); + + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + + static void onRemoveOutfit(const LLUUID& outfit_cat_id); + static void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id); + void scrollToShowItem(const LLUUID& item_id); void wearSelectedOutfit(); @@ -106,14 +105,8 @@ public: void updateMessageVisibility(); bool hasDefaultImage(const LLUUID& outfit_cat_id); - void refreshTextures(const LLUUID& category_id); void refreshOutfit(const LLUUID& category_id); - void onTexturePickerCommit(LLTextureCtrl::ETexturePickOp op, LLUUID id); - void onTexturePickerUpdateImageStats(LLPointer texture); - void onBeforeOutfitSnapshotSave(); - void onAfterOutfitSnapshotSave(); - protected: /*virtual*/ void onHighlightBaseOutfit(LLUUID base_id, LLUUID prev_id); /*virtual*/ void onSetSelectedOutfitByUUID(const LLUUID& outfit_uuid); @@ -127,14 +120,8 @@ protected: void applyFilter(LLOutfitGalleryItem* item, const std::string& filter_substring); private: - void loadPhotos(); - void uploadPhoto(LLUUID outfit_id); - void uploadOutfitImage(const std::vector& filenames, LLUUID outfit_id); - void updateSnapshotFolderObserver(); LLUUID getPhotoAssetId(const LLUUID& outfit_id); LLUUID getDefaultPhoto(); - void linkPhotoToOutfit(LLUUID outfit_id, LLUUID photo_id); - bool checkRemovePhoto(LLUUID outfit_id); void addToGallery(LLOutfitGalleryItem* item); void removeFromGalleryLast(LLOutfitGalleryItem* item); void removeFromGalleryMiddle(LLOutfitGalleryItem* item); @@ -150,6 +137,7 @@ private: void updateGalleryWidth(); LLOutfitGalleryItem* buildGalleryItem(std::string name, LLUUID outfit_id); + LLOutfitGalleryItem* getSelectedItem(); void onTextureSelectionChanged(LLInventoryItem* itemp); @@ -190,17 +178,15 @@ private: LLListContextMenu* mOutfitGalleryMenu; - LLHandle mFloaterHandle; - typedef std::map outfit_map_t; typedef outfit_map_t::value_type outfit_map_value_t; outfit_map_t mOutfitMap; - typedef std::map item_num_map_t; + typedef std::map item_num_map_t; typedef item_num_map_t::value_type item_numb_map_value_t; item_num_map_t mItemIndexMap; + std::map mIndexToItemMap; - LLInventoryCategoriesObserver* mTexturesObserver; LLInventoryCategoriesObserver* mOutfitsObserver; }; class LLOutfitGalleryContextMenu : public LLOutfitContextMenu @@ -211,17 +197,13 @@ public: LLOutfitGalleryContextMenu(LLOutfitListBase* outfit_list) : LLOutfitContextMenu(outfit_list), mOutfitList(outfit_list){} + protected: /* virtual */ LLContextMenu* createMenu(); bool onEnable(LLSD::String param); bool onVisible(LLSD::String param); - void onUploadPhoto(const LLUUID& outfit_cat_id); - void onSelectPhoto(const LLUUID& outfit_cat_id); - void onRemovePhoto(const LLUUID& outfit_cat_id); - void onTakeSnapshot(const LLUUID& outfit_cat_id); + void onThumbnail(const LLUUID& outfit_cat_id); void onCreate(const LLSD& data); - void onRemoveOutfit(const LLUUID& outfit_cat_id); - void onOutfitsRemovalConfirmation(const LLSD& notification, const LLSD& response, const LLUUID& outfit_cat_id); private: LLOutfitListBase* mOutfitList; }; @@ -236,10 +218,6 @@ public: protected: /*virtual*/ void onUpdateItemsVisibility(); private: - /*virtual*/ void onUploadFoto(); - /*virtual*/ void onSelectPhoto(); - /*virtual*/ void onTakeSnapshot(); - /*virtual*/ void onRemovePhoto(); /*virtual*/ void onChangeSortOrder(); bool hasDefaultImage(); @@ -259,14 +237,21 @@ public: /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); + /*virtual*/ void onFocusLost(); + /*virtual*/ void onFocusReceived(); + bool openOutfitsContent(); + + void setGallery(LLOutfitGallery* gallery) { mGallery = gallery; } void setDefaultImage(); bool setImageAssetId(LLUUID asset_id); LLUUID getImageAssetId(); void setOutfitName(std::string name); void setOutfitWorn(bool value); void setSelected(bool value); - void setUUID(LLUUID outfit_id) {mUUID = outfit_id;} + void setUUID(const LLUUID &outfit_id) {mUUID = outfit_id;} + LLUUID getUUID() const { return mUUID; } std::string getItemName() {return mOutfitName;} bool isDefaultImage() {return mDefaultImage;} @@ -275,6 +260,7 @@ public: void setHidden(bool hidden) {mHidden = hidden;} private: + LLOutfitGallery* mGallery; LLPointer mTexturep; LLUUID mUUID; LLUUID mImageAssetId; diff --git a/indra/newview/lloutfitobserver.cpp b/indra/newview/lloutfitobserver.cpp index 5bb69367aa..3ec5bcd9fc 100644 --- a/indra/newview/lloutfitobserver.cpp +++ b/indra/newview/lloutfitobserver.cpp @@ -34,7 +34,6 @@ LLOutfitObserver::LLOutfitObserver() : mCOFLastVersion(LLViewerInventoryCategory::VERSION_UNKNOWN) { - mItemNameHash.finalize(); gInventory.addObserver(this); } @@ -83,7 +82,7 @@ bool LLOutfitObserver::checkCOF() return false; bool cof_changed = false; - LLMD5 item_name_hash = gInventory.hashDirectDescendentNames(cof); + LLUUID item_name_hash = gInventory.hashDirectDescendentNames(cof); if (item_name_hash != mItemNameHash) { cof_changed = true; diff --git a/indra/newview/lloutfitobserver.h b/indra/newview/lloutfitobserver.h index 77041db68d..2f136d48e8 100644 --- a/indra/newview/lloutfitobserver.h +++ b/indra/newview/lloutfitobserver.h @@ -28,7 +28,6 @@ #define LL_OUTFITOBSERVER_H #include "llsingleton.h" -#include "llmd5.h" /** * Outfit observer facade that provides simple possibility to subscribe on @@ -78,7 +77,7 @@ protected: bool mLastOutfitDirtiness; - LLMD5 mItemNameHash; + LLUUID mItemNameHash; private: signal_t mBOFReplaced; diff --git a/indra/newview/lloutfitslist.cpp b/indra/newview/lloutfitslist.cpp index 4171fd8822..5c7792b0df 100644 --- a/indra/newview/lloutfitslist.cpp +++ b/indra/newview/lloutfitslist.cpp @@ -35,7 +35,7 @@ #include "llaccordionctrltab.h" #include "llagentwearables.h" #include "llappearancemgr.h" -#include "llagentbenefits.h" +#include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llinventoryfunctions.h" #include "llinventorymodel.h" @@ -122,9 +122,8 @@ void LLOutfitsList::onOpen(const LLSD& info) { if (!mIsInitialized) { - const LLUUID cof = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); // Start observing changes in Current Outfit category. - mCategoriesObserver->addCategory(cof, boost::bind(&LLOutfitsList::onCOFChanged, this)); + LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLOutfitsList::onCOFChanged, this)); } LLOutfitListBase::onOpen(info); @@ -1112,10 +1111,7 @@ LLOutfitListGearMenuBase::LLOutfitListGearMenuBase(LLOutfitListBase* olist) registrar.add("Gear.WearAdd", boost::bind(&LLOutfitListGearMenuBase::onAdd, this)); - registrar.add("Gear.UploadPhoto", boost::bind(&LLOutfitListGearMenuBase::onUploadFoto, this)); - registrar.add("Gear.SelectPhoto", boost::bind(&LLOutfitListGearMenuBase::onSelectPhoto, this)); - registrar.add("Gear.TakeSnapshot", boost::bind(&LLOutfitListGearMenuBase::onTakeSnapshot, this)); - registrar.add("Gear.RemovePhoto", boost::bind(&LLOutfitListGearMenuBase::onRemovePhoto, this)); + registrar.add("Gear.Thumbnail", boost::bind(&LLOutfitListGearMenuBase::onThumbnail, this)); registrar.add("Gear.SortByName", boost::bind(&LLOutfitListGearMenuBase::onChangeSortOrder, this)); enable_registrar.add("Gear.OnEnable", boost::bind(&LLOutfitListGearMenuBase::onEnable, this, _2)); @@ -1232,7 +1228,6 @@ bool LLOutfitListGearMenuBase::onEnable(LLSD::String param) bool LLOutfitListGearMenuBase::onVisible(LLSD::String param) { - getMenu()->getChild("upload_photo")->setLabelArg("[UPLOAD_COST]", std::to_string(LLAgentBenefitsMgr::current().getTextureUploadCost())); const LLUUID& selected_outfit_id = getSelectedOutfitID(); if (selected_outfit_id.isNull()) // no selection or invalid outfit selected { @@ -1251,24 +1246,11 @@ bool LLOutfitListGearMenuBase::onVisible(LLSD::String param) return true; } -void LLOutfitListGearMenuBase::onUploadFoto() +void LLOutfitListGearMenuBase::onThumbnail() { - -} - -void LLOutfitListGearMenuBase::onSelectPhoto() -{ - -} - -void LLOutfitListGearMenuBase::onTakeSnapshot() -{ - -} - -void LLOutfitListGearMenuBase::onRemovePhoto() -{ - + const LLUUID& selected_outfit_id = getSelectedOutfitID(); + LLSD data(selected_outfit_id); + LLFloaterReg::showInstance("change_item_thumbnail", data); } void LLOutfitListGearMenuBase::onChangeSortOrder() @@ -1288,10 +1270,7 @@ void LLOutfitListGearMenu::onUpdateItemsVisibility() if (!mMenu) return; mMenu->setItemVisible("expand", TRUE); mMenu->setItemVisible("collapse", TRUE); - mMenu->setItemVisible("upload_photo", FALSE); - mMenu->setItemVisible("select_photo", FALSE); - mMenu->setItemVisible("take_snapshot", FALSE); - mMenu->setItemVisible("remove_photo", FALSE); + mMenu->setItemVisible("thumbnail", FALSE); // Never visible? mMenu->setItemVisible("sepatator3", FALSE); mMenu->setItemVisible("sort_folders_by_name", FALSE); LLOutfitListGearMenuBase::onUpdateItemsVisibility(); diff --git a/indra/newview/lloutfitslist.h b/indra/newview/lloutfitslist.h index 81be8de94f..66b3165169 100644 --- a/indra/newview/lloutfitslist.h +++ b/indra/newview/lloutfitslist.h @@ -163,10 +163,7 @@ public: protected: virtual void onUpdateItemsVisibility(); - virtual void onUploadFoto(); - virtual void onSelectPhoto(); - virtual void onTakeSnapshot(); - virtual void onRemovePhoto(); + virtual void onThumbnail(); virtual void onChangeSortOrder(); const LLUUID& getSelectedOutfitID(); diff --git a/indra/newview/llpaneleditsky.cpp b/indra/newview/llpaneleditsky.cpp index 761d856aae..839f25761a 100644 --- a/indra/newview/llpaneleditsky.cpp +++ b/indra/newview/llpaneleditsky.cpp @@ -217,7 +217,7 @@ void LLPanelSettingsSkyAtmosTab::refresh() getChild(FIELD_SKY_DENSITY_ICE_LEVEL)->setValue(ice_level); getChild(FIELD_REFLECTION_PROBE_AMBIANCE)->setValue(rp_ambiance); - updateGammaLabel(); + updateGammaLabel(should_auto_adjust); } //------------------------------------------------------------------------- @@ -335,17 +335,19 @@ void LLPanelSettingsSkyAtmosTab::onReflectionProbeAmbianceChanged() } -void LLPanelSettingsSkyAtmosTab::updateGammaLabel() +void LLPanelSettingsSkyAtmosTab::updateGammaLabel(bool auto_adjust) { if (!mSkySettings) return; - F32 ambiance = mSkySettings->getReflectionProbeAmbiance(); + F32 ambiance = mSkySettings->getReflectionProbeAmbiance(auto_adjust); if (ambiance != 0.f) { childSetValue("scene_gamma_label", getString("hdr_string")); + getChild(FIELD_SKY_SCENE_GAMMA)->setToolTip(getString("hdr_tooltip")); } else { childSetValue("scene_gamma_label", getString("brightness_string")); + getChild(FIELD_SKY_SCENE_GAMMA)->setToolTip(std::string()); } } diff --git a/indra/newview/llpaneleditsky.h b/indra/newview/llpaneleditsky.h index 33af667ab7..523cc134a8 100644 --- a/indra/newview/llpaneleditsky.h +++ b/indra/newview/llpaneleditsky.h @@ -80,7 +80,7 @@ private: void onDropletRadiusChanged(); void onIceLevelChanged(); void onReflectionProbeAmbianceChanged(); - void updateGammaLabel(); + void updateGammaLabel(bool auto_adjust = false); }; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 5c209bc4c6..cb69f72288 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -1664,7 +1664,7 @@ class LLMetricSystemHandler : public LLCommandHandler public: LLMetricSystemHandler() : LLCommandHandler("metricsystem", UNTRUSTED_CLICK_ONLY) { } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { // change height units TRUE for meters and FALSE for feet BOOL new_value = (gSavedSettings.getBOOL("HeightUnits") == FALSE) ? TRUE : FALSE; diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index ba379f77d8..cb28fb4770 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -51,6 +51,7 @@ #include "llinventorymodelbackgroundfetch.h" #include "llfloatermediasettings.h" #include "llfloaterreg.h" +#include "llfloatertools.h" #include "lllineeditor.h" #include "llmaterialmgr.h" #include "llmaterialeditor.h" @@ -77,6 +78,7 @@ #include "llviewerregion.h" #include "llviewerstats.h" #include "llvovolume.h" +#include "llvoinventorylistener.h" #include "lluictrlfactory.h" #include "llpluginclassmedia.h" #include "llviewertexturelist.h"// Update sel manager as to which channel we're editing so it can reflect the correct overlay UI @@ -328,7 +330,7 @@ BOOL LLPanelFace::postBuild() pbr_ctrl->setImmediateFilterPermMask(PERM_NONE); pbr_ctrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER); pbr_ctrl->setBakeTextureEnabled(false); - pbr_ctrl->setInventoryPickType(EPickInventoryType::MATERIAL); + pbr_ctrl->setInventoryPickType(LLTextureCtrl::PICK_MATERIAL); } mTextureCtrl = getChild("texture control"); @@ -490,6 +492,15 @@ LLPanelFace::~LLPanelFace() unloadMedia(); } +void LLPanelFace::onVisibilityChange(BOOL new_visibility) +{ + if (new_visibility) + { + gAgent.showLatestFeatureNotification("gltf"); + } + LLPanel::onVisibilityChange(new_visibility); +} + void LLPanelFace::draw() { updateCopyTexButton(); @@ -504,6 +515,7 @@ void LLPanelFace::draw() if (sMaterialOverrideSelection.update()) { setMaterialOverridesFromSelection(); + LLMaterialEditor::updateLive(); } } @@ -520,7 +532,11 @@ void LLPanelFace::sendTexture() { id = mTextureCtrl->getImageAssetID(); } - LLSelectMgr::getInstance()->selectionSetImage(id); + if (!LLSelectMgr::getInstance()->selectionSetImage(id)) + { + // need to refresh value in texture ctrl + refresh(); + } } } @@ -987,16 +1003,19 @@ void LLPanelFace::getState() void LLPanelFace::updateUI(bool force_set_values /*false*/) { //set state of UI to match state of texture entry(ies) (calls setEnabled, setValue, etc, but NOT setVisible) - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + LLViewerObject* objectp = node ? node->getObject() : NULL; - if( objectp + if (objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify()) { BOOL editable = objectp->permModify() && !objectp->isPermanentEnforced(); + BOOL attachment = objectp->isAttachment(); bool has_pbr_material; - updateUIGLTF(objectp, has_pbr_material, force_set_values); + bool has_faces_without_pbr; + updateUIGLTF(objectp, has_pbr_material, has_faces_without_pbr, force_set_values); const bool has_material = !has_pbr_material; @@ -1017,9 +1036,68 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } } + // *NOTE: The "identical" variable is currently only used to decide if + // the texgen control should be tentative - this is not used by GLTF + // materials. -Cosmic;2022-11-09 + bool identical = true; // true because it is anded below + bool identical_diffuse = false; + bool identical_norm = false; + bool identical_spec = false; + + LLTextureCtrl *texture_ctrl = getChild("texture control"); + LLTextureCtrl *shinytexture_ctrl = getChild("shinytexture control"); + LLTextureCtrl *bumpytexture_ctrl = getChild("bumpytexture control"); + + LLUUID id; + LLUUID normmap_id; + LLUUID specmap_id; + + LLSelectedTE::getTexId(id, identical_diffuse); + LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm); + LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec); + + static S32 selected_te = -1; + static LLUUID prev_obj_id; + if ((LLToolFace::getInstance() == LLToolMgr::getInstance()->getCurrentTool()) && + !LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) + { + S32 new_selection = -1; // Don't use getLastSelectedTE, it could have been deselected + S32 num_tes = llmin((S32)objectp->getNumTEs(), (S32)objectp->getNumFaces()); + for (S32 te = 0; te < num_tes; ++te) + { + if (node->isTESelected(te)) + { + new_selection = te; + break; + } + } + + if ((new_selection != selected_te) + || (prev_obj_id != objectp->getID())) + { + bool te_has_media = objectp->getTE(new_selection) && objectp->getTE(new_selection)->hasMedia(); + bool te_has_pbr = objectp->getRenderMaterialID(new_selection).notNull(); + + if (te_has_pbr && !((mComboMatMedia->getCurrentIndex() == MATMEDIA_MEDIA) && te_has_media)) + { + mComboMatMedia->selectNthItem(MATMEDIA_PBR); + } + else if (te_has_media) + { + mComboMatMedia->selectNthItem(MATMEDIA_MEDIA); + } + else if (id.notNull() || normmap_id.notNull() || specmap_id.notNull()) + { + mComboMatMedia->selectNthItem(MATMEDIA_MATERIAL); + } + selected_te = new_selection; + prev_obj_id = objectp->getID(); + } + } + mComboMatMedia->setEnabled(editable); - LLRadioGroup* radio_mat_type = getChild("radio_material_type"); + LLRadioGroup* radio_mat_type = getChild("radio_material_type"); if (radio_mat_type->getSelectedIndex() < MATTYPE_DIFFUSE) { radio_mat_type->selectNthItem(MATTYPE_DIFFUSE); @@ -1038,34 +1116,18 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChildView("checkbox_sync_settings")->setEnabled(editable); childSetValue("checkbox_sync_settings", gSavedSettings.getBOOL("SyncMaterialSettings")); - updateVisibility(); + updateVisibility(objectp); - // *NOTE: The "identical" variable is currently only used to decide if - // the texgen control should be tentative - this is not used by GLTF - // materials. -Cosmic;2022-11-09 - bool identical = true; // true because it is anded below - bool identical_diffuse = false; - bool identical_norm = false; - bool identical_spec = false; - - LLTextureCtrl* texture_ctrl = getChild("texture control"); - LLTextureCtrl* shinytexture_ctrl = getChild("shinytexture control"); - LLTextureCtrl* bumpytexture_ctrl = getChild("bumpytexture control"); - - LLUUID id; - LLUUID normmap_id; - LLUUID specmap_id; - // Color swatch { getChildView("color label")->setEnabled(editable); } - LLColorSwatchCtrl* color_swatch = findChild("colorswatch"); + LLColorSwatchCtrl* color_swatch = findChild("colorswatch"); - LLColor4 color = LLColor4::white; - bool identical_color = false; + LLColor4 color = LLColor4::white; + bool identical_color = false; - if(color_swatch) + if (color_swatch) { LLSelectedTE::getColor(color, identical_color); LLColor4 prev_color = color_swatch->get(); @@ -1085,9 +1147,6 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild("ColorTrans")->setValue(editable ? transparency : 0); getChildView("ColorTrans")->setEnabled(editable && has_material); - // Specular map - LLSelectedTEMaterial::getSpecularID(specmap_id, identical_spec); - U8 shiny = 0; bool identical_shiny = false; @@ -1099,7 +1158,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) LLCtrlSelectionInterface* combobox_shininess = childGetSelectionInterface("combobox shininess"); if (combobox_shininess) - { + { combobox_shininess->selectNthItem((S32)shiny); } @@ -1119,8 +1178,8 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild("shinycolorswatch")->setTentative(!identical_spec); LLColorSwatchCtrl* mShinyColorSwatch = getChild("shinycolorswatch"); - if(mShinyColorSwatch) - { + if (mShinyColorSwatch) + { mShinyColorSwatch->setValid(editable); mShinyColorSwatch->setEnabled( editable ); mShinyColorSwatch->setCanApplyImmediately( editable ); @@ -1138,50 +1197,45 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) bumpy = norm_map_id.isNull() ? bumpy : BUMPY_TEXTURE; if (combobox_bumpiness) - { + { combobox_bumpiness->selectNthItem((S32)bumpy); - } + } else - { + { LL_WARNS() << "failed childGetSelectionInterface for 'combobox bumpiness'" << LL_ENDL; - } + } getChildView("combobox bumpiness")->setEnabled(editable); getChild("combobox bumpiness")->setTentative(!identical_bumpy); getChildView("label bumpiness")->setEnabled(editable); - } + } // Texture { - LLSelectedTE::getTexId(id,identical_diffuse); - - // Normal map - LLSelectedTEMaterial::getNormalID(normmap_id, identical_norm); - mIsAlpha = FALSE; LLGLenum image_format = GL_RGB; bool identical_image_format = false; LLSelectedTE::getImageFormat(image_format, identical_image_format); - mIsAlpha = FALSE; - switch (image_format) - { - case GL_RGBA: - case GL_ALPHA: - { - mIsAlpha = TRUE; - } - break; - - case GL_RGB: break; - default: - { - LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; - } - break; + mIsAlpha = FALSE; + switch (image_format) + { + case GL_RGBA: + case GL_ALPHA: + { + mIsAlpha = TRUE; } + break; - if(LLViewerMedia::getInstance()->textureHasMedia(id)) + case GL_RGB: break; + default: + { + LL_WARNS() << "Unexpected tex format in LLPanelFace...resorting to no alpha" << LL_ENDL; + } + break; + } + + if (LLViewerMedia::getInstance()->textureHasMedia(id)) { getChildView("button align")->setEnabled(editable); } @@ -1233,7 +1287,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } else if (id.isNull()) { - // None selected + // None selected texture_ctrl->setTentative(FALSE); texture_ctrl->setEnabled(FALSE); texture_ctrl->setImageAssetID(LLUUID::null); @@ -1246,7 +1300,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } else { - // Tentative: multiple selected with different textures + // Tentative: multiple selected with different textures texture_ctrl->setTentative(TRUE); texture_ctrl->setEnabled(editable && !has_pbr_material); texture_ctrl->setImageAssetID(id); @@ -1257,7 +1311,18 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) texture_ctrl->setBakeTextureEnabled(TRUE); } - + + if (attachment) + { + // attachments are in world and in inventory, + // server doesn't support changing permissions + // in such case + texture_ctrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); + } + else + { + texture_ctrl->setImmediateFilterPermMask(PERM_NONE); + } } if (shinytexture_ctrl) @@ -1265,6 +1330,15 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) shinytexture_ctrl->setTentative( !identical_spec ); shinytexture_ctrl->setEnabled( editable && !has_pbr_material); shinytexture_ctrl->setImageAssetID( specmap_id ); + + if (attachment) + { + shinytexture_ctrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); + } + else + { + shinytexture_ctrl->setImmediateFilterPermMask(PERM_NONE); + } } if (bumpytexture_ctrl) @@ -1272,6 +1346,15 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) bumpytexture_ctrl->setTentative( !identical_norm ); bumpytexture_ctrl->setEnabled( editable && !has_pbr_material); bumpytexture_ctrl->setImageAssetID( normmap_id ); + + if (attachment) + { + bumpytexture_ctrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); + } + else + { + bumpytexture_ctrl->setImmediateFilterPermMask(PERM_NONE); + } } } @@ -1515,15 +1598,14 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) combobox_texgen->selectNthItem(((S32)selected_texgen) >> 1); } else - { + { LL_WARNS() << "failed childGetSelectionInterface for 'combobox texgen'" << LL_ENDL; - } + } getChildView("combobox texgen")->setEnabled(editable); getChild("combobox texgen")->setTentative(!identical); getChildView("tex gen")->setEnabled(editable); - - } + } { U8 fullbright_flag = 0; @@ -1534,7 +1616,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild("checkbox fullbright")->setValue((S32)(fullbright_flag != 0)); getChildView("checkbox fullbright")->setEnabled(editable && !has_pbr_material); getChild("checkbox fullbright")->setTentative(!identical_fullbright); - getChild("combobox matmedia")->setEnabledByValue("Materials", !has_pbr_material); + mComboMatMedia->setEnabledByValue("Materials", !has_pbr_material); } // Repeats per meter @@ -1553,7 +1635,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) LLComboBox* mComboTexGen = getChild("combobox texgen"); if (mComboTexGen) - { + { S32 index = mComboTexGen ? mComboTexGen->getCurrentIndex() : 0; bool enabled = editable && (index != 1); bool identical_repeats = true; @@ -1649,14 +1731,14 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) if (!mIsAlpha) { // ... unless there is no alpha channel in the texture, in which case alpha mode MUST ebe none alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE; - } + } combobox_alphamode->selectNthItem(alpha_mode); - } - else - { + } + else + { LL_WARNS() << "failed childGetSelectionInterface for 'combobox alphamode'" << LL_ENDL; - } + } getChild("maskcutoff")->setValue(material->getAlphaMaskCutoff()); updateAlphaControls(); @@ -1668,15 +1750,15 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) texture_ctrl->setImageAssetID(material->getSpecularID()); if (!material->getSpecularID().isNull() && (shiny == SHINY_TEXTURE)) - { + { material->getSpecularOffset(offset_x,offset_y); material->getSpecularRepeat(repeat_x,repeat_y); if (identical_planar_texgen) - { + { repeat_x *= 2.0f; repeat_y *= 2.0f; - } + } rot = material->getSpecularRotation(); getChild("shinyScaleU")->setValue(repeat_x); @@ -1688,7 +1770,7 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) getChild("environment")->setValue(material->getEnvironmentIntensity()); updateShinyControls(!material->getSpecularID().isNull(), true); - } + } // Assert desired colorswatch color to match material AFTER updateShinyControls // to avoid getting overwritten with the default on some UI state changes. @@ -1756,14 +1838,14 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) pbr_ctrl->setEnabled(FALSE); } LLTextureCtrl* texture_ctrl = getChild("texture control"); - if(texture_ctrl) + if (texture_ctrl) { texture_ctrl->setImageAssetID( LLUUID::null ); texture_ctrl->setEnabled( FALSE ); // this is a LLUICtrl, but we don't want it to have keyboard focus so we add it as a child, not a ctrl. // texture_ctrl->setValid(FALSE); } LLColorSwatchCtrl* mColorSwatch = getChild("colorswatch"); - if(mColorSwatch) + if (mColorSwatch) { mColorSwatch->setEnabled( FALSE ); mColorSwatch->setFallbackImage(LLUI::getUIImage("locked_image.j2c") ); @@ -1798,36 +1880,96 @@ void LLPanelFace::updateUI(bool force_set_values /*false*/) } } -void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, bool force_set_values) +// One-off listener that updates the build floater UI when the prim inventory updates +class PBRPickerItemListener : public LLVOInventoryListener +{ +protected: + LLViewerObject* mObjectp; + bool mChangePending = true; +public: + + PBRPickerItemListener(LLViewerObject* object) + : mObjectp(object) + { + registerVOInventoryListener(mObjectp, nullptr); + } + + const bool isListeningFor(const LLViewerObject* objectp) const + { + return mChangePending && (objectp == mObjectp); + } + + void inventoryChanged(LLViewerObject* object, + LLInventoryObject::object_list_t* inventory, + S32 serial_num, + void* user_data) override + { + if (gFloaterTools) + { + gFloaterTools->dirty(); + } + removeVOInventoryListener(); + mChangePending = false; + } + + ~PBRPickerItemListener() + { + removeVOInventoryListener(); + mChangePending = false; + } +}; + +void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, bool& has_faces_without_pbr, bool force_set_values) { has_pbr_material = false; - const bool editable = objectp->permModify() && !objectp->isPermanentEnforced(); bool has_pbr_capabilities = LLMaterialEditor::capabilitiesAvailable(); + bool identical_pbr = true; + const bool settable = has_pbr_capabilities && objectp->permModify() && !objectp->isPermanentEnforced(); + const bool editable = LLMaterialEditor::canModifyObjectsMaterial(); + const bool saveable = LLMaterialEditor::canSaveObjectsMaterial(); // pbr material LLTextureCtrl* pbr_ctrl = findChild("pbr_control"); if (pbr_ctrl) { LLUUID pbr_id; - bool identical_pbr; - LLSelectedTE::getPbrMaterialId(pbr_id, identical_pbr); - - has_pbr_material = pbr_id.notNull(); + LLSelectedTE::getPbrMaterialId(pbr_id, identical_pbr, has_pbr_material, has_faces_without_pbr); pbr_ctrl->setTentative(identical_pbr ? FALSE : TRUE); - pbr_ctrl->setEnabled(editable && has_pbr_capabilities); + pbr_ctrl->setEnabled(settable); pbr_ctrl->setImageAssetID(pbr_id); + + if (objectp->isAttachment()) + { + pbr_ctrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER | PERM_MODIFY); + } + else + { + pbr_ctrl->setImmediateFilterPermMask(PERM_NONE); + } } - getChildView("pbr_from_inventory")->setEnabled(editable && has_pbr_capabilities); - getChildView("edit_selected_pbr")->setEnabled(editable && has_pbr_material && has_pbr_capabilities); - getChildView("save_selected_pbr")->setEnabled(objectp->permCopy() && has_pbr_material && has_pbr_capabilities); + getChildView("pbr_from_inventory")->setEnabled(settable); + getChildView("edit_selected_pbr")->setEnabled(editable && !has_faces_without_pbr); + getChildView("save_selected_pbr")->setEnabled(saveable && identical_pbr); + if (objectp->isInventoryPending()) + { + // Reuse the same listener when possible + if (!mInventoryListener || !mInventoryListener->isListeningFor(objectp)) + { + mInventoryListener = std::make_unique(objectp); + } + } + else + { + mInventoryListener = nullptr; + } const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled(); if (show_pbr) { - const bool new_state = has_pbr_capabilities && has_pbr_material; + const bool new_state = has_pbr_capabilities && has_pbr_material && !has_faces_without_pbr; LLUICtrl* gltfCtrlTextureScaleU = getChild("gltfTextureScaleU"); LLUICtrl* gltfCtrlTextureScaleV = getChild("gltfTextureScaleV"); @@ -1847,9 +1989,10 @@ void LLPanelFace::updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, } } -void LLPanelFace::updateVisibilityGLTF() +void LLPanelFace::updateVisibilityGLTF(LLViewerObject* objectp /*= nullptr */) { const bool show_pbr = mComboMatMedia->getCurrentIndex() == MATMEDIA_PBR && mComboMatMedia->getEnabled(); + const bool inventory_pending = objectp && objectp->isInventoryPending(); LLRadioGroup* radio_pbr_type = findChild("radio_pbr_type"); radio_pbr_type->setVisible(show_pbr); @@ -1860,8 +2003,9 @@ void LLPanelFace::updateVisibilityGLTF() getChildView("pbr_control")->setVisible(show_pbr_render_material_id); getChildView("pbr_from_inventory")->setVisible(show_pbr_render_material_id); - getChildView("edit_selected_pbr")->setVisible(show_pbr_render_material_id); - getChildView("save_selected_pbr")->setVisible(show_pbr_render_material_id); + getChildView("edit_selected_pbr")->setVisible(show_pbr_render_material_id && !inventory_pending); + getChildView("save_selected_pbr")->setVisible(show_pbr_render_material_id && !inventory_pending); + getChildView("material_permissions_loading_label")->setVisible(show_pbr_render_material_id && inventory_pending); getChildView("gltfTextureScaleU")->setVisible(show_pbr); getChildView("gltfTextureScaleV")->setVisible(show_pbr); @@ -1875,10 +2019,10 @@ void LLPanelFace::updateCopyTexButton() LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); mMenuClipboardTexture->setEnabled(objectp && objectp->getPCode() == LL_PCODE_VOLUME && objectp->permModify() && !objectp->isPermanentEnforced() && !objectp->isInventoryPending() - && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1)); + && (LLSelectMgr::getInstance()->getSelection()->getObjectCount() == 1) + && LLMaterialEditor::canClipboardObjectsMaterial()); std::string tooltip = (objectp && objectp->isInventoryPending()) ? LLTrans::getString("LoadingContents") : getString("paste_options"); mMenuClipboardTexture->setToolTip(tooltip); - } void LLPanelFace::refresh() @@ -2703,7 +2847,7 @@ void LLPanelFace::onCommitMaterialsMedia(LLUICtrl* ctrl, void* userdata) self->refreshMedia(); } -void LLPanelFace::updateVisibility() +void LLPanelFace::updateVisibility(LLViewerObject* objectp /* = nullptr */) { LLRadioGroup* radio_mat_type = findChild("radio_material_type"); LLRadioGroup* radio_pbr_type = findChild("radio_pbr_type"); @@ -2794,7 +2938,7 @@ void LLPanelFace::updateVisibility() getChild("rptctrl")->setVisible(show_material || show_media); // PBR controls - updateVisibilityGLTF(); + updateVisibilityGLTF(objectp); } // static @@ -3032,7 +3176,11 @@ void LLPanelFace::onCommitPbr(const LLSD& data) { id = pbr_ctrl->getImageAssetID(); } - LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id); + if (!LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id)) + { + // If failed to set material, refresh pbr_ctrl's value + refresh(); + } } } @@ -3056,8 +3204,10 @@ void LLPanelFace::onSelectPbr(const LLSD& data) { id = pbr_ctrl->getImageAssetID(); } - LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id); - LLSelectedTEMaterial::setMaterialID(this, id); + if (!LLSelectMgr::getInstance()->selectionSetGLTFMaterial(id)) + { + refresh(); + } } } @@ -4042,7 +4192,8 @@ void LLPanelFace::onCopyTexture() || objectp->getPCode() != LL_PCODE_VOLUME || !objectp->permModify() || objectp->isPermanentEnforced() - || selected_count > 1) + || selected_count > 1 + || !LLMaterialEditor::canClipboardObjectsMaterial()) { return; } @@ -4237,7 +4388,8 @@ void LLPanelFace::onPasteTexture() || objectp->getPCode() != LL_PCODE_VOLUME || !objectp->permModify() || objectp->isPermanentEnforced() - || selected_count > 1) + || selected_count > 1 + || !LLMaterialEditor::canClipboardObjectsMaterial()) { // not supposed to happen LL_WARNS() << "Failed to paste texture due to missing or wrong selection" << LL_ENDL; @@ -4666,13 +4818,6 @@ void LLPanelFace::updateGLTFTextureTransform(float value, U32 pbr_type, std::fun edit(&new_transform); } }); - - LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); - if (node) - { - LLViewerObject* object = node->getObject(); - sMaterialOverrideSelection.setObjectUpdatePending(object->getID(), node->getLastSelectedTE()); - } } void LLPanelFace::setMaterialOverridesFromSelection() @@ -4785,17 +4930,22 @@ bool LLPanelFace::Selection::update() return changed; } -void LLPanelFace::Selection::setObjectUpdatePending(const LLUUID &object_id, S32 side) -{ - mPendingObjectID = object_id; - mPendingSide = side; -} - void LLPanelFace::Selection::onSelectedObjectUpdated(const LLUUID& object_id, S32 side) { - if (object_id == mSelectedObjectID && side == mSelectedSide) + if (object_id == mSelectedObjectID) { - mChanged = true; + if (side == mLastSelectedSide) + { + mChanged = true; + } + else if (mLastSelectedSide == -1) // if last selected face was deselected + { + LLSelectNode* node = LLSelectMgr::getInstance()->getSelection()->getFirstNode(); + if (node && node->isTESelected(side)) + { + mChanged = true; + } + } } } @@ -4808,8 +4958,9 @@ bool LLPanelFace::Selection::compareSelection() mNeedsSelectionCheck = false; const S32 old_object_count = mSelectedObjectCount; + const S32 old_te_count = mSelectedTECount; const LLUUID old_object_id = mSelectedObjectID; - const S32 old_side = mSelectedSide; + const S32 old_side = mLastSelectedSide; LLObjectSelectionHandle selection = LLSelectMgr::getInstance()->getSelection(); LLSelectNode* node = selection->getFirstNode(); @@ -4817,17 +4968,23 @@ bool LLPanelFace::Selection::compareSelection() { LLViewerObject* object = node->getObject(); mSelectedObjectCount = selection->getObjectCount(); + mSelectedTECount = selection->getTECount(); mSelectedObjectID = object->getID(); - mSelectedSide = node->getLastSelectedTE(); + mLastSelectedSide = node->getLastSelectedTE(); } else { mSelectedObjectCount = 0; + mSelectedTECount = 0; mSelectedObjectID = LLUUID::null; - mSelectedSide = -1; + mLastSelectedSide = -1; } - const bool selection_changed = old_object_count != mSelectedObjectCount || old_object_id != mSelectedObjectID || old_side != mSelectedSide; + const bool selection_changed = + old_object_count != mSelectedObjectCount + || old_te_count != mSelectedTECount + || old_object_id != mSelectedObjectID + || old_side != mLastSelectedSide; mChanged = mChanged || selection_changed; return selection_changed; } @@ -4946,23 +5103,24 @@ void LLPanelFace::onPbrSelectionChanged(LLInventoryItem* itemp) LLSaleInfo sale_info; LLSelectMgr::instance().selectGetSaleInfo(sale_info); - bool can_copy = itemp->getPermissions().allowCopyBy(gAgentID); // do we have perm to copy this texture? - bool can_transfer = itemp->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID); // do we have perm to transfer this texture? - bool is_object_owner = gAgentID == obj_owner_id; // does object for which we are going to apply texture belong to the agent? - bool not_for_sale = !sale_info.isForSale(); // is object for which we are going to apply texture not for sale? + bool can_copy = itemp->getPermissions().allowCopyBy(gAgentID); // do we have perm to copy this material? + bool can_transfer = itemp->getPermissions().allowOperationBy(PERM_TRANSFER, gAgentID); // do we have perm to transfer this material? + bool can_modify = itemp->getPermissions().allowOperationBy(PERM_MODIFY, gAgentID); // do we have perm to transfer this material? + bool is_object_owner = gAgentID == obj_owner_id; // does object for which we are going to apply material belong to the agent? + bool not_for_sale = !sale_info.isForSale(); // is object for which we are going to apply material not for sale? - if (can_copy && can_transfer) + if (can_copy && can_transfer && can_modify) { pbr_ctrl->setCanApply(true, true); return; } - // if texture has (no-transfer) attribute it can be applied only for object which we own and is not for sale + // if material has (no-transfer) attribute it can be applied only for object which we own and is not for sale pbr_ctrl->setCanApply(false, can_transfer ? true : is_object_owner && not_for_sale); if (gSavedSettings.getBOOL("TextureLivePreview")) { - LLNotificationsUtil::add("LivePreviewUnavailable"); + LLNotificationsUtil::add("LivePreviewUnavailablePBR"); } } } @@ -5046,16 +5204,75 @@ void LLPanelFace::LLSelectedTE::getTexId(LLUUID& id, bool& identical) identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, id ); } -void LLPanelFace::LLSelectedTE::getPbrMaterialId(LLUUID& id, bool& identical) +void LLPanelFace::LLSelectedTE::getPbrMaterialId(LLUUID& id, bool& identical, bool& has_faces_with_pbr, bool& has_faces_without_pbr) { - struct LLSelectedTEGetmatId : public LLSelectedTEGetFunctor + struct LLSelectedTEGetmatId : public LLSelectedTEFunctor { - LLUUID get(LLViewerObject* object, S32 te_index) + LLSelectedTEGetmatId() + : mHasFacesWithoutPBR(false) + , mHasFacesWithPBR(false) + , mIdenticalId(true) + , mIdenticalOverride(true) + , mInitialized(false) + , mMaterialOverride(LLGLTFMaterial::sDefault) { - return object->getRenderMaterialID(te_index); } + bool apply(LLViewerObject* object, S32 te_index) override + { + LLUUID pbr_id = object->getRenderMaterialID(te_index); + if (pbr_id.isNull()) + { + mHasFacesWithoutPBR = true; + } + else + { + mHasFacesWithPBR = true; + } + if (mInitialized) + { + if (mPBRId != pbr_id) + { + mIdenticalId = false; + } + + LLGLTFMaterial* te_override = object->getTE(te_index)->getGLTFMaterialOverride(); + if (te_override) + { + LLGLTFMaterial override = *te_override; + override.sanitizeAssetMaterial(); + mIdenticalOverride &= (override == mMaterialOverride); + } + else + { + mIdenticalOverride &= (mMaterialOverride == LLGLTFMaterial::sDefault); + } + } + else + { + mInitialized = true; + mPBRId = pbr_id; + LLGLTFMaterial* override = object->getTE(te_index)->getGLTFMaterialOverride(); + if (override) + { + mMaterialOverride = *override; + mMaterialOverride.sanitizeAssetMaterial(); + } + } + return true; + } + bool mHasFacesWithoutPBR; + bool mHasFacesWithPBR; + bool mIdenticalId; + bool mIdenticalOverride; + bool mInitialized; + LLGLTFMaterial mMaterialOverride; + LLUUID mPBRId; } func; - identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue(&func, id); + LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func); + id = func.mPBRId; + identical = func.mIdenticalId && func.mIdenticalOverride; + has_faces_with_pbr = func.mHasFacesWithPBR; + has_faces_without_pbr = func.mHasFacesWithoutPBR; } void LLPanelFace::LLSelectedTEMaterial::getCurrent(LLMaterialPtr& material_ptr, bool& identical_material) diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index 106259b84a..d36662c11b 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -35,6 +35,8 @@ #include "lltextureentry.h" #include "llselectmgr.h" +#include + class LLButton; class LLCheckBoxCtrl; class LLColorSwatchCtrl; @@ -51,6 +53,8 @@ class LLMaterialID; class LLMediaCtrl; class LLMenuButton; +class PBRPickerItemListener; + // Represents an edit for use in replicating the op across one or more materials in the selection set. // // The apply function optionally performs the edit which it implements @@ -105,6 +109,7 @@ public: static void onMaterialOverrideReceived(const LLUUID& object_id, S32 side); + /*virtual*/ void onVisibilityChange(BOOL new_visibility); /*virtual*/ void draw(); LLMaterialPtr createDefaultMaterial(LLMaterialPtr current_material) @@ -138,7 +143,7 @@ protected: void sendTexGen(); // applies and sends bump map void sendShiny(U32 shininess); // applies and sends shininess void sendFullbright(); // applies and sends full bright - void sendGlow(); + void sendGlow(); void alignTestureLayer(); void updateCopyTexButton(); @@ -260,7 +265,6 @@ protected: private: - bool isAlpha() { return mIsAlpha; } // Convenience funcs to keep the visual flack to a minimum @@ -269,10 +273,10 @@ private: LLUUID getCurrentSpecularMap(); U32 getCurrentShininess(); U32 getCurrentBumpiness(); - U8 getCurrentDiffuseAlphaMode(); - U8 getCurrentAlphaMaskCutoff(); - U8 getCurrentEnvIntensity(); - U8 getCurrentGlossiness(); + U8 getCurrentDiffuseAlphaMode(); + U8 getCurrentAlphaMaskCutoff(); + U8 getCurrentEnvIntensity(); + U8 getCurrentGlossiness(); F32 getCurrentBumpyRot(); F32 getCurrentBumpyScaleU(); F32 getCurrentBumpyScaleV(); @@ -293,7 +297,7 @@ private: // // Do NOT call updateUI from within this function. // - void updateVisibility(); + void updateVisibility(LLViewerObject* objectp = nullptr); // Hey look everyone, a type-safe alternative to copy and paste! :) // @@ -348,7 +352,7 @@ private: U32 new_alpha_mode = new_material->getDiffuseAlphaMode(); LLUUID new_normal_map_id = new_material->getNormalID(); - LLUUID new_spec_map_id = new_material->getSpecularID(); + LLUUID new_spec_map_id = new_material->getSpecularID(); if ((new_alpha_mode == LLMaterial::DIFFUSE_ALPHA_MODE_BLEND) && !is_alpha_face) { @@ -453,8 +457,8 @@ private: void onTextureSelectionChanged(LLInventoryItem* itemp); void onPbrSelectionChanged(LLInventoryItem* itemp); - void updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, bool force_set_values); - void updateVisibilityGLTF(); + void updateUIGLTF(LLViewerObject* objectp, bool& has_pbr_material, bool& has_faces_without_pbr, bool force_set_values); + void updateVisibilityGLTF(LLViewerObject* objectp = nullptr); void updateSelectedGLTFMaterials(std::function func); void updateGLTFTextureTransform(float value, U32 pbr_type, std::function edit); @@ -483,7 +487,6 @@ private: // Prevents update() returning true until the provided object is // updated. Necessary to prevent controls updating when the mouse is // held down. - void setObjectUpdatePending(const LLUUID &object_id, S32 side); void setDirty() { mChanged = true; }; // Callbacks @@ -498,15 +501,15 @@ private: boost::signals2::scoped_connection mSelectConnection; bool mNeedsSelectionCheck = true; S32 mSelectedObjectCount = 0; + S32 mSelectedTECount = 0; LLUUID mSelectedObjectID; - S32 mSelectedSide = -1; - - LLUUID mPendingObjectID; - S32 mPendingSide = -1; + S32 mLastSelectedSide = -1; }; static Selection sMaterialOverrideSelection; + std::unique_ptr mInventoryListener; + public: #if defined(DEF_GET_MAT_STATE) #undef DEF_GET_MAT_STATE @@ -587,17 +590,15 @@ public: DEF_EDIT_MAT_STATE(LLUUID,const LLUUID&,setNormalID); DEF_EDIT_MAT_STATE(LLUUID,const LLUUID&,setSpecularID); DEF_EDIT_MAT_STATE(LLColor4U, const LLColor4U&,setSpecularLightColor); - DEF_EDIT_MAT_STATE(LLUUID, const LLUUID&, setMaterialID); }; class LLSelectedTE { public: - static void getFace(class LLFace*& face_to_return, bool& identical_face); static void getImageFormat(LLGLenum& image_format_to_return, bool& identical_face); static void getTexId(LLUUID& id, bool& identical); - static void getPbrMaterialId(LLUUID& id, bool& identical); + static void getPbrMaterialId(LLUUID& id, bool& identical, bool& has_pbr, bool& has_faces_without_pbr); static void getObjectScaleS(F32& scale_s, bool& identical); static void getObjectScaleT(F32& scale_t, bool& identical); static void getMaxDiffuseRepeats(F32& repeats, bool& identical); diff --git a/indra/newview/llpanellandmarkinfo.cpp b/indra/newview/llpanellandmarkinfo.cpp index 834e664723..cc3c51dd83 100644 --- a/indra/newview/llpanellandmarkinfo.cpp +++ b/indra/newview/llpanellandmarkinfo.cpp @@ -111,9 +111,9 @@ void LLPanelLandmarkInfo::setInfoType(EInfoType type) } // Sets CREATE_LANDMARK infotype and creates landmark at desired folder -void LLPanelLandmarkInfo::setInfoAndCreateLandmark(const LLUUID& fodler_id) +void LLPanelLandmarkInfo::setInfoAndCreateLandmark(const LLUUID& folder_id) { - setInfoType(CREATE_LANDMARK, fodler_id); + setInfoType(CREATE_LANDMARK, folder_id); } void LLPanelLandmarkInfo::setInfoType(EInfoType type, const LLUUID &folder_id) diff --git a/indra/newview/llpanellandmarkinfo.h b/indra/newview/llpanellandmarkinfo.h index 46e2a1935b..8802ce066e 100644 --- a/indra/newview/llpanellandmarkinfo.h +++ b/indra/newview/llpanellandmarkinfo.h @@ -48,7 +48,7 @@ public: /*virtual*/ void setInfoType(EInfoType type); // Sets CREATE_LANDMARK infotype and creates landmark at desired folder - void setInfoAndCreateLandmark(const LLUUID& fodler_id); + void setInfoAndCreateLandmark(const LLUUID& folder_id); /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index b14fdbf38e..49756a4e09 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -97,7 +97,7 @@ class LLLoginLocationAutoHandler : public LLCommandHandler public: // don't allow from external browsers LLLoginLocationAutoHandler() : LLCommandHandler("location_login", UNTRUSTED_BLOCK) { } - bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { @@ -1110,11 +1110,6 @@ void LLPanelLogin::onRememberPasswordCheck(void*) std::string grid(LLGridManager::getInstance()->getGridId()); std::string user_id(cred->userID()); - if (!remember_password) - { - gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id); - gSecAPIHandler->syncProtectedMap(); - } } } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index b332de3f98..20241aac24 100644 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -37,8 +37,8 @@ #include "llfilepicker.h" #include "llinventorybridge.h" #include "llinventoryfunctions.h" +#include "llinventorygallery.h" #include "llinventorymodelbackgroundfetch.h" -#include "llinventorypanel.h" #include "llfiltereditor.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterreg.h" @@ -53,12 +53,14 @@ #include "llspinctrl.h" #include "lltoggleablemenu.h" #include "lltooldraganddrop.h" +#include "lltrans.h" #include "llviewermenu.h" #include "llviewertexturelist.h" #include "llsidepanelinventory.h" #include "llfolderview.h" #include "llradiogroup.h" #include "llenvironment.h" +#include "llweb.h" const std::string FILTERS_FILENAME("filters.xml"); @@ -113,7 +115,13 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p) mMenuGearDefault(NULL), mMenuVisibility(NULL), mMenuAddHandle(), - mNeedUploadCost(true) + mNeedUploadCost(true), + mMenuViewDefault(NULL), + mSingleFolderMode(false), + mForceShowInvLayout(false), + mViewMode(MODE_COMBINATION), + mListViewRootUpdatedConnection(), + mGalleryRootUpdatedConnection() { // Menu Callbacks (non contex menus) mCommitCallbackRegistrar.add("Inventory.DoToSelected", boost::bind(&LLPanelMainInventory::doToSelected, this, _2)); @@ -124,7 +132,6 @@ LLPanelMainInventory::LLPanelMainInventory(const LLPanel::Params& p) mCommitCallbackRegistrar.add("Inventory.ShowFilters", boost::bind(&LLPanelMainInventory::toggleFindOptions, this)); mCommitCallbackRegistrar.add("Inventory.ResetFilters", boost::bind(&LLPanelMainInventory::resetFilters, this)); mCommitCallbackRegistrar.add("Inventory.SetSortBy", boost::bind(&LLPanelMainInventory::setSortBy, this, _2)); - mCommitCallbackRegistrar.add("Inventory.Share", boost::bind(&LLAvatarActions::shareWithAvatars, this)); mEnableCallbackRegistrar.add("Inventory.EnvironmentEnabled", [](LLUICtrl *, const LLSD &) { return LLPanelMainInventory::hasSettingsInventory(); }); mEnableCallbackRegistrar.add("Inventory.MaterialsEnabled", [](LLUICtrl *, const LLSD &) { return LLPanelMainInventory::hasMaterialsInventory(); }); @@ -193,7 +200,7 @@ BOOL LLPanelMainInventory::postBuild() } // Now load the stored settings from disk, if available. std::string filterSaveName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, FILTERS_FILENAME)); - LL_INFOS() << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL; + LL_INFOS("Inventory") << "LLPanelMainInventory::init: reading from " << filterSaveName << LL_ENDL; llifstream file(filterSaveName.c_str()); LLSD savedFilterState; if (file.is_open()) @@ -238,6 +245,34 @@ BOOL LLPanelMainInventory::postBuild() mGearMenuButton = getChild("options_gear_btn"); mVisibilityMenuButton = getChild("options_visibility_btn"); + mViewMenuButton = getChild("view_btn"); + + mBackBtn = getChild("back_btn"); + mForwardBtn = getChild("forward_btn"); + mUpBtn = getChild("up_btn"); + mViewModeBtn = getChild("view_mode_btn"); + mNavigationBtnsPanel = getChild("nav_buttons"); + + mDefaultViewPanel = getChild("default_inventory_panel"); + mCombinationViewPanel = getChild("combination_view_inventory"); + mCombinationGalleryLayoutPanel = getChild("comb_gallery_layout"); + mCombinationListLayoutPanel = getChild("comb_inventory_layout"); + mCombinationLayoutStack = getChild("combination_view_stack"); + + mCombinationInventoryPanel = getChild("comb_single_folder_inv"); + LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter(); + comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS); + comb_inv_filter.markDefault(); + mCombinationInventoryPanel->setSelectCallback(boost::bind(&LLPanelMainInventory::onCombinationInventorySelectionChanged, this, _1, _2)); + mListViewRootUpdatedConnection = mCombinationInventoryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, false)); + + mCombinationGalleryPanel = getChild("comb_gallery_view_inv"); + mCombinationGalleryPanel->setSortOrder(mCombinationInventoryPanel->getSortOrder()); + LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter(); + comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_ONLY_THUMBNAILS); + comb_gallery_filter.markDefault(); + mGalleryRootUpdatedConnection = mCombinationGalleryPanel->setRootChangedCallback(boost::bind(&LLPanelMainInventory::onCombinationRootChanged, this, true)); + mCombinationGalleryPanel->setSelectionChangeCallback(boost::bind(&LLPanelMainInventory::onCombinationGallerySelectionChanged, this, _1)); initListCommandsHandlers(); @@ -309,12 +344,21 @@ LLPanelMainInventory::~LLPanelMainInventory( void ) gInventory.removeObserver(this); delete mSavedFolderState; - auto menu = mMenuAddHandle.get(); - if(menu) - { - menu->die(); - mMenuAddHandle.markDead(); - } + auto menu = mMenuAddHandle.get(); + if(menu) + { + menu->die(); + mMenuAddHandle.markDead(); + } + + if (mListViewRootUpdatedConnection.connected()) + { + mListViewRootUpdatedConnection.disconnect(); + } + if (mGalleryRootUpdatedConnection.connected()) + { + mGalleryRootUpdatedConnection.disconnect(); + } } LLInventoryPanel* LLPanelMainInventory::getAllItemsPanel() @@ -363,6 +407,10 @@ BOOL LLPanelMainInventory::handleKeyHere(KEY key, MASK mask) { startSearch(); } + if(mSingleFolderMode && key == KEY_LEFT) + { + onBackFolderClicked(); + } } return LLPanel::handleKeyHere(key, mask); @@ -382,27 +430,145 @@ void LLPanelMainInventory::closeAllFolders() getPanel()->getRootFolder()->closeAllFolders(); } -void LLPanelMainInventory::newWindow() +S32 get_instance_num() { - static S32 instance_num = 0; - instance_num = (instance_num + 1) % S32_MAX; + static S32 instance_num = 0; + instance_num = (instance_num + 1) % S32_MAX; + + return instance_num; +} + +LLFloaterSidePanelContainer* LLPanelMainInventory::newWindow() +{ + S32 instance_num = get_instance_num(); if (!gAgentCamera.cameraMouselook()) { - LLFloaterReg::showTypedInstance("inventory", LLSD(instance_num)); + LLFloaterSidePanelContainer* floater = LLFloaterReg::showTypedInstance("inventory", LLSD(instance_num)); + LLSidepanelInventory* sidepanel_inventory = floater->findChild("main_panel"); + sidepanel_inventory->initInventoryViews(); + return floater; } + return NULL; +} + +//static +void LLPanelMainInventory::newFolderWindow(LLUUID folder_id, LLUUID item_to_select) +{ + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end();) + { + LLFloaterSidePanelContainer* inventory_container = dynamic_cast(*iter++); + if (inventory_container) + { + LLSidepanelInventory* sidepanel_inventory = dynamic_cast(inventory_container->findChild("main_panel", true)); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory && main_inventory->isSingleFolderMode() + && (main_inventory->getCurrentSFVRoot() == folder_id)) + { + main_inventory->setFocus(true); + if(item_to_select.notNull()) + { + main_inventory->setGallerySelection(item_to_select); + } + return; + } + } + } + } + + S32 instance_num = get_instance_num(); + + LLFloaterSidePanelContainer* inventory_container = LLFloaterReg::showTypedInstance("inventory", LLSD(instance_num)); + if(inventory_container) + { + LLSidepanelInventory* sidepanel_inventory = dynamic_cast(inventory_container->findChild("main_panel", true)); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory) + { + main_inventory->initSingleFolderRoot(folder_id); + main_inventory->toggleViewMode(); + if(folder_id.notNull()) + { + if(item_to_select.notNull()) + { + main_inventory->setGallerySelection(item_to_select, true); + } + } + } + } + } } void LLPanelMainInventory::doCreate(const LLSD& userdata) { reset_inventory_filter(); - menu_create_inventory_item(getPanel(), NULL, userdata); + if(mSingleFolderMode) + { + if(isListViewMode() || isCombinationViewMode()) + { + LLFolderViewItem* current_folder = getActivePanel()->getRootFolder(); + if (current_folder) + { + if(isCombinationViewMode()) + { + mForceShowInvLayout = true; + } + + LLHandle handle = getHandle(); + std::function callback_created = [handle](const LLUUID& new_id) + { + gInventory.notifyObservers(); // not really needed, should have been already done + LLPanelMainInventory* panel = (LLPanelMainInventory*)handle.get(); + if (new_id.notNull() && panel) + { + // might need to refresh visibility, delay rename + panel->mCombInvUUIDNeedsRename = new_id; + + if (panel->isCombinationViewMode()) + { + panel->mForceShowInvLayout = true; + } + + LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL; + } + }; + menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created); + } + } + else + { + LLHandle handle = getHandle(); + std::function callback_created = [handle](const LLUUID &new_id) + { + gInventory.notifyObservers(); // not really needed, should have been already done + if (new_id.notNull()) + { + LLPanelMainInventory* panel = (LLPanelMainInventory*)handle.get(); + if (panel) + { + panel->setGallerySelection(new_id); + LL_DEBUGS("Inventory") << "Done creating inventory: " << new_id << LL_ENDL; + } + } + }; + menu_create_inventory_item(NULL, getCurrentSFVRoot(), userdata, LLUUID::null, callback_created); + } + } + else + { + menu_create_inventory_item(getPanel(), NULL, userdata); + } } void LLPanelMainInventory::resetFilters() { LLFloaterInventoryFinder *finder = getFinder(); - getActivePanel()->getFilter().resetDefault(); + getCurrentFilter().resetDefault(); if (finder) { finder->updateElementsFromFilter(); @@ -423,6 +589,17 @@ void LLPanelMainInventory::resetAllItemsFilters() setFilterTextFromFilter(); } +void LLPanelMainInventory::findLinks(const LLUUID& item_id, const std::string& item_name) +{ + mFilterSubString = item_name; + + LLInventoryFilter &filter = mActivePanel->getFilter(); + filter.setFindAllLinksMode(item_name, item_id); + + mFilterEditor->setText(item_name); + mFilterEditor->setFocus(TRUE); +} + void LLPanelMainInventory::setSortBy(const LLSD& userdata) { U32 sort_order_mask = getActivePanel()->getSortOrder(); @@ -457,6 +634,10 @@ void LLPanelMainInventory::setSortBy(const LLSD& userdata) sort_order_mask |= LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP; } } + if(mSingleFolderMode && !isListViewMode()) + { + mCombinationGalleryPanel->setSortOrder(sort_order_mask, true); + } getActivePanel()->setSortOrder(sort_order_mask); if (isRecentItemsPanelSelected()) @@ -474,25 +655,56 @@ void LLPanelMainInventory::onSelectSearchType() std::string new_type = mSearchTypeCombo->getValue(); if (new_type == "search_by_name") { - getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_NAME); + setSearchType(LLInventoryFilter::SEARCHTYPE_NAME); } if (new_type == "search_by_creator") { - getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_CREATOR); + setSearchType(LLInventoryFilter::SEARCHTYPE_CREATOR); } if (new_type == "search_by_description") { - getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_DESCRIPTION); + setSearchType(LLInventoryFilter::SEARCHTYPE_DESCRIPTION); } if (new_type == "search_by_UUID") { - getActivePanel()->setSearchType(LLInventoryFilter::SEARCHTYPE_UUID); + setSearchType(LLInventoryFilter::SEARCHTYPE_UUID); } } +void LLPanelMainInventory::setSearchType(LLInventoryFilter::ESearchType type) +{ + if(mSingleFolderMode && isGalleryViewMode()) + { + mCombinationGalleryPanel->setSearchType(type); + } + if(mSingleFolderMode && isCombinationViewMode()) + { + mCombinationInventoryPanel->setSearchType(type); + mCombinationGalleryPanel->setSearchType(type); + } + else + { + getActivePanel()->setSearchType(type); + } +} + void LLPanelMainInventory::updateSearchTypeCombo() { - LLInventoryFilter::ESearchType search_type = getActivePanel()->getSearchType(); + LLInventoryFilter::ESearchType search_type(LLInventoryFilter::SEARCHTYPE_NAME); + + if(mSingleFolderMode && isGalleryViewMode()) + { + search_type = mCombinationGalleryPanel->getSearchType(); + } + else if(mSingleFolderMode && isCombinationViewMode()) + { + search_type = mCombinationGalleryPanel->getSearchType(); + } + else + { + search_type = getActivePanel()->getSearchType(); + } + switch(search_type) { case LLInventoryFilter::SEARCHTYPE_CREATOR: @@ -538,7 +750,7 @@ void LLPanelMainInventory::onClearSearch() } // re-open folders that were initially open in case filter was active - if (mActivePanel && (mFilterSubString.size() || initially_active)) + if (mActivePanel && (mFilterSubString.size() || initially_active) && !mSingleFolderMode) { mSavedFolderState->setApply(TRUE); mActivePanel->getRootFolder()->applyFunctorRecursively(*mSavedFolderState); @@ -548,7 +760,7 @@ void LLPanelMainInventory::onClearSearch() } mFilterSubString = ""; - LLSidepanelInventory * sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + LLSidepanelInventory * sidepanel_inventory = getParentSidepanelInventory(); if (sidepanel_inventory) { LLPanelMarketplaceInbox* inbox_panel = sidepanel_inventory->getChild("marketplace_inbox"); @@ -561,16 +773,32 @@ void LLPanelMainInventory::onClearSearch() void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) { + if(mSingleFolderMode && isGalleryViewMode()) + { + mFilterSubString = search_string; + mCombinationGalleryPanel->setFilterSubString(mFilterSubString); + return; + } + if(mSingleFolderMode && isCombinationViewMode()) + { + mCombinationGalleryPanel->setFilterSubString(search_string); + } + if (search_string == "") { onClearSearch(); } + if (!mActivePanel) { return; } - LLInventoryModelBackgroundFetch::instance().start(); + if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) + { + llassert(false); // this should have been done on startup + LLInventoryModelBackgroundFetch::instance().start(); + } mFilterSubString = search_string; if (mActivePanel->getFilterSubString().empty() && mFilterSubString.empty()) @@ -589,7 +817,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) // set new filter string setFilterSubString(mFilterSubString); - LLSidepanelInventory * sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); + LLSidepanelInventory * sidepanel_inventory = getParentSidepanelInventory(); if (sidepanel_inventory) { LLPanelMarketplaceInbox* inbox_panel = sidepanel_inventory->getChild("marketplace_inbox"); @@ -644,7 +872,7 @@ void LLPanelMainInventory::onFilterEdit(const std::string& search_string ) void LLPanelMainInventory::onFilterSelected() { // Find my index - mActivePanel = (LLInventoryPanel*)getChild("inventory filter tabs")->getCurrentPanel(); + setActivePanel(); if (!mActivePanel) { @@ -657,15 +885,19 @@ void LLPanelMainInventory::onFilterSelected() } updateSearchTypeCombo(); setFilterSubString(mFilterSubString); - LLInventoryFilter& filter = mActivePanel->getFilter(); + LLInventoryFilter& filter = getCurrentFilter(); LLFloaterInventoryFinder *finder = getFinder(); if (finder) { finder->changeFilter(&filter); + if (mSingleFolderMode) + { + finder->setTitle(getLocalizedRootName()); + } } - if (filter.isActive()) + if (filter.isActive() && !LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) { - // If our filter is active we may be the first thing requiring a fetch so we better start it here. + llassert(false); // this should have been done on startup LLInventoryModelBackgroundFetch::instance().start(); } setFilterTextFromFilter(); @@ -737,6 +969,7 @@ void LLPanelMainInventory::draw() } LLPanel::draw(); updateItemcountText(); + updateCombinationVisibility(); } void LLPanelMainInventory::updateItemcountText() @@ -776,6 +1009,21 @@ void LLPanelMainInventory::updateItemcountText() { text = getString("ItemcountUnknown", string_args); } + + if (mSingleFolderMode) + { + LLInventoryModel::cat_array_t *cats; + LLInventoryModel::item_array_t *items; + + gInventory.getDirectDescendentsOf(getCurrentSFVRoot(), cats, items); + + if (items && cats) + { + string_args["[ITEM_COUNT]"] = llformat("%d", items->size()); + string_args["[CATEGORY_COUNT]"] = llformat("%d", cats->size()); + text = getString("ItemcountCompleted", string_args); + } + } mCounterCtrl->setValue(text); mCounterCtrl->setToolTip(text); @@ -795,7 +1043,7 @@ void LLPanelMainInventory::onFocusReceived() void LLPanelMainInventory::setFilterTextFromFilter() { - mFilterText = mActivePanel->getFilter().getFilterText(); + mFilterText = getCurrentFilter().getFilterText(); } void LLPanelMainInventory::toggleFindOptions() @@ -810,8 +1058,17 @@ void LLPanelMainInventory::toggleFindOptions() LLFloater* parent_floater = gFloaterView->getParentFloater(this); if (parent_floater) parent_floater->addDependentFloater(mFinderHandle); - // start background fetch of folders - LLInventoryModelBackgroundFetch::instance().start(); + + if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) + { + llassert(false); // this should have been done on startup + LLInventoryModelBackgroundFetch::instance().start(); + } + + if (mSingleFolderMode) + { + finder->setTitle(getLocalizedRootName()); + } } else { @@ -1051,10 +1308,27 @@ void LLFloaterInventoryFinder::draw() filter &= ~(0x1 << LLInventoryType::IT_CATEGORY); } - // update the panel, panel will update the filter - mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ? - LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); - mPanelMainInventory->getPanel()->setFilterTypes(filter); + + bool is_sf_mode = mPanelMainInventory->isSingleFolderMode(); + if(is_sf_mode && mPanelMainInventory->isGalleryViewMode()) + { + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setShowFolderState(getCheckShowEmpty() ? + LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setFilterObjectTypes(filter); + } + else + { + if(is_sf_mode && mPanelMainInventory->isCombinationViewMode()) + { + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setShowFolderState(getCheckShowEmpty() ? + LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setFilterObjectTypes(filter); + } + // update the panel, panel will update the filter + mPanelMainInventory->getPanel()->setShowFolderState(getCheckShowEmpty() ? + LLInventoryFilter::SHOW_ALL_FOLDERS : LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS); + mPanelMainInventory->getPanel()->setFilterTypes(filter); + } if (getCheckSinceLogoff()) { @@ -1076,10 +1350,26 @@ void LLFloaterInventoryFinder::draw() } hours += days * 24; - mPanelMainInventory->getPanel()->setHoursAgo(hours); - mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff()); + mPanelMainInventory->setFilterTextFromFilter(); - mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection()); + if(is_sf_mode && mPanelMainInventory->isGalleryViewMode()) + { + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setHoursAgo(hours); + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateRangeLastLogoff(getCheckSinceLogoff()); + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateSearchDirection(getDateSearchDirection()); + } + else + { + if(is_sf_mode && mPanelMainInventory->isCombinationViewMode()) + { + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setHoursAgo(hours); + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateRangeLastLogoff(getCheckSinceLogoff()); + mPanelMainInventory->mCombinationGalleryPanel->getFilter().setDateSearchDirection(getDateSearchDirection()); + } + mPanelMainInventory->getPanel()->setHoursAgo(hours); + mPanelMainInventory->getPanel()->setSinceLogoff(getCheckSinceLogoff()); + mPanelMainInventory->getPanel()->setDateSearchDirection(getDateSearchDirection()); + } LLPanel::draw(); } @@ -1091,15 +1381,15 @@ void LLFloaterInventoryFinder::onCreatorSelfFilterCommit() if(show_creator_self && show_creator_other) { - mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL); + mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL); } else if(show_creator_self) { - mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF); + mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF); } else if(!show_creator_self || !show_creator_other) { - mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS); + mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS); mCreatorOthers->set(TRUE); } } @@ -1111,15 +1401,15 @@ void LLFloaterInventoryFinder::onCreatorOtherFilterCommit() if(show_creator_self && show_creator_other) { - mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL); + mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_ALL); } else if(show_creator_other) { - mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS); + mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_OTHERS); } else if(!show_creator_other || !show_creator_self) { - mFilter->setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF); + mPanelMainInventory->getCurrentFilter().setFilterCreator(LLInventoryFilter::FILTERCREATOR_SELF); mCreatorSelf->set(TRUE); } } @@ -1192,26 +1482,25 @@ void LLFloaterInventoryFinder::selectNoTypes(void* user_data) void LLPanelMainInventory::initListCommandsHandlers() { - childSetAction("trash_btn", boost::bind(&LLPanelMainInventory::onTrashButtonClick, this)); childSetAction("add_btn", boost::bind(&LLPanelMainInventory::onAddButtonClick, this)); - - mTrashButton = getChild("trash_btn"); - mTrashButton->setDragAndDropHandler(boost::bind(&LLPanelMainInventory::handleDragAndDropToTrash, this - , _4 // BOOL drop - , _5 // EDragAndDropType cargo_type - , _7 // EAcceptance* accept - )); + childSetAction("view_mode_btn", boost::bind(&LLPanelMainInventory::onViewModeClick, this)); + childSetAction("up_btn", boost::bind(&LLPanelMainInventory::onUpFolderClicked, this)); + childSetAction("back_btn", boost::bind(&LLPanelMainInventory::onBackFolderClicked, this)); + childSetAction("forward_btn", boost::bind(&LLPanelMainInventory::onForwardFolderClicked, this)); mCommitCallbackRegistrar.add("Inventory.GearDefault.Custom.Action", boost::bind(&LLPanelMainInventory::onCustomAction, this, _2)); mEnableCallbackRegistrar.add("Inventory.GearDefault.Check", boost::bind(&LLPanelMainInventory::isActionChecked, this, _2)); mEnableCallbackRegistrar.add("Inventory.GearDefault.Enable", boost::bind(&LLPanelMainInventory::isActionEnabled, this, _2)); + mEnableCallbackRegistrar.add("Inventory.GearDefault.Visible", boost::bind(&LLPanelMainInventory::isActionVisible, this, _2)); mMenuGearDefault = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory_gear_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_TOP_LEFT, true); + mGearMenuButton->setMenu(mMenuGearDefault, LLMenuButton::MP_BOTTOM_LEFT, true); + mMenuViewDefault = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory_view_default.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + mViewMenuButton->setMenu(mMenuViewDefault, LLMenuButton::MP_BOTTOM_LEFT, true); LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory_add.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mMenuAddHandle = menu->getHandle(); mMenuVisibility = LLUICtrlFactory::getInstance()->createFromFile("menu_inventory_search_visibility.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true); + mVisibilityMenuButton->setMenu(mMenuVisibility, LLMenuButton::MP_BOTTOM_LEFT, true); // Update the trash button when selected item(s) get worn or taken off. LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLPanelMainInventory::updateListCommands, this)); @@ -1219,9 +1508,6 @@ void LLPanelMainInventory::initListCommandsHandlers() void LLPanelMainInventory::updateListCommands() { - bool trash_enabled = isActionEnabled("delete"); - - mTrashButton->setEnabled(trash_enabled); } void LLPanelMainInventory::onAddButtonClick() @@ -1232,7 +1518,7 @@ void LLPanelMainInventory::onAddButtonClick() LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get(); if (menu) { - menu->getChild("New Folder")->setEnabled(!isRecentItemsPanelSelected()); + disableAddIfNeeded(); setUploadCostIfNeeded(); @@ -1240,6 +1526,215 @@ void LLPanelMainInventory::onAddButtonClick() } } +void LLPanelMainInventory::setActivePanel() +{ + // Todo: should cover gallery mode in some way + if(mSingleFolderMode && isListViewMode()) + { + mActivePanel = getChild("comb_single_folder_inv"); + } + else if(mSingleFolderMode && isCombinationViewMode()) + { + mActivePanel = getChild("comb_single_folder_inv"); + } + else + { + mActivePanel = (LLInventoryPanel*)getChild("inventory filter tabs")->getCurrentPanel(); + } + mViewModeBtn->setEnabled(mSingleFolderMode || (getAllItemsPanel() == getActivePanel())); +} + +void LLPanelMainInventory::initSingleFolderRoot(const LLUUID& start_folder_id) +{ + mCombinationInventoryPanel->initFolderRoot(start_folder_id); +} + +void LLPanelMainInventory::initInventoryViews() +{ + LLInventoryPanel* all_item = getChild(ALL_ITEMS); + all_item->initializeViewBuilding(); + LLInventoryPanel* recent_item = getChild(RECENT_ITEMS); + recent_item->initializeViewBuilding(); + LLInventoryPanel* worn_item = getChild(WORN_ITEMS); + worn_item->initializeViewBuilding(); +} + +void LLPanelMainInventory::toggleViewMode() +{ + if(mSingleFolderMode && isCombinationViewMode()) + { + mCombinationInventoryPanel->getRootFolder()->setForceArrange(false); + } + + mSingleFolderMode = !mSingleFolderMode; + mReshapeInvLayout = true; + + if (mCombinationGalleryPanel->getRootFolder().isNull()) + { + mCombinationGalleryPanel->setRootFolder(mCombinationInventoryPanel->getSingleFolderRoot()); + mCombinationGalleryPanel->updateRootFolder(); + } + + updatePanelVisibility(); + setActivePanel(); + updateTitle(); + onFilterSelected(); + + LLSidepanelInventory* sidepanel_inventory = getParentSidepanelInventory(); + if (sidepanel_inventory) + { + if(mSingleFolderMode) + { + sidepanel_inventory->hideInbox(); + } + else + { + sidepanel_inventory->toggleInbox(); + } + } +} + +void LLPanelMainInventory::onViewModeClick() +{ + LLUUID selected_folder; + LLUUID new_root_folder; + if(mSingleFolderMode) + { + selected_folder = getCurrentSFVRoot(); + } + else + { + LLFolderView* root = getActivePanel()->getRootFolder(); + std::set selection_set = root->getSelectionList(); + if (selection_set.size() == 1) + { + LLFolderViewItem* current_item = *selection_set.begin(); + if (current_item) + { + const LLUUID& id = static_cast(current_item->getViewModelItem())->getUUID(); + if(gInventory.getCategory(id) != NULL) + { + new_root_folder = id; + } + else + { + const LLViewerInventoryItem* selected_item = gInventory.getItem(id); + if (selected_item && selected_item->getParentUUID().notNull()) + { + new_root_folder = selected_item->getParentUUID(); + selected_folder = id; + } + } + } + } + mCombinationInventoryPanel->initFolderRoot(new_root_folder); + } + + toggleViewMode(); + + if (mSingleFolderMode && new_root_folder.notNull()) + { + setSingleFolderViewRoot(new_root_folder, true); + if(selected_folder.notNull() && isListViewMode()) + { + getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES); + } + } + else + { + if(selected_folder.notNull()) + { + selectAllItemsPanel(); + getActivePanel()->setSelection(selected_folder, TAKE_FOCUS_YES); + } + } +} + +void LLPanelMainInventory::onUpFolderClicked() +{ + const LLViewerInventoryCategory* cat = gInventory.getCategory(getCurrentSFVRoot()); + if (cat) + { + if (cat->getParentUUID().notNull()) + { + if(isListViewMode()) + { + mCombinationInventoryPanel->changeFolderRoot(cat->getParentUUID()); + } + if(isGalleryViewMode()) + { + mCombinationGalleryPanel->setRootFolder(cat->getParentUUID()); + } + if(isCombinationViewMode()) + { + mCombinationInventoryPanel->changeFolderRoot(cat->getParentUUID()); + } + } + } +} + +void LLPanelMainInventory::onBackFolderClicked() +{ + if(isListViewMode()) + { + mCombinationInventoryPanel->onBackwardFolder(); + } + if(isGalleryViewMode()) + { + mCombinationGalleryPanel->onBackwardFolder(); + } + if(isCombinationViewMode()) + { + mCombinationInventoryPanel->onBackwardFolder(); + } +} + +void LLPanelMainInventory::onForwardFolderClicked() +{ + if(isListViewMode()) + { + mCombinationInventoryPanel->onForwardFolder(); + } + if(isGalleryViewMode()) + { + mCombinationGalleryPanel->onForwardFolder(); + } + if(isCombinationViewMode()) + { + mCombinationInventoryPanel->onForwardFolder(); + } +} + +void LLPanelMainInventory::setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history) +{ + if(isListViewMode()) + { + mCombinationInventoryPanel->changeFolderRoot(folder_id); + if(clear_nav_history) + { + mCombinationInventoryPanel->clearNavigationHistory(); + } + } + else if(isGalleryViewMode()) + { + mCombinationGalleryPanel->setRootFolder(folder_id); + if(clear_nav_history) + { + mCombinationGalleryPanel->clearNavigationHistory(); + } + } + else if(isCombinationViewMode()) + { + mCombinationInventoryPanel->changeFolderRoot(folder_id); + } + updateNavButtons(); +} + +LLUUID LLPanelMainInventory::getSingleFolderViewRoot() +{ + return mCombinationInventoryPanel->getSingleFolderRoot(); +} + void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_view_name) { if (menu) @@ -1249,17 +1744,11 @@ void LLPanelMainInventory::showActionMenu(LLMenuGL* menu, std::string spawning_v LLView* spawning_view = getChild (spawning_view_name); S32 menu_x, menu_y; //show menu in co-ordinates of panel - spawning_view->localPointToOtherView(0, spawning_view->getRect().getHeight(), &menu_x, &menu_y, this); - menu_y += menu->getRect().getHeight(); + spawning_view->localPointToOtherView(0, 0, &menu_x, &menu_y, this); LLMenuGL::showPopup(this, menu, menu_x, menu_y); } } -void LLPanelMainInventory::onTrashButtonClick() -{ - onClipboardAction("delete"); -} - void LLPanelMainInventory::onClipboardAction(const LLSD& userdata) { std::string command_name = userdata.asString(); @@ -1268,13 +1757,22 @@ void LLPanelMainInventory::onClipboardAction(const LLSD& userdata) void LLPanelMainInventory::saveTexture(const LLSD& userdata) { - LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); - if (!current_item) - { - return; - } - - const LLUUID& item_id = static_cast(current_item->getViewModelItem())->getUUID(); + LLUUID item_id; + if(mSingleFolderMode && isGalleryViewMode()) + { + item_id = mCombinationGalleryPanel->getFirstSelectedItemID(); + if (item_id.isNull()) return; + } + else + { + LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); + if (!current_item) + { + return; + } + item_id = static_cast(current_item->getViewModelItem())->getUUID(); + } + LLPreviewTexture* preview_texture = LLFloaterReg::showTypedInstance("preview_texture", LLSD(item_id), TAKE_FOCUS_YES); if (preview_texture) { @@ -1288,6 +1786,7 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) return; const std::string command_name = userdata.asString(); + if (command_name == "new_window") { newWindow(); @@ -1357,35 +1856,69 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) } if (command_name == "find_original") { + if(mSingleFolderMode && isGalleryViewMode()) + { + LLInventoryObject *obj = gInventory.getObject(mCombinationGalleryPanel->getFirstSelectedItemID()); + if (obj && obj->getIsLinkType()) + { + show_item_original(obj->getUUID()); + } + } + else + { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) { return; } static_cast(current_item->getViewModelItem())->performAction(getActivePanel()->getModel(), "goto"); + } } if (command_name == "find_links") { - LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); - if (!current_item) - { - return; - } - const LLUUID& item_id = static_cast(current_item->getViewModelItem())->getUUID(); - const std::string &item_name = current_item->getViewModelItem()->getName(); - mFilterSubString = item_name; - - LLInventoryFilter &filter = mActivePanel->getFilter(); - filter.setFindAllLinksMode(item_name, item_id); - - mFilterEditor->setText(item_name); - mFilterEditor->setFocus(TRUE); + if(mSingleFolderMode && isGalleryViewMode()) + { + LLFloaterSidePanelContainer* inventory_container = newWindow(); + if (inventory_container) + { + LLSidepanelInventory* sidepanel_inventory = dynamic_cast(inventory_container->findChild("main_panel", true)); + if (sidepanel_inventory) + { + LLPanelMainInventory* main_inventory = sidepanel_inventory->getMainInventoryPanel(); + if (main_inventory) + { + LLInventoryObject *obj = gInventory.getObject(mCombinationGalleryPanel->getFirstSelectedItemID()); + if (obj) + { + main_inventory->findLinks(obj->getUUID(), obj->getName()); + } + } + } + } + } + else + { + LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); + if (!current_item) + { + return; + } + const LLUUID& item_id = static_cast(current_item->getViewModelItem())->getUUID(); + const std::string &item_name = current_item->getViewModelItem()->getName(); + findLinks(item_id, item_name); + } } if (command_name == "replace_links") { - LLSD params; + LLSD params; + if(mSingleFolderMode && isGalleryViewMode()) + { + params = LLSD(mCombinationGalleryPanel->getFirstSelectedItemID()); + } + else + { LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (current_item) { @@ -1400,23 +1933,72 @@ void LLPanelMainInventory::onCustomAction(const LLSD& userdata) } } } + } LLFloaterReg::showInstance("linkreplace", params); } + if (command_name == "close_inv_windows") + { + LLFloaterReg::const_instance_list_t& inst_list = LLFloaterReg::getFloaterList("inventory"); + for (LLFloaterReg::const_instance_list_t::const_iterator iter = inst_list.begin(); iter != inst_list.end();) + { + LLFloaterSidePanelContainer* iv = dynamic_cast(*iter++); + if (iv) + { + iv->closeFloater(); + } + } + LLFloaterReg::hideInstance("inventory_settings"); + } + + if (command_name == "toggle_search_outfits") + { + getCurrentFilter().toggleSearchVisibilityOutfits(); + } + if (command_name == "toggle_search_trash") { - mActivePanel->getFilter().toggleSearchVisibilityTrash(); + getCurrentFilter().toggleSearchVisibilityTrash(); } if (command_name == "toggle_search_library") { - mActivePanel->getFilter().toggleSearchVisibilityLibrary(); + getCurrentFilter().toggleSearchVisibilityLibrary(); } if (command_name == "include_links") { - mActivePanel->getFilter().toggleSearchVisibilityLinks(); - } + getCurrentFilter().toggleSearchVisibilityLinks(); + } + + if (command_name == "share") + { + if(mSingleFolderMode && isGalleryViewMode()) + { + std::set uuids{ mCombinationGalleryPanel->getFirstSelectedItemID()}; + LLAvatarActions::shareWithAvatars(uuids, gFloaterView->getParentFloater(this)); + } + else + { + LLAvatarActions::shareWithAvatars(this); + } + } + if (command_name == "shop") + { + LLWeb::loadURL(gSavedSettings.getString("MarketplaceURL")); + } + if (command_name == "list_view") + { + setViewMode(MODE_LIST); + } + if (command_name == "gallery_view") + { + setViewMode(MODE_GALLERY); + } + if (command_name == "combination_view") + { + setViewMode(MODE_COMBINATION); + } } void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility ) @@ -1434,17 +2016,26 @@ void LLPanelMainInventory::onVisibilityChange( BOOL new_visibility ) bool LLPanelMainInventory::isSaveTextureEnabled(const LLSD& userdata) { - LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); - if (current_item) - { - LLViewerInventoryItem *inv_item = dynamic_cast(static_cast(current_item->getViewModelItem())->getInventoryObject()); + LLViewerInventoryItem *inv_item = NULL; + if(mSingleFolderMode && isGalleryViewMode()) + { + inv_item = gInventory.getItem(mCombinationGalleryPanel->getFirstSelectedItemID()); + } + else + { + LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); + if (current_item) + { + inv_item = dynamic_cast(static_cast(current_item->getViewModelItem())->getInventoryObject()); + } + } if(inv_item) { bool can_save = inv_item->checkPermissionsSet(PERM_ITEM_UNRESTRICTED); - LLInventoryType::EType curr_type = static_cast(current_item->getViewModelItem())->getInventoryType(); + LLInventoryType::EType curr_type = inv_item->getInventoryType(); return can_save && (curr_type == LLInventoryType::IT_TEXTURE || curr_type == LLInventoryType::IT_SNAPSHOT); } - } + return false; } @@ -1475,9 +2066,16 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) } if (command_name == "find_original") { + LLUUID item_id; + if(mSingleFolderMode && isGalleryViewMode()) + { + item_id = mCombinationGalleryPanel->getFirstSelectedItemID(); + } + else{ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = static_cast(current_item->getViewModelItem())->getUUID(); + item_id = static_cast(current_item->getViewModelItem())->getUUID(); + } const LLViewerInventoryItem *item = gInventory.getItem(item_id); if (item && item->getIsLinkType() && !item->getIsBrokenLink()) { @@ -1488,12 +2086,19 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (command_name == "find_links") { + LLUUID item_id; + if(mSingleFolderMode && isGalleryViewMode()) + { + item_id = mCombinationGalleryPanel->getFirstSelectedItemID(); + } + else{ LLFolderView* root = getActivePanel()->getRootFolder(); std::set selection_set = root->getSelectionList(); if (selection_set.size() != 1) return FALSE; LLFolderViewItem* current_item = root->getCurSelectedItem(); if (!current_item) return FALSE; - const LLUUID& item_id = static_cast(current_item->getViewModelItem())->getUUID(); + item_id = static_cast(current_item->getViewModelItem())->getUUID(); + } const LLInventoryObject *obj = gInventory.getObject(item_id); if (obj && !obj->getIsLinkType() && LLAssetType::lookupCanLink(obj->getType())) { @@ -1517,10 +2122,16 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) if (command_name == "share") { + if(mSingleFolderMode && isGalleryViewMode()) + { + return can_share_item(mCombinationGalleryPanel->getFirstSelectedItemID()); + } + else{ LLFolderViewItem* current_item = getActivePanel()->getRootFolder()->getCurSelectedItem(); if (!current_item) return FALSE; LLSidepanelInventory* parent = LLFloaterSidePanelContainer::getPanel("inventory"); return parent ? parent->canShare() : FALSE; + } } if (command_name == "empty_trash") { @@ -1538,9 +2149,24 @@ BOOL LLPanelMainInventory::isActionEnabled(const LLSD& userdata) return TRUE; } +bool LLPanelMainInventory::isActionVisible(const LLSD& userdata) +{ + const std::string param_str = userdata.asString(); + if (param_str == "single_folder_view") + { + return mSingleFolderMode; + } + if (param_str == "multi_folder_view") + { + return !mSingleFolderMode; + } + + return true; +} + BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata) { - U32 sort_order_mask = getActivePanel()->getSortOrder(); + U32 sort_order_mask = (mSingleFolderMode && isGalleryViewMode()) ? mCombinationGalleryPanel->getSortOrder() : getActivePanel()->getSortOrder(); const std::string command_name = userdata.asString(); if (command_name == "sort_by_name") { @@ -1562,38 +2188,42 @@ BOOL LLPanelMainInventory::isActionChecked(const LLSD& userdata) return sort_order_mask & LLInventoryFilter::SO_SYSTEM_FOLDERS_TO_TOP; } + if (command_name == "toggle_search_outfits") + { + return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_OUTFITS) != 0; + } + if (command_name == "toggle_search_trash") { - return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_TRASH) != 0; + return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_TRASH) != 0; } if (command_name == "toggle_search_library") { - return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LIBRARY) != 0; + return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LIBRARY) != 0; } if (command_name == "include_links") { - return (mActivePanel->getFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) != 0; + return (getCurrentFilter().getSearchVisibilityTypes() & LLInventoryFilter::VISIBILITY_LINKS) != 0; } + if (command_name == "list_view") + { + return isListViewMode(); + } + if (command_name == "gallery_view") + { + return isGalleryViewMode(); + } + if (command_name == "combination_view") + { + return isCombinationViewMode(); + } + return FALSE; } -bool LLPanelMainInventory::handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept) -{ - *accept = ACCEPT_NO; - - const bool is_enabled = isActionEnabled("delete"); - if (is_enabled) *accept = ACCEPT_YES_MULTI; - - if (is_enabled && drop) - { - onClipboardAction("delete"); - } - return true; -} - void LLPanelMainInventory::setUploadCostIfNeeded() { LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get(); @@ -1609,6 +2239,55 @@ void LLPanelMainInventory::setUploadCostIfNeeded() } } +bool is_add_allowed(LLUUID folder_id) +{ + if(!gInventory.isObjectDescendentOf(folder_id, gInventory.getRootFolderID())) + { + return false; + } + + std::vector not_allowed_types; + not_allowed_types.push_back(LLFolderType::FT_LOST_AND_FOUND); + not_allowed_types.push_back(LLFolderType::FT_FAVORITE); + not_allowed_types.push_back(LLFolderType::FT_MARKETPLACE_LISTINGS); + not_allowed_types.push_back(LLFolderType::FT_TRASH); + not_allowed_types.push_back(LLFolderType::FT_CURRENT_OUTFIT); + not_allowed_types.push_back(LLFolderType::FT_INBOX); + + for (std::vector::const_iterator it = not_allowed_types.begin(); + it != not_allowed_types.end(); ++it) + { + if(gInventory.isObjectDescendentOf(folder_id, gInventory.findCategoryUUIDForType(*it))) + { + return false; + } + } + + LLViewerInventoryCategory* cat = gInventory.getCategory(folder_id); + if (cat && (cat->getPreferredType() == LLFolderType::FT_OUTFIT)) + { + return false; + } + return true; +} + +void LLPanelMainInventory::disableAddIfNeeded() +{ + LLMenuGL* menu = (LLMenuGL*)mMenuAddHandle.get(); + if (menu) + { + bool enable = !mSingleFolderMode || is_add_allowed(getCurrentSFVRoot()); + + menu->getChild("New Folder")->setEnabled(enable && !isRecentItemsPanelSelected()); + menu->getChild("New Script")->setEnabled(enable); + menu->getChild("New Note")->setEnabled(enable); + menu->getChild("New Gesture")->setEnabled(enable); + menu->setItemEnabled("New Clothes", enable); + menu->setItemEnabled("New Body Parts", enable); + menu->setItemEnabled("New Settings", enable); + } +} + bool LLPanelMainInventory::hasSettingsInventory() { return LLEnvironment::instance().isInventoryEnabled(); @@ -1622,5 +2301,356 @@ bool LLPanelMainInventory::hasMaterialsInventory() return (!agent_url.empty() && !task_url.empty()); } +void LLPanelMainInventory::updateTitle() +{ + LLFloater* inventory_floater = gFloaterView->getParentFloater(this); + if(inventory_floater) + { + if(mSingleFolderMode) + { + inventory_floater->setTitle(getLocalizedRootName()); + LLFloaterInventoryFinder *finder = getFinder(); + if (finder) + { + finder->setTitle(getLocalizedRootName()); + } + } + else + { + inventory_floater->setTitle(getString("inventory_title")); + } + } + updateNavButtons(); +} + +void LLPanelMainInventory::onCombinationRootChanged(bool gallery_clicked) +{ + if(gallery_clicked) + { + mCombinationInventoryPanel->changeFolderRoot(mCombinationGalleryPanel->getRootFolder()); + } + else + { + mCombinationGalleryPanel->setRootFolder(mCombinationInventoryPanel->getSingleFolderRoot()); + } + mForceShowInvLayout = false; + updateTitle(); + mReshapeInvLayout = true; +} + +void LLPanelMainInventory::onCombinationGallerySelectionChanged(const LLUUID& category_id) +{ +} + +void LLPanelMainInventory::onCombinationInventorySelectionChanged(const std::deque& items, BOOL user_action) +{ + onSelectionChange(mCombinationInventoryPanel, items, user_action); +} + +void LLPanelMainInventory::updatePanelVisibility() +{ + mDefaultViewPanel->setVisible(!mSingleFolderMode); + mCombinationViewPanel->setVisible(mSingleFolderMode); + mNavigationBtnsPanel->setVisible(mSingleFolderMode); + mViewModeBtn->setImageOverlay(mSingleFolderMode ? getString("default_mode_btn") : getString("single_folder_mode_btn")); + mViewModeBtn->setEnabled(mSingleFolderMode || (getAllItemsPanel() == getActivePanel())); + if (mSingleFolderMode) + { + if (isCombinationViewMode()) + { + LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter(); + comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_EXCLUDE_THUMBNAILS); + comb_inv_filter.markDefault(); + + LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter(); + comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_ONLY_THUMBNAILS); + comb_gallery_filter.markDefault(); + + // visibility will be controled by updateCombinationVisibility() + mCombinationGalleryLayoutPanel->setVisible(true); + mCombinationGalleryPanel->setVisible(true); + mCombinationListLayoutPanel->setVisible(true); + } + else + { + LLInventoryFilter& comb_inv_filter = mCombinationInventoryPanel->getFilter(); + comb_inv_filter.setFilterThumbnails(LLInventoryFilter::FILTER_INCLUDE_THUMBNAILS); + comb_inv_filter.markDefault(); + + LLInventoryFilter& comb_gallery_filter = mCombinationGalleryPanel->getFilter(); + comb_gallery_filter.setFilterThumbnails(LLInventoryFilter::FILTER_INCLUDE_THUMBNAILS); + comb_gallery_filter.markDefault(); + + mCombinationLayoutStack->setPanelSpacing(0); + mCombinationGalleryLayoutPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); + mCombinationGalleryPanel->setVisible(mSingleFolderMode && isGalleryViewMode()); // to prevent or process updates + mCombinationListLayoutPanel->setVisible(mSingleFolderMode && isListViewMode()); + } + } + else + { + mCombinationGalleryLayoutPanel->setVisible(false); + mCombinationGalleryPanel->setVisible(false); // to prevent updates + mCombinationListLayoutPanel->setVisible(false); + } +} + +void LLPanelMainInventory::updateCombinationVisibility() +{ + if(mSingleFolderMode && isCombinationViewMode()) + { + bool is_gallery_empty = !mCombinationGalleryPanel->hasVisibleItems(); + bool show_inv_pane = mCombinationInventoryPanel->hasVisibleItems() || is_gallery_empty || mForceShowInvLayout; + + const S32 DRAG_HANDLE_PADDING = 12; // for drag handle to not overlap gallery when both inventories are visible + mCombinationLayoutStack->setPanelSpacing(show_inv_pane ? DRAG_HANDLE_PADDING : 0); + + mCombinationGalleryLayoutPanel->setVisible(!is_gallery_empty); + mCombinationListLayoutPanel->setVisible(show_inv_pane); + mCombinationInventoryPanel->getRootFolder()->setForceArrange(!show_inv_pane); + if(mCombinationInventoryPanel->hasVisibleItems()) + { + mForceShowInvLayout = false; + } + if(is_gallery_empty) + { + mCombinationGalleryPanel->handleModifiedFilter(); + } + + getActivePanel()->getRootFolder(); + + if (mReshapeInvLayout + && show_inv_pane + && (mCombinationGalleryPanel->hasVisibleItems() || mCombinationGalleryPanel->areViewsInitialized()) + && mCombinationInventoryPanel->areViewsInitialized()) + { + mReshapeInvLayout = false; + + // force drop previous shape (because panel doesn't decrease shape properly) + LLRect list_latout = mCombinationListLayoutPanel->getRect(); + list_latout.mTop = list_latout.mBottom; // min height is at 100, so it should snap to be bigger + mCombinationListLayoutPanel->setShape(list_latout, false); + + LLRect inv_inner_rect = mCombinationInventoryPanel->getScrollableContainer()->getScrolledViewRect(); + S32 inv_height = inv_inner_rect.getHeight() + + (mCombinationInventoryPanel->getScrollableContainer()->getBorderWidth() * 2) + + mCombinationInventoryPanel->getScrollableContainer()->getSize(); + LLRect inner_galery_rect = mCombinationGalleryPanel->getScrollableContainer()->getScrolledViewRect(); + S32 gallery_height = inner_galery_rect.getHeight() + + (mCombinationGalleryPanel->getScrollableContainer()->getBorderWidth() * 2) + + mCombinationGalleryPanel->getScrollableContainer()->getSize(); + LLRect layout_rect = mCombinationViewPanel->getRect(); + + // by default make it take 1/3 of the panel + S32 list_default_height = layout_rect.getHeight() / 3; + // Don't set height from gallery_default_height - needs to account for a resizer in such case + S32 gallery_default_height = layout_rect.getHeight() - list_default_height; + + if (inv_height > list_default_height + && gallery_height < gallery_default_height) + { + LLRect gallery_latout = mCombinationGalleryLayoutPanel->getRect(); + gallery_latout.mTop = gallery_latout.mBottom + gallery_height; + mCombinationGalleryLayoutPanel->setShape(gallery_latout, true /*tell stack to account for new shape*/); + } + else if (inv_height < list_default_height + && gallery_height > gallery_default_height) + { + LLRect list_latout = mCombinationListLayoutPanel->getRect(); + list_latout.mTop = list_latout.mBottom + inv_height; + mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/); + } + else + { + LLRect list_latout = mCombinationListLayoutPanel->getRect(); + list_latout.mTop = list_latout.mBottom + list_default_height; + mCombinationListLayoutPanel->setShape(list_latout, true /*tell stack to account for new shape*/); + } + } + } + + if (mSingleFolderMode + && !isGalleryViewMode() + && mCombInvUUIDNeedsRename.notNull() + && mCombinationInventoryPanel->areViewsInitialized()) + { + mCombinationInventoryPanel->setSelectionByID(mCombInvUUIDNeedsRename, TRUE); + mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection(); + mCombinationInventoryPanel->getRootFolder()->setNeedsAutoRename(TRUE); + mCombInvUUIDNeedsRename.setNull(); + } +} + +void LLPanelMainInventory::updateNavButtons() +{ + if(isListViewMode()) + { + mBackBtn->setEnabled(mCombinationInventoryPanel->isBackwardAvailable()); + mForwardBtn->setEnabled(mCombinationInventoryPanel->isForwardAvailable()); + } + if(isGalleryViewMode()) + { + mBackBtn->setEnabled(mCombinationGalleryPanel->isBackwardAvailable()); + mForwardBtn->setEnabled(mCombinationGalleryPanel->isForwardAvailable()); + } + if(isCombinationViewMode()) + { + mBackBtn->setEnabled(mCombinationInventoryPanel->isBackwardAvailable()); + mForwardBtn->setEnabled(mCombinationInventoryPanel->isForwardAvailable()); + } + + const LLViewerInventoryCategory* cat = gInventory.getCategory(getCurrentSFVRoot()); + bool up_enabled = (cat && cat->getParentUUID().notNull()); + mUpBtn->setEnabled(up_enabled); +} + +LLSidepanelInventory* LLPanelMainInventory::getParentSidepanelInventory() +{ + LLFloaterSidePanelContainer* inventory_container = dynamic_cast(gFloaterView->getParentFloater(this)); + if(inventory_container) + { + return dynamic_cast(inventory_container->findChild("main_panel", true)); + } + return NULL; +} + +void LLPanelMainInventory::setViewMode(EViewModeType mode) +{ + if(mode != mViewMode) + { + std::list forward_history; + std::list backward_history; + U32 sort_order = 0; + switch(mViewMode) + { + case MODE_LIST: + forward_history = mCombinationInventoryPanel->getNavForwardList(); + backward_history = mCombinationInventoryPanel->getNavBackwardList(); + sort_order = mCombinationInventoryPanel->getSortOrder(); + break; + case MODE_GALLERY: + forward_history = mCombinationGalleryPanel->getNavForwardList(); + backward_history = mCombinationGalleryPanel->getNavBackwardList(); + sort_order = mCombinationGalleryPanel->getSortOrder(); + break; + case MODE_COMBINATION: + forward_history = mCombinationInventoryPanel->getNavForwardList(); + backward_history = mCombinationInventoryPanel->getNavBackwardList(); + mCombinationInventoryPanel->getRootFolder()->setForceArrange(false); + sort_order = mCombinationInventoryPanel->getSortOrder(); + break; + } + + LLUUID cur_root = getCurrentSFVRoot(); + mViewMode = mode; + + updatePanelVisibility(); + + if(isListViewMode()) + { + mCombinationInventoryPanel->changeFolderRoot(cur_root); + mCombinationInventoryPanel->setNavForwardList(forward_history); + mCombinationInventoryPanel->setNavBackwardList(backward_history); + mCombinationInventoryPanel->setSortOrder(sort_order); + } + if(isGalleryViewMode()) + { + mCombinationGalleryPanel->setRootFolder(cur_root); + mCombinationGalleryPanel->setNavForwardList(forward_history); + mCombinationGalleryPanel->setNavBackwardList(backward_history); + mCombinationGalleryPanel->setSortOrder(sort_order, true); + } + if(isCombinationViewMode()) + { + mCombinationInventoryPanel->changeFolderRoot(cur_root); + mCombinationGalleryPanel->setRootFolder(cur_root); + mCombinationInventoryPanel->setNavForwardList(forward_history); + mCombinationInventoryPanel->setNavBackwardList(backward_history); + mCombinationGalleryPanel->setNavForwardList(forward_history); + mCombinationGalleryPanel->setNavBackwardList(backward_history); + mCombinationInventoryPanel->setSortOrder(sort_order); + mCombinationGalleryPanel->setSortOrder(sort_order, true); + } + + updateNavButtons(); + + onFilterSelected(); + if((isListViewMode() && (mActivePanel->getFilterSubString() != mFilterSubString)) || + (isGalleryViewMode() && (mCombinationGalleryPanel->getFilterSubString() != mFilterSubString))) + { + onFilterEdit(mFilterSubString); + } + } +} + +std::string LLPanelMainInventory::getLocalizedRootName() +{ + return mSingleFolderMode ? get_localized_folder_name(getCurrentSFVRoot()) : ""; +} + +LLUUID LLPanelMainInventory::getCurrentSFVRoot() +{ + if(isListViewMode()) + { + return mCombinationInventoryPanel->getSingleFolderRoot(); + } + if(isGalleryViewMode()) + { + return mCombinationGalleryPanel->getRootFolder(); + } + if(isCombinationViewMode()) + { + return mCombinationInventoryPanel->getSingleFolderRoot(); + } + return LLUUID::null; +} + +LLInventoryFilter& LLPanelMainInventory::getCurrentFilter() +{ + if(mSingleFolderMode && isGalleryViewMode()) + { + return mCombinationGalleryPanel->getFilter(); + } + else + { + return mActivePanel->getFilter(); + } +} + +void LLPanelMainInventory::setGallerySelection(const LLUUID& item_id, bool new_window) +{ + if(mSingleFolderMode && isGalleryViewMode()) + { + mCombinationGalleryPanel->changeItemSelection(item_id, true); + } + else if(mSingleFolderMode && isCombinationViewMode()) + { + if(mCombinationGalleryPanel->getFilter().checkAgainstFilterThumbnails(item_id)) + { + mCombinationGalleryPanel->changeItemSelection(item_id, false); + scrollToGallerySelection(); + } + else + { + mCombinationInventoryPanel->setSelection(item_id, true); + scrollToInvPanelSelection(); + } + } + else if (mSingleFolderMode && isListViewMode()) + { + mCombinationInventoryPanel->setSelection(item_id, true); + } +} + +void LLPanelMainInventory::scrollToGallerySelection() +{ + mCombinationGalleryPanel->scrollToShowItem(mCombinationGalleryPanel->getFirstSelectedItemID()); +} + +void LLPanelMainInventory::scrollToInvPanelSelection() +{ + mCombinationInventoryPanel->getRootFolder()->scrollToShowSelection(); +} + // List Commands // //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpanelmaininventory.h b/indra/newview/llpanelmaininventory.h index 8f357d9e5a..fbfca43f64 100644 --- a/indra/newview/llpanelmaininventory.h +++ b/indra/newview/llpanelmaininventory.h @@ -30,6 +30,7 @@ #include "llpanel.h" #include "llinventoryobserver.h" +#include "llinventorypanel.h" #include "lldndbutton.h" #include "llfolderview.h" @@ -37,14 +38,17 @@ class LLComboBox; class LLFolderViewItem; class LLInventoryPanel; +class LLInventoryGallery; class LLSaveFolderState; class LLFilterEditor; class LLTabContainer; class LLFloaterInventoryFinder; class LLMenuButton; class LLMenuGL; +class LLSidepanelInventory; class LLToggleableMenu; class LLFloater; +class LLFloaterSidePanelContainer; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLPanelMainInventory @@ -63,6 +67,13 @@ public: BOOL postBuild(); + enum EViewModeType + { + MODE_LIST, + MODE_GALLERY, + MODE_COMBINATION + }; + virtual BOOL handleKeyHere(KEY key, MASK mask); // Inherited functionality @@ -80,6 +91,7 @@ public: LLInventoryPanel* getAllItemsPanel(); void selectAllItemsPanel(); const LLInventoryPanel* getActivePanel() const { return mActivePanel; } + void setActivePanel(); bool isRecentItemsPanelSelected(); @@ -91,13 +103,40 @@ public: void setFocusFilterEditor(); - static void newWindow(); + static LLFloaterSidePanelContainer* newWindow(); + static void newFolderWindow(LLUUID folder_id = LLUUID(), LLUUID item_to_select = LLUUID()); void toggleFindOptions(); void resetFilters(); void resetAllItemsFilters(); + void findLinks(const LLUUID& item_id, const std::string& item_name); + + void onViewModeClick(); + void toggleViewMode(); + void initSingleFolderRoot(const LLUUID& start_folder_id = LLUUID::null); + void initInventoryViews(); + void onUpFolderClicked(); + void onBackFolderClicked(); + void onForwardFolderClicked(); + void setSingleFolderViewRoot(const LLUUID& folder_id, bool clear_nav_history = true); + void setGallerySelection(const LLUUID& item_id, bool new_window = false); + LLUUID getSingleFolderViewRoot(); + bool isSingleFolderMode() { return mSingleFolderMode; } + + void scrollToGallerySelection(); + void scrollToInvPanelSelection(); + + void setViewMode(EViewModeType mode); + bool isListViewMode() { return (mViewMode == MODE_LIST); } + bool isGalleryViewMode() { return (mViewMode == MODE_GALLERY); } + bool isCombinationViewMode() { return (mViewMode == MODE_COMBINATION); } + LLUUID getCurrentSFVRoot(); + std::string getLocalizedRootName(); + + LLInventoryFilter& getCurrentFilter(); + protected: // // Misc functions @@ -127,9 +166,15 @@ protected: bool isSaveTextureEnabled(const LLSD& userdata); void updateItemcountText(); + void updatePanelVisibility(); + void updateCombinationVisibility(); + void onFocusReceived(); void onSelectSearchType(); void updateSearchTypeCombo(); + void setSearchType(LLInventoryFilter::ESearchType type); + + LLSidepanelInventory* getParentSidepanelInventory(); private: LLFloaterInventoryFinder* getFinder(); @@ -150,7 +195,26 @@ private: std::string mCategoryCountString; LLComboBox* mSearchTypeCombo; + LLButton* mBackBtn; + LLButton* mForwardBtn; + LLButton* mUpBtn; + LLButton* mViewModeBtn; + LLLayoutPanel* mNavigationBtnsPanel; + LLPanel* mDefaultViewPanel; + LLPanel* mCombinationViewPanel; + + bool mSingleFolderMode; + EViewModeType mViewMode; + + LLInventorySingleFolderPanel* mCombinationInventoryPanel; + LLInventoryGallery* mCombinationGalleryPanel; + LLPanel* mCombinationGalleryLayoutPanel; + LLLayoutPanel* mCombinationListLayoutPanel; + LLLayoutStack* mCombinationLayoutStack; + + boost::signals2::connection mListViewRootUpdatedConnection; + boost::signals2::connection mGalleryRootUpdatedConnection; ////////////////////////////////////////////////////////////////////////////////// // List Commands // @@ -159,27 +223,38 @@ protected: void updateListCommands(); void onAddButtonClick(); void showActionMenu(LLMenuGL* menu, std::string spawning_view_name); - void onTrashButtonClick(); void onClipboardAction(const LLSD& userdata); BOOL isActionEnabled(const LLSD& command_name); BOOL isActionChecked(const LLSD& userdata); void onCustomAction(const LLSD& command_name); - bool handleDragAndDropToTrash(BOOL drop, EDragAndDropType cargo_type, EAcceptance* accept); + bool isActionVisible(const LLSD& userdata); static bool hasSettingsInventory(); static bool hasMaterialsInventory(); + void updateTitle(); + void updateNavButtons(); + + void onCombinationRootChanged(bool gallery_clicked); + void onCombinationGallerySelectionChanged(const LLUUID& category_id); + void onCombinationInventorySelectionChanged(const std::deque& items, BOOL user_action); /** * Set upload cost in "Upload" sub menu. */ void setUploadCostIfNeeded(); + void disableAddIfNeeded(); private: - LLDragAndDropButton* mTrashButton; LLToggleableMenu* mMenuGearDefault; + LLToggleableMenu* mMenuViewDefault; LLToggleableMenu* mMenuVisibility; LLMenuButton* mGearMenuButton; + LLMenuButton* mViewMenuButton; LLMenuButton* mVisibilityMenuButton; LLHandle mMenuAddHandle; bool mNeedUploadCost; + + bool mForceShowInvLayout; + bool mReshapeInvLayout; + LLUUID mCombInvUUIDNeedsRename; // List Commands // //////////////////////////////////////////////////////////////////////////////// }; diff --git a/indra/newview/llpanelmarketplaceinbox.cpp b/indra/newview/llpanelmarketplaceinbox.cpp index 8a86f4f63d..3638ee14fc 100644 --- a/indra/newview/llpanelmarketplaceinbox.cpp +++ b/indra/newview/llpanelmarketplaceinbox.cpp @@ -75,9 +75,6 @@ BOOL LLPanelMarketplaceInbox::postBuild() void LLPanelMarketplaceInbox::onSelectionChange() { - LLSidepanelInventory* sidepanel_inventory = LLFloaterSidePanelContainer::getPanel("inventory"); - - sidepanel_inventory->updateVerbs(); } diff --git a/indra/newview/llpanelmarketplaceinboxinventory.cpp b/indra/newview/llpanelmarketplaceinboxinventory.cpp index 7a6631448b..e13bd0412d 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.cpp +++ b/indra/newview/llpanelmarketplaceinboxinventory.cpp @@ -62,10 +62,13 @@ LLInboxInventoryPanel::LLInboxInventoryPanel(const LLInboxInventoryPanel::Params : LLInventoryPanel(p) { LLInboxNewItemsStorage::getInstance()->load(); + LLInboxNewItemsStorage::getInstance()->addInboxPanel(this); } LLInboxInventoryPanel::~LLInboxInventoryPanel() -{} +{ + LLInboxNewItemsStorage::getInstance()->removeInboxPanel(this); +} void LLInboxInventoryPanel::initFromParams(const LLInventoryPanel::Params& params) { @@ -108,6 +111,21 @@ LLFolderViewItem * LLInboxInventoryPanel::createFolderViewItem(LLInvFVBridge * b return LLUICtrlFactory::create(params); } +void LLInboxInventoryPanel::onRemoveItemFreshness(const LLUUID& item_id) +{ + LLInboxFolderViewFolder* inbox_folder_view = dynamic_cast(getFolderByID(item_id)); + if(inbox_folder_view) + { + inbox_folder_view->setFresh(false); + } + + LLInboxFolderViewItem* inbox_item_view = dynamic_cast(getItemByID(item_id)); + if(inbox_item_view) + { + inbox_item_view->setFresh(false); + } +} + // // LLInboxFolderViewFolder Implementation // @@ -340,4 +358,18 @@ void LLInboxNewItemsStorage::load() } } } + +void LLInboxNewItemsStorage::removeItem(const LLUUID& id) +{ + mNewItemsIDs.erase(id); + + //notify inbox panels + for (auto inbox : mInboxPanels) + { + if(inbox) + { + inbox->onRemoveItemFreshness(id); + } + } +} // eof diff --git a/indra/newview/llpanelmarketplaceinboxinventory.h b/indra/newview/llpanelmarketplaceinboxinventory.h index 3e508e801b..9eef5f209c 100644 --- a/indra/newview/llpanelmarketplaceinboxinventory.h +++ b/indra/newview/llpanelmarketplaceinboxinventory.h @@ -49,6 +49,8 @@ public: void initFromParams(const LLInventoryPanel::Params&); LLFolderViewFolder* createFolderViewFolder(LLInvFVBridge * bridge, bool allow_drop); LLFolderViewItem * createFolderViewItem(LLInvFVBridge * bridge); + + void onRemoveItemFreshness(const LLUUID& item_id); }; @@ -77,6 +79,7 @@ public: void deFreshify(); bool isFresh() const { return mFresh; } + void setFresh(bool is_fresh) { mFresh = is_fresh; } protected: bool mFresh; @@ -108,6 +111,7 @@ public: void deFreshify(); bool isFresh() const { return mFresh; } + void setFresh(bool is_fresh) { mFresh = is_fresh; } protected: bool mFresh; @@ -125,11 +129,16 @@ public: void load(); void addFreshItem(const LLUUID& id) { mNewItemsIDs.insert(id); } - void removeItem(const LLUUID& id) { mNewItemsIDs.erase(id); } + void removeItem(const LLUUID& id); bool isItemFresh(const LLUUID& id) { return (mNewItemsIDs.find(id) != mNewItemsIDs.end()); } + void addInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.insert(inbox); } + void removeInboxPanel(LLInboxInventoryPanel* inbox) { mInboxPanels.erase(inbox); } + private: std::set mNewItemsIDs; + + std::set mInboxPanels; }; #endif //LL_INBOXINVENTORYPANEL_H diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp index 02911313ed..3c3cd5d522 100644 --- a/indra/newview/llpanelnearbymedia.cpp +++ b/indra/newview/llpanelnearbymedia.cpp @@ -619,7 +619,6 @@ void LLPanelNearByMedia::refreshList() LLViewerMedia::impl_list impls = media_inst->getPriorityList(); LLViewerMedia::impl_list::iterator priority_iter; - U32 enabled_count = 0; U32 disabled_count = 0; // iterate over the impl list, creating rows as necessary. @@ -662,13 +661,10 @@ void LLPanelNearByMedia::refreshList() { disabled_count++; } - else { - enabled_count++; } } - } mDisableAllCtrl->setEnabled((gSavedSettings.getBOOL("AudioStreamingMusic") || - gSavedSettings.getBOOL("AudioStreamingMedia")) && + gSavedSettings.getBOOL("AudioStreamingMedia")) && (media_inst->isAnyMediaShowing() || media_inst->isParcelMediaPlaying() || media_inst->isParcelAudioPlaying())); diff --git a/indra/newview/llpanelobjectinventory.cpp b/indra/newview/llpanelobjectinventory.cpp index 2db8620d2d..f3a41f2e25 100644 --- a/indra/newview/llpanelobjectinventory.cpp +++ b/indra/newview/llpanelobjectinventory.cpp @@ -117,6 +117,7 @@ public: virtual PermissionMask getPermissionMask() const { return PERM_NONE; } /*virtual*/ LLFolderType::EType getPreferredType() const { return LLFolderType::FT_NONE; } virtual const LLUUID& getUUID() const { return mUUID; } + virtual const LLUUID& getThumbnailUUID() const { return LLUUID::null;} virtual time_t getCreationDate() const; virtual void setCreationDate(time_t creation_date_utc); @@ -125,6 +126,7 @@ public: virtual BOOL canOpenItem() const { return FALSE; } virtual void closeItem() {} virtual void selectItem() {} + virtual void navigateToFolder(bool new_window = false, bool change_mode = false) {} virtual BOOL isItemRenameable() const; virtual BOOL renameItem(const std::string& new_name); virtual BOOL isItemMovable() const; @@ -1427,7 +1429,8 @@ void LLPanelObjectInventory::reset() LLEditMenuHandler::gEditMenuHandler = mFolders; } - LLRect scroller_rect(0, getRect().getHeight(), getRect().getWidth(), 0); + int offset = hasBorder() ? getBorder()->getBorderWidth() << 1 : 0; + LLRect scroller_rect(0, getRect().getHeight() - offset, getRect().getWidth() - offset, 0); LLScrollContainer::Params scroll_p; scroll_p.name("task inventory scroller"); scroll_p.rect(scroller_rect); @@ -1456,21 +1459,6 @@ void LLPanelObjectInventory::inventoryChanged(LLViewerObject* object, { mInventoryNeedsUpdate = TRUE; } - - // refresh any properties floaters that are hanging around. - if(inventory) - { - for (LLInventoryObject::object_list_t::const_iterator iter = inventory->begin(); - iter != inventory->end(); ) - { - LLInventoryObject* item = *iter++; - LLFloaterProperties* floater = LLFloaterReg::findTypedInstance("properties", item->getUUID()); - if(floater) - { - floater->refresh(); - } - } - } } void LLPanelObjectInventory::updateInventory() diff --git a/indra/newview/llpaneloutfitedit.cpp b/indra/newview/llpaneloutfitedit.cpp index 35582d2967..4a755a6e93 100644 --- a/indra/newview/llpaneloutfitedit.cpp +++ b/indra/newview/llpaneloutfitedit.cpp @@ -699,8 +699,12 @@ void LLPanelOutfitEdit::onFolderViewFilterCommitted(LLUICtrl* ctrl) LLOpenFoldersWithSelection opener; mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener); mInventoryItemsPanel->getRootFolder()->scrollToShowSelection(); - - LLInventoryModelBackgroundFetch::instance().start(); + + if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) + { + llassert(false); // this should have been done on startup + LLInventoryModelBackgroundFetch::instance().start(); + } } void LLPanelOutfitEdit::onListViewFilterCommitted(LLUICtrl* ctrl) @@ -737,8 +741,12 @@ void LLPanelOutfitEdit::onSearchEdit(const std::string& string) mInventoryItemsPanel->getRootFolder()->applyFunctorRecursively(opener); mInventoryItemsPanel->getRootFolder()->scrollToShowSelection(); } - - LLInventoryModelBackgroundFetch::instance().start(); + + if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) + { + llassert(false); // this should have been done on startup + LLInventoryModelBackgroundFetch::instance().start(); + } if (mInventoryItemsPanel->getFilterSubString().empty() && mSearchString.empty()) { diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index 531073526b..d8c34d5c40 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -87,7 +87,7 @@ BOOL LLPanelOutfitsInventory::postBuild() // ( This is only necessary if we want to show a warning if a user deletes an item that has a // a link in an outfit, see "ConfirmItemDeleteHasLinks". ) - const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS, false); + const LLUUID &outfits_cat = gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS); if (outfits_cat.notNull()) { LLInventoryModelBackgroundFetch::instance().start(outfits_cat); @@ -166,7 +166,11 @@ void LLPanelOutfitsInventory::onSearchEdit(const std::string& string) mActivePanel->setFilterSubString(LLStringUtil::null); } - LLInventoryModelBackgroundFetch::instance().start(); + if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) + { + llassert(false); // this should have been done on startup + LLInventoryModelBackgroundFetch::instance().start(); + } if (mActivePanel->getFilterSubString().empty() && string.empty()) { diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 3e770958da..67f913a067 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -76,30 +76,34 @@ std::string click_action_to_string_value( U8 action); U8 string_value_to_click_action(std::string p_value) { - if(p_value == "Touch") + if (p_value == "Touch") { return CLICK_ACTION_TOUCH; } - if(p_value == "Sit") + if (p_value == "Sit") { return CLICK_ACTION_SIT; } - if(p_value == "Buy") + if (p_value == "Buy") { return CLICK_ACTION_BUY; } - if(p_value == "Pay") + if (p_value == "Pay") { return CLICK_ACTION_PAY; } - if(p_value == "Open") + if (p_value == "Open") { return CLICK_ACTION_OPEN; } - if(p_value == "Zoom") + if (p_value == "Zoom") { return CLICK_ACTION_ZOOM; } + if (p_value == "Ignore") + { + return CLICK_ACTION_IGNORE; + } if (p_value == "None") { return CLICK_ACTION_DISABLED; @@ -130,6 +134,9 @@ std::string click_action_to_string_value( U8 action) case CLICK_ACTION_ZOOM: return "Zoom"; break; + case CLICK_ACTION_IGNORE: + return "Ignore"; + break; case CLICK_ACTION_DISABLED: return "None"; break; @@ -274,12 +281,12 @@ void LLPanelPermissions::disableAll() combo_click_action->setEnabled(FALSE); combo_click_action->clear(); } - getChildView("B:")->setVisible( FALSE); - getChildView("O:")->setVisible( FALSE); - getChildView("G:")->setVisible( FALSE); - getChildView("E:")->setVisible( FALSE); - getChildView("N:")->setVisible( FALSE); - getChildView("F:")->setVisible( FALSE); + getChildView("B:")->setVisible(FALSE); + getChildView("O:")->setVisible(FALSE); + getChildView("G:")->setVisible(FALSE); + getChildView("E:")->setVisible(FALSE); + getChildView("N:")->setVisible(FALSE); + getChildView("F:")->setVisible(FALSE); } void LLPanelPermissions::refresh() @@ -949,19 +956,19 @@ void LLPanelPermissions::refresh() getChild("search_check")->setValue(include_in_search); getChild("search_check")->setTentative( !all_include_in_search); - // Click action (touch, sit, buy) + // Click action (touch, sit, buy, pay, open, play, open media, zoom, ignore) U8 click_action = 0; if (LLSelectMgr::getInstance()->selectionGetClickAction(&click_action)) { LLComboBox* combo_click_action = getChild("clickaction"); - if(combo_click_action) + if (combo_click_action) { const std::string combo_value = click_action_to_string_value(click_action); combo_click_action->setValue(LLSD(combo_value)); } } - if(LLSelectMgr::getInstance()->getSelection()->isAttachment()) + if (LLSelectMgr::getInstance()->getSelection()->isAttachment()) { getChildView("checkbox for sale")->setEnabled(FALSE); getChildView("Edit Cost")->setEnabled(FALSE); diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 0f00231643..1d5ed93c4d 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -91,8 +91,10 @@ class LLParcelHandler : public LLCommandHandler public: // requires trusted browser to trigger LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (params.size() < 2) { diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 816633b545..1a4546875d 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -71,6 +71,7 @@ #include "llpanelblockedlist.h" #include "llpanelprofileclassifieds.h" #include "llpanelprofilepicks.h" +#include "llthumbnailctrl.h" #include "lltrans.h" #include "llviewercontrol.h" #include "llviewermenu.h" //is_agent_mappable @@ -159,6 +160,7 @@ void request_avatar_properties_coro(std::string cap_url, LLUUID agent_id) 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->customer_type = result["customer_type"].asString(); avatar_data->flags = 0; @@ -365,7 +367,7 @@ LLUUID post_profile_image(std::string cap_url, const LLSD &first_data, std::stri httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); - LL_WARNS("AvatarProperties") << result << LL_ENDL; + LL_DEBUGS("AvatarProperties") << result << LL_ENDL; if (!status) { @@ -460,8 +462,10 @@ public: // requires trusted browser to trigger LLProfileHandler() : LLCommandHandler("profile", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (params.size() < 1) return false; std::string agent_name = params[0]; @@ -508,8 +512,10 @@ public: return false; } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (params.size() < 2) return false; LLUUID avatar_id; @@ -908,7 +914,7 @@ BOOL LLPanelProfileSecondLife::postBuild() { mGroupList = getChild("group_list"); mShowInSearchCombo = getChild("show_in_search"); - mSecondLifePic = getChild("2nd_life_pic"); + mSecondLifePic = getChild("2nd_life_pic"); mSecondLifePicLayout = getChild("image_panel"); mDescriptionEdit = getChild("sl_description_edit"); mAgentActionMenuButton = getChild("agent_actions_menu"); @@ -1051,6 +1057,8 @@ void LLPanelProfileSecondLife::resetData() mCantEditObjectsIcon->setEnabled(false); childSetVisible("partner_layout", FALSE); + childSetVisible("badge_layout", FALSE); + childSetVisible("partner_spacer_layout", TRUE); } void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avatar_data) @@ -1258,6 +1266,59 @@ void LLPanelProfileSecondLife::fillAccountStatus(const LLAvatarData* avatar_data std::string caption_text = getString("CaptionTextAcctInfo", args); getChild("account_info")->setValue(caption_text); + + const S32 LINDEN_EMPLOYEE_INDEX = 3; + LLDate sl_release; + sl_release.fromYMDHMS(2003, 6, 23, 0, 0, 0); + std::string customer_lower = avatar_data->customer_type; + LLStringUtil::toLower(customer_lower); + if (avatar_data->caption_index == LINDEN_EMPLOYEE_INDEX) + { + getChild("badge_icon")->setValue("Profile_Badge_Linden"); + getChild("badge_text")->setValue(getString("BadgeLinden")); + childSetVisible("badge_layout", TRUE); + childSetVisible("partner_spacer_layout", FALSE); + } + else if (avatar_data->born_on < sl_release) + { + getChild("badge_icon")->setValue("Profile_Badge_Beta"); + getChild("badge_text")->setValue(getString("BadgeBeta")); + childSetVisible("badge_layout", TRUE); + childSetVisible("partner_spacer_layout", FALSE); + } + else if (customer_lower == "beta_lifetime") + { + getChild("badge_icon")->setValue("Profile_Badge_Beta_Lifetime"); + getChild("badge_text")->setValue(getString("BadgeBetaLifetime")); + childSetVisible("badge_layout", TRUE); + childSetVisible("partner_spacer_layout", FALSE); + } + else if (customer_lower == "lifetime") + { + getChild("badge_icon")->setValue("Profile_Badge_Lifetime"); + getChild("badge_text")->setValue(getString("BadgeLifetime")); + childSetVisible("badge_layout", TRUE); + childSetVisible("partner_spacer_layout", FALSE); + } + else if (customer_lower == "secondlifetime_premium") + { + getChild("badge_icon")->setValue("Profile_Badge_Premium_Lifetime"); + getChild("badge_text")->setValue(getString("BadgePremiumLifetime")); + childSetVisible("badge_layout", TRUE); + childSetVisible("partner_spacer_layout", FALSE); + } + else if (customer_lower == "secondlifetime_premium_plus") + { + getChild("badge_icon")->setValue("Profile_Badge_Pplus_Lifetime"); + getChild("badge_text")->setValue(getString("BadgePremiumPlusLifetime")); + childSetVisible("badge_layout", TRUE); + childSetVisible("partner_spacer_layout", FALSE); + } + else + { + childSetVisible("badge_layout", FALSE); + childSetVisible("partner_spacer_layout", TRUE); + } } void LLPanelProfileSecondLife::fillRightsData() @@ -1412,7 +1473,7 @@ void LLPanelProfileSecondLife::updateOnlineStatus() } else { - childSetVisible("frind_layout", false); + childSetVisible("friend_layout", false); childSetVisible("online_layout", false); childSetVisible("offline_layout", false); } @@ -1420,7 +1481,7 @@ void LLPanelProfileSecondLife::updateOnlineStatus() void LLPanelProfileSecondLife::processOnlineStatus(bool is_friend, bool show_online, bool online) { - childSetVisible("frind_layout", is_friend); + childSetVisible("friend_layout", is_friend); childSetVisible("online_layout", online && show_online); childSetVisible("offline_layout", !online && show_online); } @@ -1437,7 +1498,6 @@ void LLPanelProfileSecondLife::setLoaded() } - class LLProfileImagePicker : public LLFilePickerThread { public: @@ -1484,15 +1544,20 @@ void LLProfileImagePicker::notify(const std::vector& filenames) 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; + LLSD notif_args; + notif_args["REASON"] = LLImage::getLastError().c_str(); + LLNotificationsUtil::add("CannotUploadTexture", notif_args); + LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", " << notif_args["REASON"].asString() << 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; + LLSD args; + args["CAPABILITY"] = PROFILE_IMAGE_UPLOAD_CAP; + LLNotificationsUtil::add("RegionCapabilityRequestError", args); + LL_WARNS("AvatarProperties") << "Failed to upload profile image of type " << (S32)mType << ", no cap found" << LL_ENDL; return; } @@ -1894,30 +1959,16 @@ void LLPanelProfileSecondLife::onShowTexturePicker() mFloaterTexturePickerHandle = texture_floaterp->getHandle(); - texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id) + texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&) { 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); + onCommitProfileImage(asset_id); } }); texture_floaterp->setLocalTextureEnabled(FALSE); texture_floaterp->setBakeTextureEnabled(FALSE); - texture_floaterp->setCanApply(false, true); + texture_floaterp->setCanApply(false, true, false); parent_floater->addDependentFloater(mFloaterTexturePickerHandle); @@ -2132,7 +2183,7 @@ LLPanelProfileFirstLife::~LLPanelProfileFirstLife() BOOL LLPanelProfileFirstLife::postBuild() { mDescriptionEdit = getChild("fl_description_edit"); - mPicture = getChild("real_world_pic"); + mPicture = getChild("real_world_pic"); mUploadPhoto = getChild("fl_upload_image"); mChangePhoto = getChild("fl_change_image"); @@ -2234,29 +2285,15 @@ void LLPanelProfileFirstLife::onChangePhoto() mFloaterTexturePickerHandle = texture_floaterp->getHandle(); - texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLUUID id) + texture_floaterp->setOnFloaterCommitCallback([this](LLTextureCtrl::ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID&) { 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); + onCommitPhoto(asset_id); } }); texture_floaterp->setLocalTextureEnabled(FALSE); - texture_floaterp->setCanApply(false, true); + texture_floaterp->setCanApply(false, true, false); parent_floater->addDependentFloater(mFloaterTexturePickerHandle); diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index d32bb943bd..11632a10ae 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -58,6 +58,7 @@ class LLTextBase; class LLMenuButton; class LLLineEditor; class LLTextEditor; +class LLThumbnailCtrl; class LLPanelProfileClassifieds; class LLPanelProfilePicks; class LLViewerFetchedTexture; @@ -192,7 +193,7 @@ private: LLGroupList* mGroupList; LLComboBox* mShowInSearchCombo; - LLIconCtrl* mSecondLifePic; + LLThumbnailCtrl* mSecondLifePic; LLPanel* mSecondLifePicLayout; LLTextEditor* mDescriptionEdit; LLMenuButton* mAgentActionMenuButton; @@ -301,7 +302,7 @@ protected: void onDiscardDescriptionChanges(); LLTextEditor* mDescriptionEdit; - LLIconCtrl* mPicture; + LLThumbnailCtrl* mPicture; LLButton* mUploadPhoto; LLButton* mChangePhoto; LLButton* mRemovePhoto; diff --git a/indra/newview/llpanelprofileclassifieds.cpp b/indra/newview/llpanelprofileclassifieds.cpp index 1ff12b4f37..dec6cfd83b 100644 --- a/indra/newview/llpanelprofileclassifieds.cpp +++ b/indra/newview/llpanelprofileclassifieds.cpp @@ -106,7 +106,7 @@ public: return true; } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_STARTED) { diff --git a/indra/newview/llpanelprofilepicks.cpp b/indra/newview/llpanelprofilepicks.cpp index 45d0252e4f..0535036cb0 100644 --- a/indra/newview/llpanelprofilepicks.cpp +++ b/indra/newview/llpanelprofilepicks.cpp @@ -87,8 +87,10 @@ public: return true; } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_STARTED) { @@ -529,6 +531,7 @@ void LLPanelProfilePick::setAvatarId(const LLUUID& avatar_id) pick_name = parcel->getName(); pick_desc = parcel->getDesc(); snapshot_id = parcel->getSnapshotID(); + mPickDescription->setParseHTML(false); } LLViewerRegion* region = gAgent.getRegion(); diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index ed244f773c..d6c36bbfb7 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -365,6 +365,15 @@ void LLPanelVolume::getState( ) { LightTextureCtrl->setEnabled(FALSE); LightTextureCtrl->setValid(FALSE); + + if (objectp->isAttachment()) + { + LightTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); + } + else + { + LightTextureCtrl->setImmediateFilterPermMask(PERM_NONE); + } } getChildView("Light Intensity")->setEnabled(false); @@ -1409,6 +1418,19 @@ void LLPanelVolume::setLightTextureID(const LLUUID &asset_id, const LLUUID &item if (volobjp) { LLViewerInventoryItem* item = gInventory.getItem(item_id); + + if (item && volobjp->isAttachment()) + { + const LLPermissions& perm = item->getPermissions(); + BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE; + if (!unrestricted) + { + // Attachments are in world and in inventory simultaneously, + // at the moment server doesn't support such a situation. + return; + } + } + if (item && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) { LLToolDragAndDrop::handleDropMaterialProtections(volobjp, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); diff --git a/indra/newview/llpanelwearing.cpp b/indra/newview/llpanelwearing.cpp index 3347c40687..5242c4fef9 100644 --- a/indra/newview/llpanelwearing.cpp +++ b/indra/newview/llpanelwearing.cpp @@ -40,6 +40,7 @@ #include "llinventorymodel.h" #include "llinventoryobserver.h" #include "llmenubutton.h" +#include "lloutfitobserver.h" #include "llscrolllistctrl.h" #include "llviewermenu.h" #include "llviewerregion.h" @@ -218,8 +219,6 @@ LLPanelWearing::LLPanelWearing() , mIsInitialized(false) , mAttachmentsChangedConnection() { - mCategoriesObserver = new LLInventoryCategoriesObserver(); - mGearMenu = new LLWearingGearMenu(this); mContextMenu = new LLWearingContextMenu(); mAttachmentsMenu = new LLTempAttachmentsContextMenu(this); @@ -231,12 +230,6 @@ LLPanelWearing::~LLPanelWearing() delete mContextMenu; delete mAttachmentsMenu; - if (gInventory.containsObserver(mCategoriesObserver)) - { - gInventory.removeObserver(mCategoriesObserver); - } - delete mCategoriesObserver; - if (mAttachmentsChangedConnection.connected()) { mAttachmentsChangedConnection.disconnect(); @@ -281,10 +274,8 @@ void LLPanelWearing::onOpen(const LLSD& /*info*/) if (!category) return; - gInventory.addObserver(mCategoriesObserver); - // Start observing changes in Current Outfit category. - mCategoriesObserver->addCategory(cof, boost::bind(&LLWearableItemsList::updateList, mCOFItemsList, cof)); + LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLWearableItemsList::updateList, mCOFItemsList, cof)); // Fetch Current Outfit contents and refresh the list to display // initially fetched items. If not all items are fetched now @@ -566,7 +557,7 @@ void LLPanelWearing::onRemoveAttachment() { LLSelectMgr::getInstance()->deselectAll(); LLSelectMgr::getInstance()->selectObjectAndFamily(mAttachmentsMap[item->getUUID()]); - LLSelectMgr::getInstance()->sendDropAttachment(); + LLSelectMgr::getInstance()->sendDetach(); } } diff --git a/indra/newview/llpanelwearing.h b/indra/newview/llpanelwearing.h index 715404a457..18e543eec6 100644 --- a/indra/newview/llpanelwearing.h +++ b/indra/newview/llpanelwearing.h @@ -90,7 +90,6 @@ private: void getAttachmentLimitsCoro(std::string url); - LLInventoryCategoriesObserver* mCategoriesObserver; LLWearableItemsList* mCOFItemsList; LLScrollListCtrl* mTempItemsList; LLWearingGearMenu* mGearMenu; diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index b9b2279e77..0117db86e8 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -241,14 +241,22 @@ void LLPreview::refreshFromItem() // static BOOL LLPreview::canModify(const LLUUID taskUUID, const LLInventoryItem* item) { + const LLViewerObject* object = nullptr; if (taskUUID.notNull()) { - LLViewerObject* object = gObjectList.findObject(taskUUID); - if(object && !object->permModify()) - { - // No permission to edit in-world inventory - return FALSE; - } + object = gObjectList.findObject(taskUUID); + } + + return canModify(object, item); +} + +// static +BOOL LLPreview::canModify(const LLViewerObject* object, const LLInventoryItem* item) +{ + if (object && !object->permModify()) + { + // No permission to edit in-world inventory + return FALSE; } return item && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE); diff --git a/indra/newview/llpreview.h b/indra/newview/llpreview.h index ab60f4c008..3688ee0192 100644 --- a/indra/newview/llpreview.h +++ b/indra/newview/llpreview.h @@ -36,6 +36,7 @@ #include class LLInventoryItem; +class LLViewerObject; class LLLineEditor; class LLRadioGroup; class LLPreview; @@ -109,6 +110,7 @@ public: // We can't modify Item or description in preview if either in-world Object // or Item itself is unmodifiable static BOOL canModify(const LLUUID taskUUID, const LLInventoryItem* item); + static BOOL canModify(const LLViewerObject* object, const LLInventoryItem* item); protected: virtual void onCommit(); diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index 20b821f895..6681703625 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -39,7 +39,9 @@ #include "llinventorydefines.h" #include "llinventorymodel.h" #include "lllineeditor.h" +#include "llmd5.h" #include "llnotificationsutil.h" +#include "llmd5.h" #include "llresmgr.h" #include "roles_constants.h" #include "llscrollbar.h" diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 8b93dd103d..aceb8539d2 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -40,6 +40,7 @@ #include "llinventorymodel.h" #include "llkeyboard.h" #include "lllineeditor.h" +#include "llmd5.h" #include "llhelp.h" #include "llnotificationsutil.h" #include "llresmgr.h" diff --git a/indra/newview/llreflectionmap.cpp b/indra/newview/llreflectionmap.cpp index efaf068bd2..ec54fa1165 100644 --- a/indra/newview/llreflectionmap.cpp +++ b/indra/newview/llreflectionmap.cpp @@ -117,7 +117,7 @@ void LLReflectionMap::autoAdjustOrigin() { int face = -1; LLVector4a intersection; - LLDrawable* drawable = mGroup->lineSegmentIntersect(bounds[0], corners[i], false, false, true, &face, &intersection); + LLDrawable* drawable = mGroup->lineSegmentIntersect(bounds[0], corners[i], false, false, true, true, &face, &intersection); if (drawable != nullptr) { hit = true; @@ -157,6 +157,10 @@ void LLReflectionMap::autoAdjustOrigin() } mRadius = llmax(sqrtf(r2.getF32()), 8.f); + + // make sure near clip doesn't poke through ground + fp[2] = llmax(fp[2], height+mRadius*0.5f); + } } else if (mViewerObject) @@ -204,6 +208,14 @@ F32 LLReflectionMap::getNearClip() { ret = ((LLVOVolume*)mViewerObject)->getReflectionProbeNearClip(); } + else if (mGroup) + { + ret = mRadius * 0.5f; // default to half radius for automatic object probes + } + else + { + ret = 1.f; // default to 1m for automatic terrain probes + } return llmax(ret, MINIMUM_NEAR_CLIP); } diff --git a/indra/newview/llreflectionmapmanager.cpp b/indra/newview/llreflectionmapmanager.cpp index bb0bb04797..915c8893a4 100644 --- a/indra/newview/llreflectionmapmanager.cpp +++ b/indra/newview/llreflectionmapmanager.cpp @@ -244,11 +244,14 @@ void LLReflectionMapManager::update() continue; } - if (probe != mDefaultProbe && !probe->isRelevant()) - { + if (probe != mDefaultProbe && + (!probe->isRelevant() || mPaused)) + { // skip irrelevant probes (or all non-default probes if paused) continue; } + + LLVector4a d; if (probe != mDefaultProbe) @@ -807,6 +810,16 @@ void LLReflectionMapManager::reset() mReset = true; } +void LLReflectionMapManager::pause() +{ + mPaused = true; +} + +void LLReflectionMapManager::resume() +{ + mPaused = false; +} + void LLReflectionMapManager::shift(const LLVector4a& offset) { for (auto& probe : mProbes) diff --git a/indra/newview/llreflectionmapmanager.h b/indra/newview/llreflectionmapmanager.h index 5a3901cae9..b77a33da89 100644 --- a/indra/newview/llreflectionmapmanager.h +++ b/indra/newview/llreflectionmapmanager.h @@ -84,6 +84,12 @@ public: // reset all state on the next update void reset(); + // pause all updates other than the default probe + void pause(); + + // unpause (see pause) + void resume(); + // called on region crossing to "shift" probes into new coordinate frame void shift(const LLVector4a& offset); @@ -191,5 +197,8 @@ private: // if true, reset all probe render state on the next update (for teleports and sky changes) bool mReset = false; + + // if true, only update the default probe + bool mPaused = false; }; diff --git a/indra/newview/llsceneview.cpp b/indra/newview/llsceneview.cpp index 5e339a52bf..9b1d2d48c6 100644 --- a/indra/newview/llsceneview.cpp +++ b/indra/newview/llsceneview.cpp @@ -100,8 +100,6 @@ void LLSceneView::draw() F32 total_physics[] = { 0.f, 0.f }; - U32 object_count = 0; - LLViewerRegion* region = gAgent.getRegion(); if (region) { @@ -116,8 +114,7 @@ void LLSceneView::draw() U32 idx = object->isAttachment() ? 1 : 0; LLVolume* volume = object->getVolume(); - object_count++; - + F32 radius = object->getScale().magVec(); size[idx].push_back(radius); diff --git a/indra/newview/llscripteditor.cpp b/indra/newview/llscripteditor.cpp index 140cbbedbe..3278bd3aa9 100644 --- a/indra/newview/llscripteditor.cpp +++ b/indra/newview/llscripteditor.cpp @@ -187,7 +187,7 @@ void LLScriptEditor::drawSelectionBackground() // Draw selection even if we don't have keyboard focus for search/replace if( hasSelection() && !mLineInfoList.empty()) { - std::vector selection_rects = getSelctionRects(); + std::vector selection_rects = getSelectionRects(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); const LLColor4& color = mReadOnly ? mReadOnlyFgColor : mFgColor; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index dca341e5a2..c86e43333c 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -57,7 +57,6 @@ #include "llviewerwindow.h" #include "lldrawable.h" #include "llfloaterinspect.h" -#include "llfloaterproperties.h" #include "llfloaterreporter.h" #include "llfloaterreg.h" #include "llfloatertools.h" @@ -1773,14 +1772,20 @@ void LLObjectSelection::applyNoCopyTextureToTEs(LLViewerInventoryItem* item) } } -void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) +bool LLObjectSelection::applyRestrictedPbrMaterialToTEs(LLViewerInventoryItem* item) { if (!item) { - return; + return false; } LLUUID asset_id = item->getAssetUUID(); + if (asset_id.isNull()) + { + asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } + + bool material_copied_all_faces = true; for (iterator iter = begin(); iter != end(); ++iter) { @@ -1797,12 +1802,17 @@ void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) { if (node->isTESelected(te)) { - //(no-copy) materials must be moved to the object's inventory only once + //(no-copy), (no-modify), and (no-transfer) materials must be moved to the object's inventory only once // without making any copies if (!material_copied && asset_id.notNull()) { - LLToolDragAndDrop::handleDropMaterialProtections(object, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); - material_copied = true; + material_copied = (bool)LLToolDragAndDrop::handleDropMaterialProtections(object, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); + } + if (!material_copied) + { + // Applying the material is not possible for this object given the current inventory + material_copied_all_faces = false; + break; } // apply texture for the selected faces @@ -1814,6 +1824,8 @@ void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) } LLGLTFMaterialList::flushUpdates(); + + return material_copied_all_faces; } @@ -1821,7 +1833,7 @@ void LLObjectSelection::applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item) // selectionSetImage() //----------------------------------------------------------------------------- // *TODO: re-arch texture applying out of lltooldraganddrop -void LLSelectMgr::selectionSetImage(const LLUUID& imageid) +bool LLSelectMgr::selectionSetImage(const LLUUID& imageid) { // First for (no copy) textures and multiple object selection LLViewerInventoryItem* item = gInventory.getItem(imageid); @@ -1829,9 +1841,11 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) && (mSelectedObjects->getNumNodes() > 1) ) { - LL_WARNS() << "Attempted to apply no-copy texture to multiple objects" - << LL_ENDL; - return; + LL_DEBUGS() << "Attempted to apply no-copy texture " << imageid + << " to multiple objects" << LL_ENDL; + + LLNotificationsUtil::add("FailedToApplyTextureNoCopyToMultiple"); + return false; } struct f : public LLSelectedTEFunctor @@ -1841,10 +1855,24 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mImageID(id) {} bool apply(LLViewerObject* objectp, S32 te) { - if(objectp && !objectp->permModify()) + if(!objectp || !objectp->permModify()) { return false; } + + // Might be better to run willObjectAcceptInventory + if (mItem && objectp->isAttachment()) + { + const LLPermissions& perm = mItem->getPermissions(); + BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE; + if (!unrestricted) + { + // Attachments are in world and in inventory simultaneously, + // at the moment server doesn't support such a situation. + return false; + } + } + if (mItem) { LLToolDragAndDrop::dropTextureOneFace(objectp, @@ -1896,12 +1924,14 @@ void LLSelectMgr::selectionSetImage(const LLUUID& imageid) } } sendfunc(item); getSelection()->applyToObjects(&sendfunc); + + return true; } //----------------------------------------------------------------------------- // selectionSetGLTFMaterial() //----------------------------------------------------------------------------- -void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) +bool LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) { // First for (no copy) textures and multiple object selection LLViewerInventoryItem* item = gInventory.getItem(mat_id); @@ -1909,22 +1939,37 @@ void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) && (mSelectedObjects->getNumNodes() > 1)) { - LL_WARNS() << "Attempted to apply no-copy material to multiple objects" - << LL_ENDL; - return; + LL_DEBUGS() << "Attempted to apply no-copy material " << mat_id + << "to multiple objects" << LL_ENDL; + + LLNotificationsUtil::add("FailedToApplyGLTFNoCopyToMultiple"); + return false; } struct f : public LLSelectedTEFunctor { LLViewerInventoryItem* mItem; LLUUID mMatId; + bool material_copied_any_face = false; + bool material_copied_all_faces = true; f(LLViewerInventoryItem* item, const LLUUID& id) : mItem(item), mMatId(id) {} bool apply(LLViewerObject* objectp, S32 te) { - if (objectp && !objectp->permModify()) + if (!objectp || !objectp->permModify()) { return false; } + if (mItem && objectp->isAttachment()) + { + const LLPermissions& perm = mItem->getPermissions(); + BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE; + if (!unrestricted) + { + // Attachments are in world and in inventory simultaneously, + // at the moment server doesn't support such a situation. + return false; + } + } LLUUID asset_id = mMatId; if (mItem) { @@ -1935,6 +1980,10 @@ void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) return false; } asset_id = mItem->getAssetUUID(); + if (asset_id.isNull()) + { + asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } } // Blank out most override data on the object and send to server @@ -1944,14 +1993,19 @@ void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) } }; - if (item && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) + bool success = true; + if (item && + (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()) || + !item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()) || + !item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID()) + )) { - getSelection()->applyNoCopyPbrMaterialToTEs(item); + success = success && getSelection()->applyRestrictedPbrMaterialToTEs(item); } else { f setfunc(item, mat_id); - getSelection()->applyToTEs(&setfunc); + success = success && getSelection()->applyToTEs(&setfunc); } struct g : public LLSelectedObjectFunctor @@ -1980,9 +2034,11 @@ void LLSelectMgr::selectionSetGLTFMaterial(const LLUUID& mat_id) return true; } } sendfunc(item); - getSelection()->applyToObjects(&sendfunc); + success = success && getSelection()->applyToObjects(&sendfunc); LLGLTFMaterialList::flushUpdates(); + + return success; } //----------------------------------------------------------------------------- @@ -2232,7 +2288,19 @@ void LLSelectMgr::selectionSetBumpmap(U8 bumpmap, const LLUUID &image_id) if (item && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) { LLViewerObject *object = mSelectedObjects->getFirstRootObject(); - if (!object) return; + if (!object) + { + return; + } + const LLPermissions& perm = item->getPermissions(); + BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE; + BOOL attached = object->isAttachment(); + if (attached && !unrestricted) + { + // Attachments are in world and in inventory simultaneously, + // at the moment server doesn't support such a situation. + return; + } LLToolDragAndDrop::handleDropMaterialProtections(object, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); } getSelection()->applyToTEs(&setfunc); @@ -2292,7 +2360,19 @@ void LLSelectMgr::selectionSetShiny(U8 shiny, const LLUUID &image_id) if (item && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) { LLViewerObject *object = mSelectedObjects->getFirstRootObject(); - if (!object) return; + if (!object) + { + return; + } + const LLPermissions& perm = item->getPermissions(); + BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE; + BOOL attached = object->isAttachment(); + if (attached && !unrestricted) + { + // Attachments are in world and in inventory simultaneously, + // at the moment server doesn't support such a situation. + return; + } LLToolDragAndDrop::handleDropMaterialProtections(object, item, LLToolDragAndDrop::SOURCE_AGENT, LLUUID::null); } getSelection()->applyToTEs(&setfunc); @@ -5440,8 +5520,8 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle, LLViewerRegion* last_region; LLViewerRegion* current_region; - S32 objects_sent = 0; - S32 packets_sent = 0; +// S32 objects_sent = 0; +// S32 packets_sent = 0; S32 objects_in_this_packet = 0; bool link_operation = message_name == "ObjectLink"; @@ -5573,7 +5653,7 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle, (*pack_body)(node, user_data); // do any related logging (*log_func)(node, user_data); - ++objects_sent; +// ++objects_sent; ++objects_in_this_packet; // and on to the next object @@ -5591,7 +5671,7 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle, { // otherwise send current message and start new one gMessageSystem->sendReliable( last_region->getHost()); - packets_sent++; +// packets_sent++; objects_in_this_packet = 0; gMessageSystem->newMessage(message_name.c_str()); @@ -5608,7 +5688,7 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle, { // add root instance into new message (*pack_body)(linkset_root, user_data); - ++objects_sent; +// ++objects_sent; ++objects_in_this_packet; } } @@ -5622,7 +5702,7 @@ void LLSelectMgr::sendListToRegions(LLObjectSelectionHandle selected_handle, if (gMessageSystem->getCurrentSendTotal() > 0) { gMessageSystem->sendReliable( current_region->getHost()); - packets_sent++; +// packets_sent++; } else { @@ -7057,8 +7137,6 @@ void dialog_refresh_all() gMenuAttachmentOther->arrange(); } - LLFloaterProperties::dirtyAll(); - LLFloaterInspect* inspect_instance = LLFloaterReg::getTypedInstance("inspect"); if(inspect_instance) { diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index ca9a32f0db..f89209b437 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -378,7 +378,17 @@ public: * Then this only texture is used for all selected faces. */ void applyNoCopyTextureToTEs(LLViewerInventoryItem* item); - void applyNoCopyPbrMaterialToTEs(LLViewerInventoryItem* item); + /* + * Multi-purpose function for applying PBR materials to the + * selected object or faces, any combination of copy/mod/transfer + * permission restrictions. This method moves the restricted + * material to the object's inventory and doesn't make a copy of the + * material for each face. Then this only material is used for + * all selected faces. + * Returns false if applying the material failed on one or more selected + * faces. + */ + bool applyRestrictedPbrMaterialToTEs(LLViewerInventoryItem* item); ESelectType getSelectType() const { return mSelectType; } @@ -634,8 +644,8 @@ public: void selectionSetDensity(F32 density); void selectionSetRestitution(F32 restitution); void selectionSetMaterial(U8 material); - void selectionSetImage(const LLUUID& imageid); // could be item or asset id - void selectionSetGLTFMaterial(const LLUUID& mat_id); // could be item or asset id + bool selectionSetImage(const LLUUID& imageid); // could be item or asset id + bool selectionSetGLTFMaterial(const LLUUID& mat_id); // material id only void selectionSetColor(const LLColor4 &color); void selectionSetColorOnly(const LLColor4 &color); // Set only the RGB channels void selectionSetAlphaOnly(const F32 alpha); // Set only the alpha channel diff --git a/indra/newview/llsettingsvo.cpp b/indra/newview/llsettingsvo.cpp index 264359a3a9..42587658a6 100644 --- a/indra/newview/llsettingsvo.cpp +++ b/indra/newview/llsettingsvo.cpp @@ -97,6 +97,20 @@ namespace //========================================================================= +void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function created_cb) +{ + inventory_result_fn cb = NULL; + + if (created_cb != NULL) + { + cb = [created_cb](LLUUID asset_id, LLUUID inventory_id, LLUUID object_id, LLSD results) + { + created_cb(inventory_id); + }; + } + createNewInventoryItem(stype, parent_id, cb); +} + void LLSettingsVOBase::createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID &parent_id, inventory_result_fn callback) { LLTransactionID tid; @@ -318,7 +332,7 @@ void LLSettingsVOBase::onAssetDownloadComplete(const LLUUID &asset_id, S32 statu std::stringstream llsdstream(buffer); LLSD llsdsettings; - if (LLSDSerialize::deserialize(llsdsettings, llsdstream, -1)) + if (LLSDSerialize::deserialize(llsdsettings, llsdstream, LLSDSerialize::SIZE_UNLIMITED)) { settings = createFromLLSD(llsdsettings); } @@ -385,7 +399,7 @@ LLSettingsBase::ptr_t LLSettingsVOBase::importFile(const std::string &filename) return LLSettingsBase::ptr_t(); } - if (!LLSDSerialize::deserialize(settings, file, -1)) + if (!LLSDSerialize::deserialize(settings, file, LLSDSerialize::SIZE_UNLIMITED)) { LL_WARNS("SETTINGS") << "Unable to deserialize settings from '" << filename << "'" << LL_ENDL; return LLSettingsBase::ptr_t(); @@ -721,6 +735,15 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) static LLCachedControl should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true); static LLCachedControl auto_adjust_ambient_scale(gSavedSettings, "RenderSkyAutoAdjustAmbientScale", 0.75f); static LLCachedControl auto_adjust_hdr_scale(gSavedSettings, "RenderSkyAutoAdjustHDRScale", 2.f); + static LLCachedControl auto_adjust_blue_horizon_scale(gSavedSettings, "RenderSkyAutoAdjustBlueHorizonScale", 1.f); + static LLCachedControl auto_adjust_blue_density_scale(gSavedSettings, "RenderSkyAutoAdjustBlueDensityScale", 1.f); + static LLCachedControl auto_adjust_sun_color_scale(gSavedSettings, "RenderSkyAutoAdjustSunColorScale", 1.f); + static LLCachedControl auto_adjust_probe_ambiance(gSavedSettings, "RenderSkyAutoAdjustProbeAmbiance", 1.f); + static LLCachedControl sunlight_scale(gSavedSettings, "RenderSkySunlightScale", 1.5f); + static LLCachedControl ambient_scale(gSavedSettings, "RenderSkyAmbientScale", 1.5f); + + shader->uniform1f(LLShaderMgr::SKY_SUNLIGHT_SCALE, sunlight_scale); + shader->uniform1f(LLShaderMgr::SKY_AMBIENT_SCALE, ambient_scale); static LLCachedControl cloud_shadow_scale(gSavedSettings, "RenderCloudShadowAmbianceFactor", 0.125f); F32 probe_ambiance = getTotalReflectionProbeAmbiance(cloud_shadow_scale); @@ -733,14 +756,23 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) { if (psky->getReflectionProbeAmbiance() != 0.f) { - shader->uniform3fv(LLShaderMgr::AMBIENT, getAmbientColor().mV); + shader->uniform3fv(LLShaderMgr::AMBIENT, LLVector3(ambient.mV)); shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, sqrtf(g)*2.0); // use a modifier here so 1.0 maps to the "most desirable" default and the maximum value doesn't go off the rails } else if (psky->canAutoAdjust() && should_auto_adjust) { // auto-adjust legacy sky to take advantage of probe ambiance shader->uniform3fv(LLShaderMgr::AMBIENT, (ambient * auto_adjust_ambient_scale).mV); shader->uniform1f(LLShaderMgr::SKY_HDR_SCALE, auto_adjust_hdr_scale); - probe_ambiance = 1.f; // NOTE -- must match LLSettingsSky::getReflectionProbeAmbiance value for "auto_adjust" true + LLColor3 blue_horizon = getBlueHorizon() * auto_adjust_blue_horizon_scale; + LLColor3 blue_density = getBlueDensity() * auto_adjust_blue_density_scale; + LLColor3 sun_diffuse = getSunDiffuse() * auto_adjust_sun_color_scale; + + shader->uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, sun_diffuse.mV); + shader->uniform3fv(LLShaderMgr::BLUE_DENSITY, blue_density.mV); + shader->uniform3fv(LLShaderMgr::BLUE_HORIZON, blue_horizon.mV); + + LLSettingsSky::sAutoAdjustProbeAmbiance = auto_adjust_probe_ambiance; + probe_ambiance = auto_adjust_probe_ambiance; // NOTE -- must match LLSettingsSky::getReflectionProbeAmbiance value for "auto_adjust" true } else { @@ -755,7 +787,7 @@ void LLSettingsVOSky::applySpecial(void *ptarget, bool force) shader->uniform1f(LLShaderMgr::SUN_MOON_GLOW_FACTOR, getSunMoonGlowFactor()); shader->uniform1f(LLShaderMgr::DENSITY_MULTIPLIER, getDensityMultiplier()); shader->uniform1f(LLShaderMgr::DISTANCE_MULTIPLIER, getDistanceMultiplier()); - + shader->uniform1f(LLShaderMgr::GAMMA, g); } diff --git a/indra/newview/llsettingsvo.h b/indra/newview/llsettingsvo.h index 05ec0e9275..4f410ab7d9 100644 --- a/indra/newview/llsettingsvo.h +++ b/indra/newview/llsettingsvo.h @@ -49,6 +49,7 @@ public: typedef std::function inventory_download_fn; typedef std::function inventory_result_fn; + static void createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID& parent_id, std::function created_cb); static void createNewInventoryItem(LLSettingsType::type_e stype, const LLUUID &parent_id, inventory_result_fn callback = inventory_result_fn()); static void createInventoryItem(const LLSettingsBase::ptr_t &settings, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback = inventory_result_fn()); static void createInventoryItem(const LLSettingsBase::ptr_t &settings, U32 next_owner_perm, const LLUUID &parent_id, std::string settings_name, inventory_result_fn callback = inventory_result_fn()); diff --git a/indra/newview/llshareavatarhandler.cpp b/indra/newview/llshareavatarhandler.cpp index 142e00c3f7..8c5ebb75ef 100644 --- a/indra/newview/llshareavatarhandler.cpp +++ b/indra/newview/llshareavatarhandler.cpp @@ -38,7 +38,7 @@ public: { } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableAvatarShare")) { diff --git a/indra/newview/llsidepanelinventory.cpp b/indra/newview/llsidepanelinventory.cpp index a5dcdc41ed..e970f70e92 100644 --- a/indra/newview/llsidepanelinventory.cpp +++ b/indra/newview/llsidepanelinventory.cpp @@ -73,6 +73,8 @@ static const char * const INBOX_LAYOUT_PANEL_NAME = "inbox_layout_panel"; static const char * const INVENTORY_LAYOUT_STACK_NAME = "inventory_layout_stack"; static const char * const MARKETPLACE_INBOX_PANEL = "marketplace_inbox"; +static bool sLoginCompleted = false; + // // Helpers // @@ -115,21 +117,19 @@ private: LLSidepanelInventory::LLSidepanelInventory() : LLPanel() - , mItemPanel(NULL) , mPanelMainInventory(NULL) , mInboxEnabled(false) , mCategoriesObserver(NULL) , mInboxAddedObserver(NULL) + , mInboxLayoutPanel(NULL) { //buildFromFile( "panel_inventory.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder() } LLSidepanelInventory::~LLSidepanelInventory() { - LLLayoutPanel* inbox_layout_panel = getChild(INBOX_LAYOUT_PANEL_NAME); - // Save the InventoryMainPanelHeight in settings per account - gSavedPerAccountSettings.setS32("InventoryInboxHeight", inbox_layout_panel->getTargetDim()); + gSavedPerAccountSettings.setS32("InventoryInboxHeight", mInboxLayoutPanel->getTargetDim()); if (mCategoriesObserver && gInventory.containsObserver(mCategoriesObserver)) { @@ -158,29 +158,11 @@ BOOL LLSidepanelInventory::postBuild() // UI elements from inventory panel { mInventoryPanel = getChild("sidepanel_inventory_panel"); - - mInfoBtn = mInventoryPanel->getChild("info_btn"); - mInfoBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onInfoButtonClicked, this)); - - mShareBtn = mInventoryPanel->getChild("share_btn"); - mShareBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShareButtonClicked, this)); - - mShopBtn = mInventoryPanel->getChild("shop_btn"); - mShopBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onShopButtonClicked, this)); - - mWearBtn = mInventoryPanel->getChild("wear_btn"); - mWearBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onWearButtonClicked, this)); - - mPlayBtn = mInventoryPanel->getChild("play_btn"); - mPlayBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onPlayButtonClicked, this)); - - mTeleportBtn = mInventoryPanel->getChild("teleport_btn"); - mTeleportBtn->setClickedCallback(boost::bind(&LLSidepanelInventory::onTeleportButtonClicked, this)); mPanelMainInventory = mInventoryPanel->getChild("panel_main_inventory"); mPanelMainInventory->setSelectCallback(boost::bind(&LLSidepanelInventory::onSelectionChange, this, _1, _2)); - LLTabContainer* tabs = mPanelMainInventory->getChild("inventory filter tabs"); - tabs->setCommitCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this)); + //LLTabContainer* tabs = mPanelMainInventory->getChild("inventory filter tabs"); + //tabs->setCommitCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this)); /* EXT-4846 : "Can we suppress the "Landmarks" and "My Favorites" folder since they have their own Task Panel?" @@ -190,25 +172,7 @@ BOOL LLSidepanelInventory::postBuild() my_inventory_panel->addHideFolderType(LLFolderType::FT_FAVORITE); */ - LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this)); - } - - // UI elements from item panel - { - mItemPanel = getChild("sidepanel__item_panel"); - - LLButton* back_btn = mItemPanel->getChild("back_btn"); - back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this)); - } - - // UI elements from task panel - { - mTaskPanel = findChild("sidepanel__task_panel"); - if (mTaskPanel) - { - LLButton* back_btn = mTaskPanel->getChild("back_btn"); - back_btn->setClickedCallback(boost::bind(&LLSidepanelInventory::onBackButtonClicked, this)); - } + //LLOutfitObserver::instance().addCOFChangedCallback(boost::bind(&LLSidepanelInventory::updateVerbs, this)); } // Received items inbox setup @@ -220,38 +184,55 @@ BOOL LLSidepanelInventory::postBuild() inbox_button->setCommitCallback(boost::bind(&LLSidepanelInventory::onToggleInboxBtn, this)); - // Get the previous inbox state from "InventoryInboxToggleState" setting. - bool is_inbox_collapsed = !inbox_button->getToggleState(); + // For main Inventory floater: Get the previous inbox state from "InventoryInboxToggleState" setting. + // For additional Inventory floaters: Collapsed state is default. + bool is_inbox_collapsed = !inbox_button->getToggleState() || sLoginCompleted; // Restore the collapsed inbox panel state - LLLayoutPanel* inbox_panel = getChild(INBOX_LAYOUT_PANEL_NAME); - inv_stack->collapsePanel(inbox_panel, is_inbox_collapsed); - if (!is_inbox_collapsed) - { - inbox_panel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); - } + mInboxLayoutPanel = getChild(INBOX_LAYOUT_PANEL_NAME); + inv_stack->collapsePanel(mInboxLayoutPanel, is_inbox_collapsed); + if (!is_inbox_collapsed) + { + mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); + } - // Set the inbox visible based on debug settings (final setting comes from http request below) - enableInbox(gSavedSettings.getBOOL("InventoryDisplayInbox")); - - // Trigger callback for after login so we can setup to track inbox changes after initial inventory load - LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSidepanelInventory::updateInbox, this)); + if (sLoginCompleted) + { + //save the state of Inbox panel only for main Inventory floater + inbox_button->removeControlVariable(); + inbox_button->setToggleState(false); + updateInbox(); + } + else + { + // Trigger callback for after login so we can setup to track inbox changes after initial inventory load + LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLSidepanelInventory::updateInbox, this)); + } } gSavedSettings.getControl("InventoryDisplayInbox")->getCommitSignal()->connect(boost::bind(&handleInventoryDisplayInboxChanged)); - // Update the verbs buttons state. - updateVerbs(); + LLFloater *floater = dynamic_cast(getParent()); + if (floater && floater->getKey().isUndefined() && !sLoginCompleted) + { + // Prefill inventory for primary inventory floater + // Other floaters should fill on visibility change + // + // see get_instance_num(); + // Primary inventory floater will have undefined key + initInventoryViews(); + } return TRUE; } void LLSidepanelInventory::updateInbox() { + sLoginCompleted = true; // // Track inbox folder changes // - const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX, true); + const LLUUID inbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_INBOX); // Set up observer to listen for creation of inbox if it doesn't exist if (inbox_id.isNull()) @@ -336,8 +317,20 @@ void LLSidepanelInventory::enableInbox(bool enabled) { mInboxEnabled = enabled; - LLLayoutPanel * inbox_layout_panel = getChild(INBOX_LAYOUT_PANEL_NAME); - inbox_layout_panel->setVisible(enabled); + if(!enabled || !mPanelMainInventory->isSingleFolderMode()) + { + toggleInbox(); + } +} + +void LLSidepanelInventory::hideInbox() +{ + mInboxLayoutPanel->setVisible(false); +} + +void LLSidepanelInventory::toggleInbox() +{ + mInboxLayoutPanel->setVisible(mInboxEnabled); } void LLSidepanelInventory::openInbox() @@ -367,25 +360,24 @@ void LLSidepanelInventory::onInboxChanged(const LLUUID& inbox_id) void LLSidepanelInventory::onToggleInboxBtn() { LLButton* inboxButton = getChild(INBOX_BUTTON_NAME); - LLLayoutPanel* inboxPanel = getChild(INBOX_LAYOUT_PANEL_NAME); LLLayoutStack* inv_stack = getChild(INVENTORY_LAYOUT_STACK_NAME); const bool inbox_expanded = inboxButton->getToggleState(); // Expand/collapse the indicated panel - inv_stack->collapsePanel(inboxPanel, !inbox_expanded); + inv_stack->collapsePanel(mInboxLayoutPanel, !inbox_expanded); if (inbox_expanded) { - inboxPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); - if (inboxPanel->isInVisibleChain()) + mInboxLayoutPanel->setTargetDim(gSavedPerAccountSettings.getS32("InventoryInboxHeight")); + if (mInboxLayoutPanel->isInVisibleChain()) { gSavedPerAccountSettings.setU32("LastInventoryInboxActivity", time_corrected()); } } else { - gSavedPerAccountSettings.setS32("InventoryInboxHeight", inboxPanel->getTargetDim()); + gSavedPerAccountSettings.setS32("InventoryInboxHeight", mInboxLayoutPanel->getTargetDim()); } } @@ -409,47 +401,7 @@ void LLSidepanelInventory::onOpen(const LLSD& key) } #endif - if(key.size() == 0) - return; - - mItemPanel->reset(); - - if (key.has("id")) - { - mItemPanel->setItemID(key["id"].asUUID()); - if (key.has("object")) - { - mItemPanel->setObjectID(key["object"].asUUID()); - } - showItemInfoPanel(); - } - if (key.has("task")) - { - if (mTaskPanel) - mTaskPanel->setObjectSelection(LLSelectMgr::getInstance()->getSelection()); - showTaskInfoPanel(); - } -} - -void LLSidepanelInventory::onInfoButtonClicked() -{ - LLInventoryItem *item = getSelectedItem(); - if (item) - { - mItemPanel->reset(); - mItemPanel->setItemID(item->getUUID()); - showItemInfoPanel(); - } -} - -void LLSidepanelInventory::onShareButtonClicked() -{ - LLAvatarActions::shareWithAvatars(this); -} - -void LLSidepanelInventory::onShopButtonClicked() -{ - LLWeb::loadURL(gSavedSettings.getString("MarketplaceURL")); + gAgent.showLatestFeatureNotification("inventory"); } void LLSidepanelInventory::performActionOnSelection(const std::string &action) @@ -471,47 +423,6 @@ void LLSidepanelInventory::performActionOnSelection(const std::string &action) static_cast(current_item->getViewModelItem())->performAction(mPanelMainInventory->getActivePanel()->getModel(), action); } -void LLSidepanelInventory::onWearButtonClicked() -{ - // Get selected items set. - const std::set selected_uuids_set = LLAvatarActions::getInventorySelectedUUIDs(); - if (selected_uuids_set.empty()) return; // nothing selected - - // Convert the set to a vector. - uuid_vec_t selected_uuids_vec; - for (std::set::const_iterator it = selected_uuids_set.begin(); it != selected_uuids_set.end(); ++it) - { - selected_uuids_vec.push_back(*it); - } - - // Wear all selected items. - wear_multiple(selected_uuids_vec, true); -} - -void LLSidepanelInventory::onPlayButtonClicked() -{ - const LLInventoryItem *item = getSelectedItem(); - if (!item) - { - return; - } - - switch(item->getInventoryType()) - { - case LLInventoryType::IT_GESTURE: - performActionOnSelection("play"); - break; - default: - performActionOnSelection("open"); - break; - } -} - -void LLSidepanelInventory::onTeleportButtonClicked() -{ - performActionOnSelection("teleport"); -} - void LLSidepanelInventory::onBackButtonClicked() { showInventoryPanel(); @@ -519,102 +430,17 @@ void LLSidepanelInventory::onBackButtonClicked() void LLSidepanelInventory::onSelectionChange(const std::deque &items, BOOL user_action) { - updateVerbs(); -} -void LLSidepanelInventory::showItemInfoPanel() -{ - mItemPanel->setVisible(TRUE); - if (mTaskPanel) - mTaskPanel->setVisible(FALSE); - mInventoryPanel->setVisible(FALSE); - - mItemPanel->dirty(); - mItemPanel->setIsEditing(FALSE); -} - -void LLSidepanelInventory::showTaskInfoPanel() -{ - mItemPanel->setVisible(FALSE); - mInventoryPanel->setVisible(FALSE); - - if (mTaskPanel) - { - mTaskPanel->setVisible(TRUE); - mTaskPanel->dirty(); - mTaskPanel->setIsEditing(FALSE); - } } void LLSidepanelInventory::showInventoryPanel() { - mItemPanel->setVisible(FALSE); - if (mTaskPanel) - mTaskPanel->setVisible(FALSE); mInventoryPanel->setVisible(TRUE); - updateVerbs(); } -void LLSidepanelInventory::updateVerbs() +void LLSidepanelInventory::initInventoryViews() { - mInfoBtn->setEnabled(FALSE); - mShareBtn->setEnabled(FALSE); - - mWearBtn->setVisible(FALSE); - mWearBtn->setEnabled(FALSE); - mPlayBtn->setVisible(FALSE); - mPlayBtn->setEnabled(FALSE); - mPlayBtn->setToolTip(std::string("")); - mTeleportBtn->setVisible(FALSE); - mTeleportBtn->setEnabled(FALSE); - mShopBtn->setVisible(TRUE); - - mShareBtn->setEnabled(canShare()); - - const LLInventoryItem *item = getSelectedItem(); - if (!item) - return; - - bool is_single_selection = getSelectedCount() == 1; - - mInfoBtn->setEnabled(is_single_selection); - - switch(item->getInventoryType()) - { - case LLInventoryType::IT_WEARABLE: - case LLInventoryType::IT_OBJECT: - case LLInventoryType::IT_ATTACHMENT: - mWearBtn->setVisible(TRUE); - mWearBtn->setEnabled(canWearSelected()); - mShopBtn->setVisible(FALSE); - break; - case LLInventoryType::IT_SOUND: - mPlayBtn->setVisible(TRUE); - mPlayBtn->setEnabled(TRUE); - mPlayBtn->setToolTip(LLTrans::getString("InventoryPlaySoundTooltip")); - mShopBtn->setVisible(FALSE); - break; - case LLInventoryType::IT_GESTURE: - mPlayBtn->setVisible(TRUE); - mPlayBtn->setEnabled(TRUE); - mPlayBtn->setToolTip(LLTrans::getString("InventoryPlayGestureTooltip")); - mShopBtn->setVisible(FALSE); - break; - case LLInventoryType::IT_ANIMATION: - mPlayBtn->setVisible(TRUE); - mPlayBtn->setEnabled(TRUE); - mPlayBtn->setEnabled(TRUE); - mPlayBtn->setToolTip(LLTrans::getString("InventoryPlayAnimationTooltip")); - mShopBtn->setVisible(FALSE); - break; - case LLInventoryType::IT_LANDMARK: - mTeleportBtn->setVisible(TRUE); - mTeleportBtn->setEnabled(TRUE); - mShopBtn->setVisible(FALSE); - break; - default: - break; - } + mPanelMainInventory->initInventoryViews(); } bool LLSidepanelInventory::canShare() @@ -737,12 +563,10 @@ void LLSidepanelInventory::clearSelections(bool clearMain, bool clearInbox) } } - if (clearInbox && mInboxEnabled && mInventoryPanelInbox.get()) + if (clearInbox && mInboxEnabled && !mInventoryPanelInbox.isDead()) { mInventoryPanelInbox.get()->getRootFolder()->clearSelection(); } - - updateVerbs(); } std::set LLSidepanelInventory::getInboxSelectionList() diff --git a/indra/newview/llsidepanelinventory.h b/indra/newview/llsidepanelinventory.h index a3cd20a2c6..08989bb6af 100644 --- a/indra/newview/llsidepanelinventory.h +++ b/indra/newview/llsidepanelinventory.h @@ -66,9 +66,8 @@ public: void clearSelections(bool clearMain, bool clearInbox); std::set getInboxSelectionList(); - void showItemInfoPanel(); - void showTaskInfoPanel(); void showInventoryPanel(); + void initInventoryViews(); // checks can share selected item(s) bool canShare(); @@ -76,13 +75,13 @@ public: void onToggleInboxBtn(); void enableInbox(bool enabled); + void toggleInbox(); + void hideInbox(); void openInbox(); bool isInboxEnabled() const { return mInboxEnabled; } - void updateVerbs(); - static void cleanup(); protected: @@ -103,27 +102,14 @@ protected: private: LLPanel* mInventoryPanel; // Main inventory view LLHandle mInventoryPanelInbox; - LLSidepanelItemInfo* mItemPanel; // Individual item view - LLSidepanelTaskInfo* mTaskPanel; // Individual in-world object view LLPanelMainInventory* mPanelMainInventory; + LLLayoutPanel* mInboxLayoutPanel; + protected: - void onInfoButtonClicked(); - void onShareButtonClicked(); - void onShopButtonClicked(); - void onWearButtonClicked(); - void onPlayButtonClicked(); - void onTeleportButtonClicked(); void onBackButtonClicked(); private: - LLButton* mInfoBtn; - LLButton* mShareBtn; - LLButton* mWearBtn; - LLButton* mPlayBtn; - LLButton* mTeleportBtn; - LLButton* mShopBtn; - bool mInboxEnabled; LLInventoryCategoriesObserver* mCategoriesObserver; diff --git a/indra/newview/llsidepaneliteminfo.cpp b/indra/newview/llsidepaneliteminfo.cpp index b23e24a222..d6d5a4ef2d 100644 --- a/indra/newview/llsidepaneliteminfo.cpp +++ b/indra/newview/llsidepaneliteminfo.cpp @@ -31,16 +31,23 @@ #include "llagent.h" #include "llavataractions.h" +#include "llavatarnamecache.h" #include "llbutton.h" +#include "llcallbacklist.h" #include "llcombobox.h" +#include "llfloater.h" #include "llfloaterreg.h" #include "llgroupactions.h" +#include "llgroupmgr.h" +#include "lliconctrl.h" #include "llinventorydefines.h" +#include "llinventoryicon.h" #include "llinventorymodel.h" #include "llinventoryobserver.h" #include "lllineeditor.h" #include "llradiogroup.h" #include "llslurl.h" +#include "lltexteditor.h" #include "llviewercontrol.h" #include "llviewerinventory.h" #include "llviewerobjectlist.h" @@ -72,49 +79,6 @@ private: S32 mId; }; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class LLItemPropertiesObserver -// -// Helper class to watch for changes to the item. -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class LLItemPropertiesObserver : public LLInventoryObserver -{ -public: - LLItemPropertiesObserver(LLSidepanelItemInfo* floater) - : mFloater(floater) - { - gInventory.addObserver(this); - } - virtual ~LLItemPropertiesObserver() - { - gInventory.removeObserver(this); - } - virtual void changed(U32 mask); -private: - LLSidepanelItemInfo* mFloater; // Not a handle because LLSidepanelItemInfo is managing LLItemPropertiesObserver -}; - -void LLItemPropertiesObserver::changed(U32 mask) -{ - const std::set& mChangedItemIDs = gInventory.getChangedIDs(); - std::set::const_iterator it; - - const LLUUID& item_id = mFloater->getItemID(); - - for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) - { - // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) - if (*it == item_id) - { - // if there's a change we're interested in. - if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) - { - mFloater->dirty(); - } - } - } -} - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLObjectInventoryObserver // @@ -158,36 +122,48 @@ static LLPanelInjector t_item_info("sidepanel_item_info"); // Default constructor LLSidepanelItemInfo::LLSidepanelItemInfo(const LLPanel::Params& p) - : LLSidepanelInventorySubpanel(p) + : LLPanel(p) , mItemID(LLUUID::null) , mObjectInventoryObserver(NULL) , mUpdatePendingId(-1) + , mIsDirty(false) /*Not ready*/ + , mParentFloater(NULL) { - mPropertiesObserver = new LLItemPropertiesObserver(this); + gInventory.addObserver(this); + gIdleCallbacks.addFunction(&LLSidepanelItemInfo::onIdle, (void*)this); } // Destroys the object LLSidepanelItemInfo::~LLSidepanelItemInfo() { - delete mPropertiesObserver; - mPropertiesObserver = NULL; + gInventory.removeObserver(this); + gIdleCallbacks.deleteFunction(&LLSidepanelItemInfo::onIdle, (void*)this); stopObjectInventoryObserver(); + + if (mOwnerCacheConnection.connected()) + { + mOwnerCacheConnection.disconnect(); + } + if (mCreatorCacheConnection.connected()) + { + mCreatorCacheConnection.disconnect(); + } } // virtual BOOL LLSidepanelItemInfo::postBuild() { - LLSidepanelInventorySubpanel::postBuild(); - + mChangeThumbnailBtn = getChild("change_thumbnail_btn"); + mItemTypeIcon = getChild("item_type_icon"); + mLabelOwnerName = getChild("LabelOwnerName"); + mLabelCreatorName = getChild("LabelCreatorName"); + getChild("LabelItemName")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); getChild("LabelItemName")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onCommitName,this)); - getChild("LabelItemDesc")->setPrevalidate(&LLTextValidate::validateASCIIPrintableNoPipe); getChild("LabelItemDesc")->setCommitCallback(boost::bind(&LLSidepanelItemInfo:: onCommitDescription, this)); - // Creator information - getChild("BtnCreator")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickCreator,this)); - // owner information - getChild("BtnOwner")->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onClickOwner,this)); + // Thumnail edition + mChangeThumbnailBtn->setCommitCallback(boost::bind(&LLSidepanelItemInfo::onEditThumbnail, this)); // acquired date // owner permissions // Permissions debug text @@ -226,6 +202,12 @@ void LLSidepanelItemInfo::setItemID(const LLUUID& item_id) mItemID = item_id; mUpdatePendingId = -1; } + dirty(); +} + +void LLSidepanelItemInfo::setParentFloater(LLFloater* parent) +{ + mParentFloater = parent; } const LLUUID& LLSidepanelItemInfo::getObjectID() const @@ -249,12 +231,11 @@ void LLSidepanelItemInfo::onUpdateCallback(const LLUUID& item_id, S32 received_u void LLSidepanelItemInfo::reset() { - LLSidepanelInventorySubpanel::reset(); - mObjectID = LLUUID::null; mItemID = LLUUID::null; stopObjectInventoryObserver(); + dirty(); } void LLSidepanelItemInfo::refresh() @@ -262,60 +243,37 @@ void LLSidepanelItemInfo::refresh() LLViewerInventoryItem* item = findItem(); if(item) { - refreshFromItem(item); - updateVerbs(); + const LLUUID trash_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH); + bool in_trash = (item->getUUID() == trash_id) || gInventory.isObjectDescendentOf(item->getUUID(), trash_id); + if (in_trash && mParentFloater) + { + // Close properties when moving to trash + // Aren't supposed to view properties from trash + mParentFloater->closeFloater(); + } + else + { + refreshFromItem(item); + } return; } - else - { - if (getIsEditing()) - { - setIsEditing(FALSE); - } - } - if (!getIsEditing()) - { - const std::string no_item_names[]={ - "LabelItemName", - "LabelItemDesc", - "LabelCreatorName", - "LabelOwnerName" - }; - - for(size_t t=0; tsetEnabled(false); - } - - setPropertiesFieldsEnabled(false); - - const std::string hide_names[]={ - "BaseMaskDebug", - "OwnerMaskDebug", - "GroupMaskDebug", - "EveryoneMaskDebug", - "NextMaskDebug" - }; - for(size_t t=0; tsetVisible(false); - } - } - - if (!item) - { - const std::string no_edit_mode_names[]={ - "BtnCreator", - "BtnOwner", - }; - for(size_t t=0; tsetEnabled(false); - } - } - - updateVerbs(); + if (mObjectID.notNull()) + { + LLViewerObject* object = gObjectList.findObject(mObjectID); + if (object) + { + // Object exists, but object's content is not nessesary + // loaded, so assume item exists as well + return; + } + } + + if (mParentFloater) + { + // if we failed to get item, it likely no longer exists + mParentFloater->closeFloater(); + } } void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) @@ -333,7 +291,7 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } // do not enable the UI for incomplete items. - BOOL is_complete = item->isFinished(); + bool is_complete = item->isFinished(); const BOOL cannot_restrict_permissions = LLInventoryType::cannotRestrictPermissions(item->getInventoryType()); const BOOL is_calling_card = (item->getInventoryType() == LLInventoryType::IT_CALLINGCARD); const BOOL is_settings = (item->getInventoryType() == LLInventoryType::IT_SETTINGS); @@ -385,8 +343,22 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) getChild("LabelItemName")->setValue(item->getName()); getChildView("LabelItemDescTitle")->setEnabled(TRUE); getChildView("LabelItemDesc")->setEnabled(is_modifiable); - getChildView("IconLocked")->setVisible(!is_modifiable); getChild("LabelItemDesc")->setValue(item->getDescription()); + getChild("item_thumbnail")->setValue(item->getThumbnailUUID()); + + LLUIImagePtr icon_img = LLInventoryIcon::getIcon(item->getType(), item->getInventoryType(), item->getFlags(), FALSE); + mItemTypeIcon->setImage(icon_img); + + // Style for creator and owner links + LLStyle::Params style_params; + LLColor4 link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.color = link_color; + style_params.readonly_color = link_color; + style_params.is_link = true; // link will be added later + const LLFontGL* fontp = mLabelCreatorName->getFont(); + style_params.font.name = LLFontGL::nameFromFont(fontp); + style_params.font.size = LLFontGL::sizeFromFont(fontp); + style_params.font.style = "UNDERLINE"; ////////////////// // CREATOR NAME // @@ -397,19 +369,34 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) if (item->getCreatorUUID().notNull()) { LLUUID creator_id = item->getCreatorUUID(); - std::string name = - LLSLURL("agent", creator_id, "completename").getSLURLString(); - getChildView("BtnCreator")->setEnabled(TRUE); + std::string slurl = + LLSLURL("agent", creator_id, "inspect").getSLURLString(); + + style_params.link_href = slurl; + + LLAvatarName av_name; + if (LLAvatarNameCache::get(creator_id, &av_name)) + { + updateCreatorName(creator_id, av_name, style_params); + } + else + { + if (mCreatorCacheConnection.connected()) + { + mCreatorCacheConnection.disconnect(); + } + mLabelCreatorName->setText(LLTrans::getString("None")); + mCreatorCacheConnection = LLAvatarNameCache::get(creator_id, boost::bind(&LLSidepanelItemInfo::updateCreatorName, this, _1, _2, style_params)); + } + getChildView("LabelCreatorTitle")->setEnabled(TRUE); - getChildView("LabelCreatorName")->setEnabled(FALSE); - getChild("LabelCreatorName")->setValue(name); + mLabelCreatorName->setEnabled(TRUE); } else { - getChildView("BtnCreator")->setEnabled(FALSE); getChildView("LabelCreatorTitle")->setEnabled(FALSE); - getChildView("LabelCreatorName")->setEnabled(FALSE); - getChild("LabelCreatorName")->setValue(getString("unknown_multiple")); + mLabelCreatorName->setEnabled(FALSE); + mLabelCreatorName->setValue(getString("unknown_multiple")); } //////////////// @@ -417,28 +404,60 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) //////////////// if(perm.isOwned()) { - std::string name; + std::string slurl; if (perm.isGroupOwned()) { - gCacheName->getGroupName(perm.getGroup(), name); + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(perm.getGroup()); + + slurl = LLSLURL("group", perm.getGroup(), "inspect").getSLURLString(); + style_params.link_href = slurl; + if (group_data && group_data->isGroupPropertiesDataComplete()) + { + mLabelOwnerName->setText(group_data->mName, style_params); + } + else + { + // Triggers refresh + LLGroupMgr::getInstance()->sendGroupPropertiesRequest(perm.getGroup()); + + std::string name; + gCacheName->getGroupName(perm.getGroup(), name); + mLabelOwnerName->setText(name, style_params); + } } else { LLUUID owner_id = perm.getOwner(); - name = LLSLURL("agent", owner_id, "completename").getSLURLString(); + slurl = LLSLURL("agent", owner_id, "inspect").getSLURLString(); + + style_params.link_href = slurl; + LLAvatarName av_name; + if (LLAvatarNameCache::get(owner_id, &av_name)) + { + updateOwnerName(owner_id, av_name, style_params); + } + else + { + if (mOwnerCacheConnection.connected()) + { + mOwnerCacheConnection.disconnect(); + } + mLabelOwnerName->setText(LLTrans::getString("None")); + mOwnerCacheConnection = LLAvatarNameCache::get(owner_id, boost::bind(&LLSidepanelItemInfo::updateOwnerName, this, _1, _2, style_params)); + } } - getChildView("BtnOwner")->setEnabled(TRUE); getChildView("LabelOwnerTitle")->setEnabled(TRUE); - getChildView("LabelOwnerName")->setEnabled(FALSE); - getChild("LabelOwnerName")->setValue(name); + mLabelOwnerName->setEnabled(TRUE); } else { - getChildView("BtnOwner")->setEnabled(FALSE); getChildView("LabelOwnerTitle")->setEnabled(FALSE); - getChildView("LabelOwnerName")->setEnabled(FALSE); - getChild("LabelOwnerName")->setValue(getString("public")); + mLabelOwnerName->setEnabled(FALSE); + mLabelOwnerName->setValue(getString("public")); } + + // Not yet supported for task inventories + mChangeThumbnailBtn->setEnabled(mObjectID.isNull() && ALEXANDRIA_LINDEN_ID != perm.getOwner()); //////////// // ORIGIN // @@ -548,6 +567,8 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) if( gSavedSettings.getBOOL("DebugPermissions") ) { + childSetVisible("layout_debug_permissions", true); + BOOL slam_perm = FALSE; BOOL overwrite_group = FALSE; BOOL overwrite_everyone = FALSE; @@ -565,38 +586,29 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) perm_string = "B: "; perm_string += mask_to_string(base_mask); getChild("BaseMaskDebug")->setValue(perm_string); - getChildView("BaseMaskDebug")->setVisible(TRUE); perm_string = "O: "; perm_string += mask_to_string(owner_mask); getChild("OwnerMaskDebug")->setValue(perm_string); - getChildView("OwnerMaskDebug")->setVisible(TRUE); perm_string = "G"; perm_string += overwrite_group ? "*: " : ": "; perm_string += mask_to_string(group_mask); getChild("GroupMaskDebug")->setValue(perm_string); - getChildView("GroupMaskDebug")->setVisible(TRUE); perm_string = "E"; perm_string += overwrite_everyone ? "*: " : ": "; perm_string += mask_to_string(everyone_mask); getChild("EveryoneMaskDebug")->setValue(perm_string); - getChildView("EveryoneMaskDebug")->setVisible(TRUE); perm_string = "N"; perm_string += slam_perm ? "*: " : ": "; perm_string += mask_to_string(next_owner_mask); getChild("NextMaskDebug")->setValue(perm_string); - getChildView("NextMaskDebug")->setVisible(TRUE); } else { - getChildView("BaseMaskDebug")->setVisible(FALSE); - getChildView("OwnerMaskDebug")->setVisible(FALSE); - getChildView("GroupMaskDebug")->setVisible(FALSE); - getChildView("EveryoneMaskDebug")->setVisible(FALSE); - getChildView("NextMaskDebug")->setVisible(FALSE); + childSetVisible("layout_debug_permissions", false); } ///////////// @@ -731,6 +743,68 @@ void LLSidepanelItemInfo::refreshFromItem(LLViewerInventoryItem* item) } } +void LLSidepanelItemInfo::updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params) +{ + if (mCreatorCacheConnection.connected()) + { + mCreatorCacheConnection.disconnect(); + } + std::string name = creator_name.getCompleteName(); + mLabelCreatorName->setText(name, style_params); +} + +void LLSidepanelItemInfo::updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params) +{ + if (mOwnerCacheConnection.connected()) + { + mOwnerCacheConnection.disconnect(); + } + std::string name = owner_name.getCompleteName(); + mLabelOwnerName->setText(name, style_params); +} + +void LLSidepanelItemInfo::changed(U32 mask) +{ + const LLUUID& item_id = getItemID(); + if (getObjectID().notNull() || item_id.isNull()) + { + // Task inventory or not set up yet + return; + } + + const std::set& mChangedItemIDs = gInventory.getChangedIDs(); + std::set::const_iterator it; + + for (it = mChangedItemIDs.begin(); it != mChangedItemIDs.end(); it++) + { + // set dirty for 'item profile panel' only if changed item is the item for which 'item profile panel' is shown (STORM-288) + if (*it == item_id) + { + // if there's a change we're interested in. + if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0) + { + dirty(); + } + } + } +} + +void LLSidepanelItemInfo::dirty() +{ + mIsDirty = true; +} + +// static +void LLSidepanelItemInfo::onIdle( void* user_data ) +{ + LLSidepanelItemInfo* self = reinterpret_cast(user_data); + + if( self->mIsDirty ) + { + self->refresh(); + self->mIsDirty = false; + } +} void LLSidepanelItemInfo::setAssociatedExperience( LLHandle hInfo, const LLSD& experience ) { @@ -853,7 +927,7 @@ void LLSidepanelItemInfo::onCommitDescription() LLViewerInventoryItem* item = findItem(); if(!item) return; - LLLineEditor* labelItemDesc = getChild("LabelItemDesc"); + LLTextEditor* labelItemDesc = getChild("LabelItemDesc"); if(!labelItemDesc) { return; @@ -966,7 +1040,14 @@ void LLSidepanelItemInfo::updatePermissions() } } -// static +void LLSidepanelItemInfo::onEditThumbnail() +{ + LLSD data; + data["task_id"] = mObjectID; + data["item_id"] = mItemID; + LLFloaterReg::showInstance("change_item_thumbnail", data); +} + void LLSidepanelItemInfo::onCommitSaleInfo(LLUICtrl* ctrl) { if (ctrl) diff --git a/indra/newview/llsidepaneliteminfo.h b/indra/newview/llsidepaneliteminfo.h index 5f29254182..b916f44520 100644 --- a/indra/newview/llsidepaneliteminfo.h +++ b/indra/newview/llsidepaneliteminfo.h @@ -27,42 +27,55 @@ #ifndef LL_LLSIDEPANELITEMINFO_H #define LL_LLSIDEPANELITEMINFO_H -#include "llsidepanelinventorysubpanel.h" +#include "llinventoryobserver.h" +#include "llpanel.h" +#include "llstyle.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLSidepanelItemInfo // Object properties for inventory side panel. //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLAvatarName; class LLButton; +class LLFloater; +class LLIconCtrl; class LLViewerInventoryItem; class LLItemPropertiesObserver; class LLObjectInventoryObserver; class LLViewerObject; class LLPermissions; +class LLTextBox; -class LLSidepanelItemInfo : public LLSidepanelInventorySubpanel +class LLSidepanelItemInfo : public LLPanel, public LLInventoryObserver { public: LLSidepanelItemInfo(const LLPanel::Params& p = getDefaultParams()); virtual ~LLSidepanelItemInfo(); - /*virtual*/ BOOL postBuild(); + /*virtual*/ BOOL postBuild() override; /*virtual*/ void reset(); void setObjectID(const LLUUID& object_id); void setItemID(const LLUUID& item_id); - void setEditMode(BOOL edit); + void setParentFloater(LLFloater* parent); // For simplicity const LLUUID& getObjectID() const; const LLUUID& getItemID() const; // if received update and item id (from callback) matches internal ones, update UI void onUpdateCallback(const LLUUID& item_id, S32 received_update_id); + + void changed(U32 mask) override; + void dirty(); + + static void onIdle( void* user_data ); + void updateOwnerName(const LLUUID& owner_id, const LLAvatarName& owner_name, const LLStyle::Params& style_params); + void updateCreatorName(const LLUUID& creator_id, const LLAvatarName& creator_name, const LLStyle::Params& style_params); protected: - /*virtual*/ void refresh(); - /*virtual*/ void save(); + void refresh() override; + void save(); LLViewerInventoryItem* findItem() const; LLViewerObject* findObject() const; @@ -75,14 +88,23 @@ private: void startObjectInventoryObserver(); void stopObjectInventoryObserver(); void setPropertiesFieldsEnabled(bool enabled); + + boost::signals2::connection mOwnerCacheConnection; + boost::signals2::connection mCreatorCacheConnection; LLUUID mItemID; // inventory UUID for the inventory item. LLUUID mObjectID; // in-world task UUID, or null if in agent inventory. - LLItemPropertiesObserver* mPropertiesObserver; // for syncing changes to item LLObjectInventoryObserver* mObjectInventoryObserver; // for syncing changes to items inside an object // We can send multiple properties updates simultaneously, make sure only last response counts and there won't be a race condition. S32 mUpdatePendingId; + bool mIsDirty; // item properties need to be updated + LLFloater* mParentFloater; + + LLUICtrl* mChangeThumbnailBtn; + LLIconCtrl* mItemTypeIcon; + LLTextBox* mLabelOwnerName; + LLTextBox* mLabelCreatorName; // // UI Elements @@ -94,6 +116,7 @@ protected: void onCommitDescription(); void onCommitPermissions(LLUICtrl* ctrl); void updatePermissions(); + void onEditThumbnail(); void onCommitSaleInfo(LLUICtrl* ctrl); void updateSaleInfo(); void onCommitChanges(LLPointer item); diff --git a/indra/newview/llsidepaneltaskinfo.cpp b/indra/newview/llsidepaneltaskinfo.cpp index 6216057c17..fe61b7a02a 100644 --- a/indra/newview/llsidepaneltaskinfo.cpp +++ b/indra/newview/llsidepaneltaskinfo.cpp @@ -42,6 +42,7 @@ #include "llresmgr.h" #include "lltextbox.h" #include "llbutton.h" +#include "llcallbacklist.h" #include "llcheckboxctrl.h" #include "llviewerobject.h" #include "llselectmgr.h" @@ -75,9 +76,11 @@ static LLPanelInjector t_task_info("sidepanel_task_info"); // Default constructor LLSidepanelTaskInfo::LLSidepanelTaskInfo() + : mVisibleDebugPermissions(true) // space was allocated by default { setMouseOpaque(FALSE); mSelectionUpdateSlot = LLSelectMgr::instance().mUpdateSignal.connect(boost::bind(&LLSidepanelTaskInfo::refreshAll, this)); + gIdleCallbacks.addFunction(&LLSidepanelTaskInfo::onIdle, (void*)this); } @@ -85,6 +88,7 @@ LLSidepanelTaskInfo::~LLSidepanelTaskInfo() { if (sActivePanel == this) sActivePanel = NULL; + gIdleCallbacks.deleteFunction(&LLSidepanelTaskInfo::onIdle, (void*)this); if (mSelectionUpdateSlot.connected()) { @@ -95,8 +99,6 @@ LLSidepanelTaskInfo::~LLSidepanelTaskInfo() // virtual BOOL LLSidepanelTaskInfo::postBuild() { - LLSidepanelInventorySubpanel::postBuild(); - mOpenBtn = getChild("open_btn"); mOpenBtn->setClickedCallback(boost::bind(&LLSidepanelTaskInfo::onOpenButtonClicked, this)); mPayBtn = getChild("pay_btn"); @@ -151,12 +153,12 @@ BOOL LLSidepanelTaskInfo::postBuild() mDALabelClickAction = getChildView("label click action"); mDAComboClickAction = getChild("clickaction"); mDAPathfindingAttributes = getChild("pathfinding_attributes_value"); - mDAB = getChildView("B:"); - mDAO = getChildView("O:"); - mDAG = getChildView("G:"); - mDAE = getChildView("E:"); - mDAN = getChildView("N:"); - mDAF = getChildView("F:"); + mDAB = getChild("B:"); + mDAO = getChild("O:"); + mDAG = getChild("G:"); + mDAE = getChild("E:"); + mDAN = getChild("N:"); + mDAF = getChild("F:"); return TRUE; } @@ -206,12 +208,22 @@ void LLSidepanelTaskInfo::disableAll() disablePermissions(); - mDAB->setVisible(FALSE); - mDAO->setVisible(FALSE); - mDAG->setVisible(FALSE); - mDAE->setVisible(FALSE); - mDAN->setVisible(FALSE); - mDAF->setVisible(FALSE); + if (mVisibleDebugPermissions) + { + mDAB->setVisible(FALSE); + mDAO->setVisible(FALSE); + mDAG->setVisible(FALSE); + mDAE->setVisible(FALSE); + mDAN->setVisible(FALSE); + mDAF->setVisible(FALSE); + + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + LLRect parent_rect = parent_floater->getRect(); + LLRect debug_rect = mDAB->getRect(); + // use double the debug rect for padding (since it isn't trivial to extract top_pad) + parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() - (debug_rect.getHeight() * 2)); + mVisibleDebugPermissions = false; + } mOpenBtn->setEnabled(FALSE); mPayBtn->setEnabled(FALSE); @@ -258,6 +270,8 @@ void LLSidepanelTaskInfo::disablePermissions() void LLSidepanelTaskInfo::refresh() { + mIsDirty = false; + LLButton* btn_deed_to_group = mDeedBtn; if (btn_deed_to_group) { @@ -611,23 +625,23 @@ void LLSidepanelTaskInfo::refresh() if (gSavedSettings.getBOOL("DebugPermissions") ) { - if (valid_base_perms) - { - getChild("B:")->setValue("B: " + mask_to_string(base_mask_on)); - getChildView("B:")->setVisible( TRUE); - - getChild("O:")->setValue("O: " + mask_to_string(owner_mask_on)); - getChildView("O:")->setVisible( TRUE); - - getChild("G:")->setValue("G: " + mask_to_string(group_mask_on)); - getChildView("G:")->setVisible( TRUE); - - getChild("E:")->setValue("E: " + mask_to_string(everyone_mask_on)); - getChildView("E:")->setVisible( TRUE); - - getChild("N:")->setValue("N: " + mask_to_string(next_owner_mask_on)); - getChildView("N:")->setVisible( TRUE); - } + if (valid_base_perms) + { + mDAB->setValue("B: " + mask_to_string(base_mask_on)); + mDAB->setVisible( TRUE); + + mDAO->setValue("O: " + mask_to_string(owner_mask_on)); + mDAO->setVisible( TRUE); + + mDAG->setValue("G: " + mask_to_string(group_mask_on)); + mDAG->setVisible( TRUE); + + mDAE->setValue("E: " + mask_to_string(everyone_mask_on)); + mDAE->setVisible( TRUE); + + mDAN->setValue("N: " + mask_to_string(next_owner_mask_on)); + mDAN->setVisible( TRUE); + } U32 flag_mask = 0x0; if (objectp->permMove()) flag_mask |= PERM_MOVE; @@ -635,18 +649,35 @@ void LLSidepanelTaskInfo::refresh() if (objectp->permCopy()) flag_mask |= PERM_COPY; if (objectp->permTransfer()) flag_mask |= PERM_TRANSFER; - getChild("F:")->setValue("F:" + mask_to_string(flag_mask)); - getChildView("F:")->setVisible( TRUE); - } - else - { - getChildView("B:")->setVisible( FALSE); - getChildView("O:")->setVisible( FALSE); - getChildView("G:")->setVisible( FALSE); - getChildView("E:")->setVisible( FALSE); - getChildView("N:")->setVisible( FALSE); - getChildView("F:")->setVisible( FALSE); - } + mDAF->setValue("F:" + mask_to_string(flag_mask)); + mDAF->setVisible(TRUE); + + if (!mVisibleDebugPermissions) + { + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + LLRect parent_rect = parent_floater->getRect(); + LLRect debug_rect = mDAB->getRect(); + // use double the debug rect for padding (since it isn't trivial to extract top_pad) + parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() + (debug_rect.getHeight() * 2)); + mVisibleDebugPermissions = true; + } + } + else if (mVisibleDebugPermissions) + { + mDAB->setVisible(FALSE); + mDAO->setVisible(FALSE); + mDAG->setVisible(FALSE); + mDAE->setVisible(FALSE); + mDAN->setVisible(FALSE); + mDAF->setVisible(FALSE); + + LLFloater* parent_floater = gFloaterView->getParentFloater(this); + LLRect parent_rect = parent_floater->getRect(); + LLRect debug_rect = mDAB->getRect(); + // use double the debug rect for padding (since it isn't trivial to extract top_pad) + parent_floater->reshape(parent_rect.getWidth(), parent_rect.getHeight() - (debug_rect.getHeight() * 2)); + mVisibleDebugPermissions = false; + } BOOL has_change_perm_ability = FALSE; BOOL has_change_sale_ability = FALSE; @@ -854,7 +885,7 @@ void LLSidepanelTaskInfo::refresh() const BOOL all_include_in_search = LLSelectMgr::getInstance()->selectionGetIncludeInSearch(&include_in_search); getChildView("search_check")->setEnabled(has_change_sale_ability && all_volume); getChild("search_check")->setValue(include_in_search); - getChild("search_check")->setTentative( !all_include_in_search); + getChild("search_check")->setTentative(!all_include_in_search); // Click action (touch, sit, buy) U8 click_action = 0; @@ -869,33 +900,6 @@ void LLSidepanelTaskInfo::refresh() getChildView("label click action")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume); getChildView("clickaction")->setEnabled(is_perm_modify && is_nonpermanent_enforced && all_volume); - if (!getIsEditing()) - { - const std::string no_item_names[] = - { - "Object Name", - "Object Description", - "button set group", - "checkbox share with group", - "button deed", - "checkbox allow everyone move", - "checkbox allow everyone copy", - "checkbox for sale", - "sale type", - "Edit Cost", - "checkbox next owner can modify", - "checkbox next owner can copy", - "checkbox next owner can transfer", - "clickaction", - "search_check", - "perm_modify", - "Group Name", - }; - for (size_t t=0; tsetEnabled( FALSE); - } - } updateVerbs(); } @@ -1155,7 +1159,8 @@ static U8 string_value_to_click_action(std::string p_value) void LLSidepanelTaskInfo::onCommitClickAction(LLUICtrl* ctrl, void*) { LLComboBox* box = (LLComboBox*)ctrl; - if (!box) return; + if (!box) + return; std::string value = box->getValue().asString(); U8 click_action = string_value_to_click_action(value); doClickAction(click_action); @@ -1207,16 +1212,6 @@ void LLSidepanelTaskInfo::onCommitIncludeInSearch(LLUICtrl* ctrl, void* data) // virtual void LLSidepanelTaskInfo::updateVerbs() { - LLSidepanelInventorySubpanel::updateVerbs(); - - /* - mOpenBtn->setVisible(!getIsEditing()); - mPayBtn->setVisible(!getIsEditing()); - mBuyBtn->setVisible(!getIsEditing()); - //const LLViewerObject *obj = getFirstSelectedObject(); - //mEditBtn->setEnabled(obj && obj->permModify()); - */ - LLSafeHandle object_selection = LLSelectMgr::getInstance()->getSelection(); const BOOL any_selected = (object_selection->getNumNodes() > 0); @@ -1301,6 +1296,23 @@ LLSidepanelTaskInfo* LLSidepanelTaskInfo::getActivePanel() return sActivePanel; } +void LLSidepanelTaskInfo::dirty() +{ + mIsDirty = true; +} + +// static +void LLSidepanelTaskInfo::onIdle( void* user_data ) +{ + LLSidepanelTaskInfo* self = reinterpret_cast(user_data); + + if( self->mIsDirty ) + { + self->refresh(); + self->mIsDirty = false; + } +} + LLViewerObject* LLSidepanelTaskInfo::getObject() { if (!mObject->isDead()) diff --git a/indra/newview/llsidepaneltaskinfo.h b/indra/newview/llsidepaneltaskinfo.h index ac9c57f2e2..2baafc67e7 100644 --- a/indra/newview/llsidepaneltaskinfo.h +++ b/indra/newview/llsidepaneltaskinfo.h @@ -27,8 +27,8 @@ #ifndef LL_LLSIDEPANELTASKINFO_H #define LL_LLSIDEPANELTASKINFO_H -#include "llsidepanelinventorysubpanel.h" #include "lluuid.h" +#include "llpanel.h" #include "llselectmgr.h" //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -43,14 +43,14 @@ class LLNameBox; class LLViewerObject; class LLTextBase; -class LLSidepanelTaskInfo : public LLSidepanelInventorySubpanel +class LLSidepanelTaskInfo : public LLPanel { public: LLSidepanelTaskInfo(); virtual ~LLSidepanelTaskInfo(); - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onVisibilityChange ( BOOL new_visibility ); + BOOL postBuild() override; + void onVisibilityChange ( BOOL new_visibility ) override; void setObjectSelection(LLObjectSelectionHandle selection); @@ -58,10 +58,12 @@ public: LLViewerObject* getFirstSelectedObject(); static LLSidepanelTaskInfo *getActivePanel(); + void dirty(); + static void onIdle( void* user_data ); protected: - /*virtual*/ void refresh(); // refresh all labels as needed - /*virtual*/ void save(); - /*virtual*/ void updateVerbs(); + void refresh() override; // refresh all labels as needed + void save(); + void updateVerbs(); void refreshAll(); // ignore current keyboard focus and update all fields @@ -103,6 +105,8 @@ private: LLUUID mCreatorID; LLUUID mOwnerID; LLUUID mLastOwnerID; + + bool mIsDirty; protected: void onOpenButtonClicked(); @@ -121,6 +125,10 @@ protected: private: LLPointer mObject; LLObjectSelectionHandle mObjectSelection; + + // mVisibleDebugPermissions doesn't nessesarily matche state + // of viewes and is primarily for floater resize + bool mVisibleDebugPermissions; static LLSidepanelTaskInfo* sActivePanel; private: @@ -148,12 +156,12 @@ private: LLView* mDALabelClickAction; LLComboBox* mDAComboClickAction; LLTextBase* mDAPathfindingAttributes; - LLView* mDAB; - LLView* mDAO; - LLView* mDAG; - LLView* mDAE; - LLView* mDAN; - LLView* mDAF; + LLUICtrl* mDAB; + LLUICtrl* mDAO; + LLUICtrl* mDAG; + LLUICtrl* mDAE; + LLUICtrl* mDAN; + LLUICtrl* mDAF; boost::signals2::connection mSelectionUpdateSlot; }; diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index ed7e18fadc..b7a1832b17 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -51,6 +51,7 @@ #include "llviewercontrol.h" #include "llviewermenufile.h" // upload_new_resource() #include "llviewerstats.h" +#include "llviewertexturelist.h" #include "llwindow.h" #include "llworld.h" #include @@ -873,6 +874,11 @@ LLPointer LLSnapshotLivePreview::getEncodedImage() return mPreviewImageEncoded; } +bool LLSnapshotLivePreview::createUploadFile(const std::string &out_filename, const S32 max_image_dimentions, const S32 min_image_dimentions) +{ + return LLViewerTextureList::createUploadFile(mPreviewImage, out_filename, max_image_dimentions, min_image_dimentions); +} + // We actually estimate the data size so that we do not require actual compression when showing the preview // Note : whenever formatted image is computed, mDataSize will be updated to reflect the true size void LLSnapshotLivePreview::estimateDataSize() diff --git a/indra/newview/llsnapshotlivepreview.h b/indra/newview/llsnapshotlivepreview.h index 1f81307976..6e38a957b4 100644 --- a/indra/newview/llsnapshotlivepreview.h +++ b/indra/newview/llsnapshotlivepreview.h @@ -106,6 +106,7 @@ public: LLPointer getFormattedImage(); LLPointer getEncodedImage(); + bool createUploadFile(const std::string &out_file, const S32 max_image_dimentions, const S32 min_image_dimentions); /// Sets size of preview thumbnail image and the surrounding rect. void setThumbnailPlaceholderRect(const LLRect& rect) {mThumbnailPlaceholderRect = rect; } diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index f52f1a925d..29d42b3c00 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -235,6 +235,59 @@ BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate) return FALSE; } +void LLSpatialGroup::expandExtents(const LLVector4a* addingExtents, const LLXformMatrix& currentTransform) +{ + // Get coordinates of the adding extents + const LLVector4a& min = addingExtents[0]; + const LLVector4a& max = addingExtents[1]; + + // Get coordinates of all corners of the bounding box + LLVector3 corners[] = + { + LLVector3(min[0], min[1], min[2]), + LLVector3(min[0], min[1], max[2]), + LLVector3(min[0], max[1], min[2]), + LLVector3(min[0], max[1], max[2]), + LLVector3(max[0], min[1], min[2]), + LLVector3(max[0], min[1], max[2]), + LLVector3(max[0], max[1], min[2]), + LLVector3(max[0], max[1], max[2]) + }; + + // New extents (to be expanded) + LLVector3 extents[] = + { + LLVector3(mExtents[0].getF32ptr()), + LLVector3(mExtents[1].getF32ptr()) + }; + + LLQuaternion backwardRotation = ~currentTransform.getRotation(); + for (LLVector3& corner : corners) + { + // Make coordinates relative to the current position + corner -= currentTransform.getPosition(); + // Rotate coordinates backward to the current rotation + corner.rotVec(backwardRotation); + // Expand root extents on the current corner + for (int j = 0; j < 3; ++j) + { + if (corner[j] < extents[0][j]) + extents[0][j] = corner[j]; + if (corner[j] > extents[1][j]) + extents[1][j] = corner[j]; + } + } + + // Set new expanded extents + mExtents[0].load3(extents[0].mV); + mExtents[1].load3(extents[1].mV); + + // Calculate new center and size + mBounds[0].setAdd(mExtents[0], mExtents[1]); + mBounds[0].mul(0.5f); + mBounds[1].setSub(mExtents[0], mExtents[1]); + mBounds[1].mul(0.5f); +} BOOL LLSpatialGroup::addObject(LLDrawable *drawablep) { @@ -3507,8 +3560,9 @@ public: BOOL mPickTransparent; BOOL mPickRigged; BOOL mPickUnselectable; + BOOL mPickReflectionProbe; - LLOctreeIntersect(const LLVector4a& start, const LLVector4a& end, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, + LLOctreeIntersect(const LLVector4a& start, const LLVector4a& end, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, BOOL pick_reflection_probe, S32* face_hit, LLVector4a* intersection, LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) : mStart(start), mEnd(end), @@ -3520,7 +3574,8 @@ public: mHit(NULL), mPickTransparent(pick_transparent), mPickRigged(pick_rigged), - mPickUnselectable(pick_unselectable) + mPickUnselectable(pick_unselectable), + mPickReflectionProbe(pick_reflection_probe) { } @@ -3596,8 +3651,14 @@ public: { LLViewerObject* vobj = drawable->getVObj(); - if (vobj) + if (vobj && + (!vobj->isReflectionProbe() || mPickReflectionProbe)) { + if (vobj->getClickAction() == CLICK_ACTION_IGNORE && !LLFloater::isVisible(gFloaterTools)) + { + return false; + } + LLVector4a intersection; bool skip_check = false; if (vobj->isAvatar()) @@ -3621,7 +3682,9 @@ public: } } - if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, mPickTransparent, mPickRigged, mPickUnselectable, mFaceHit, &intersection, mTexCoord, mNormal, mTangent)) + if (!skip_check && vobj->lineSegmentIntersect(mStart, mEnd, -1, + (mPickReflectionProbe && vobj->isReflectionProbe()) ? TRUE : mPickTransparent, // always pick transparent when picking selection probe + mPickRigged, mPickUnselectable, mFaceHit, &intersection, mTexCoord, mNormal, mTangent)) { mEnd = intersection; // shorten ray so we only find CLOSER hits if (mIntersection) @@ -3642,6 +3705,7 @@ LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector4a& start, co BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, + BOOL pick_reflection_probe, S32* face_hit, // return the face hit LLVector4a* intersection, // return the intersection point LLVector2* tex_coord, // return the texture coordinates of the intersection point @@ -3650,7 +3714,7 @@ LLDrawable* LLSpatialPartition::lineSegmentIntersect(const LLVector4a& start, co ) { - LLOctreeIntersect intersect(start, end, pick_transparent, pick_rigged, pick_unselectable, face_hit, intersection, tex_coord, normal, tangent); + LLOctreeIntersect intersect(start, end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, face_hit, intersection, tex_coord, normal, tangent); LLDrawable* drawable = intersect.check(mOctree); return drawable; @@ -3660,6 +3724,7 @@ LLDrawable* LLSpatialGroup::lineSegmentIntersect(const LLVector4a& start, const BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, + BOOL pick_reflection_probe, S32* face_hit, // return the face hit LLVector4a* intersection, // return the intersection point LLVector2* tex_coord, // return the texture coordinates of the intersection point @@ -3668,7 +3733,7 @@ LLDrawable* LLSpatialGroup::lineSegmentIntersect(const LLVector4a& start, const ) { - LLOctreeIntersect intersect(start, end, pick_transparent, pick_rigged, pick_unselectable, face_hit, intersection, tex_coord, normal, tangent); + LLOctreeIntersect intersect(start, end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, face_hit, intersection, tex_coord, normal, tangent); LLDrawable* drawable = intersect.check(getOctreeNode()); return drawable; @@ -4011,7 +4076,8 @@ void LLCullResult::assertDrawMapsEmpty() { if (mRenderMapSize[i] != 0) { - LL_ERRS() << "Stale LLDrawInfo's in LLCullResult!" << LL_ENDL; + LL_ERRS() << "Stale LLDrawInfo's in LLCullResult!" + << " (mRenderMapSize[" << i << "] = " << mRenderMapSize[i] << ")" << LL_ENDL; } } } diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h index 88584f535a..4214c26391 100644 --- a/indra/newview/llspatialpartition.h +++ b/indra/newview/llspatialpartition.h @@ -294,6 +294,7 @@ public: BOOL addObject(LLDrawable *drawablep); BOOL removeObject(LLDrawable *drawablep, BOOL from_octree = FALSE); BOOL updateInGroup(LLDrawable *drawablep, BOOL immediate = FALSE); // Update position if it's in the group + void expandExtents(const LLVector4a* addingExtents, const LLXformMatrix& currentTransform); void shift(const LLVector4a &offset); // TODO: this no longer appears to be called, figure out if it's important and if not remove it @@ -315,6 +316,7 @@ public: BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, + BOOL pick_reflection_probe, S32* face_hit, // return the face hit LLVector4a* intersection = NULL, // return the intersection point LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point @@ -389,6 +391,7 @@ public: BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, + BOOL pick_reflection_probe, S32* face_hit, // return the face hit LLVector4a* intersection = NULL, // return the intersection point LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point @@ -719,8 +722,10 @@ public: class LLControlAVBridge : public LLVolumeBridge { + using super = LLVolumeBridge; public: LLControlAVBridge(LLDrawable* drawablep, LLViewerRegion* regionp); + virtual void updateSpatialExtents(); }; class LLHUDBridge : public LLVolumeBridge diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp index b641afc1ef..af0b5a40b4 100644 --- a/indra/newview/llsprite.cpp +++ b/indra/newview/llsprite.cpp @@ -79,7 +79,6 @@ void LLSprite::updateFace(LLFace &face) // First, figure out how many vertices/indices we need. U32 num_vertices, num_indices; - U32 vertex_count = 0; // Get the total number of vertices and indices if (mFollow) @@ -202,25 +201,21 @@ void LLSprite::updateFace(LLFace &face) *verticesp = mC; tex_coordsp++; verticesp++; - vertex_count++; *tex_coordsp = LLVector2(0.f, 1.f); *verticesp = mB; tex_coordsp++; verticesp++; - vertex_count++; *tex_coordsp = LLVector2(1.f, 1.f); *verticesp = mA; tex_coordsp++; verticesp++; - vertex_count++; *tex_coordsp = LLVector2(1.f, 0.0f); *verticesp = mD; tex_coordsp++; verticesp++; - vertex_count++; // Generate indices, since they're easy. // Just a series of quads. diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 39d640e85f..6f8ffd3610 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -257,7 +257,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 +const S32 MAX_SEED_CAP_ATTEMPTS_BEFORE_ABORT = 4; // Give region 4 chances std::unique_ptr LLStartUp::sStateWatcher(new LLEventStream("StartupState")); std::unique_ptr LLStartUp::sListener(new LLStartupListener()); @@ -664,9 +664,22 @@ bool idle_startup() #else void* window_handle = NULL; #endif - bool init = gAudiop->init(window_handle, LLAppViewer::instance()->getSecondLifeTitle()); - if(init) + if (gAudiop->init(window_handle, LLAppViewer::instance()->getSecondLifeTitle())) { + if (FALSE == gSavedSettings.getBOOL("UseMediaPluginsForStreamingAudio")) + { + LL_INFOS("AppInit") << "Using default impl to render streaming audio" << LL_ENDL; + gAudiop->setStreamingAudioImpl(gAudiop->createDefaultStreamingAudioImpl()); + } + + // if the audio engine hasn't set up its own preferred handler for streaming audio + // then set up the generic streaming audio implementation which uses media plugins + if (NULL == gAudiop->getStreamingAudioImpl()) + { + LL_INFOS("AppInit") << "Using media plugins to render streaming audio" << LL_ENDL; + gAudiop->setStreamingAudioImpl(new LLStreamingAudio_MediaPlugins()); + } + gAudiop->setMuted(TRUE); } else @@ -675,16 +688,6 @@ bool idle_startup() delete gAudiop; gAudiop = NULL; } - - if (gAudiop) - { - // if the audio engine hasn't set up its own preferred handler for streaming audio then set up the generic streaming audio implementation which uses media plugins - if (NULL == gAudiop->getStreamingAudioImpl()) - { - LL_INFOS("AppInit") << "Using media plugins to render streaming audio" << LL_ENDL; - gAudiop->setStreamingAudioImpl(new LLStreamingAudio_MediaPlugins()); - } - } } } @@ -1396,17 +1399,32 @@ bool idle_startup() } else if (regionp->capabilitiesError()) { - // Try to connect despite capabilities' error state - LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + LL_WARNS("AppInit") << "Failed to get capabilities. Backing up to login screen!" << LL_ENDL; + if (gRememberPassword) + { + LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); + } + else + { + LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); + } + reset_login(); } else { U32 num_retries = regionp->getNumSeedCapRetries(); - if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_LOGIN) + if (num_retries > MAX_SEED_CAP_ATTEMPTS_BEFORE_ABORT) { - // Region will keep trying to get capabilities, - // but for now continue as if caps were granted - LLStartUp::setStartupState(STATE_SEED_CAP_GRANTED); + LL_WARNS("AppInit") << "Failed to get capabilities. Backing up to login screen!" << LL_ENDL; + if (gRememberPassword) + { + LLNotificationsUtil::add("LoginPacketNeverReceived", LLSD(), LLSD(), login_alert_status); + } + else + { + LLNotificationsUtil::add("LoginPacketNeverReceivedNoTP", LLSD(), LLSD(), login_alert_status); + } + reset_login(); } else if (num_retries > 0) { @@ -1901,7 +1919,36 @@ bool idle_startup() LLNotificationsUtil::add("InventoryUnusable"); } + LLInventoryModelBackgroundFetch::instance().start(); gInventory.createCommonSystemCategories(); + LLStartUp::setStartupState(STATE_INVENTORY_CALLBACKS ); + display_startup(); + + return FALSE; + } + + //--------------------------------------------------------------------- + // STATE_INVENTORY_CALLBACKS + //--------------------------------------------------------------------- + if (STATE_INVENTORY_CALLBACKS == LLStartUp::getStartupState()) + { + if (!LLInventoryModel::isSysFoldersReady()) + { + display_startup(); + return FALSE; + } + LLInventoryModelBackgroundFetch::instance().start(); + LLUUID cof_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + LLViewerInventoryCategory* cof = gInventory.getCategory(cof_id); + if (cof + && cof->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN) + { + // Special case, dupplicate request prevention. + // Cof folder will be requested via FetchCOF + // in appearance manager, prevent recursive fetch + cof->setFetching(LLViewerInventoryCategory::FETCH_RECURSIVE); + } + // It's debatable whether this flag is a good idea - sets all // bits, and in general it isn't true that inventory @@ -2163,7 +2210,7 @@ bool idle_startup() gAgentWearables.notifyLoadingStarted(); gAgent.setOutfitChosen(TRUE); gAgentWearables.sendDummyAgentWearablesUpdate(); - callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), set_flags_and_update_appearance); + callAfterCOFFetch(set_flags_and_update_appearance); } display_startup(); @@ -2700,6 +2747,7 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFunc("InitiateDownload", process_initiate_download); msg->setHandlerFunc("LandStatReply", LLFloaterTopObjects::handle_land_reply); msg->setHandlerFunc("GenericMessage", process_generic_message); + msg->setHandlerFunc("GenericStreamingMessage", process_generic_streaming_message); msg->setHandlerFunc("LargeGenericMessage", process_large_generic_message); msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message); @@ -2774,7 +2822,7 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name, // Not going through the processAgentInitialWearables path, so need to set this here. LLAppearanceMgr::instance().setAttachmentInvLinkEnable(true); // Initiate creation of COF, since we're also bypassing that. - gInventory.findCategoryUUIDForType(LLFolderType::FT_CURRENT_OUTFIT); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_CURRENT_OUTFIT); ESex gender; if (gender_name == "male") @@ -2827,8 +2875,15 @@ void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name, bool do_append = false; LLViewerInventoryCategory *cat = gInventory.getCategory(cat_id); // Need to fetch cof contents before we can wear. - callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), + if (do_copy) + { + callAfterCategoryFetch(LLAppearanceMgr::instance().getCOF(), boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append)); + } + else + { + callAfterCategoryLinksFetch(cat_id, boost::bind(&LLAppearanceMgr::wearInventoryCategory, LLAppearanceMgr::getInstance(), cat, do_copy, do_append)); + } LL_DEBUGS() << "initial outfit category id: " << cat_id << LL_ENDL; } @@ -2881,6 +2936,7 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_AGENT_SEND ); RTNENUM( STATE_AGENT_WAIT ); RTNENUM( STATE_INVENTORY_SEND ); + RTNENUM(STATE_INVENTORY_CALLBACKS ); RTNENUM( STATE_MISC ); RTNENUM( STATE_PRECACHE ); RTNENUM( STATE_WEARABLES_WAIT ); @@ -2935,6 +2991,11 @@ void reset_login() LLFloaterReg::hideVisibleInstances(); LLStartUp::setStartupState( STATE_BROWSER_INIT ); + if (LLVoiceClient::instanceExists()) + { + LLVoiceClient::getInstance()->terminate(); + } + // Clear any verified certs and verify them again on next login // to ensure cert matches server instead of just getting reused LLPointer store = gSecAPIHandler->getCertificateStore(""); @@ -3250,7 +3311,7 @@ LLSD transform_cert_args(LLPointer cert) // are actually arrays, and we want to format them as comma separated // strings, so special case those. LLSDSerialize::toXML(cert_info[iter->first], std::cout); - if((iter->first== std::string(CERT_KEY_USAGE)) | + if((iter->first == std::string(CERT_KEY_USAGE)) || (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) { value = ""; @@ -3608,7 +3669,7 @@ bool process_login_success_response() std::string flag = login_flags["ever_logged_in"]; if(!flag.empty()) { - gAgent.setFirstLogin((flag == "N") ? TRUE : FALSE); + gAgent.setFirstLogin(flag == "N"); } /* Flag is currently ignored by the viewer. @@ -3699,7 +3760,7 @@ bool process_login_success_response() std::string fake_initial_outfit_name = gSavedSettings.getString("FakeInitialOutfitName"); if (!fake_initial_outfit_name.empty()) { - gAgent.setFirstLogin(TRUE); + gAgent.setFirstLogin(true); sInitialOutfit = fake_initial_outfit_name; if (sInitialOutfitGender.empty()) { @@ -3734,7 +3795,9 @@ bool process_login_success_response() // Only save mfa_hash for future logins if the user wants their info remembered. - if(response.has("mfa_hash") && gSavedSettings.getBOOL("RememberUser") && gSavedSettings.getBOOL("RememberPassword")) + if(response.has("mfa_hash") + && gSavedSettings.getBOOL("RememberUser") + && LLLoginInstance::getInstance()->saveMFA()) { std::string grid(LLGridManager::getInstance()->getGridId()); std::string user_id(gUserCredential->userID()); @@ -3742,6 +3805,13 @@ bool process_login_success_response() // TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically gSecAPIHandler->syncProtectedMap(); } + else if (!LLLoginInstance::getInstance()->saveMFA()) + { + std::string grid(LLGridManager::getInstance()->getGridId()); + std::string user_id(gUserCredential->userID()); + gSecAPIHandler->removeFromProtectedMap("mfa_hash", grid, user_id); + gSecAPIHandler->syncProtectedMap(); + } bool success = false; // JC: gesture loading done below, when we have an asset system diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index fe8e215f76..921f088423 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -71,6 +71,7 @@ typedef enum { STATE_AGENT_SEND, // Connect to a region STATE_AGENT_WAIT, // Wait for region STATE_INVENTORY_SEND, // Do inventory transfer + STATE_INVENTORY_CALLBACKS, // Wait for missing system folders and register callbacks STATE_MISC, // Do more things (set bandwidth, start audio, save location, etc) STATE_PRECACHE, // Wait a bit for textures to download STATE_WEARABLES_WAIT, // Wait for clothing to download diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 0a87b14e17..1ef5d1c50b 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -729,7 +729,7 @@ class LLBalanceHandler : public LLCommandHandler public: // Requires "trusted" browser/URL source LLBalanceHandler() : LLCommandHandler("balance", UNTRUSTED_BLOCK) { } - bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (tokens.size() == 1 && tokens[0].asString() == "request") diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index ef58bba71e..3abe8ab1dc 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -138,6 +138,8 @@ bool get_can_copy_texture(LLUUID asset_id) return get_is_predefined_texture(asset_id) || get_copy_free_item_by_asset_id(asset_id).notNull(); } +S32 LLFloaterTexturePicker::sLastPickerMode = 0; + LLFloaterTexturePicker::LLFloaterTexturePicker( LLView* owner, LLUUID image_asset_id, @@ -170,13 +172,16 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mSelectedItemPinned( FALSE ), mCanApply(true), mCanPreview(true), + mLimitsSet(false), + mMaxDim(S32_MAX), + mMinDim(0), mPreviewSettingChanged(false), mOnFloaterCommitCallback(NULL), mOnFloaterCloseCallback(NULL), mSetImageAssetIDCallback(NULL), mOnUpdateImageStatsCallback(NULL), mBakeTextureEnabled(FALSE), - mInventoryPickType(EPickInventoryType::TEXTURE) + mInventoryPickType(LLTextureCtrl::PICK_TEXTURE) { mCanApplyImmediately = can_apply_immediately; buildFromFile("floater_texture_ctrl.xml"); @@ -210,15 +215,38 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti mModeSelector->selectByValue(0); onModeSelect(0,this); } - - LLUUID item_id = findItemID(mImageAssetID, FALSE); + + LLUUID item_id; + LLFolderView* root_folder = mInventoryPanel->getRootFolder(); + if (root_folder && root_folder->getCurSelectedItem()) + { + LLFolderViewItem* last_selected = root_folder->getCurSelectedItem(); + LLFolderViewModelItemInventory* inv_view = static_cast(last_selected->getViewModelItem()); + + LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID()); + + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL + && mImageAssetID == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID + && itemp && itemp->getAssetUUID().isNull()) + { + item_id = inv_view->getUUID(); + } + else if (itemp && itemp->getAssetUUID() == mImageAssetID) + { + item_id = inv_view->getUUID(); + } + } + if (item_id.isNull()) + { + item_id = findItemID(mImageAssetID, FALSE); + } if (item_id.isNull()) { mInventoryPanel->getRootFolder()->clearSelection(); } else { - LLInventoryItem* itemp = gInventory.getItem(image_id); + LLInventoryItem* itemp = gInventory.getItem(item_id); if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID())) { // no copy texture @@ -232,12 +260,20 @@ void LLFloaterTexturePicker::setImageID(const LLUUID& image_id, bool set_selecti mInventoryPanel->setSelection(item_id, TAKE_FOCUS_NO); } } - - - } } +void LLFloaterTexturePicker::setImageIDFromItem(const LLInventoryItem* itemp, bool set_selection) +{ + LLUUID asset_id = itemp->getAssetUUID(); + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL && asset_id.isNull()) + { + // If an inventory item has a null asset, consider it a valid blank material(gltf) + asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } + setImageID(asset_id, set_selection); +} + void LLFloaterTexturePicker::setActive( BOOL active ) { if (!active && getChild("Pipette")->getValue().asBoolean()) @@ -264,8 +300,9 @@ void LLFloaterTexturePicker::stopUsingPipette() } } -void LLFloaterTexturePicker::updateImageStats() +bool LLFloaterTexturePicker::updateImageStats() { + bool result = true; if (mGLTFMaterial.notNull()) { S32 width = 0; @@ -321,14 +358,31 @@ void LLFloaterTexturePicker::updateImageStats() else if (mTexturep.notNull()) { //RN: have we received header data for this image? - if (mTexturep->getFullWidth() > 0 && mTexturep->getFullHeight() > 0) + S32 width = mTexturep->getFullWidth(); + S32 height = mTexturep->getFullHeight(); + if (width > 0 && height > 0) { - std::string formatted_dims = llformat("%d x %d", mTexturep->getFullWidth(),mTexturep->getFullHeight()); - mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims); - if (mOnUpdateImageStatsCallback) - { - mOnUpdateImageStatsCallback(mTexturep); - } + if ((mLimitsSet && (width != height)) + || width < mMinDim + || width > mMaxDim + || height < mMinDim + || height > mMaxDim + ) + { + std::string formatted_dims = llformat("%dx%d", width, height); + mResolutionWarning->setTextArg("[TEXDIM]", formatted_dims); + result = false; + } + else + { + std::string formatted_dims = llformat("%d x %d", width, height); + mResolutionLabel->setTextArg("[DIMENSIONS]", formatted_dims); + } + + if (mOnUpdateImageStatsCallback) + { + mOnUpdateImageStatsCallback(mTexturep); + } } else { @@ -339,6 +393,21 @@ void LLFloaterTexturePicker::updateImageStats() { mResolutionLabel->setTextArg("[DIMENSIONS]", std::string("")); } + mResolutionLabel->setVisible(result); + mResolutionWarning->setVisible(!result); + + // Hide buttons and pipete to make space for mResolutionWarning + // Hiding buttons is suboptimal, but at the moment limited to inventory thumbnails + // may be this should be an info/warning icon with a tooltip? + S32 index = mModeSelector->getValue().asInteger(); + if (index == 0) + { + mDefaultBtn->setVisible(result); + mNoneBtn->setVisible(result); + mBlankBtn->setVisible(result); + mPipetteBtn->setVisible(result); + } + return result; } // virtual @@ -356,11 +425,11 @@ BOOL LLFloaterTexturePicker::handleDragAndDrop( bool is_material = cargo_type == DAD_MATERIAL; bool allow_dnd = false; - if (mInventoryPickType == EPickInventoryType::MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { allow_dnd = is_material; } - else if (mInventoryPickType == EPickInventoryType::TEXTURE) + else if (mInventoryPickType == LLTextureCtrl::PICK_TEXTURE) { allow_dnd = is_texture || is_mesh; } @@ -388,7 +457,7 @@ BOOL LLFloaterTexturePicker::handleDragAndDrop( { if (drop) { - setImageID( item->getAssetUUID() ); + setImageIDFromItem(item); commitIfImmediateSet(); } @@ -448,6 +517,15 @@ BOOL LLFloaterTexturePicker::handleKeyHere(KEY key, MASK mask) return LLFloater::handleKeyHere(key, mask); } +void LLFloaterTexturePicker::onOpen(const LLSD& key) +{ + if (sLastPickerMode != 0 + && mModeSelector->selectByValue(sLastPickerMode)) + { + changeMode(); + } +} + void LLFloaterTexturePicker::onClose(bool app_quitting) { if (mOwner && mOnFloaterCloseCallback) @@ -455,6 +533,7 @@ void LLFloaterTexturePicker::onClose(bool app_quitting) mOnFloaterCloseCallback(); } stopUsingPipette(); + sLastPickerMode = mModeSelector->getValue().asInteger(); } // virtual @@ -471,11 +550,22 @@ BOOL LLFloaterTexturePicker::postBuild() mTentativeLabel = getChild("Multiple"); mResolutionLabel = getChild("size_lbl"); + mResolutionWarning = getChild("over_limit_lbl"); - childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this); - childSetAction("None", LLFloaterTexturePicker::onBtnNone,this); - childSetAction("Blank", LLFloaterTexturePicker::onBtnBlank,this); + mDefaultBtn = getChild("Default"); + mNoneBtn = getChild("None"); + mBlankBtn = getChild("Blank"); + mPipetteBtn = getChild("Pipette"); + mSelectBtn = getChild("Select"); + mCancelBtn = getChild("Cancel"); + + mDefaultBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSetToDefault,this)); + mNoneBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnNone, this)); + mBlankBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnBlank, this)); + mPipetteBtn->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onBtnPipette, this)); + mSelectBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnSelect, this)); + mCancelBtn->setClickedCallback(boost::bind(LLFloaterTexturePicker::onBtnCancel, this)); mFilterEdit = getChild("inventory search editor"); mFilterEdit->setCommitCallback(boost::bind(&LLFloaterTexturePicker::onFilterEdit, this, _2)); @@ -556,12 +646,13 @@ void LLFloaterTexturePicker::draw() // This is going to spam mOnUpdateImageStatsCallback, // either move elsewhere or fix to cause update once per image - updateImageStats(); + bool valid_dims = updateImageStats(); // if we're inactive, gray out "apply immediate" checkbox - getChildView("Select")->setEnabled(mActive && mCanApply); - getChildView("Pipette")->setEnabled(mActive); - getChild("Pipette")->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); + getChildView("show_folders_check")->setEnabled(mActive && mCanApplyImmediately && !mNoCopyTextureSelected); + mSelectBtn->setEnabled(mActive && mCanApply && valid_dims); + mPipetteBtn->setEnabled(mActive); + mPipetteBtn->setValue(LLToolMgr::getInstance()->getCurrentTool() == LLToolPipette::getInstance()); //BOOL allow_copy = FALSE; if( mOwner ) @@ -570,7 +661,7 @@ void LLFloaterTexturePicker::draw() mGLTFMaterial = NULL; if (mImageAssetID.notNull()) { - if (mInventoryPickType == EPickInventoryType::MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { mGLTFMaterial = (LLFetchedGLTFMaterial*) gGLTFMaterialList.getMaterial(mImageAssetID); llassert(mGLTFMaterial == nullptr || dynamic_cast(gGLTFMaterialList.getMaterial(mImageAssetID)) != nullptr); @@ -606,9 +697,9 @@ void LLFloaterTexturePicker::draw() mTentativeLabel->setVisible( FALSE ); } - getChildView("Default")->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); - getChildView("Blank")->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID.notNull()) || mTentative); - getChildView("None")->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative)); + mDefaultBtn->setEnabled(mImageAssetID != mDefaultImageAssetID || mTentative); + mBlankBtn->setEnabled((mImageAssetID != mBlankImageAssetID && mBlankImageAssetID.notNull()) || mTentative); + mNoneBtn->setEnabled(mAllowNoTexture && (!mImageAssetID.isNull() || mTentative)); LLFloater::draw(); @@ -685,9 +776,22 @@ void LLFloaterTexturePicker::draw() const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL copyable_only, BOOL ignore_library) { + LLUUID loockup_id = asset_id; + if (loockup_id.isNull()) + { + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) + { + loockup_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } + else + { + return LLUUID::null; + } + } + LLViewerInventoryCategory::cat_array_t cats; LLViewerInventoryItem::item_array_t items; - LLAssetIDMatches asset_id_matches(asset_id); + LLAssetIDMatches asset_id_matches(loockup_id); gInventory.collectDescendentsIf(LLUUID::null, cats, items, @@ -728,17 +832,93 @@ const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL co void LLFloaterTexturePicker::commitIfImmediateSet() { - if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply) + if (!mNoCopyTextureSelected && mCanApply) { - mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, LLUUID::null); + commitCallback(LLTextureCtrl::TEXTURE_CHANGE); } } +void LLFloaterTexturePicker::commitCallback(LLTextureCtrl::ETexturePickOp op) +{ + if (!mOnFloaterCommitCallback) + { + return; + } + LLUUID asset_id = mImageAssetID; + LLUUID inventory_id; + LLPickerSource mode = (LLPickerSource)mModeSelector->getValue().asInteger(); + + switch (mode) + { + case PICKER_INVENTORY: + { + LLFolderView* root_folder = mInventoryPanel->getRootFolder(); + if (root_folder && root_folder->getCurSelectedItem()) + { + LLFolderViewItem* last_selected = root_folder->getCurSelectedItem(); + LLFolderViewModelItemInventory* inv_view = static_cast(last_selected->getViewModelItem()); + + LLInventoryItem* itemp = gInventory.getItem(inv_view->getUUID()); + + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL + && mImageAssetID == LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID + && itemp && itemp->getAssetUUID().isNull()) + { + inventory_id = inv_view->getUUID(); + } + else if (itemp && itemp->getAssetUUID() == mImageAssetID) + { + inventory_id = inv_view->getUUID(); + } + else + { + mode = PICKER_UNKNOWN; // source of id unknown + } + } + else + { + mode = PICKER_UNKNOWN; // source of id unknown + } + break; + } + case PICKER_LOCAL: + { + if (!mLocalScrollCtrl->getAllSelected().empty()) + { + LLSD data = mLocalScrollCtrl->getFirstSelected()->getValue(); + LLUUID temp_id = data["id"]; + S32 asset_type = data["type"].asInteger(); + + if (LLAssetType::AT_MATERIAL == asset_type) + { + asset_id = LLLocalGLTFMaterialMgr::getInstance()->getWorldID(temp_id); + } + else + { + asset_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id); + } + } + else + { + asset_id = mImageAssetID; + mode = PICKER_UNKNOWN; // source of id unknown + } + break; + } + case PICKER_BAKE: + break; + default: + mode = PICKER_UNKNOWN; // source of id unknown + break; + } + + mOnFloaterCommitCallback(op, mode, asset_id, inventory_id); +} void LLFloaterTexturePicker::commitCancel() { if (!mNoCopyTextureSelected && mOnFloaterCommitCallback && mCanApply) { - mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, LLUUID::null); + mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, mOriginalImageAssetID, LLUUID::null); } } @@ -792,7 +972,7 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata) self->setImageID( self->mOriginalImageAssetID ); if (self->mOnFloaterCommitCallback) { - self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, LLUUID::null); + self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CANCEL, PICKER_UNKNOWN, self->mOriginalImageAssetID, LLUUID::null); } self->mViewModel->resetDirty(); self->closeFloater(); @@ -801,30 +981,10 @@ void LLFloaterTexturePicker::onBtnCancel(void* userdata) // static void LLFloaterTexturePicker::onBtnSelect(void* userdata) { - LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; - LLUUID local_id = LLUUID::null; - if (self->mOwner) - { - if (self->mLocalScrollCtrl->getVisible() && !self->mLocalScrollCtrl->getAllSelected().empty()) - { - LLSD data = self->mLocalScrollCtrl->getFirstSelected()->getValue(); - LLUUID temp_id = data["id"]; - S32 asset_type = data["type"].asInteger(); - - if (LLAssetType::AT_MATERIAL == asset_type) - { - local_id = LLLocalGLTFMaterialMgr::getInstance()->getWorldID(temp_id); - } - else - { - local_id = LLLocalBitmapMgr::getInstance()->getWorldID(temp_id); - } - } - } - + LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; if (self->mOnFloaterCommitCallback) { - self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_SELECT, local_id); + self->commitCallback(LLTextureCtrl::TEXTURE_SELECT); } self->closeFloater(); } @@ -860,7 +1020,7 @@ void LLFloaterTexturePicker::onSelectionChange(const std::dequegetAssetUUID(),false); + setImageIDFromItem(itemp, false); mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? if(!mPreviewSettingChanged) @@ -884,81 +1044,8 @@ void LLFloaterTexturePicker::onSelectionChange(const std::dequemModeSelector->getValue().asInteger(); - - self->getChild("Default")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("Blank")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("None")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("inventory search editor")->setVisible(index == 0 ? TRUE : FALSE); - self->getChild("inventory panel")->setVisible(index == 0 ? TRUE : FALSE); - - self->getChild("l_add_btn")->setVisible(index == 1 ? TRUE : FALSE); - self->getChild("l_rem_btn")->setVisible(index == 1 ? TRUE : FALSE); - self->getChild("l_upl_btn")->setVisible(index == 1 ? TRUE : FALSE); - self->getChild("l_name_list")->setVisible(index == 1 ? TRUE : FALSE); - - self->getChild("l_bake_use_texture_combo_box")->setVisible(index == 2 ? TRUE : FALSE); - - bool pipette_visible = (index == 0) - && (self->mInventoryPickType != EPickInventoryType::MATERIAL); - self->getChild("Pipette")->setVisible(pipette_visible); - - if (index == 2) - { - self->stopUsingPipette(); - - S8 val = -1; - - LLUUID imageID = self->mImageAssetID; - if (imageID == IMG_USE_BAKED_HEAD) - { - val = 0; - } - else if (imageID == IMG_USE_BAKED_UPPER) - { - val = 1; - } - else if (imageID == IMG_USE_BAKED_LOWER) - { - val = 2; - } - else if (imageID == IMG_USE_BAKED_EYES) - { - val = 3; - } - else if (imageID == IMG_USE_BAKED_SKIRT) - { - val = 4; - } - else if (imageID == IMG_USE_BAKED_HAIR) - { - val = 5; - } - else if (imageID == IMG_USE_BAKED_LEFTARM) - { - val = 6; - } - else if (imageID == IMG_USE_BAKED_LEFTLEG) - { - val = 7; - } - else if (imageID == IMG_USE_BAKED_AUX1) - { - val = 8; - } - else if (imageID == IMG_USE_BAKED_AUX2) - { - val = 9; - } - else if (imageID == IMG_USE_BAKED_AUX3) - { - val = 10; - } - - - self->getChild("l_bake_use_texture_combo_box")->setSelectedByValue(val, TRUE); - } + LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata; + self->changeMode(); } // static @@ -966,15 +1053,15 @@ void LLFloaterTexturePicker::onBtnAdd(void* userdata) { LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)userdata; - if (self->mInventoryPickType == EPickInventoryType::TEXTURE_MATERIAL) + if (self->mInventoryPickType == LLTextureCtrl::PICK_TEXTURE_MATERIAL) { LLFilePickerReplyThread::startPicker(boost::bind(&onPickerCallback, _1, self->getHandle()), LLFilePicker::FFLOAD_MATERIAL_TEXTURE, true); } - else if (self->mInventoryPickType == EPickInventoryType::TEXTURE) + else if (self->mInventoryPickType == LLTextureCtrl::PICK_TEXTURE) { LLFilePickerReplyThread::startPicker(boost::bind(&onPickerCallback, _1, self->getHandle()), LLFilePicker::FFLOAD_IMAGE, true); } - else if (self->mInventoryPickType == EPickInventoryType::MATERIAL) + else if (self->mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { LLFilePickerReplyThread::startPicker(boost::bind(&onPickerCallback, _1, self->getHandle()), LLFilePicker::FFLOAD_MATERIAL, true); } @@ -1090,7 +1177,7 @@ void LLFloaterTexturePicker::onLocalScrollCommit(LLUICtrl* ctrl, void* userdata) { if (self->mOnFloaterCommitCallback) { - self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, inworld_id); + self->mOnFloaterCommitCallback(LLTextureCtrl::TEXTURE_CHANGE, PICKER_LOCAL, inworld_id, LLUUID::null); } } } @@ -1180,10 +1267,10 @@ void LLFloaterTexturePicker::onBakeTextureSelect(LLUICtrl* ctrl, void *user_data } } -void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply) +void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply, bool inworld_image) { - getChildRef("Select").setEnabled(can_apply); - getChildRef("preview_disabled").setVisible(!can_preview); + mSelectBtn->setEnabled(can_apply); + getChildRef("preview_disabled").setVisible(!can_preview && inworld_image); getChildRef("apply_immediate_check").setVisible(can_preview); mCanApply = can_apply; @@ -1191,6 +1278,15 @@ void LLFloaterTexturePicker::setCanApply(bool can_preview, bool can_apply) mPreviewSettingChanged = true; } +void LLFloaterTexturePicker::setMinDimentionsLimits(S32 min_dim) +{ + mMinDim = min_dim; + mLimitsSet = true; + + std::string formatted_dims = llformat("%dx%d", mMinDim, mMinDim); + mResolutionWarning->setTextArg("[MINTEXDIM]", formatted_dims); +} + void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) { std::string upper_case_search_string = search_string; @@ -1225,20 +1321,98 @@ void LLFloaterTexturePicker::onFilterEdit(const std::string& search_string ) mInventoryPanel->setFilterSubString(search_string); } +void LLFloaterTexturePicker::changeMode() +{ + int index = mModeSelector->getValue().asInteger(); + + mDefaultBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); + mBlankBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); + mNoneBtn->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); + mFilterEdit->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); + mInventoryPanel->setVisible(index == PICKER_INVENTORY ? TRUE : FALSE); + + getChild("l_add_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); + getChild("l_rem_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); + getChild("l_upl_btn")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); + getChild("l_name_list")->setVisible(index == PICKER_LOCAL ? TRUE : FALSE); + + getChild("l_bake_use_texture_combo_box")->setVisible(index == PICKER_BAKE ? TRUE : FALSE); + getChild("hide_base_mesh_region")->setVisible(FALSE);// index == 2 ? TRUE : FALSE); + + bool pipette_visible = (index == PICKER_INVENTORY) + && (mInventoryPickType != LLTextureCtrl::PICK_MATERIAL); + mPipetteBtn->setVisible(pipette_visible); + + if (index == PICKER_BAKE) + { + stopUsingPipette(); + + S8 val = -1; + + LLUUID imageID = mImageAssetID; + if (imageID == IMG_USE_BAKED_HEAD) + { + val = 0; + } + else if (imageID == IMG_USE_BAKED_UPPER) + { + val = 1; + } + else if (imageID == IMG_USE_BAKED_LOWER) + { + val = 2; + } + else if (imageID == IMG_USE_BAKED_EYES) + { + val = 3; + } + else if (imageID == IMG_USE_BAKED_SKIRT) + { + val = 4; + } + else if (imageID == IMG_USE_BAKED_HAIR) + { + val = 5; + } + else if (imageID == IMG_USE_BAKED_LEFTARM) + { + val = 6; + } + else if (imageID == IMG_USE_BAKED_LEFTLEG) + { + val = 7; + } + else if (imageID == IMG_USE_BAKED_AUX1) + { + val = 8; + } + else if (imageID == IMG_USE_BAKED_AUX2) + { + val = 9; + } + else if (imageID == IMG_USE_BAKED_AUX3) + { + val = 10; + } + + getChild("l_bake_use_texture_combo_box")->setSelectedByValue(val, TRUE); + } +} + void LLFloaterTexturePicker::refreshLocalList() { mLocalScrollCtrl->clearRows(); - if (mInventoryPickType == EPickInventoryType::TEXTURE_MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_TEXTURE_MATERIAL) { LLLocalBitmapMgr::getInstance()->feedScrollList(mLocalScrollCtrl); LLLocalGLTFMaterialMgr::getInstance()->feedScrollList(mLocalScrollCtrl); } - else if (mInventoryPickType == EPickInventoryType::TEXTURE) + else if (mInventoryPickType == LLTextureCtrl::PICK_TEXTURE) { LLLocalBitmapMgr::getInstance()->feedScrollList(mLocalScrollCtrl); } - else if (mInventoryPickType == EPickInventoryType::MATERIAL) + else if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { LLLocalGLTFMaterialMgr::getInstance()->feedScrollList(mLocalScrollCtrl); } @@ -1248,18 +1422,18 @@ void LLFloaterTexturePicker::refreshInventoryFilter() { U32 filter_types = 0x0; - if (mInventoryPickType == EPickInventoryType::TEXTURE_MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_TEXTURE_MATERIAL) { filter_types |= 0x1 << LLInventoryType::IT_TEXTURE; filter_types |= 0x1 << LLInventoryType::IT_SNAPSHOT; filter_types |= 0x1 << LLInventoryType::IT_MATERIAL; } - else if (mInventoryPickType == EPickInventoryType::TEXTURE) + else if (mInventoryPickType == LLTextureCtrl::PICK_TEXTURE) { filter_types |= 0x1 << LLInventoryType::IT_TEXTURE; filter_types |= 0x1 << LLInventoryType::IT_SNAPSHOT; } - else if (mInventoryPickType == EPickInventoryType::MATERIAL) + else if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { filter_types |= 0x1 << LLInventoryType::IT_MATERIAL; } @@ -1294,13 +1468,13 @@ void LLFloaterTexturePicker::setBakeTextureEnabled(BOOL enabled) onModeSelect(0, this); } -void LLFloaterTexturePicker::setInventoryPickType(EPickInventoryType type) +void LLFloaterTexturePicker::setInventoryPickType(LLTextureCtrl::EPickInventoryType type) { mInventoryPickType = type; refreshLocalList(); refreshInventoryFilter(); - if (mInventoryPickType == EPickInventoryType::MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { getChild("Pipette")->setVisible(false); } @@ -1316,7 +1490,7 @@ void LLFloaterTexturePicker::setInventoryPickType(EPickInventoryType type) setTitle(pick + mLabel); } - else if(mInventoryPickType == EPickInventoryType::MATERIAL) + else if(mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { setTitle(getString("pick_material")); } @@ -1326,6 +1500,12 @@ void LLFloaterTexturePicker::setInventoryPickType(EPickInventoryType type) } } +void LLFloaterTexturePicker::setImmediateFilterPermMask(PermissionMask mask) +{ + mImmediateFilterPermMask = mask; + mInventoryPanel->setFilterPermMask(mask); +} + void LLFloaterTexturePicker::onPickerCallback(const std::vector& filenames, LLHandle handle) { std::vector::const_iterator iter = filenames.begin(); @@ -1352,16 +1532,16 @@ void LLFloaterTexturePicker::onPickerCallback(const std::vector& fi LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)handle.get(); self->mLocalScrollCtrl->clearRows(); - if (self->mInventoryPickType == EPickInventoryType::TEXTURE_MATERIAL) + if (self->mInventoryPickType == LLTextureCtrl::PICK_TEXTURE_MATERIAL) { LLLocalBitmapMgr::getInstance()->feedScrollList(self->mLocalScrollCtrl); LLLocalGLTFMaterialMgr::getInstance()->feedScrollList(self->mLocalScrollCtrl); } - else if (self->mInventoryPickType == EPickInventoryType::TEXTURE) + else if (self->mInventoryPickType == LLTextureCtrl::PICK_TEXTURE) { LLLocalBitmapMgr::getInstance()->feedScrollList(self->mLocalScrollCtrl); } - else if (self->mInventoryPickType == EPickInventoryType::MATERIAL) + else if (self->mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { LLLocalGLTFMaterialMgr::getInstance()->feedScrollList(self->mLocalScrollCtrl); } @@ -1374,7 +1554,7 @@ void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te ) if (inventory_item_id.notNull()) { LLToolPipette::getInstance()->setResult(TRUE, ""); - if (mInventoryPickType == EPickInventoryType::MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { // tes have no data about material ids // Plus gltf materials are layered with overrides, @@ -1513,6 +1693,17 @@ void LLTextureCtrl::setCanApply(bool can_preview, bool can_apply) } } +void LLTextureCtrl::setImmediateFilterPermMask(PermissionMask mask) +{ + mImmediateFilterPermMask = mask; + + LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); + if (floaterp) + { + floaterp->setImmediateFilterPermMask(mask); + } +} + void LLTextureCtrl::setVisible( BOOL visible ) { if( !visible ) @@ -1613,7 +1804,10 @@ void LLTextureCtrl::showPicker(BOOL take_focus) } if (texture_floaterp) { - texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2)); + texture_floaterp->setOnFloaterCommitCallback(boost::bind(&LLTextureCtrl::onFloaterCommit, this, _1, _2, _3, _4)); + } + if (texture_floaterp) + { texture_floaterp->setSetImageAssetIDCallback(boost::bind(&LLTextureCtrl::setImageAssetID, this, _1)); texture_floaterp->setBakeTextureEnabled(mBakeTextureEnabled); @@ -1680,7 +1874,7 @@ BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask) if (!mOpenTexPreview) { showPicker(FALSE); - if (mInventoryPickType == EPickInventoryType::MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { //grab materials first... LLInventoryModelBackgroundFetch::instance().start(gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL)); @@ -1691,7 +1885,10 @@ BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask) LLInventoryModelBackgroundFetch::instance().start(gInventory.findCategoryUUIDForType(LLFolderType::FT_TEXTURE)); } //...then start full inventory fetch. - LLInventoryModelBackgroundFetch::instance().start(); + if (!LLInventoryModelBackgroundFetch::instance().inventoryFetchStarted()) + { + LLInventoryModelBackgroundFetch::instance().start(); + } handled = TRUE; } else @@ -1731,7 +1928,7 @@ void LLTextureCtrl::onFloaterClose() mFloaterHandle.markDead(); } -void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) +void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& asset_id, const LLUUID& inv_id) { LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)mFloaterHandle.get(); @@ -1745,22 +1942,29 @@ void LLTextureCtrl::onFloaterCommit(ETexturePickOp op, LLUUID id) else if (mCommitOnSelection || op == TEXTURE_SELECT) mViewModel->setDirty(); // *TODO: shouldn't we be using setValue() here? - if(floaterp->isDirty() || id.notNull()) // mModelView->setDirty does not work. + if(floaterp->isDirty() || asset_id.notNull()) // mModelView->setDirty does not work. { setTentative( FALSE ); - if (id.notNull()) - { - mImageItemID = id; - mImageAssetID = id; - } - else - { - mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE); - LL_DEBUGS() << "mImageItemID: " << mImageItemID << LL_ENDL; - mImageAssetID = floaterp->getAssetID(); - LL_DEBUGS() << "mImageAssetID: " << mImageAssetID << LL_ENDL; - } + switch(source) + { + case PICKER_INVENTORY: + mImageItemID = inv_id; + mImageAssetID = asset_id; + break; + case PICKER_BAKE: + case PICKER_LOCAL: + mImageItemID = LLUUID::null; + mImageAssetID = asset_id; + break; + case PICKER_UNKNOWN: + default: + mImageItemID = floaterp->findItemID(asset_id, FALSE); + mImageAssetID = asset_id; + break; + } + + LL_DEBUGS() << "mImageAssetID: " << mImageAssetID << ", mImageItemID: " << mImageItemID << LL_ENDL; if (op == TEXTURE_SELECT && mOnSelectCallback) { @@ -1860,11 +2064,11 @@ BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, bool is_material = cargo_type == DAD_MATERIAL; bool allow_dnd = false; - if (mInventoryPickType == EPickInventoryType::MATERIAL) + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL) { allow_dnd = is_material; } - else if (mInventoryPickType == EPickInventoryType::TEXTURE) + else if (mInventoryPickType == LLTextureCtrl::PICK_TEXTURE) { allow_dnd = is_texture || is_mesh; } @@ -2071,8 +2275,16 @@ BOOL LLTextureCtrl::doDrop(LLInventoryItem* item) return mDropCallback(this, item); } - // no callback installed, so just set the image ids and carry on. - setImageAssetID( item->getAssetUUID() ); + // no callback installed, so just set the image ids and carry on. + LLUUID asset_id = item->getAssetUUID(); + + if (mInventoryPickType == LLTextureCtrl::PICK_MATERIAL && asset_id.isNull()) + { + // If an inventory material has a null asset, consider it a valid blank material(gltf) + asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } + + setImageAssetID(asset_id); mImageItemID = item->getUUID(); return TRUE; } @@ -2099,11 +2311,11 @@ LLSD LLTextureCtrl::getValue() const namespace LLInitParam { - void TypeValues::declareValues() + void TypeValues::declareValues() { - declare("texture_material", EPickInventoryType::TEXTURE_MATERIAL); - declare("texture", EPickInventoryType::TEXTURE); - declare("material", EPickInventoryType::MATERIAL); + declare("texture_material", LLTextureCtrl::PICK_TEXTURE_MATERIAL); + declare("texture", LLTextureCtrl::PICK_TEXTURE); + declare("material", LLTextureCtrl::PICK_MATERIAL); } } diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 28630d4ac4..08c530314e 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -64,25 +64,34 @@ bool get_is_predefined_texture(LLUUID asset_id); LLUUID get_copy_free_item_by_asset_id(LLUUID image_id, bool no_trans_perm = false); bool get_can_copy_texture(LLUUID image_id); -enum class EPickInventoryType +enum class LLTexPickInventoryType : U32 { - TEXTURE_MATERIAL = 0, - TEXTURE = 1, - MATERIAL = 2, + TEXTURE_MATERIAL = 0, + TEXTURE = 1, + MATERIAL = 2, }; namespace LLInitParam { template<> - struct TypeValues : public TypeValuesHelper + struct TypeValues : public TypeValuesHelper { static void declareValues(); }; } +enum LLPickerSource +{ + PICKER_INVENTORY, + PICKER_LOCAL, + PICKER_BAKE, + PICKER_UNKNOWN, // on cancel, default ids +}; + ////////////////////////////////////////////////////////////////////////////////////////// // LLTextureCtrl + class LLTextureCtrl : public LLUICtrl { @@ -94,6 +103,12 @@ public: TEXTURE_CANCEL } ETexturePickOp; + // *HACK: Can't forward-declare an enum scoped inside a class. Maybe there's a better way to initialize LLInitParam::TypeValues that doesn't run into this limitation. + typedef LLTexPickInventoryType EPickInventoryType; + static const EPickInventoryType PICK_TEXTURE_MATERIAL = LLTexPickInventoryType::TEXTURE_MATERIAL; + static const EPickInventoryType PICK_TEXTURE = LLTexPickInventoryType::TEXTURE; + static const EPickInventoryType PICK_MATERIAL = LLTexPickInventoryType::MATERIAL; + public: struct Params : public LLInitParam::Block { @@ -118,7 +133,7 @@ public: : image_id("image"), default_image_id("default_image_id"), default_image_name("default_image_name"), - pick_type("pick_type", EPickInventoryType::TEXTURE), + pick_type("pick_type", PICK_TEXTURE), allow_no_texture("allow_no_texture", false), can_apply_immediately("can_apply_immediately"), no_commit_on_selection("no_commit_on_selection", false), @@ -194,8 +209,7 @@ public: void setCanApply(bool can_preview, bool can_apply); - void setImmediateFilterPermMask(PermissionMask mask) - { mImmediateFilterPermMask = mask; } + void setImmediateFilterPermMask(PermissionMask mask); void setDnDFilterPermMask(PermissionMask mask) { mDnDFilterPermMask = mask; } PermissionMask getImmediateFilterPermMask() { return mImmediateFilterPermMask; } @@ -203,7 +217,7 @@ public: void closeDependentFloater(); void onFloaterClose(); - void onFloaterCommit(ETexturePickOp op, LLUUID id); + void onFloaterCommit(ETexturePickOp op, LLPickerSource source, const LLUUID& local_id, const LLUUID& inv_id); // This call is returned when a drag is detected. Your callback // should return TRUE if the drag is acceptable. @@ -275,7 +289,7 @@ private: ////////////////////////////////////////////////////////////////////////////////////////// // LLFloaterTexturePicker -typedef boost::function floater_commit_callback; +typedef boost::function floater_commit_callback; typedef boost::function floater_close_callback; typedef boost::function set_image_asset_id_callback; typedef boost::function texture)> set_on_update_image_stats_callback; @@ -309,11 +323,12 @@ public: // LLFloater overrides /*virtual*/ BOOL postBuild(); + /*virtual*/ void onOpen(const LLSD& key); /*virtual*/ void onClose(bool app_settings); // New functions void setImageID(const LLUUID& image_asset_id, bool set_selection = true); - void updateImageStats(); + bool updateImageStats(); // true if within limits const LLUUID& getAssetID() { return mImageAssetID; } const LLUUID& findItemID(const LLUUID& asset_id, BOOL copyable_only, BOOL ignore_library = FALSE); void setCanApplyImmediately(BOOL b); @@ -325,11 +340,13 @@ public: void stopUsingPipette(); void commitIfImmediateSet(); + void commitCallback(LLTextureCtrl::ETexturePickOp op); void commitCancel(); void onFilterEdit(const std::string& search_string); - void setCanApply(bool can_preview, bool can_apply); + void setCanApply(bool can_preview, bool can_apply, bool inworld_image = true); + void setMinDimentionsLimits(S32 min_dim); void setTextureSelectedCallback(const texture_selected_callback& cb) { mTextureSelectedCallback = cb; } void setOnFloaterCloseCallback(const floater_close_callback& cb) { mOnFloaterCloseCallback = cb; } void setOnFloaterCommitCallback(const floater_commit_callback& cb) { mOnFloaterCommitCallback = cb; } @@ -360,13 +377,16 @@ public: void setLocalTextureEnabled(BOOL enabled); void setBakeTextureEnabled(BOOL enabled); - void setInventoryPickType(EPickInventoryType type); + void setInventoryPickType(LLTextureCtrl::EPickInventoryType type); + void setImmediateFilterPermMask(PermissionMask mask); static void onPickerCallback(const std::vector& filenames, LLHandle handle); protected: + void changeMode(); void refreshLocalList(); void refreshInventoryFilter(); + void setImageIDFromItem(const LLInventoryItem* itemp, bool set_selection = true); LLPointer mTexturep; LLPointer mGLTFMaterial; @@ -385,6 +405,7 @@ protected: LLTextBox* mTentativeLabel; LLTextBox* mResolutionLabel; + LLTextBox* mResolutionWarning; std::string mPendingName; BOOL mActive; @@ -401,12 +422,21 @@ protected: LLComboBox* mModeSelector; LLScrollListCtrl* mLocalScrollCtrl; + LLButton* mDefaultBtn; + LLButton* mNoneBtn; + LLButton* mBlankBtn; + LLButton* mPipetteBtn; + LLButton* mSelectBtn; + LLButton* mCancelBtn; private: bool mCanApply; bool mCanPreview; bool mPreviewSettingChanged; - EPickInventoryType mInventoryPickType; + bool mLimitsSet; + S32 mMaxDim; + S32 mMinDim; + LLTextureCtrl::EPickInventoryType mInventoryPickType; texture_selected_callback mTextureSelectedCallback; @@ -416,6 +446,8 @@ private: set_on_update_image_stats_callback mOnUpdateImageStatsCallback; BOOL mBakeTextureEnabled; + + static S32 sLastPickerMode; }; #endif // LL_LLTEXTURECTRL_H diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index 2128b55029..e669393dba 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -239,6 +239,10 @@ static const S32 HTTP_NONPIPE_REQUESTS_LOW_WATER = 20; // request (e.g. 'Range: -') which seems to fix the problem. static const S32 HTTP_REQUESTS_RANGE_END_MAX = 20000000; +// stop after 720 seconds, might be overkill, but cap request can keep going forever. +static const S32 MAX_CAP_MISSING_RETRIES = 720; +static const S32 CAP_MISSING_EXPIRATION_DELAY = 1; // seconds + ////////////////////////////////////////////////////////////////////////////// namespace { @@ -526,6 +530,7 @@ private: e_state mState; void setState(e_state new_state); + LLViewerRegion* getRegion(); e_write_to_cache_state mWriteToCacheState; LLTextureFetch* mFetcher; @@ -579,6 +584,10 @@ private: LLCore::HttpStatus mGetStatus; std::string mGetReason; LLAdaptiveRetryPolicy mFetchRetryPolicy; + bool mCanUseCapability; + LLTimer mRegionRetryTimer; + S32 mRegionRetryAttempt; + LLUUID mLastRegionId; // Work Data @@ -928,7 +937,9 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mCacheReadCount(0U), mCacheWriteCount(0U), mResourceWaitCount(0U), - mFetchRetryPolicy(10.f,3600.f,2.f,10) + mFetchRetryPolicy(10.f,3600.f,2.f,10), + mCanUseCapability(true), + mRegionRetryAttempt(0) { mType = host.isOk() ? LLImageBase::TYPE_AVATAR_BAKE : LLImageBase::TYPE_NORMAL; // LL_INFOS(LOG_TXT) << "Create: " << mID << " mHost:" << host << " Discard=" << discard << LL_ENDL; @@ -1089,6 +1100,18 @@ bool LLTextureFetchWorker::doWork(S32 param) return true; // abort } } + if (mState > CACHE_POST && !mCanUseCapability && mCanUseHTTP) + { + if (mRegionRetryAttempt > MAX_CAP_MISSING_RETRIES) + { + mCanUseHTTP = false; + } + else if (!mRegionRetryTimer.hasExpired()) + { + return false; + } + // else retry + } if(mState > CACHE_POST && !mCanUseHTTP) { LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("tfwdw - state > cache_post"); @@ -1290,16 +1313,7 @@ bool LLTextureFetchWorker::doWork(S32 param) // if (mHost.isInvalid()) get_url = false; if ( use_http && mCanUseHTTP && mUrl.empty())//get http url. { - LLViewerRegion* region = NULL; - if (mHost.isInvalid()) - { - region = gAgent.getRegion(); - } - else if (LLWorld::instanceExists()) - { - region = LLWorld::getInstance()->getRegion(mHost); - } - + LLViewerRegion* region = getRegion(); if (region) { std::string http_url = region->getViewerAssetUrl(); @@ -1312,19 +1326,27 @@ bool LLTextureFetchWorker::doWork(S32 param) setUrl(http_url + "/?texture_id=" + mID.asString().c_str()); LL_DEBUGS(LOG_TXT) << "Texture URL: " << mUrl << LL_ENDL; mWriteToCacheState = CAN_WRITE ; //because this texture has a fixed texture id. + mCanUseCapability = true; + mRegionRetryAttempt = 0; + mLastRegionId = region->getRegionID(); } else { - mCanUseHTTP = false ; - LL_WARNS(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; + mCanUseCapability = false; + mRegionRetryAttempt++; + mRegionRetryTimer.setTimerExpirySec(CAP_MISSING_EXPIRATION_DELAY); + // ex: waiting for caps + LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: empty URL." << LL_ENDL; } } else { + mCanUseCapability = false; + mRegionRetryAttempt++; + mRegionRetryTimer.setTimerExpirySec(CAP_MISSING_EXPIRATION_DELAY); // This will happen if not logged in or if a region deoes not have HTTP Texture enabled //LL_WARNS(LOG_TXT) << "Region not found for host: " << mHost << LL_ENDL; - LL_WARNS(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; - mCanUseHTTP = false; + LL_INFOS_ONCE(LOG_TXT) << "Texture not available via HTTP: no region " << mUrl << LL_ENDL; } } else if (mFTType == FTT_SERVER_BAKE) @@ -1332,7 +1354,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mWriteToCacheState = CAN_WRITE; } - if (mCanUseHTTP && !mUrl.empty()) + if (mCanUseCapability && mCanUseHTTP && !mUrl.empty()) { setState(WAIT_HTTP_RESOURCE); if(mWriteToCacheState != NOT_WRITE) @@ -1534,10 +1556,37 @@ bool LLTextureFetchWorker::doWork(S32 param) } return true; } + + if (mCanUseHTTP && !mUrl.empty() && cur_size <= 0) + { + LLViewerRegion* region = getRegion(); + if (!region || mLastRegionId != region->getRegionID()) + { + // cap failure? try on new region. + mUrl.clear(); + ++mRetryAttempt; + mLastRegionId.setNull(); + setState(INIT); + return false; + } + } } else if (http_service_unavail == mGetStatus) { LL_INFOS_ONCE(LOG_TXT) << "Texture server busy (503): " << mUrl << LL_ENDL; + if (mCanUseHTTP && !mUrl.empty() && cur_size <= 0) + { + LLViewerRegion* region = getRegion(); + if (!region || mLastRegionId != region->getRegionID()) + { + // try on new region. + mUrl.clear(); + ++mRetryAttempt; + mLastRegionId.setNull(); + setState(INIT); + return false; + } + } } else if (http_not_sat == mGetStatus) { @@ -2128,7 +2177,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, LL_DEBUGS(LOG_TXT) << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; if (data_size > 0) { - LLViewerStatsRecorder::instance().textureFetch(data_size); // *TODO: set the formatted image data here directly to avoid the copy // Hold on to body for later copy @@ -2194,6 +2242,13 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, mHaveAllData = TRUE; } mRequestedSize = data_size; + + if (mHaveAllData) + { + LLViewerStatsRecorder::instance().textureFetch(); + } + + // *TODO: set the formatted image data here directly to avoid the copy } else { @@ -2202,11 +2257,6 @@ S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, mLoaded = TRUE; - if (LLViewerStatsRecorder::instanceExists()) - { - // Do not create this instance inside thread - LLViewerStatsRecorder::instance().log(0.2f); - } return data_size ; } @@ -3053,6 +3103,20 @@ void LLTextureFetchWorker::setState(e_state new_state) mState = new_state; } +LLViewerRegion* LLTextureFetchWorker::getRegion() +{ + LLViewerRegion* region = NULL; + if (mHost.isInvalid()) + { + region = gAgent.getRegion(); + } + else if (LLWorld::instanceExists()) + { + region = LLWorld::getInstance()->getRegion(mHost); + } + return region; +} + ////////////////////////////////////////////////////////////////////////////// // Threads: T* diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index ca93eb648b..62703e3499 100644 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -821,8 +821,8 @@ void LLTextureView::draw() if (imagep->getID() == LLAppViewer::getTextureFetch()->mDebugID) { - static S32 debug_count = 0; - ++debug_count; // for breakpoints +// static S32 debug_count = 0; +// ++debug_count; // for breakpoints } F32 pri; diff --git a/indra/newview/llthumbnailctrl.cpp b/indra/newview/llthumbnailctrl.cpp new file mode 100644 index 0000000000..04130fc724 --- /dev/null +++ b/indra/newview/llthumbnailctrl.cpp @@ -0,0 +1,239 @@ +/** + * @file llthumbnailctrl.cpp + * @brief LLThumbnailCtrl base class + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023, 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 "llthumbnailctrl.h" + +#include "linden_common.h" +#include "llagent.h" +#include "lluictrlfactory.h" +#include "lluuid.h" +#include "lltrans.h" +#include "llviewborder.h" +#include "llviewertexture.h" +#include "llviewertexturelist.h" +#include "llwindow.h" + +static LLDefaultChildRegistry::Register r("thumbnail"); + +LLThumbnailCtrl::Params::Params() +: border("border") +, border_color("border_color") +, fallback_image("fallback_image") +, image_name("image_name") +, border_visible("show_visible", false) +, interactable("interactable", false) +, show_loading("show_loading", true) +{} + +LLThumbnailCtrl::LLThumbnailCtrl(const LLThumbnailCtrl::Params& p) +: LLUICtrl(p) +, mBorderColor(p.border_color()) +, mBorderVisible(p.border_visible()) +, mFallbackImagep(p.fallback_image) +, mInteractable(p.interactable()) +, mShowLoadingPlaceholder(p.show_loading()) +, mPriority(LLGLTexture::BOOST_PREVIEW) +{ + mLoadingPlaceholderString = LLTrans::getString("texture_loading"); + + LLRect border_rect = getLocalRect(); + LLViewBorder::Params vbparams(p.border); + vbparams.name("border"); + vbparams.rect(border_rect); + mBorder = LLUICtrlFactory::create (vbparams); + addChild(mBorder); + + if (p.image_name.isProvided()) + { + setValue(p.image_name()); + } +} + +LLThumbnailCtrl::~LLThumbnailCtrl() +{ + mTexturep = nullptr; + mImagep = nullptr; + mFallbackImagep = nullptr; +} + + +void LLThumbnailCtrl::draw() +{ + LLRect draw_rect = getLocalRect(); + + if (mBorderVisible) + { + mBorder->setKeyboardFocusHighlight(hasFocus()); + + gl_rect_2d( draw_rect, mBorderColor.get(), FALSE ); + draw_rect.stretch( -1 ); + } + + // If we're in a focused floater, don't apply the floater's alpha to the texture. + const F32 alpha = getTransparencyType() == TT_ACTIVE ? 1.0f : getCurrentTransparency(); + if( mTexturep ) + { + if( mTexturep->getComponents() == 4 ) + { + const LLColor4 color(.098f, .098f, .098f); + gl_rect_2d( draw_rect, color, TRUE); + } + + gl_draw_scaled_image( draw_rect.mLeft, draw_rect.mBottom, draw_rect.getWidth(), draw_rect.getHeight(), mTexturep, UI_VERTEX_COLOR % alpha); + + mTexturep->setKnownDrawSize(draw_rect.getWidth(), draw_rect.getHeight()); + } + else if( mImagep.notNull() ) + { + mImagep->draw(draw_rect, UI_VERTEX_COLOR % alpha ); + } + else if (mFallbackImagep.notNull()) + { + if (draw_rect.getWidth() > mFallbackImagep->getWidth() + && draw_rect.getHeight() > mFallbackImagep->getHeight()) + { + S32 img_width = mFallbackImagep->getWidth(); + S32 img_height = mFallbackImagep->getHeight(); + S32 rect_width = draw_rect.getWidth(); + S32 rect_height = draw_rect.getHeight(); + + LLRect fallback_rect; + fallback_rect.mLeft = draw_rect.mLeft + (rect_width - img_width) / 2; + fallback_rect.mRight = fallback_rect.mLeft + img_width; + fallback_rect.mBottom = draw_rect.mBottom + (rect_height - img_height) / 2; + fallback_rect.mTop = fallback_rect.mBottom + img_height; + + mFallbackImagep->draw(fallback_rect, UI_VERTEX_COLOR % alpha); + } + else + { + mFallbackImagep->draw(draw_rect, UI_VERTEX_COLOR % alpha); + } + } + else + { + gl_rect_2d( draw_rect, LLColor4::grey % alpha, TRUE ); + + // Draw X + gl_draw_x( draw_rect, LLColor4::black ); + } + + // Show "Loading..." string on the top left corner while this texture is loading. + // Using the discard level, do not show the string if the texture is almost but not + // fully loaded. + if (mTexturep.notNull() + && mShowLoadingPlaceholder + && !mTexturep->isFullyLoaded()) + { + U32 v_offset = 25; + LLFontGL* font = LLFontGL::getFontSansSerif(); + + // Don't show as loaded if the texture is almost fully loaded (i.e. discard1) unless god + if ((mTexturep->getDiscardLevel() > 1) || gAgent.isGodlike()) + { + font->renderUTF8( + mLoadingPlaceholderString, + 0, + llfloor(draw_rect.mLeft+3), + llfloor(draw_rect.mTop-v_offset), + LLColor4::white, + LLFontGL::LEFT, + LLFontGL::BASELINE, + LLFontGL::DROP_SHADOW); + } + } + + LLUICtrl::draw(); +} + +void LLThumbnailCtrl::clearTexture() +{ + mImageAssetID = LLUUID::null; + mTexturep = nullptr; + mImagep = nullptr; +} + +// virtual +// value might be a string or a UUID +void LLThumbnailCtrl::setValue(const LLSD& value) +{ + LLSD tvalue(value); + if (value.isString() && LLUUID::validate(value.asString())) + { + //RN: support UUIDs masquerading as strings + tvalue = LLSD(LLUUID(value.asString())); + } + + LLUICtrl::setValue(tvalue); + + mImageAssetID = LLUUID::null; + mTexturep = nullptr; + mImagep = nullptr; + + if (tvalue.isUUID()) + { + mImageAssetID = tvalue.asUUID(); + if (mImageAssetID.notNull()) + { + // Should it support baked textures? + mTexturep = LLViewerTextureManager::getFetchedTexture(mImageAssetID, FTT_DEFAULT, MIPMAP_YES, LLGLTexture::BOOST_NONE, LLViewerTexture::LOD_TEXTURE); + + mTexturep->setBoostLevel(mPriority); + mTexturep->forceToSaveRawImage(0); + + S32 desired_draw_width = mTexturep->getWidth(); + S32 desired_draw_height = mTexturep->getHeight(); + + mTexturep->setKnownDrawSize(desired_draw_width, desired_draw_height); + } + } + else if (tvalue.isString()) + { + mImagep = LLUI::getUIImage(tvalue.asString(), LLGLTexture::BOOST_UI); + if (mImagep) + { + LLViewerFetchedTexture* texture = dynamic_cast(mImagep->getImage().get()); + if(texture) + { + mImageAssetID = texture->getID(); + } + } + } +} + +BOOL LLThumbnailCtrl::handleHover(S32 x, S32 y, MASK mask) +{ + if (mInteractable && getEnabled()) + { + getWindow()->setCursor(UI_CURSOR_HAND); + return TRUE; + } + return LLUICtrl::handleHover(x, y, mask); +} + + diff --git a/indra/newview/llthumbnailctrl.h b/indra/newview/llthumbnailctrl.h new file mode 100644 index 0000000000..686603b373 --- /dev/null +++ b/indra/newview/llthumbnailctrl.h @@ -0,0 +1,88 @@ +/** + * @file llthumbnailctrl.h + * @brief LLThumbnailCtrl base class + * + * $LicenseInfo:firstyear=2023&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2023 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_LLTHUMBNAILCTRL_H +#define LL_LLTHUMBNAILCTRL_H + +#include "llui.h" +#include "lluictrl.h" +#include "llviewborder.h" // for params + +class LLUICtrlFactory; +class LLUUID; +class LLViewerFetchedTexture; + +// +// Classes +// + +// +class LLThumbnailCtrl +: public LLUICtrl +{ +public: + struct Params : public LLInitParam::Block + { + Optional border; + Optional border_color; + Optional image_name; + Optional fallback_image; + Optional border_visible; + Optional interactable; + Optional show_loading; + + Params(); + }; +protected: + LLThumbnailCtrl(const Params&); + friend class LLUICtrlFactory; + +public: + virtual ~LLThumbnailCtrl(); + + virtual void draw() override; + + virtual void setValue(const LLSD& value ) override; + void clearTexture(); + + virtual BOOL handleHover(S32 x, S32 y, MASK mask) override; + +private: + S32 mPriority; + bool mBorderVisible; + bool mInteractable; + bool mShowLoadingPlaceholder; + std::string mLoadingPlaceholderString; + LLUUID mImageAssetID; + LLViewBorder* mBorder; + LLUIColor mBorderColor; + + LLPointer mTexturep; + LLPointer mImagep; + LLPointer mFallbackImagep; +}; + +#endif diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 692e8d91a9..d35833fac9 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -279,6 +279,10 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal if (!edit_text_name.empty()) { S32 y = VPAD + BTN_HEIGHT + VPAD/2; + if (form->getIgnoreType() != LLNotificationForm::IGNORE_NO) + { + y += EDITOR_HEIGHT; + } mLineEditor = LLUICtrlFactory::getInstance()->createFromFile("alert_line_editor.xml", this, LLPanel::child_registry_t::instance()); if (mLineEditor) @@ -522,6 +526,10 @@ void LLToastAlertPanel::onButtonPressed( const LLSD& data, S32 button ) { response[mLineEditor->getName()] = mLineEditor->getValue(); } + if (mNotification->getForm()->getIgnoreType() != LLNotificationForm::IGNORE_NO) + { + response["ignore"] = mNotification->isIgnored(); + } response[button_data->mButton->getName()] = true; // If we declared a URL and chose the URL option, go to the url diff --git a/indra/newview/lltoolbarview.cpp b/indra/newview/lltoolbarview.cpp index 4f47c465c4..f6628293ee 100644 --- a/indra/newview/lltoolbarview.cpp +++ b/indra/newview/lltoolbarview.cpp @@ -322,9 +322,9 @@ bool LLToolBarView::loadToolbars(bool force_default) } // SL-18581: Don't show the starter avatar toolbar button for NUX users - LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS)); if (gAgent.isFirstLogin()) { + LLViewerInventoryCategory* my_outfits_cat = gInventory.getCategory(gInventory.findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS)); LL_WARNS() << "First login: checking for NUX user." << LL_ENDL; if (my_outfits_cat != NULL && my_outfits_cat->getDescendentCount() > 0) { diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp index b8357b3454..aaf2bacc7b 100644 --- a/indra/newview/lltoolcomp.cpp +++ b/indra/newview/lltoolcomp.cpp @@ -268,7 +268,8 @@ BOOL LLToolCompTranslate::handleHover(S32 x, S32 y, MASK mask) BOOL LLToolCompTranslate::handleMouseDown(S32 x, S32 y, MASK mask) { mMouseDown = TRUE; - gViewerWindow->pickAsync(x, y, mask, pickCallback, /*BOOL pick_transparent*/ FALSE, LLFloaterReg::instanceVisible("build")); + gViewerWindow->pickAsync(x, y, mask, pickCallback, /*BOOL pick_transparent*/ FALSE, LLFloaterReg::instanceVisible("build"), FALSE, + gSavedSettings.getBOOL("SelectReflectionProbes"));; return TRUE; } diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 6633951db3..f1813c9d17 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -40,6 +40,7 @@ #include "llfloatertools.h" #include "llgesturemgr.h" #include "llgiveinventory.h" +#include "llgltfmateriallist.h" #include "llhudmanager.h" #include "llhudeffecttrail.h" #include "llimview.h" @@ -766,7 +767,7 @@ void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop, if (!handled) { // Disallow drag and drop to 3D from the marketplace - const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.notNull()) { for (S32 item_index = 0; item_index < (S32)mCargoIDs.size(); item_index++) @@ -929,6 +930,8 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, LLToolDragAndDrop::ESource source, const LLUUID& src_id) { + if (!item) return FALSE; + // Always succeed if.... // material is from the library // or already in the contents of the object @@ -947,7 +950,14 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, { hit_obj->requestInventory(); LLSD args; - args["ERROR_MESSAGE"] = "Unable to add texture.\nPlease wait a few seconds and try again."; + if (LLAssetType::AT_MATERIAL == item->getType()) + { + args["ERROR_MESSAGE"] = "Unable to add material.\nPlease wait a few seconds and try again."; + } + else + { + args["ERROR_MESSAGE"] = "Unable to add texture.\nPlease wait a few seconds and try again."; + } LLNotificationsUtil::add("ErrorMessage", args); return FALSE; } @@ -956,11 +966,9 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, // if the asset is already in the object's inventory // then it can always be added to a side. // This saves some work if the task's inventory is already loaded - // and ensures that the texture item is only added once. + // and ensures that the asset item is only added once. return TRUE; } - - if (!item) return FALSE; LLPointer new_item = new LLViewerInventoryItem(item); if (!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())) @@ -994,7 +1002,7 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, return FALSE; } } - // Add the texture item to the target object's inventory. + // Add the asset item to the target object's inventory. if (LLAssetType::AT_TEXTURE == new_item->getType() || LLAssetType::AT_MATERIAL == new_item->getType()) { @@ -1004,20 +1012,24 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, { hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); } - // TODO: Check to see if adding the item was successful; if not, then - // we should return false here. + // Force the object to update and refetch its inventory so it has this asset. + hit_obj->dirtyInventory(); + hit_obj->requestInventory(); + // TODO: Check to see if adding the item was successful; if not, then + // we should return false here. This will requre a separate listener + // since without listener, we have no way to receive update } else if (!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID())) { - // Check that we can add the texture as inventory to the object + // Check that we can add the asset as inventory to the object if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE ) { return FALSE; } // *FIX: may want to make sure agent can paint hit_obj. - // Add the texture item to the target object's inventory. + // Add the asset item to the target object's inventory. if (LLAssetType::AT_TEXTURE == new_item->getType() || LLAssetType::AT_MATERIAL == new_item->getType()) { @@ -1027,7 +1039,27 @@ BOOL LLToolDragAndDrop::handleDropMaterialProtections(LLViewerObject* hit_obj, { hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); } - // Force the object to update and refetch its inventory so it has this texture. + // Force the object to update and refetch its inventory so it has this asset. + hit_obj->dirtyInventory(); + hit_obj->requestInventory(); + // TODO: Check to see if adding the item was successful; if not, then + // we should return false here. This will requre a separate listener + // since without listener, we have no way to receive update + } + else if (LLAssetType::AT_MATERIAL == new_item->getType() && + !item->getPermissions().allowOperationBy(PERM_MODIFY, gAgent.getID())) + { + // Check that we can add the material as inventory to the object + if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE ) + { + return FALSE; + } + // *FIX: may want to make sure agent can paint hit_obj. + + // Add the material item to the target object's inventory. + hit_obj->updateMaterialInventory(new_item, TASK_INVENTORY_ITEM_KEY, true); + + // Force the object to update and refetch its inventory so it has this material. hit_obj->dirtyInventory(); hit_obj->requestInventory(); // TODO: Check to see if adding the item was successful; if not, then @@ -1095,6 +1127,9 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj, LL_WARNS() << "LLToolDragAndDrop::dropTextureOneFace no material item." << LL_ENDL; return; } + + // SL-20013 must save asset_id before handleDropMaterialProtections since our item instance + // may be deleted if it is moved into task inventory LLUUID asset_id = item->getAssetUUID(); BOOL success = handleDropMaterialProtections(hit_obj, item, source, src_id); if (!success) @@ -1102,6 +1137,12 @@ void LLToolDragAndDrop::dropMaterialOneFace(LLViewerObject* hit_obj, return; } + if (asset_id.isNull()) + { + // use blank material + asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } + hit_obj->setRenderMaterialID(hit_face, asset_id); dialog_refresh_all(); @@ -1121,13 +1162,23 @@ void LLToolDragAndDrop::dropMaterialAllFaces(LLViewerObject* hit_obj, LL_WARNS() << "LLToolDragAndDrop::dropTextureAllFaces no material item." << LL_ENDL; return; } + + // SL-20013 must save asset_id before handleDropMaterialProtections since our item instance + // may be deleted if it is moved into task inventory LLUUID asset_id = item->getAssetUUID(); BOOL success = handleDropMaterialProtections(hit_obj, item, source, src_id); + if (!success) { return; } + if (asset_id.isNull()) + { + // use blank material + asset_id = LLGLTFMaterialList::BLANK_MATERIAL_ASSET_ID; + } + hit_obj->setRenderMaterialIDs(asset_id); dialog_refresh_all(); // send the update to the simulator @@ -1597,6 +1648,8 @@ EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LL BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE; if(attached && !unrestricted) { + // Attachments are in world and in inventory simultaneously, + // at the moment server doesn't support such a situation. return ACCEPT_NO_LOCKED; } else if(modify && transfer && volume && !worn) @@ -1809,8 +1862,8 @@ EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv( return ACCEPT_NO; } - const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - if(gInventory.isObjectDescendentOf(item->getUUID(), outbox_id)) + const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX); + if(outbox_id.notNull() && gInventory.isObjectDescendentOf(item->getUUID(), outbox_id)) { // Legacy return ACCEPT_NO; @@ -2353,8 +2406,8 @@ EAcceptance LLToolDragAndDrop::dad3dWearCategory( return ACCEPT_NO; } - const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX, false); - if(gInventory.isObjectDescendentOf(category->getUUID(), outbox_id)) + const LLUUID &outbox_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_OUTBOX); + if(outbox_id.notNull() && gInventory.isObjectDescendentOf(category->getUUID(), outbox_id)) { // Legacy return ACCEPT_NO; diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 2c5b8ffae4..935d61f7ea 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -114,7 +114,7 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) mMouseDownY = y; LLTimer pick_timer; BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick"); - LLPickInfo transparent_pick = gViewerWindow->pickImmediate(x, y, TRUE /*includes transparent*/, pick_rigged); + LLPickInfo transparent_pick = gViewerWindow->pickImmediate(x, y, TRUE /*includes transparent*/, pick_rigged, FALSE, TRUE, FALSE); LLPickInfo visible_pick = gViewerWindow->pickImmediate(x, y, FALSE, pick_rigged); LLViewerObject *transp_object = transparent_pick.getObject(); LLViewerObject *visible_object = visible_pick.getObject(); @@ -183,11 +183,15 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) // an item. BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask) { + BOOL pick_reflection_probe = gSavedSettings.getBOOL("SelectReflectionProbes"); + // don't pick transparent so users can't "pay" transparent objects mPick = gViewerWindow->pickImmediate(x, y, /*BOOL pick_transparent*/ FALSE, /*BOOL pick_rigged*/ TRUE, - /*BOOL pick_particle*/ TRUE); + /*BOOL pick_particle*/ TRUE, + /*BOOL pick_unselectable*/ TRUE, + pick_reflection_probe); mPick.mKeyMask = mask; // claim not handled so UI focus stays same diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp index c6f3905ddc..f7455c6cb8 100644 --- a/indra/newview/lltoolselect.cpp +++ b/indra/newview/lltoolselect.cpp @@ -66,7 +66,9 @@ BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask) // do immediate pick query BOOL pick_rigged = false; //gSavedSettings.getBOOL("AnimatedObjectsAllowLeftClick"); BOOL pick_transparent = gSavedSettings.getBOOL("SelectInvisibleObjects"); - mPick = gViewerWindow->pickImmediate(x, y, pick_transparent, pick_rigged); + BOOL pick_reflection_probe = gSavedSettings.getBOOL("SelectReflectionProbes"); + + mPick = gViewerWindow->pickImmediate(x, y, pick_transparent, pick_rigged, FALSE, TRUE, pick_reflection_probe); // Pass mousedown to agent LLTool::handleMouseDown(x, y, mask); diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 34d3ed8bb1..c37c955e8d 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -41,8 +41,8 @@ #include "llurlregistry.h" -static const std::string BING_NOTRANSLATE_OPENING_TAG("
"); -static const std::string BING_NOTRANSLATE_CLOSING_TAG("
"); +static const std::string AZURE_NOTRANSLATE_OPENING_TAG("
"); +static const std::string AZURE_NOTRANSLATE_CLOSING_TAG("
"); /** * Handler of an HTTP machine translation service. @@ -80,7 +80,18 @@ public: * @param[in] key Key to verify. */ virtual std::string getKeyVerificationURL( - const std::string &key) const = 0; + const LLSD &key) const = 0; + + /** + * Check API verification response. + * + * @param[out] bool true if valid. + * @param[in] response + * @param[in] status + */ + virtual bool checkVerificationResponse( + const LLSD &response, + int status) const = 0; /** * Parse translation response. @@ -92,6 +103,7 @@ public: * @param[out] err_msg Error message (in case of error). */ virtual bool parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -105,14 +117,30 @@ public: virtual LLTranslate::EService getCurrentService() = 0; - virtual void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) = 0; + virtual void verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) = 0; virtual void translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); virtual ~LLTranslationAPIHandler() {} - void verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc); + void verifyKeyCoro(LLTranslate::EService service, LLSD key, LLTranslate::KeyVerificationResult_fn fnc); void translateMessageCoro(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure); + + virtual void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const = 0; + virtual void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD &key) const = 0; + virtual LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const = 0; + virtual LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const = 0; }; void LLTranslationAPIHandler::translateMessage(LanguagePair_t fromTo, std::string msg, LLTranslate::TranslationSuccess_fn success, LLTranslate::TranslationFailure_fn failure) @@ -122,8 +150,7 @@ void LLTranslationAPIHandler::translateMessage(LanguagePair_t fromTo, std::strin } - -void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std::string key, LLTranslate::KeyVerificationResult_fn fnc) +void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, LLSD key, LLTranslate::KeyVerificationResult_fn fnc) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t @@ -140,8 +167,7 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std:: LLVersionInfo::instance().getPatch(), LLVersionInfo::instance().getBuild()); - httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); - httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + initHttpHeader(httpHeaders, user_agent, key); httpOpts->setFollowRedirects(true); httpOpts->setSSLVerifyPeer(false); @@ -153,17 +179,29 @@ void LLTranslationAPIHandler::verifyKeyCoro(LLTranslate::EService service, std:: return; } - LLSD result = httpAdapter->getAndSuspend(httpRequest, url, httpOpts, httpHeaders); + std::string::size_type delim_pos = url.find("://"); + if (delim_pos == std::string::npos) + { + LL_INFOS("Translate") << "URL is missing a scheme" << LL_ENDL; + return; + } + + LLSD result = verifyAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); bool bOk = true; - if (!status) + int parseResult = status.getType(); + if (!checkVerificationResponse(httpResults, parseResult)) + { bOk = false; + } if (!fnc.empty()) - fnc(service, bOk); + { + fnc(service, bOk, parseResult); + } } void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::string msg, @@ -184,8 +222,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s LLVersionInfo::instance().getPatch(), LLVersionInfo::instance().getBuild()); - httpHeaders->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); - httpHeaders->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + initHttpHeader(httpHeaders, user_agent); httpOpts->setSSLVerifyPeer(false); std::string url = this->getTranslateURL(fromTo.first, fromTo.second, msg); @@ -195,7 +232,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s return; } - LLSD result = httpAdapter->getRawAndSuspend(httpRequest, url, httpOpts, httpHeaders); + LLSD result = sendMessageAndSuspend(httpAdapter, httpRequest, httpOpts, httpHeaders, url, msg, fromTo.first, fromTo.second); if (LLApp::isQuitting()) { @@ -216,7 +253,7 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s try { - res = this->parseResponse(parseResult, body, translation, detected_lang, err_msg); + res = this->parseResponse(httpResults, parseResult, body, translation, detected_lang, err_msg); } catch (std::out_of_range&) { @@ -242,6 +279,11 @@ void LLTranslationAPIHandler::translateMessageCoro(LanguagePair_t fromTo, std::s } else { + if (err_msg.empty() && httpResults.has("error_body")) + { + err_msg = httpResults["error_body"].asString(); + } + if (err_msg.empty()) { err_msg = LLTrans::getString("TranslationResponseParseError"); @@ -262,23 +304,44 @@ class LLGoogleTranslationHandler : public LLTranslationAPIHandler LOG_CLASS(LLGoogleTranslationHandler); public: - /*virtual*/ std::string getTranslateURL( + std::string getTranslateURL( const std::string &from_lang, const std::string &to_lang, - const std::string &text) const; - /*virtual*/ std::string getKeyVerificationURL( - const std::string &key) const; - /*virtual*/ bool parseResponse( + const std::string &text) const override; + std::string getKeyVerificationURL( + const LLSD &key) const override; + bool checkVerificationResponse( + const LLSD &response, + int status) const override; + bool parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, std::string& detected_lang, - std::string& err_msg) const; - /*virtual*/ bool isConfigured() const; + std::string& err_msg) const override; + bool isConfigured() const override; - /*virtual*/ LLTranslate::EService getCurrentService() { return LLTranslate::EService::SERVICE_GOOGLE; } + LLTranslate::EService getCurrentService() override { return LLTranslate::EService::SERVICE_GOOGLE; } - /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); + void verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) override; + + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD &key) const override; + LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const override; + + LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const override; private: static void parseErrorResponse( @@ -311,15 +374,24 @@ std::string LLGoogleTranslationHandler::getTranslateURL( // virtual std::string LLGoogleTranslationHandler::getKeyVerificationURL( - const std::string& key) const + const LLSD& key) const { - std::string url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") - + key + "&target=en"; + std::string url = std::string("https://www.googleapis.com/language/translate/v2/languages?key=") + + key.asString() +"&target=en"; return url; } +//virtual +bool LLGoogleTranslationHandler::checkVerificationResponse( + const LLSD &response, + int status) const +{ + return status == HTTP_OK; +} + // virtual bool LLGoogleTranslationHandler::parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -407,75 +479,196 @@ bool LLGoogleTranslationHandler::parseTranslation( // static std::string LLGoogleTranslationHandler::getAPIKey() { - return gSavedSettings.getString("GoogleTranslateAPIKey"); + static LLCachedControl google_key(gSavedSettings, "GoogleTranslateAPIKey"); + return google_key; } /*virtual*/ -void LLGoogleTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) +void LLGoogleTranslationHandler::verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) { LLCoros::instance().launch("Google /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, this, LLTranslate::SERVICE_GOOGLE, key, fnc)); } +/*virtual*/ +void LLGoogleTranslationHandler::initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const +{ + headers->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); +} + +/*virtual*/ +void LLGoogleTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent, + const LLSD &key) const +{ + headers->append(HTTP_OUT_HEADER_ACCEPT, HTTP_CONTENT_TEXT_PLAIN); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); +} + +LLSD LLGoogleTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const +{ + return adapter->getRawAndSuspend(request, url, options, headers); +} + +LLSD LLGoogleTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const +{ + return adapter->getAndSuspend(request, url, options, headers); +} //========================================================================= /// Microsoft Translator v2 API handler. -class LLBingTranslationHandler : public LLTranslationAPIHandler +class LLAzureTranslationHandler : public LLTranslationAPIHandler { - LOG_CLASS(LLBingTranslationHandler); + LOG_CLASS(LLAzureTranslationHandler); public: - /*virtual*/ std::string getTranslateURL( + std::string getTranslateURL( const std::string &from_lang, const std::string &to_lang, - const std::string &text) const; - /*virtual*/ std::string getKeyVerificationURL( - const std::string &key) const; - /*virtual*/ bool parseResponse( + const std::string &text) const override; + std::string getKeyVerificationURL( + const LLSD &key) const override; + bool checkVerificationResponse( + const LLSD &response, + int status) const override; + bool parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, std::string& detected_lang, - std::string& err_msg) const; - /*virtual*/ bool isConfigured() const; + std::string& err_msg) const override; + bool isConfigured() const override; - /*virtual*/ LLTranslate::EService getCurrentService() { return LLTranslate::EService::SERVICE_BING; } + LLTranslate::EService getCurrentService() override { return LLTranslate::EService::SERVICE_AZURE; } - /*virtual*/ void verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc); + void verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) override; + + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD &key) const override; + LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const override; + + LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const override; private: - static std::string getAPIKey(); + static std::string parseErrorResponse( + const std::string& body); + static LLSD getAPIKey(); static std::string getAPILanguageCode(const std::string& lang); }; //------------------------------------------------------------------------- // virtual -std::string LLBingTranslationHandler::getTranslateURL( +std::string LLAzureTranslationHandler::getTranslateURL( const std::string &from_lang, const std::string &to_lang, const std::string &text) const { - std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=") - + getAPIKey() + "&text=" + LLURI::escape(text) + "&to=" + getAPILanguageCode(to_lang); - if (!from_lang.empty()) - { - url += "&from=" + getAPILanguageCode(from_lang); - } + std::string url; + LLSD key = getAPIKey(); + if (key.isMap()) + { + std::string endpoint = key["endpoint"].asString(); + + if (*endpoint.rbegin() != '/') + { + endpoint += "/"; + } + url = endpoint + std::string("translate?api-version=3.0&to=") + + getAPILanguageCode(to_lang); + } return url; } // virtual -std::string LLBingTranslationHandler::getKeyVerificationURL( - const std::string& key) const +std::string LLAzureTranslationHandler::getKeyVerificationURL( + const LLSD& key) const { - std::string url = std::string("http://api.microsofttranslator.com/v2/Http.svc/GetLanguagesForTranslate?appId=") - + key; + std::string url; + if (key.isMap()) + { + std::string endpoint = key["endpoint"].asString(); + if (*endpoint.rbegin() != '/') + { + endpoint += "/"; + } + url = endpoint + std::string("translate?api-version=3.0&to=en"); + } return url; } +//virtual +bool LLAzureTranslationHandler::checkVerificationResponse( + const LLSD &response, + int status) const +{ + if (status == HTTP_UNAUTHORIZED) + { + LL_DEBUGS("Translate") << "Key unathorised" << LL_ENDL; + return false; + } + + if (status == HTTP_NOT_FOUND) + { + LL_DEBUGS("Translate") << "Either endpoint doesn't have requested resource" << LL_ENDL; + return false; + } + + if (status != HTTP_BAD_REQUEST) + { + LL_DEBUGS("Translate") << "Unexpected error code" << LL_ENDL; + return false; + } + + if (!response.has("error_body")) + { + LL_DEBUGS("Translate") << "Unexpected response, no error returned" << LL_ENDL; + return false; + } + + // Expected: "{\"error\":{\"code\":400000,\"message\":\"One of the request inputs is not valid.\"}}" + // But for now just verify response is a valid json + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(response["error_body"].asString(), root)) + { + LL_DEBUGS("Translate") << "Failed to parse error_body:" << reader.getFormatedErrorMessages() << LL_ENDL; + return false; + } + + return true; +} + // virtual -bool LLBingTranslationHandler::parseResponse( +bool LLAzureTranslationHandler::parseResponse( + const LLSD& http_response, int& status, const std::string& body, std::string& translation, @@ -484,65 +677,443 @@ bool LLBingTranslationHandler::parseResponse( { if (status != HTTP_OK) { - static const std::string MSG_BEGIN_MARKER = "Message: "; - size_t begin = body.find(MSG_BEGIN_MARKER); - if (begin != std::string::npos) - { - begin += MSG_BEGIN_MARKER.size(); - } - else - { - begin = 0; - err_msg.clear(); - } - size_t end = body.find("

", begin); - err_msg = body.substr(begin, end-begin); - LLStringUtil::replaceString(err_msg, " ", ""); // strip CR + if (http_response.has("error_body")) + err_msg = parseErrorResponse(http_response["error_body"].asString()); return false; } - // Sample response: Hola - size_t begin = body.find(">"); - if (begin == std::string::npos || begin >= (body.size() - 1)) - { - begin = 0; - } - else - { - ++begin; - } + //Example: + // "[{\"detectedLanguage\":{\"language\":\"en\",\"score\":1.0},\"translations\":[{\"text\":\"Hello, what is your name?\",\"to\":\"en\"}]}]" - size_t end = body.find("
", begin); + Json::Value root; + Json::Reader reader; - detected_lang = ""; // unsupported by this API - translation = body.substr(begin, end-begin); - LLStringUtil::replaceString(translation, " ", ""); // strip CR - return true; + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; + } + + if (!root.isArray()) // empty response? should not happen + { + return false; + } + + // Request succeeded, extract translation from the response. + + const Json::Value& data = root[0U]; + if (!data.isObject() + || !data.isMember("detectedLanguage") + || !data.isMember("translations")) + { + return false; + } + + const Json::Value& detectedLanguage = data["detectedLanguage"]; + if (!detectedLanguage.isObject() || !detectedLanguage.isMember("language")) + { + return false; + } + detected_lang = detectedLanguage["language"].asString(); + + const Json::Value& translations = data["translations"]; + if (!translations.isArray() || translations.size() == 0) + { + return false; + } + + const Json::Value& first = translations[0U]; + if (!first.isObject() || !first.isMember("text")) + { + return false; + } + + translation = first["text"].asString(); + + return true; } // virtual -bool LLBingTranslationHandler::isConfigured() const +bool LLAzureTranslationHandler::isConfigured() const { - return !getAPIKey().empty(); + return getAPIKey().isMap(); +} + +//static +std::string LLAzureTranslationHandler::parseErrorResponse( + const std::string& body) +{ + // Expected: "{\"error\":{\"code\":400000,\"message\":\"One of the request inputs is not valid.\"}}" + // But for now just verify response is a valid json with an error + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + return std::string(); + } + + if (!root.isObject() || !root.isMember("error")) + { + return std::string(); + } + + const Json::Value& error_map = root["error"]; + + if (!error_map.isObject() || !error_map.isMember("message")) + { + return std::string(); + } + + return error_map["message"].asString(); } // static -std::string LLBingTranslationHandler::getAPIKey() +LLSD LLAzureTranslationHandler::getAPIKey() { - return gSavedSettings.getString("BingTranslateAPIKey"); + static LLCachedControl azure_key(gSavedSettings, "AzureTranslateAPIKey"); + return azure_key; } // static -std::string LLBingTranslationHandler::getAPILanguageCode(const std::string& lang) +std::string LLAzureTranslationHandler::getAPILanguageCode(const std::string& lang) { return lang == "zh" ? "zh-CHT" : lang; // treat Chinese as Traditional Chinese } /*virtual*/ -void LLBingTranslationHandler::verifyKey(const std::string &key, LLTranslate::KeyVerificationResult_fn fnc) +void LLAzureTranslationHandler::verifyKey(const LLSD &key, LLTranslate::KeyVerificationResult_fn fnc) { - LLCoros::instance().launch("Bing /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, - this, LLTranslate::SERVICE_BING, key, fnc)); + LLCoros::instance().launch("Azure /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, + this, LLTranslate::SERVICE_AZURE, key, fnc)); +} +/*virtual*/ +void LLAzureTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent) const +{ + initHttpHeader(headers, user_agent, getAPIKey()); +} + +/*virtual*/ +void LLAzureTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent, + const LLSD &key) const +{ + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, HTTP_CONTENT_JSON); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + + if (key.has("id")) + { + // Token based autorization + headers->append("Ocp-Apim-Subscription-Key", key["id"].asString()); + } + if (key.has("region")) + { + // ex: "westeurope" + headers->append("Ocp-Apim-Subscription-Region", key["region"].asString()); + } +} + +LLSD LLAzureTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url, + const std::string & msg, + const std::string& from_lang, + const std::string& to_lang) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "[{\"text\":\""; + outs << msg; + outs << "\"}]"; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); +} + +LLSD LLAzureTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string & url) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "[{\"intentionally_invalid_400\"}]"; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); +} + +//========================================================================= +/// DeepL Translator API handler. +class LLDeepLTranslationHandler: public LLTranslationAPIHandler +{ + LOG_CLASS(LLDeepLTranslationHandler); + +public: + std::string getTranslateURL( + const std::string& from_lang, + const std::string& to_lang, + const std::string& text) const override; + std::string getKeyVerificationURL( + const LLSD& key) const override; + bool checkVerificationResponse( + const LLSD& response, + int status) const override; + bool parseResponse( + const LLSD& http_response, + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const override; + bool isConfigured() const override; + + LLTranslate::EService getCurrentService() override + { + return LLTranslate::EService::SERVICE_DEEPL; + } + + void verifyKey(const LLSD& key, LLTranslate::KeyVerificationResult_fn fnc) override; + + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent) const override; + void initHttpHeader(LLCore::HttpHeaders::ptr_t headers, const std::string& user_agent, const LLSD& key) const override; + LLSD sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url, + const std::string& msg, + const std::string& from_lang, + const std::string& to_lang) const override; + + LLSD verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url) const override; +private: + static std::string parseErrorResponse( + const std::string& body); + static LLSD getAPIKey(); + static std::string getAPILanguageCode(const std::string& lang); +}; + +//------------------------------------------------------------------------- +// virtual +std::string LLDeepLTranslationHandler::getTranslateURL( + const std::string& from_lang, + const std::string& to_lang, + const std::string& text) const +{ + std::string url; + LLSD key = getAPIKey(); + if (key.isMap()) + { + url = key["domain"].asString(); + + if (*url.rbegin() != '/') + { + url += "/"; + } + url += std::string("v2/translate"); + } + return url; +} + + +// virtual +std::string LLDeepLTranslationHandler::getKeyVerificationURL( + const LLSD& key) const +{ + std::string url; + if (key.isMap()) + { + url = key["domain"].asString(); + + if (*url.rbegin() != '/') + { + url += "/"; + } + url += std::string("v2/translate"); + } + return url; +} + +//virtual +bool LLDeepLTranslationHandler::checkVerificationResponse( + const LLSD& response, + int status) const +{ + // Might need to parse body to make sure we got + // a valid response and not a message + return status == HTTP_OK; +} + +// virtual +bool LLDeepLTranslationHandler::parseResponse( + const LLSD& http_response, + int& status, + const std::string& body, + std::string& translation, + std::string& detected_lang, + std::string& err_msg) const +{ + if (status != HTTP_OK) + { + if (http_response.has("error_body")) + err_msg = parseErrorResponse(http_response["error_body"].asString()); + return false; + } + + //Example: + // "{\"translations\":[{\"detected_source_language\":\"EN\",\"text\":\"test\"}]}" + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + err_msg = reader.getFormatedErrorMessages(); + return false; + } + + if (!root.isObject() + || !root.isMember("translations")) // empty response? should not happen + { + return false; + } + + // Request succeeded, extract translation from the response. + const Json::Value& translations = root["translations"]; + if (!translations.isArray() || translations.size() == 0) + { + return false; + } + + const Json::Value& data= translations[0U]; + if (!data.isObject() + || !data.isMember("detected_source_language") + || !data.isMember("text")) + { + return false; + } + + detected_lang = data["detected_source_language"].asString(); + LLStringUtil::toLower(detected_lang); + translation = data["text"].asString(); + + return true; +} + +// virtual +bool LLDeepLTranslationHandler::isConfigured() const +{ + return getAPIKey().isMap(); +} + +//static +std::string LLDeepLTranslationHandler::parseErrorResponse( + const std::string& body) +{ + // Example: "{\"message\":\"One of the request inputs is not valid.\"}" + + Json::Value root; + Json::Reader reader; + + if (!reader.parse(body, root)) + { + return std::string(); + } + + if (!root.isObject() || !root.isMember("message")) + { + return std::string(); + } + + return root["message"].asString(); +} + +// static +LLSD LLDeepLTranslationHandler::getAPIKey() +{ + static LLCachedControl deepl_key(gSavedSettings, "DeepLTranslateAPIKey"); + return deepl_key; +} + +// static +std::string LLDeepLTranslationHandler::getAPILanguageCode(const std::string& lang) +{ + return lang == "zh" ? "zh-CHT" : lang; // treat Chinese as Traditional Chinese +} + +/*virtual*/ +void LLDeepLTranslationHandler::verifyKey(const LLSD& key, LLTranslate::KeyVerificationResult_fn fnc) +{ + LLCoros::instance().launch("DeepL /Verify Key", boost::bind(&LLTranslationAPIHandler::verifyKeyCoro, + this, LLTranslate::SERVICE_DEEPL, key, fnc)); +} +/*virtual*/ +void LLDeepLTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent) const +{ + initHttpHeader(headers, user_agent, getAPIKey()); +} + +/*virtual*/ +void LLDeepLTranslationHandler::initHttpHeader( + LLCore::HttpHeaders::ptr_t headers, + const std::string& user_agent, + const LLSD& key) const +{ + headers->append(HTTP_OUT_HEADER_CONTENT_TYPE, "application/x-www-form-urlencoded"); + headers->append(HTTP_OUT_HEADER_USER_AGENT, user_agent); + + if (key.has("id")) + { + std::string authkey = "DeepL-Auth-Key " + key["id"].asString(); + headers->append(HTTP_OUT_HEADER_AUTHORIZATION, authkey); + } +} + +LLSD LLDeepLTranslationHandler::sendMessageAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url, + const std::string& msg, + const std::string& from_lang, + const std::string& to_lang) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "text="; + std::string escaped_string = LLURI::escape(msg); + outs << escaped_string; + outs << "&target_lang="; + std::string lang = to_lang; + LLStringUtil::toUpper(lang); + outs << lang; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); +} + +LLSD LLDeepLTranslationHandler::verifyAndSuspend(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t adapter, + LLCore::HttpRequest::ptr_t request, + LLCore::HttpOptions::ptr_t options, + LLCore::HttpHeaders::ptr_t headers, + const std::string& url) const +{ + LLCore::BufferArray::ptr_t rawbody(new LLCore::BufferArray); + LLCore::BufferArrayStream outs(rawbody.get()); + outs << "text=&target_lang=EN"; + + return adapter->postRawAndSuspend(request, url, rawbody, options, headers); } //========================================================================= @@ -569,58 +1140,78 @@ void LLTranslate::translateMessage(const std::string &from_lang, const std::stri std::string LLTranslate::addNoTranslateTags(std::string mesg) { - if (getPreferredHandler().getCurrentService() != SERVICE_BING) + if (getPreferredHandler().getCurrentService() == SERVICE_GOOGLE) { return mesg; } - std::string upd_msg(mesg); - LLUrlMatch match; - S32 dif = 0; - //surround all links (including SLURLs) with 'no-translate' tags to prevent unnecessary translation - while (LLUrlRegistry::instance().findUrl(mesg, match)) + if (getPreferredHandler().getCurrentService() == SERVICE_DEEPL) { - upd_msg.insert(dif + match.getStart(), BING_NOTRANSLATE_OPENING_TAG); - upd_msg.insert(dif + BING_NOTRANSLATE_OPENING_TAG.size() + match.getEnd() + 1, BING_NOTRANSLATE_CLOSING_TAG); - mesg.erase(match.getStart(), match.getEnd() - match.getStart()); - dif += match.getEnd() - match.getStart() + BING_NOTRANSLATE_OPENING_TAG.size() + BING_NOTRANSLATE_CLOSING_TAG.size(); + return mesg; } - return upd_msg; + + if (getPreferredHandler().getCurrentService() == SERVICE_AZURE) + { + // https://learn.microsoft.com/en-us/azure/cognitive-services/translator/prevent-translation + std::string upd_msg(mesg); + LLUrlMatch match; + S32 dif = 0; + //surround all links (including SLURLs) with 'no-translate' tags to prevent unnecessary translation + while (LLUrlRegistry::instance().findUrl(mesg, match)) + { + upd_msg.insert(dif + match.getStart(), AZURE_NOTRANSLATE_OPENING_TAG); + upd_msg.insert(dif + AZURE_NOTRANSLATE_OPENING_TAG.size() + match.getEnd() + 1, AZURE_NOTRANSLATE_CLOSING_TAG); + mesg.erase(match.getStart(), match.getEnd() - match.getStart()); + dif += match.getEnd() - match.getStart() + AZURE_NOTRANSLATE_OPENING_TAG.size() + AZURE_NOTRANSLATE_CLOSING_TAG.size(); + } + return upd_msg; + } + return mesg; } std::string LLTranslate::removeNoTranslateTags(std::string mesg) { - if (getPreferredHandler().getCurrentService() != SERVICE_BING) + if (getPreferredHandler().getCurrentService() == SERVICE_GOOGLE) { return mesg; } - std::string upd_msg(mesg); - LLUrlMatch match; - S32 opening_tag_size = BING_NOTRANSLATE_OPENING_TAG.size(); - S32 closing_tag_size = BING_NOTRANSLATE_CLOSING_TAG.size(); - S32 dif = 0; - //remove 'no-translate' tags we added to the links before - while (LLUrlRegistry::instance().findUrl(mesg, match)) + if (getPreferredHandler().getCurrentService() == SERVICE_DEEPL) { - if (upd_msg.substr(dif + match.getStart() - opening_tag_size, opening_tag_size) == BING_NOTRANSLATE_OPENING_TAG) - { - upd_msg.erase(dif + match.getStart() - opening_tag_size, opening_tag_size); - dif -= opening_tag_size; - - if (upd_msg.substr(dif + match.getEnd() + 1, closing_tag_size) == BING_NOTRANSLATE_CLOSING_TAG) - { - upd_msg.replace(dif + match.getEnd() + 1, closing_tag_size, " "); - dif -= closing_tag_size - 1; - } - } - mesg.erase(match.getStart(), match.getUrl().size()); - dif += match.getUrl().size(); + return mesg; } - return upd_msg; + + if (getPreferredHandler().getCurrentService() == SERVICE_AZURE) + { + std::string upd_msg(mesg); + LLUrlMatch match; + S32 opening_tag_size = AZURE_NOTRANSLATE_OPENING_TAG.size(); + S32 closing_tag_size = AZURE_NOTRANSLATE_CLOSING_TAG.size(); + S32 dif = 0; + //remove 'no-translate' tags we added to the links before + while (LLUrlRegistry::instance().findUrl(mesg, match)) + { + if (upd_msg.substr(dif + match.getStart() - opening_tag_size, opening_tag_size) == AZURE_NOTRANSLATE_OPENING_TAG) + { + upd_msg.erase(dif + match.getStart() - opening_tag_size, opening_tag_size); + dif -= opening_tag_size; + + if (upd_msg.substr(dif + match.getEnd() + 1, closing_tag_size) == AZURE_NOTRANSLATE_CLOSING_TAG) + { + upd_msg.replace(dif + match.getEnd() + 1, closing_tag_size, " "); + dif -= closing_tag_size - 1; + } + } + mesg.erase(match.getStart(), match.getUrl().size()); + dif += match.getUrl().size(); + } + return upd_msg; + } + + return mesg; } /*static*/ -void LLTranslate::verifyKey(EService service, const std::string &key, KeyVerificationResult_fn fnc) +void LLTranslate::verifyKey(EService service, const LLSD &key, KeyVerificationResult_fn fnc) { LLTranslationAPIHandler& handler = getHandler(service); @@ -686,13 +1277,21 @@ LLSD LLTranslate::asLLSD() const // static LLTranslationAPIHandler& LLTranslate::getPreferredHandler() { - EService service = SERVICE_BING; + EService service = SERVICE_AZURE; std::string service_str = gSavedSettings.getString("TranslationService"); if (service_str == "google") { service = SERVICE_GOOGLE; } + if (service_str == "azure") + { + service = SERVICE_AZURE; + } + if (service_str == "deepl") + { + service = SERVICE_DEEPL; + } return getHandler(service); } @@ -701,12 +1300,19 @@ LLTranslationAPIHandler& LLTranslate::getPreferredHandler() LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; - static LLBingTranslationHandler bing; + static LLAzureTranslationHandler azure; + static LLDeepLTranslationHandler deepl; - if (service == SERVICE_GOOGLE) - { - return google; - } + switch (service) + { + case SERVICE_AZURE: + return azure; + case SERVICE_GOOGLE: + return google; + case SERVICE_DEEPL: + return deepl; + } + + return azure; - return bing; } diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index 58707e2d36..4a5d80737c 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -59,11 +59,12 @@ class LLTranslate: public LLSingleton public : typedef enum e_service { - SERVICE_BING, + SERVICE_AZURE, SERVICE_GOOGLE, + SERVICE_DEEPL, } EService; - typedef boost::function KeyVerificationResult_fn; + typedef boost::function KeyVerificationResult_fn; typedef boost::function TranslationSuccess_fn; typedef boost::function TranslationFailure_fn; @@ -78,12 +79,12 @@ public : static void translateMessage(const std::string &from_lang, const std::string &to_lang, const std::string &mesg, TranslationSuccess_fn success, TranslationFailure_fn failure); /** - * Verify given API key of a translation service. - * - * @param receiver Object to pass verification result to. - * @param key Key to verify. - */ - static void verifyKey(EService service, const std::string &key, KeyVerificationResult_fn fnc); + * Verify given API key of a translation service. + * + * @param receiver Object to pass verification result to. + * @param key Key to verify. + */ + static void verifyKey(EService service, const LLSD &key, KeyVerificationResult_fn fnc); /** * @return translation target language diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 0da769999b..76fb138768 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -97,6 +97,8 @@ private: // Called by LLWorldMap when a region name has been resolved to a // location in-world, used by places-panel display. + static bool handleGrid(const LLSLURL& slurl); + friend class LLTeleportHandler; }; @@ -155,7 +157,7 @@ bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, LL_INFOS() << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << LL_ENDL; const LLSD& query_map = LLURI::queryMap(slurl.getAppQuery()); bool handled = LLCommandDispatcher::dispatch( - slurl.getAppCmd(), slurl.getAppPath(), query_map, web, nav_type, trusted_browser); + slurl.getAppCmd(), slurl.getAppPath(), query_map, slurl.getGrid(), web, nav_type, trusted_browser); // alert if we didn't handle this secondlife:///app/ SLURL // (but still return true because it is a valid app SLURL) @@ -184,6 +186,11 @@ bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string return true; } + if (!handleGrid(slurl)) + { + return true; + } + // Request a region handle by name LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), LLURLDispatcherImpl::regionNameCallback, @@ -202,31 +209,39 @@ void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& s } } +bool LLURLDispatcherImpl::handleGrid(const LLSLURL& slurl) +{ + if (LLGridManager::getInstance()->getGrid(slurl.getGrid()) + != LLGridManager::getInstance()->getGrid()) + { + LLSD args; + args["SLURL"] = slurl.getLocationString(); + args["CURRENT_GRID"] = LLGridManager::getInstance()->getGridLabel(); + std::string grid_label = + LLGridManager::getInstance()->getGridLabel(slurl.getGrid()); + + if (!grid_label.empty()) + { + args["GRID"] = grid_label; + } + else + { + args["GRID"] = slurl.getGrid(); + } + LLNotificationsUtil::add("CantTeleportToGrid", args); + return false; + } + return true; +} + /* static */ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - - // we can't teleport cross grid at this point - if( LLGridManager::getInstance()->getGrid(slurl.getGrid()) - != LLGridManager::getInstance()->getGrid()) - { - LLSD args; - args["SLURL"] = slurl.getLocationString(); - args["CURRENT_GRID"] = LLGridManager::getInstance()->getGridLabel(); - std::string grid_label = - LLGridManager::getInstance()->getGridLabel(slurl.getGrid()); - - if(!grid_label.empty()) - { - args["GRID"] = grid_label; - } - else - { - args["GRID"] = slurl.getGrid(); - } - LLNotificationsUtil::add("CantTeleportToGrid", args); - return; - } + if (!handleGrid(slurl)) + { + // we can't teleport cross grid at this point + return; + } LLVector3d global_pos = from_region_handle(region_handle); global_pos += LLVector3d(slurl.getPosition()); @@ -274,8 +289,10 @@ public: &LLTeleportHandler::from_event); } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& tokens, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { // construct a "normal" SLURL, resolve the region to // a global position, and teleport to it @@ -297,7 +314,7 @@ public: LLSD payload; payload["region_name"] = region_name; - payload["callback_url"] = LLSLURL(region_name, coords).getSLURLString(); + payload["callback_url"] = LLSLURL(grid, region_name, coords).getSLURLString(); LLNotificationsUtil::add("TeleportViaSLAPP", args, payload); return true; diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index cc73f7ca80..6a0edbecb1 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -91,17 +91,18 @@ void LLViewerAudio::startInternetStreamWithAutoFade(const std::string &streamURI return; } - // Record the URI we are going to be switching to + if (!gAudiop) + { + LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; + return; + } + + // Record the URI we are going to be switching to mNextStreamURI = streamURI; switch (mFadeState) { case FADE_IDLE: - if (!gAudiop) - { - LL_WARNS("AudioEngine") << "LLAudioEngine instance doesn't exist!" << LL_ENDL; - break; - } // If a stream is playing fade it out first if (!gAudiop->getInternetStreamURL().empty()) { @@ -114,28 +115,28 @@ void LLViewerAudio::startInternetStreamWithAutoFade(const std::string &streamURI mFadeState = FADE_IN; LLStreamingAudioInterface *stream = gAudiop->getStreamingAudioImpl(); - if(stream && stream->supportsAdjustableBufferSizes()) - stream->setBufferSizes(gSavedSettings.getU32("FMODExStreamBufferSize"),gSavedSettings.getU32("FMODExDecodeBufferSize")); + if (stream && stream->supportsAdjustableBufferSizes()) + stream->setBufferSizes(gSavedSettings.getU32("FMODExStreamBufferSize"), gSavedSettings.getU32("FMODExDecodeBufferSize")); gAudiop->startInternetStream(mNextStreamURI); - startFading(); - registerIdleListener(); - break; } + startFading(); + break; + case FADE_OUT: startFading(); - registerIdleListener(); break; case FADE_IN: - registerIdleListener(); break; default: LL_WARNS() << "Unknown fading state: " << mFadeState << LL_ENDL; - break; + return; } + + registerIdleListener(); } // A return of false from onIdleUpdate means it will be called again next idle update. @@ -236,15 +237,12 @@ void LLViewerAudio::startFading() // This minimum fade time prevents divide by zero and negative times const F32 AUDIO_MUSIC_MINIMUM_FADE_TIME = 0.01f; - if(mDone) + if (mDone) { // The fade state here should only be one of FADE_IN or FADE_OUT, but, in case it is not, // rather than check for both states assume a fade in and check for the fade out case. - mFadeTime = AUDIO_MUSIC_FADE_IN_TIME; - if (LLViewerAudio::getInstance()->getFadeState() == LLViewerAudio::FADE_OUT) - { - mFadeTime = AUDIO_MUSIC_FADE_OUT_TIME; - } + mFadeTime = LLViewerAudio::getInstance()->getFadeState() == LLViewerAudio::FADE_OUT ? + AUDIO_MUSIC_FADE_OUT_TIME : AUDIO_MUSIC_FADE_IN_TIME; // Prevent invalid fade time mFadeTime = llmax(mFadeTime, AUDIO_MUSIC_MINIMUM_FADE_TIME); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index b6f01f2843..ba1add9b92 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -428,12 +428,6 @@ static bool handleRenderDynamicLODChanged(const LLSD& newvalue) return true; } -static bool handleRenderLocalLightsChanged(const LLSD& newvalue) -{ - gPipeline.setLightingDetail(-1); - return true; -} - static bool handleReflectionProbeDetailChanged(const LLSD& newvalue) { if (gPipeline.isInit()) @@ -728,6 +722,7 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "RenderUIBuffer", handleWindowResized); setting_setup_signal_listener(gSavedSettings, "RenderDepthOfField", handleReleaseGLBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderFSAASamples", handleReleaseGLBufferChanged); + setting_setup_signal_listener(gSavedSettings, "RenderPostProcessingHDR", handleReleaseGLBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderSpecularResX", handleLUTBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderSpecularResY", handleLUTBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderSpecularExponent", handleLUTBufferChanged); @@ -736,6 +731,8 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "RenderGlow", handleReleaseGLBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderGlow", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "RenderGlowResolutionPow", handleReleaseGLBufferChanged); + setting_setup_signal_listener(gSavedSettings, "RenderGlowHDR", handleReleaseGLBufferChanged); + setting_setup_signal_listener(gSavedSettings, "RenderGlowNoise", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "RenderGammaFull", handleSetShaderChanged); setting_setup_signal_listener(gSavedSettings, "RenderVolumeLODFactor", handleVolumeLODChanged); setting_setup_signal_listener(gSavedSettings, "RenderAvatarLODFactor", handleAvatarLODChanged); @@ -747,7 +744,6 @@ void settings_setup_listeners() setting_setup_signal_listener(gSavedSettings, "RenderFogRatio", handleFogRatioChanged); setting_setup_signal_listener(gSavedSettings, "RenderMaxPartCount", handleMaxPartCountChanged); setting_setup_signal_listener(gSavedSettings, "RenderDynamicLOD", handleRenderDynamicLODChanged); - setting_setup_signal_listener(gSavedSettings, "RenderLocalLights", handleRenderLocalLightsChanged); setting_setup_signal_listener(gSavedSettings, "RenderVSyncEnable", handleVSyncChanged); setting_setup_signal_listener(gSavedSettings, "RenderDeferredNoise", handleReleaseGLBufferChanged); setting_setup_signal_listener(gSavedSettings, "RenderDebugPipeline", handleRenderDebugPipelineChanged); diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index ca87ff0092..6da0e9d695 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -56,6 +56,7 @@ #include "llfloaterbvhpreview.h" #include "llfloatercamera.h" #include "llfloatercamerapresets.h" +#include "llfloaterchangeitemthumbnail.h" #include "llfloaterchatvoicevolume.h" #include "llfloaterclassified.h" #include "llfloaterconversationlog.h" @@ -84,6 +85,7 @@ #include "llfloaterimagepreview.h" #include "llfloaterimsession.h" #include "llfloaterinspect.h" +#include "llfloaterinventorysettings.h" #include "llfloaterjoystick.h" #include "llfloaterlagmeter.h" #include "llfloaterland.h" @@ -98,12 +100,12 @@ #include "llfloatermyscripts.h" #include "llfloatermyenvironment.h" #include "llfloaternamedesc.h" +#include "llfloaternewfeaturenotification.h" #include "llfloaternotificationsconsole.h" #include "llfloaternotificationstabbed.h" #include "llfloaterobjectweights.h" #include "llfloateropenobject.h" -#include "llfloateroutfitphotopreview.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h" #include "llfloaterpathfindingcharacters.h" #include "llfloaterpathfindingconsole.h" #include "llfloaterpathfindinglinksets.h" @@ -116,7 +118,6 @@ #include "llfloaterpreferenceviewadvanced.h" #include "llfloaterpreviewtrash.h" #include "llfloaterprofile.h" -#include "llfloaterproperties.h" #include "llfloaterregiondebugconsole.h" #include "llfloaterregioninfo.h" #include "llfloaterregionrestarting.h" @@ -229,6 +230,7 @@ public: "avatar_picker", "camera", "camera_presets", + "change_item_thumbnail" "classified", "add_landmark", "delete_pref_preset", @@ -247,6 +249,7 @@ public: "message_critical", // Modal!!! Login specific. If this is in use elsewhere, better to create a non modal variant "message_tos", // Modal!!! Login specific. "mute_object_by_name", + "new_feature_notification", "publish_classified", "save_pref_preset", "save_camera_preset", @@ -272,6 +275,7 @@ public: bool handle( const LLSD& params, const LLSD& query_map, + const std::string& grid, LLMediaCtrl* web) override { if (params.size() != 1) @@ -329,6 +333,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); 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("change_item_thumbnail", "floater_change_item_thumbnail.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); @@ -372,6 +377,8 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("inventory", "floater_my_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("inspect", "floater_inspect.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("item_properties", "floater_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("task_properties", "floater_task_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("inventory_settings", "floater_inventory_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLInspectAvatarUtil::registerFloater(); LLInspectGroupUtil::registerFloater(); LLInspectObjectUtil::registerFloater(); @@ -395,6 +402,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("mute_object_by_name", "floater_mute_object.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("new_feature_notification", "floater_new_feature_notification.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("notifications_console", "floater_notifications_console.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -403,7 +411,6 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("object_weights", "floater_object_weights.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("openobject", "floater_openobject.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("outgoing_call", "floater_outgoing_call.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("outfit_photo_preview", "floater_outfit_photo_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterPayUtil::registerFloater(); LLFloaterReg::add("pathfinding_characters", "floater_pathfinding_characters.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -431,7 +438,6 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("preview_sound", "floater_preview_sound.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_texture", "floater_preview_texture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build, "preview"); LLFloaterReg::add("preview_trash", "floater_preview_trash.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("properties", "floater_inventory_item_properties.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("publish_classified", "floater_publish_classified.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("save_pref_preset", "floater_save_pref_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("save_camera_preset", "floater_save_camera_preset.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); @@ -468,7 +474,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("scene_load_stats", "floater_scene_load_stats.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("stop_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("snapshot", "floater_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - LLFloaterReg::add("simple_outfit_snapshot", "floater_simple_outfit_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("simple_snapshot", "floater_simple_snapshot.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("search", "floater_search.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("profile", "floater_profile.xml",(LLFloaterBuildFunc)&LLFloaterReg::build); LLFloaterReg::add("guidebook", "floater_how_to.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); diff --git a/indra/newview/llviewergenericmessage.cpp b/indra/newview/llviewergenericmessage.cpp index d3de9d72bf..aaa1313ff6 100644 --- a/indra/newview/llviewergenericmessage.cpp +++ b/indra/newview/llviewergenericmessage.cpp @@ -32,9 +32,10 @@ #include "lldispatcher.h" #include "lluuid.h" #include "message.h" +#include "llgenericstreamingmessage.h" #include "llagent.h" - +#include "llgltfmateriallist.h" LLDispatcher gGenericDispatcher; @@ -92,6 +93,21 @@ void process_generic_message(LLMessageSystem* msg, void**) } } +void process_generic_streaming_message(LLMessageSystem* msg, void**) +{ + LLGenericStreamingMessage data; + data.unpack(msg); + switch (data.mMethod) + { + case LLGenericStreamingMessage::METHOD_GLTF_MATERIAL_OVERRIDE: + gGLTFMaterialList.applyOverrideMessage(msg, data.mData); + break; + default: + LL_WARNS_ONCE() << "Received unknown method" << LL_ENDL; + break; + } +} + void process_large_generic_message(LLMessageSystem* msg, void**) { LLUUID agent_id; diff --git a/indra/newview/llviewergenericmessage.h b/indra/newview/llviewergenericmessage.h index 170f38a485..96a73a3d5f 100644 --- a/indra/newview/llviewergenericmessage.h +++ b/indra/newview/llviewergenericmessage.h @@ -38,6 +38,7 @@ void send_generic_message(const std::string& method, const LLUUID& invoice = LLUUID::null); void process_generic_message(LLMessageSystem* msg, void**); +void process_generic_streaming_message(LLMessageSystem* msg, void**); void process_large_generic_message(LLMessageSystem* msg, void**); diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index 3273090da5..3181ae6283 100644 --- a/indra/newview/llviewerhelp.cpp +++ b/indra/newview/llviewerhelp.cpp @@ -45,7 +45,7 @@ public: // requests will be throttled from a non-trusted browser LLHelpHandler() : LLCommandHandler("help", UNTRUSTED_CLICK_ONLY) {} - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { LLViewerHelp* vhelp = LLViewerHelp::getInstance(); if (! vhelp) diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp index 6bab2c2100..7207d6c6ba 100644 --- a/indra/newview/llviewerinput.cpp +++ b/indra/newview/llviewerinput.cpp @@ -1597,7 +1597,8 @@ bool LLViewerInput::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) { - BOOL handled = gViewerWindow->handleAnyMouseClick(window_impl, pos, mask, clicktype, down); + bool is_toolmgr_action = false; + BOOL handled = gViewerWindow->handleAnyMouseClick(window_impl, pos, mask, clicktype, down, is_toolmgr_action); if (clicktype != CLICK_NONE) { @@ -1616,9 +1617,9 @@ BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, // If the first LMB click is handled by the menu, skip the following double click static bool skip_double_click = false; - if (clicktype == CLICK_LEFT && down ) + if (clicktype == CLICK_LEFT && down) { - skip_double_click = handled; + skip_double_click = is_toolmgr_action ? false : handled; } if (double_click_sp && down) diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 518967709d..5ee613d49d 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -235,8 +235,10 @@ public: // requires trusted browser to trigger LLInventoryHandler() : LLCommandHandler("inventory", UNTRUSTED_CLICK_ONLY) { } - bool handle(const LLSD& params, const LLSD& query_map, - LLMediaCtrl* web) + bool handle(const LLSD& params, + const LLSD& query_map, + const std::string& grid, + LLMediaCtrl* web) { if (params.size() < 1) { @@ -313,7 +315,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& uuid, time_t creation_date_utc) : LLInventoryItem(uuid, parent_uuid, perm, asset_uuid, type, inv_type, name, desc, sale_info, flags, creation_date_utc), - mIsComplete(TRUE) + mIsComplete(true) { } @@ -322,7 +324,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id, const std::string& name, LLInventoryType::EType inv_type) : LLInventoryItem(), - mIsComplete(FALSE) + mIsComplete(false) { mUUID = item_id; mParentUUID = parent_id; @@ -332,7 +334,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id, LLViewerInventoryItem::LLViewerInventoryItem() : LLInventoryItem(), - mIsComplete(FALSE) + mIsComplete(false) { } @@ -349,7 +351,7 @@ LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other) LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) : LLInventoryItem(other), - mIsComplete(TRUE) + mIsComplete(true) { } @@ -431,48 +433,43 @@ void LLViewerInventoryItem::fetchFromServer(void) const { if(!mIsComplete) { - std::string url; + if (AISAPI::isAvailable()) // AIS v 3 + { + LLInventoryModelBackgroundFetch::getInstance()->scheduleItemFetch(mUUID); + } + else + { + std::string url; - LLViewerRegion* region = gAgent.getRegion(); - // we have to check region. It can be null after region was destroyed. See EXT-245 - if (region) - { - if (gAgent.getID() != mPermissions.getOwner()) - { - url = region->getCapability("FetchLib2"); - } - else - { - url = region->getCapability("FetchInventory2"); - } - } - else - { - LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL; - } + LLViewerRegion* region = gAgent.getRegion(); + // we have to check region. It can be null after region was destroyed. See EXT-245 + if (region) + { + if (gAgent.getID() != mPermissions.getOwner()) + { + url = region->getCapability("FetchLib2"); + } + else + { + url = region->getCapability("FetchInventory2"); + } + } + else + { + LL_WARNS(LOG_INV) << "Agent Region is absent" << LL_ENDL; + } - if (!url.empty()) - { - LLSD body; - body["agent_id"] = gAgent.getID(); - body["items"][0]["owner_id"] = mPermissions.getOwner(); - body["items"][0]["item_id"] = mUUID; + if (!url.empty()) + { + LLSD body; + body["agent_id"] = gAgent.getID(); + body["items"][0]["owner_id"] = mPermissions.getOwner(); + body["items"][0]["item_id"] = mUUID; - LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body)); - gInventory.requestPost(true, url, body, handler, "Inventory Item"); - } - else - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("FetchInventory"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("OwnerID", mPermissions.getOwner()); - msg->addUUID("ItemID", mUUID); - gAgent.sendReliableMessage(); - } + LLCore::HttpHandler::ptr_t handler(new LLInventoryModel::FetchItemHttpHandler(body)); + gInventory.requestPost(true, url, body, handler, "Inventory Item"); + } + } } } @@ -567,7 +564,8 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid, LLInventoryCategory(uuid, parent_uuid, pref, name), mOwnerID(owner_id), mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN), - mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) + mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN), + mFetching(FETCH_NONE) { mDescendentsRequested.reset(); } @@ -575,7 +573,8 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid, LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) : mOwnerID(owner_id), mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN), - mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN) + mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN), + mFetching(FETCH_NONE) { mDescendentsRequested.reset(); } @@ -583,6 +582,7 @@ LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) : LLViewerInventoryCategory::LLViewerInventoryCategory(const LLViewerInventoryCategory* other) { copyViewerCategory(other); + mFetching = FETCH_NONE; } LLViewerInventoryCategory::~LLViewerInventoryCategory() @@ -658,7 +658,6 @@ bool LLViewerInventoryCategory::fetch() mDescendentsRequested.reset(); mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY); - std::string url; if (gAgent.getRegion()) { @@ -668,15 +667,52 @@ bool LLViewerInventoryCategory::fetch() { LL_WARNS(LOG_INV) << "agent region is null" << LL_ENDL; } - if (!url.empty()) //Capability found. Build up LLSD and use it. + if (!url.empty() || AISAPI::isAvailable()) { - LLInventoryModelBackgroundFetch::instance().start(mUUID, false); + LLInventoryModelBackgroundFetch::instance().start(mUUID, false); } return true; } return false; } +LLViewerInventoryCategory::EFetchType LLViewerInventoryCategory::getFetching() +{ + // if timer hasn't expired, request was scheduled, but not in progress + // if mFetching request was actually started + if (mDescendentsRequested.hasExpired()) + { + mFetching = FETCH_NONE; + } + return mFetching; +} + +void LLViewerInventoryCategory::setFetching(LLViewerInventoryCategory::EFetchType fetching) +{ + if (fetching > mFetching) // allow a switch from normal to recursive + { + if (mDescendentsRequested.hasExpired() || (mFetching == FETCH_NONE)) + { + mDescendentsRequested.reset(); + if (AISAPI::isAvailable()) + { + mDescendentsRequested.setTimerExpirySec(AISAPI::HTTP_TIMEOUT); + } + else + { + const F32 FETCH_TIMER_EXPIRY = 30.0f; + mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY); + } + } + mFetching = fetching; + } + else if (fetching == FETCH_NONE) + { + mDescendentsRequested.stop(); + mFetching = fetching; + } +} + S32 LLViewerInventoryCategory::getViewerDescendentCount() const { LLInventoryModel::cat_array_t* cats; @@ -1016,13 +1052,18 @@ void create_gltf_material_cb(const LLUUID& inv_item) LLInventoryCallbackManager gInventoryCallbacks; -void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, - const LLUUID& parent, const LLTransactionID& transaction_id, - const std::string& name, - const std::string& desc, LLAssetType::EType asset_type, - LLInventoryType::EType inv_type, U8 subtype, - U32 next_owner_perm, - LLPointer cb) +void create_inventory_item( + const LLUUID& agent_id, + const LLUUID& session_id, + const LLUUID& parent_id, + const LLTransactionID& transaction_id, + const std::string& name, + const std::string& desc, + LLAssetType::EType asset_type, + LLInventoryType::EType inv_type, + U8 subtype, + U32 next_owner_perm, + LLPointer cb) { //check if name is equal to one of special inventory items names //EXT-5839 @@ -1043,6 +1084,54 @@ void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, } } +#ifdef USE_AIS_FOR_NC + // D567 18.03.2023 not yet implemented within AIS3 + if (AISAPI::isAvailable()) + { + LLSD new_inventory = LLSD::emptyMap(); + new_inventory["items"] = LLSD::emptyArray(); + + LLPermissions perms; + perms.init( + gAgentID, + gAgentID, + LLUUID::null, + LLUUID::null); + perms.initMasks( + PERM_ALL, + PERM_ALL, + PERM_NONE, + PERM_NONE, + next_owner_perm); + + LLUUID null_id; + LLPointer item = new LLViewerInventoryItem( + null_id, /*don't know yet*/ + parent_id, + perms, + null_id, /*don't know yet*/ + asset_type, + inv_type, + server_name, + desc, + LLSaleInfo(), + 0, + 0 /*don't know yet, whenever server creates it*/); + LLSD item_sd = item->asLLSD(); + new_inventory["items"].append(item_sd); + AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1); + AISAPI::CreateInventory( + parent_id, + new_inventory, + cr); + return; + } + else + { + LL_WARNS() << "AIS v3 not available" << LL_ENDL; + } +#endif + LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_CreateInventoryItem); msg->nextBlock(_PREHASH_AgentData); @@ -1050,7 +1139,7 @@ void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id, msg->addUUIDFast(_PREHASH_SessionID, session_id); msg->nextBlock(_PREHASH_InventoryBlock); msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb)); - msg->addUUIDFast(_PREHASH_FolderID, parent); + msg->addUUIDFast(_PREHASH_FolderID, parent_id); msg->addUUIDFast(_PREHASH_TransactionID, transaction_id); msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_perm); msg->addS8Fast(_PREHASH_Type, (S8)asset_type); @@ -1306,17 +1395,15 @@ void update_inventory_category( LL_DEBUGS(LOG_INV) << "cat_id: [" << cat_id << "] name " << (obj ? obj->getName() : "(NOT FOUND)") << LL_ENDL; if(obj) { - if (LLFolderType::lookupIsProtectedType(obj->getPreferredType())) + if (LLFolderType::lookupIsProtectedType(obj->getPreferredType()) + && (updates.size() != 1 || !updates.has("thumbnail"))) { LLNotificationsUtil::add("CannotModifyProtectedCategories"); return; } - LLPointer new_cat = new LLViewerInventoryCategory(obj); - new_cat->fromLLSD(updates); - LLSD new_llsd = new_cat->asLLSD(); AISAPI::completion_t cr = boost::bind(&doInventoryCb, cb, _1); - AISAPI::UpdateCategory(cat_id, new_llsd, cr); + AISAPI::UpdateCategory(cat_id, updates, cr); } } @@ -1368,25 +1455,10 @@ void remove_inventory_item( gInventory.onObjectDeletedFromServer(item_id); } } - else // no cap - { - LLMessageSystem* msg = gMessageSystem; - msg->newMessageFast(_PREHASH_RemoveInventoryItem); - msg->nextBlockFast(_PREHASH_AgentData); - msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); - msg->nextBlockFast(_PREHASH_InventoryData); - msg->addUUIDFast(_PREHASH_ItemID, item_id); - gAgent.sendReliableMessage(); - - // Update inventory and call callback immediately since - // message-based system has no callback mechanism (!) - gInventory.onObjectDeletedFromServer(item_id); - if (cb) - { - cb->fire(item_id); - } - } + else + { + LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL; + } } else { @@ -1515,28 +1587,10 @@ void purge_descendents_of(const LLUUID& id, LLPointer cb) AISAPI::completion_t cr = (cb) ? boost::bind(&doInventoryCb, cb, _1) : AISAPI::completion_t(); AISAPI::PurgeDescendents(id, cr); } - else // no cap - { - // Fast purge - LL_DEBUGS(LOG_INV) << "purge_descendents_of fast case " << cat->getName() << LL_ENDL; - - // send it upstream - LLMessageSystem* msg = gMessageSystem; - msg->newMessage("PurgeInventoryDescendents"); - msg->nextBlock("AgentData"); - msg->addUUID("AgentID", gAgent.getID()); - msg->addUUID("SessionID", gAgent.getSessionID()); - msg->nextBlock("InventoryData"); - msg->addUUID("FolderID", id); - gAgent.sendReliableMessage(); - - // Update model immediately because there is no callback mechanism. - gInventory.onDescendentsPurgedFromServer(id); - if (cb) - { - cb->fire(id); - } - } + else + { + LL_WARNS(LOG_INV) << "Tried to use inventory without AIS API" << LL_ENDL; + } } } @@ -1604,17 +1658,79 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, } } +void move_or_copy_inventory_from_object(const LLUUID& destination_id, + const LLUUID& object_id, + const LLUUID& item_id, + LLPointer cb) +{ + LLViewerObject* object = gObjectList.findObject(object_id); + if (!object) + { + return; + } + const LLInventoryItem* item = object->getInventoryItem(item_id); + if (!item) + { + return; + } + + class LLItemAddedObserver : public LLInventoryObserver + { + public: + LLItemAddedObserver(const LLUUID& copied_asset_id, LLPointer cb) + : LLInventoryObserver(), + mAssetId(copied_asset_id), + mCallback(cb) + { + } + + void changed(U32 mask) override + { + if((mask & (LLInventoryObserver::ADD)) == 0) + { + return; + } + for (const LLUUID& changed_id : gInventory.getChangedIDs()) + { + LLViewerInventoryItem* changed_item = gInventory.getItem(changed_id); + if (changed_item->getAssetUUID() == mAssetId) + { + changeComplete(changed_item->getUUID()); + return; + } + } + } + + private: + void changeComplete(const LLUUID& item_id) + { + mCallback->fire(item_id); + gInventory.removeObserver(this); + delete this; + } + + LLUUID mAssetId; + LLPointer mCallback; + }; + + const LLUUID& asset_id = item->getAssetUUID(); + LLItemAddedObserver* observer = new LLItemAddedObserver(asset_id, cb); + gInventory.addObserver(observer); + object->moveInventory(destination_id, item_id); +} + void create_new_item(const std::string& name, const LLUUID& parent_id, LLAssetType::EType asset_type, LLInventoryType::EType inv_type, - U32 next_owner_perm) + U32 next_owner_perm, + std::function created_cb = NULL) { std::string desc; LLViewerAssetType::generateDescriptionFor(asset_type, desc); next_owner_perm = (next_owner_perm) ? next_owner_perm : PERM_MOVE | PERM_TRANSFER; - LLPointer cb = NULL; + LLPointer cb = NULL; switch (inv_type) { @@ -1645,9 +1761,16 @@ void create_new_item(const std::string& name, next_owner_perm = LLFloaterPerms::getNextOwnerPerms("Materials"); break; } - default: - break; - } + default: + { + cb = new LLBoostFuncInventoryCallback(); + break; + } + } + if (created_cb != NULL) + { + cb->addOnFireFunc(created_cb); + } create_inventory_item(gAgent.getID(), gAgent.getSessionID(), @@ -1700,65 +1823,95 @@ const std::string NEW_MATERIAL_NAME = "New Material"; // *TODO:Translate? (proba // ! REFACTOR ! Really need to refactor this so that it's not a bunch of if-then statements... void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, const LLSD& userdata, const LLUUID& default_parent_uuid) { - std::string type_name = userdata.asString(); - - if (("inbox" == type_name) || ("category" == type_name) || ("current" == type_name) || ("outfit" == type_name) || ("my_otfts" == type_name)) - { - LLFolderType::EType preferred_type = LLFolderType::lookup(type_name); + menu_create_inventory_item(panel, bridge ? bridge->getUUID() : LLUUID::null, userdata, default_parent_uuid); +} - LLUUID parent_id; - if (bridge) - { - parent_id = bridge->getUUID(); - } - else if (default_parent_uuid.notNull()) - { - parent_id = default_parent_uuid; - } - else - { - parent_id = gInventory.getRootFolderID(); - } +void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const LLSD& userdata, const LLUUID& default_parent_uuid, std::function created_cb) +{ + std::string type_name = userdata.asString(); + + if (("inbox" == type_name) || ("category" == type_name) || ("current" == type_name) || ("outfit" == type_name) || ("my_otfts" == type_name)) + { + LLFolderType::EType preferred_type = LLFolderType::lookup(type_name); - LLUUID category = gInventory.createNewCategory(parent_id, preferred_type, LLStringUtil::null); - gInventory.notifyObservers(); - panel->setSelectionByID(category, TRUE); - } - else if ("lsl" == type_name) - { - const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT); - create_new_item(NEW_LSL_NAME, - parent_id, - LLAssetType::AT_LSL_TEXT, - LLInventoryType::IT_LSL, - PERM_MOVE | PERM_TRANSFER); // overridden in create_new_item - } - else if ("notecard" == type_name) - { - const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_NOTECARD); - create_new_item(NEW_NOTECARD_NAME, - parent_id, - LLAssetType::AT_NOTECARD, - LLInventoryType::IT_NOTECARD, - PERM_ALL); // overridden in create_new_item - } - else if ("gesture" == type_name) - { - const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); - create_new_item(NEW_GESTURE_NAME, - parent_id, - LLAssetType::AT_GESTURE, - LLInventoryType::IT_GESTURE, - PERM_ALL); // overridden in create_new_item - } + LLUUID parent_id; + if (dest_id.notNull()) + { + parent_id = dest_id; + } + else if (default_parent_uuid.notNull()) + { + parent_id = default_parent_uuid; + } + else + { + parent_id = gInventory.getRootFolderID(); + } + + std::function callback_cat_created = NULL; + if (panel) + { + LLHandle handle = panel->getHandle(); + callback_cat_created = [handle](const LLUUID& new_category_id) + { + gInventory.notifyObservers(); + LLInventoryPanel* panel = static_cast(handle.get()); + if (panel) + { + panel->setSelectionByID(new_category_id, TRUE); + } + LL_DEBUGS(LOG_INV) << "Done creating inventory: " << new_category_id << LL_ENDL; + }; + } + else if (created_cb != NULL) + { + callback_cat_created = created_cb; + } + gInventory.createNewCategory( + parent_id, + preferred_type, + LLStringUtil::null, + callback_cat_created); + } + else if ("lsl" == type_name) + { + const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_LSL_TEXT); + create_new_item(NEW_LSL_NAME, + parent_id, + LLAssetType::AT_LSL_TEXT, + LLInventoryType::IT_LSL, + PERM_MOVE | PERM_TRANSFER, + created_cb); // overridden in create_new_item + } + else if ("notecard" == type_name) + { + const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_NOTECARD); + create_new_item(NEW_NOTECARD_NAME, + parent_id, + LLAssetType::AT_NOTECARD, + LLInventoryType::IT_NOTECARD, + PERM_ALL, + created_cb); // overridden in create_new_item + } + else if ("gesture" == type_name) + { + const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_GESTURE); + create_new_item(NEW_GESTURE_NAME, + parent_id, + LLAssetType::AT_GESTURE, + LLInventoryType::IT_GESTURE, + PERM_ALL, + created_cb); // overridden in create_new_item + } else if ("material" == type_name) { - const LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL); + const LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_MATERIAL); create_new_item(NEW_MATERIAL_NAME, parent_id, LLAssetType::AT_MATERIAL, LLInventoryType::IT_MATERIAL, - PERM_ALL); // overridden in create_new_item + PERM_ALL, + created_cb); // overridden in create_new_item } else if (("sky" == type_name) || ("water" == type_name) || ("daycycle" == type_name)) { @@ -1782,25 +1935,28 @@ void menu_create_inventory_item(LLInventoryPanel* panel, LLFolderBridge *bridge, return; } - LLUUID parent_id = bridge ? bridge->getUUID() : gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); + LLUUID parent_id = dest_id.notNull() ? dest_id : gInventory.findCategoryUUIDForType(LLFolderType::FT_SETTINGS); - LLSettingsVOBase::createNewInventoryItem(stype, parent_id); + LLSettingsVOBase::createNewInventoryItem(stype, parent_id, created_cb); + } + else + { + // Use for all clothing and body parts. Adding new wearable types requires updating LLWearableDictionary. + LLWearableType::EType wearable_type = LLWearableType::getInstance()->typeNameToType(type_name); + if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT) + { + const LLUUID parent_id = dest_id; + LLAgentWearables::createWearable(wearable_type, false, parent_id, created_cb); + } + else + { + LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL; + } + } + if(panel) + { + panel->getRootFolder()->setNeedsAutoRename(TRUE); } - else - { - // Use for all clothing and body parts. Adding new wearable types requires updating LLWearableDictionary. - LLWearableType::EType wearable_type = LLWearableType::getInstance()->typeNameToType(type_name); - if (wearable_type >= LLWearableType::WT_SHAPE && wearable_type < LLWearableType::WT_COUNT) - { - const LLUUID parent_id = bridge ? bridge->getUUID() : LLUUID::null; - LLAgentWearables::createWearable(wearable_type, false, parent_id); - } - else - { - LL_WARNS(LOG_INV) << "Can't create unrecognized type " << type_name << LL_ENDL; - } - } - panel->getRootFolder()->setNeedsAutoRename(TRUE); } LLAssetType::EType LLViewerInventoryItem::getType() const @@ -1926,6 +2082,25 @@ const LLSaleInfo& LLViewerInventoryItem::getSaleInfo() const return LLInventoryItem::getSaleInfo(); } +const LLUUID& LLViewerInventoryItem::getThumbnailUUID() const +{ + if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_TEXTURE) + { + return mAssetUUID; + } + if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK) + { + LLViewerInventoryItem *linked_item = gInventory.getItem(mAssetUUID); + return linked_item ? linked_item->getThumbnailUUID() : LLUUID::null; + } + if (mThumbnailUUID.isNull() && mType == LLAssetType::AT_LINK_FOLDER) + { + LLViewerInventoryCategory *linked_cat = gInventory.getCategory(mAssetUUID); + return linked_cat ? linked_cat->getThumbnailUUID() : LLUUID::null; + } + return mThumbnailUUID; +} + LLInventoryType::EType LLViewerInventoryItem::getInventoryType() const { if (const LLViewerInventoryItem *linked_item = getLinkedItem()) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 24b632632b..bce8da0a69 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -72,6 +72,7 @@ public: virtual const LLUUID& getCreatorUUID() const; virtual const std::string& getDescription() const; virtual const LLSaleInfo& getSaleInfo() const; + virtual const LLUUID& getThumbnailUUID() const; virtual LLInventoryType::EType getInventoryType() const; virtual bool isWearableType() const; virtual LLWearableType::EType getWearableType() const; @@ -134,8 +135,8 @@ public: virtual BOOL importLegacyStream(std::istream& input_stream); // new methods - BOOL isFinished() const { return mIsComplete; } - void setComplete(BOOL complete) { mIsComplete = complete; } + bool isFinished() const { return mIsComplete; } + void setComplete(bool complete) { mIsComplete = complete; } //void updateAssetOnServer() const; virtual void setTransactionID(const LLTransactionID& transaction_id); @@ -163,7 +164,7 @@ public: BOOL regenerateLink(); public: - BOOL mIsComplete; + bool mIsComplete; LLTransactionID mTransactionID; }; @@ -208,9 +209,18 @@ public: S32 getVersion() const; void setVersion(S32 version); - // Returns true if a fetch was issued. + // Returns true if a fetch was issued (not nessesary in progress). bool fetch(); + typedef enum { + FETCH_NONE = 0, + FETCH_NORMAL, + FETCH_RECURSIVE, + } EFetchType; + EFetchType getFetching(); + // marks as fetch being in progress or as done + void setFetching(EFetchType); + // used to help make caching more robust - for example, if // someone is getting 4 packets but logs out after 3. the viewer // may never know the cache is wrong. @@ -239,12 +249,14 @@ protected: LLUUID mOwnerID; S32 mVersion; S32 mDescendentCount; + EFetchType mFetching; LLFrameTimer mDescendentsRequested; }; class LLInventoryCallback : public LLRefCount { public: + virtual ~LLInventoryCallback() {} virtual void fire(const LLUUID& inv_item) = 0; }; @@ -283,17 +295,29 @@ class LLBoostFuncInventoryCallback: public LLInventoryCallback { public: - LLBoostFuncInventoryCallback(inventory_func_type fire_func = no_op_inventory_func, + LLBoostFuncInventoryCallback(inventory_func_type fire_func, nullary_func_type destroy_func = no_op): - mFireFunc(fire_func), mDestroyFunc(destroy_func) { + mFireFuncs.push_back(fire_func); } + LLBoostFuncInventoryCallback() + { + } + + void addOnFireFunc(inventory_func_type fire_func) + { + mFireFuncs.push_back(fire_func); + } + // virtual void fire(const LLUUID& item_id) { - mFireFunc(item_id); + for (inventory_func_type &func: mFireFuncs) + { + func(item_id); + } } // virtual @@ -304,7 +328,7 @@ public: private: - inventory_func_type mFireFunc; + std::list mFireFuncs; nullary_func_type mDestroyFunc; }; @@ -439,12 +463,18 @@ void copy_inventory_from_notecard(const LLUUID& destination_id, const LLInventoryItem *src, U32 callback_id = 0); +void move_or_copy_inventory_from_object(const LLUUID& destination_id, + const LLUUID& object_id, + const LLUUID& item_id, + LLPointer cb); void menu_create_inventory_item(LLInventoryPanel* root, LLFolderBridge* bridge, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null); +void menu_create_inventory_item(LLInventoryPanel* panel, LLUUID dest_id, const LLSD& userdata, const LLUUID& default_parent_uuid = LLUUID::null, std::function folder_created_cb = NULL); + void slam_inventory_folder(const LLUUID& folder_id, const LLSD& contents, LLPointer cb); diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index ba80eeb6b6..e914e2121c 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -63,6 +63,7 @@ LLViewerMediaFocus::~LLViewerMediaFocus() { // The destructor for LLSingletons happens at atexit() time, which is too late to do much. // Clean up in cleanupClass() instead. + gFocusMgr.removeKeyboardFocusWithoutCallback(this); } void LLViewerMediaFocus::setFocusFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 6e5c268c00..3708ad82e8 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -120,8 +120,10 @@ #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerstats.h" +#include "llviewerstatsrecorder.h" #include "llvoavatarself.h" #include "llvoicevivox.h" +#include "llworld.h" #include "llworldmap.h" #include "pipeline.h" #include "llviewerjoystick.h" @@ -328,6 +330,7 @@ void handle_debug_avatar_textures(void*); void handle_grab_baked_texture(void*); BOOL enable_grab_baked_texture(void*); void handle_dump_region_object_cache(void*); +void handle_reset_interest_lists(void *); BOOL enable_save_into_task_inventory(void*); @@ -399,14 +402,14 @@ void set_merchant_SLM_menu() LLCommand* command = LLCommandManager::instance().getCommand("marketplacelistings"); gToolBarView->enableCommand(command->id(), true); - const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, false); + const LLUUID marketplacelistings_id = gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS); if (marketplacelistings_id.isNull()) { U32 mkt_status = LLMarketplaceData::instance().getSLMStatus(); bool is_merchant = (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MERCHANT) || (mkt_status == MarketplaceStatusCodes::MARKET_PLACE_MIGRATED_MERCHANT); if (is_merchant) { - gInventory.findCategoryUUIDForType(LLFolderType::FT_MARKETPLACE_LISTINGS, true); + gInventory.ensureCategoryForTypeExists(LLFolderType::FT_MARKETPLACE_LISTINGS); LL_WARNS("SLM") << "Creating the marketplace listings folder for a merchant" << LL_ENDL; } } @@ -1244,49 +1247,66 @@ class LLAdvancedDumpRegionObjectCache : public view_listener_t } }; -class LLAdvancedInterestListFullUpdate : public view_listener_t +class LLAdvancedToggleInterestList360Mode : public view_listener_t { - bool handleEvent(const LLSD& userdata) - { - LLSD request; - LLSD body; - static bool using_360 = false; - - if (using_360) - { - body["mode"] = LLSD::String("default"); +public: + bool handleEvent(const LLSD &userdata) + { + // Toggle the mode - regions will get updated + if (gAgent.getInterestListMode() == LLViewerRegion::IL_MODE_360) + { + gAgent.changeInterestListMode(LLViewerRegion::IL_MODE_DEFAULT); } else { - body["mode"] = LLSD::String("360"); + gAgent.changeInterestListMode(LLViewerRegion::IL_MODE_360); } - using_360 = !using_360; + return true; + } +}; - if (gAgent.requestPostCapability("InterestList", body, [](const LLSD& response) - { - LL_INFOS("360Capture") << - "InterestList capability responded: \n" << - ll_pretty_print_sd(response) << - LL_ENDL; - })) - { - LL_INFOS("360Capture") << - "Successfully posted an InterestList capability request with payload: \n" << - ll_pretty_print_sd(body) << - LL_ENDL; - return true; - } - else - { - LL_INFOS("360Capture") << - "Unable to post an InterestList capability request with payload: \n" << - ll_pretty_print_sd(body) << - LL_ENDL; - return false; - } +class LLAdvancedCheckInterestList360Mode : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + return (gAgent.getInterestListMode() == LLViewerRegion::IL_MODE_360); } }; +class LLAdvancedToggleStatsRecorder : public view_listener_t +{ + bool handleEvent(const LLSD &userdata) + { + if (LLViewerStatsRecorder::instance().isEnabled()) + { // Turn off both recording and logging + LLViewerStatsRecorder::instance().enableObjectStatsRecording(false); + } + else + { // Turn on both recording and logging + LLViewerStatsRecorder::instance().enableObjectStatsRecording(true, true); + } + return true; + } +}; + +class LLAdvancedCheckStatsRecorder : public view_listener_t +{ + bool handleEvent(const LLSD &userdata) + { // Use the logging state as the indicator of whether the stats recorder is on + return LLViewerStatsRecorder::instance().isLogging(); + } +}; + +class LLAdvancedResetInterestLists : public view_listener_t +{ + bool handleEvent(const LLSD &userdata) + { // Reset all region interest lists + handle_reset_interest_lists(NULL); + return true; + } +}; + + class LLAdvancedBuyCurrencyTest : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -2800,7 +2820,12 @@ bool enable_object_inspect() struct LLSelectedTEGetmatIdAndPermissions : public LLSelectedTEFunctor { - LLSelectedTEGetmatIdAndPermissions() : mCanCopy(true), mCanModify(true), mCanTransfer(true) {} + LLSelectedTEGetmatIdAndPermissions() + : mCanCopy(true) + , mCanModify(true) + , mCanTransfer(true) + , mHasNonPbrFaces(false) + {} bool apply(LLViewerObject* objectp, S32 te_index) { mCanCopy &= (bool)objectp->permCopy(); @@ -2811,11 +2836,16 @@ struct LLSelectedTEGetmatIdAndPermissions : public LLSelectedTEFunctor { mMaterialId = mat_id; } + else + { + mHasNonPbrFaces = true; + } return true; } bool mCanCopy; bool mCanModify; bool mCanTransfer; + bool mHasNonPbrFaces; LLUUID mMaterialId; }; @@ -2828,7 +2858,7 @@ bool enable_object_edit_gltf_material() LLSelectedTEGetmatIdAndPermissions func; LLSelectMgr::getInstance()->getSelection()->applyToTEs(&func); - return func.mCanModify && func.mMaterialId.notNull(); + return func.mCanModify && !func.mHasNonPbrFaces; } bool enable_object_open() @@ -3019,9 +3049,7 @@ void handle_object_inspect() LLViewerObject* selected_objectp = selection->getFirstRootObject(); if (selected_objectp) { - LLSD key; - key["task"] = "task"; - LLFloaterSidePanelContainer::showPanel("inventory", key); + LLFloaterReg::showInstance("task_properties"); } /* @@ -3859,6 +3887,22 @@ void handle_dump_region_object_cache(void*) } } +void handle_reset_interest_lists(void *) +{ + // Check all regions and reset their interest list + for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); + iter != LLWorld::getInstance()->getRegionList().end(); + ++iter) + { + LLViewerRegion *regionp = *iter; + if (regionp && regionp->isAlive() && regionp->capabilitiesReceived()) + { + regionp->resetInterestList(); + } + } +} + + void handle_dump_focus() { LLUICtrl *ctrl = dynamic_cast(gFocusMgr.getKeyboardFocus()); @@ -4469,33 +4513,6 @@ void handle_duplicate_in_place(void*) LLSelectMgr::getInstance()->selectDuplicate(offset, TRUE); } -/* dead code 30-apr-2008 -void handle_deed_object_to_group(void*) -{ - LLUUID group_id; - - LLSelectMgr::getInstance()->selectGetGroup(group_id); - LLSelectMgr::getInstance()->sendOwner(LLUUID::null, group_id, FALSE); - LLViewerStats::getInstance()->incStat(LLViewerStats::ST_RELEASE_COUNT); -} - -BOOL enable_deed_object_to_group(void*) -{ - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) return FALSE; - LLPermissions perm; - LLUUID group_id; - - if (LLSelectMgr::getInstance()->selectGetGroup(group_id) && - gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && - LLSelectMgr::getInstance()->selectGetPermissions(perm) && - perm.deedToGroup(gAgent.getID(), group_id)) - { - return TRUE; - } - return FALSE; -} - -*/ /* @@ -8118,6 +8135,18 @@ class LLToolsSelectInvisibleObjects : public view_listener_t } }; +class LLToolsSelectReflectionProbes: public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + BOOL cur_val = gSavedSettings.getBOOL("SelectReflectionProbes"); + + gSavedSettings.setBOOL("SelectReflectionProbes", !cur_val); + + return true; + } +}; + class LLToolsSelectBySurrounding : public view_listener_t { bool handleEvent(const LLSD& userdata) @@ -9060,6 +9089,25 @@ class LLWorldPostProcess : public view_listener_t } }; +class LLWorldCheckBanLines : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + S32 callback_data = userdata.asInteger(); + return gSavedSettings.getS32("ShowBanLines") == callback_data; + } +}; + +class LLWorldShowBanLines : public view_listener_t +{ + bool handleEvent(const LLSD& userdata) + { + S32 callback_data = userdata.asInteger(); + gSavedSettings.setS32("ShowBanLines", callback_data); + return true; + } +}; + void handle_flush_name_caches() { if (gCacheName) gCacheName->clear(); @@ -9349,12 +9397,15 @@ void initialize_menus() view_listener_t::addMenu(new LLWorldEnvPreset(), "World.EnvPreset"); view_listener_t::addMenu(new LLWorldEnableEnvPreset(), "World.EnableEnvPreset"); view_listener_t::addMenu(new LLWorldPostProcess(), "World.PostProcess"); + view_listener_t::addMenu(new LLWorldCheckBanLines() , "World.CheckBanLines"); + view_listener_t::addMenu(new LLWorldShowBanLines() , "World.ShowBanLines"); // Tools menu view_listener_t::addMenu(new LLToolsSelectTool(), "Tools.SelectTool"); view_listener_t::addMenu(new LLToolsSelectOnlyMyObjects(), "Tools.SelectOnlyMyObjects"); view_listener_t::addMenu(new LLToolsSelectOnlyMovableObjects(), "Tools.SelectOnlyMovableObjects"); view_listener_t::addMenu(new LLToolsSelectInvisibleObjects(), "Tools.SelectInvisibleObjects"); + view_listener_t::addMenu(new LLToolsSelectReflectionProbes(), "Tools.SelectReflectionProbes"); view_listener_t::addMenu(new LLToolsSelectBySurrounding(), "Tools.SelectBySurrounding"); view_listener_t::addMenu(new LLToolsShowHiddenSelection(), "Tools.ShowHiddenSelection"); view_listener_t::addMenu(new LLToolsShowSelectionLightRadius(), "Tools.ShowSelectionLightRadius"); @@ -9443,7 +9494,11 @@ void initialize_menus() // Advanced > World view_listener_t::addMenu(new LLAdvancedDumpScriptedCamera(), "Advanced.DumpScriptedCamera"); view_listener_t::addMenu(new LLAdvancedDumpRegionObjectCache(), "Advanced.DumpRegionObjectCache"); - view_listener_t::addMenu(new LLAdvancedInterestListFullUpdate(), "Advanced.InterestListFullUpdate"); + view_listener_t::addMenu(new LLAdvancedToggleStatsRecorder(), "Advanced.ToggleStatsRecorder"); + view_listener_t::addMenu(new LLAdvancedCheckStatsRecorder(), "Advanced.CheckStatsRecorder"); + view_listener_t::addMenu(new LLAdvancedToggleInterestList360Mode(), "Advanced.ToggleInterestList360Mode"); + view_listener_t::addMenu(new LLAdvancedCheckInterestList360Mode(), "Advanced.CheckInterestList360Mode"); + view_listener_t::addMenu(new LLAdvancedResetInterestLists(), "Advanced.ResetInterestLists"); // Advanced > UI commit.add("Advanced.WebBrowserTest", boost::bind(&handle_web_browser_test, _2)); // sigh! this one opens the MEDIA browser diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 688b77f747..e2791ba128 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -40,7 +40,7 @@ #include "llmaterialeditor.h" #include "llfloaterperms.h" #include "llfloatersnapshot.h" -#include "llfloatersimpleoutfitsnapshot.h" +#include "llfloatersimplesnapshot.h" #include "llimage.h" #include "llimagebmp.h" #include "llimagepng.h" @@ -834,9 +834,7 @@ class LLFileEnableCloseAllWindows : public view_listener_t bool handleEvent(const LLSD& userdata) { LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); - LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance(); - bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain()) - || (floater_outfit_snapshot && floater_outfit_snapshot->isInVisibleChain()); + bool is_floaters_snapshot_opened = (floater_snapshot && floater_snapshot->isInVisibleChain()); bool open_children = gFloaterView->allChildrenClosed() && !is_floaters_snapshot_opened; return !open_children && !LLNotificationsUI::LLToast::isAlertToastShown(); } @@ -851,9 +849,6 @@ class LLFileCloseAllWindows : public view_listener_t LLFloaterSnapshot* floater_snapshot = LLFloaterSnapshot::findInstance(); if (floater_snapshot) floater_snapshot->closeFloater(app_quitting); - LLFloaterSimpleOutfitSnapshot* floater_outfit_snapshot = LLFloaterSimpleOutfitSnapshot::findInstance(); - if (floater_outfit_snapshot) - floater_outfit_snapshot->closeFloater(app_quitting); if (gMenuHolder) gMenuHolder->hideMenus(); return true; } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index f14e3ed737..ada898b98c 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -76,6 +76,7 @@ #include "llnotifications.h" #include "llnotificationsutil.h" #include "llpanelgrouplandmoney.h" +#include "llpanelmaininventory.h" #include "llrecentpeople.h" #include "llscriptfloater.h" #include "llscriptruntimeperms.h" @@ -99,6 +100,7 @@ #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerstats.h" +#include "llviewerstatsrecorder.h" #include "llviewertexteditor.h" #include "llviewerthrottle.h" #include "llviewerwindow.h" @@ -1532,7 +1534,7 @@ void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_nam LLFloaterReg::showInstance("preview_sound", LLSD(obj_id), take_focus); break; case LLAssetType::AT_MATERIAL: - LLFloaterReg::showInstance("material editor", LLSD(obj_id), take_focus); + // Explicitly do nothing -- we don't want to open the material editor every time you add a material to inventory break; default: LL_DEBUGS("Messaging") << "No preview method for previewable asset type : " << LLAssetType::lookupHumanReadable(asset_type) << LL_ENDL; @@ -1542,11 +1544,35 @@ void open_inventory_offer(const uuid_vec_t& objects, const std::string& from_nam } //////////////////////////////////////////////////////////////////////////////// + static LLUICachedControl find_original_new_floater("FindOriginalOpenWindow", false); + //show in a new single-folder window + if(find_original_new_floater && !from_name.empty()) + { + const LLInventoryObject *obj = gInventory.getObject(obj_id); + if (obj && obj->getParentUUID().notNull()) + { + if (obj->getActualType() == LLAssetType::AT_CATEGORY) + { + LLPanelMainInventory::newFolderWindow(obj_id); + } + else + { + LLPanelMainInventory::newFolderWindow(obj->getParentUUID(), obj_id); + } + } + } + else + { // Highlight item const BOOL auto_open = gSavedSettings.getBOOL("ShowInInventory") && // don't open if showininventory is false !from_name.empty(); // don't open if it's not from anyone. - LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id); + if(auto_open) + { + LLFloaterReg::showInstance("inventory"); + } + LLInventoryPanel::openInventoryPanelAndSetSelection(auto_open, obj_id, true); + } } } @@ -3765,31 +3791,34 @@ void process_kill_object(LLMessageSystem *mesgsys, void **user_data) continue; } - LLViewerObject *objectp = gObjectList.findObject(id); - if (objectp) + LLViewerObject *objectp = gObjectList.findObject(id); + if (objectp) + { + // Display green bubble on kill + if ( gShowObjectUpdates ) { - // Display green bubble on kill - if ( gShowObjectUpdates ) - { - LLColor4 color(0.f,1.f,0.f,1.f); - gPipeline.addDebugBlip(objectp->getPositionAgent(), color); - LL_DEBUGS("MessageBlip") << "Kill blip for local " << local_id << " at " << objectp->getPositionAgent() << LL_ENDL; - } - - // Do the kill - gObjectList.killObject(objectp); + LLColor4 color(0.f,1.f,0.f,1.f); + gPipeline.addDebugBlip(objectp->getPositionAgent(), color); + LL_DEBUGS("MessageBlip") << "Kill blip for local " << local_id << " at " << objectp->getPositionAgent() << LL_ENDL; } - if(delete_object) - { - regionp->killCacheEntry(local_id); + // Do the kill + gObjectList.killObject(objectp); + } + + if(delete_object) + { + regionp->killCacheEntry(local_id); } // We should remove the object from selection after it is marked dead by gObjectList to make LLToolGrab, // which is using the object, release the mouse capture correctly when the object dies. // See LLToolGrab::handleHoverActive() and LLToolGrab::handleHoverNonPhysical(). LLSelectMgr::getInstance()->removeObjectFromSelections(id); - } + + } // end for loop + + LLViewerStatsRecorder::instance().recordObjectKills(num_objects); } void process_time_synch(LLMessageSystem *mesgsys, void **user_data) @@ -5157,6 +5186,11 @@ bool attempt_standard_notification(LLMessageSystem* msgsystem) LandBuyAccessBlocked_AdultsOnlyContent -----------------------------------------------------------------------*/ + static LLCachedControl ban_lines_mode(gSavedSettings , "ShowBanLines" , LLViewerParcelMgr::PARCEL_BAN_LINES_ON_COLLISION); + if (ban_lines_mode == LLViewerParcelMgr::PARCEL_BAN_LINES_ON_COLLISION) + { + LLViewerParcelMgr::getInstance()->resetCollisionTimer(); + } if (handle_special_notification(notificationID, llsdBlock)) { return true; @@ -5325,6 +5359,13 @@ void process_alert_message(LLMessageSystem *msgsystem, void **user_data) { BOOL modal = FALSE; process_alert_core(message, modal); + + static LLCachedControl ban_lines_mode(gSavedSettings , "ShowBanLines" , LLViewerParcelMgr::PARCEL_BAN_LINES_ON_COLLISION); + if (ban_lines_mode == LLViewerParcelMgr::PARCEL_BAN_LINES_ON_COLLISION + && message.find("Cannot enter parcel") != std::string::npos) + { + LLViewerParcelMgr::getInstance()->resetCollisionTimer(); + } } } @@ -5908,42 +5949,47 @@ void container_inventory_arrived(LLViewerObject* object, { // create a new inventory category to put this in LLUUID cat_id; - cat_id = gInventory.createNewCategory(gInventory.getRootFolderID(), - LLFolderType::FT_NONE, - LLTrans::getString("AcquiredItems")); + gInventory.createNewCategory( + gInventory.getRootFolderID(), + LLFolderType::FT_NONE, + LLTrans::getString("AcquiredItems"), + [inventory](const LLUUID &new_cat_id) + { + LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); + LLInventoryObject::object_list_t::const_iterator end = inventory->end(); + for (; it != end; ++it) + { + if ((*it)->getType() != LLAssetType::AT_CATEGORY) + { + LLInventoryObject* obj = (LLInventoryObject*)(*it); + LLInventoryItem* item = (LLInventoryItem*)(obj); + LLUUID item_id; + item_id.generate(); + time_t creation_date_utc = time_corrected(); + LLPointer new_item + = new LLViewerInventoryItem(item_id, + new_cat_id, + item->getPermissions(), + item->getAssetUUID(), + item->getType(), + item->getInventoryType(), + item->getName(), + item->getDescription(), + LLSaleInfo::DEFAULT, + item->getFlags(), + creation_date_utc); + new_item->updateServer(TRUE); + gInventory.updateItem(new_item); + } + } + gInventory.notifyObservers(); - LLInventoryObject::object_list_t::const_iterator it = inventory->begin(); - LLInventoryObject::object_list_t::const_iterator end = inventory->end(); - for ( ; it != end; ++it) - { - if ((*it)->getType() != LLAssetType::AT_CATEGORY) - { - LLInventoryObject* obj = (LLInventoryObject*)(*it); - LLInventoryItem* item = (LLInventoryItem*)(obj); - LLUUID item_id; - item_id.generate(); - time_t creation_date_utc = time_corrected(); - LLPointer new_item - = new LLViewerInventoryItem(item_id, - cat_id, - item->getPermissions(), - item->getAssetUUID(), - item->getType(), - item->getInventoryType(), - item->getName(), - item->getDescription(), - LLSaleInfo::DEFAULT, - item->getFlags(), - creation_date_utc); - new_item->updateServer(TRUE); - gInventory.updateItem(new_item); - } - } - gInventory.notifyObservers(); - if(active_panel) - { - active_panel->setSelection(cat_id, TAKE_FOCUS_NO); - } + LLInventoryPanel *active_panel = LLInventoryPanel::getActiveInventoryPanel(); + if (active_panel) + { + active_panel->setSelection(new_cat_id, TAKE_FOCUS_NO); + } + }); } else if (inventory->size() == 2) { diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index d21d6f7027..a1d068461e 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -63,7 +63,6 @@ #include "llcontrolavatar.h" #include "lldrawable.h" #include "llface.h" -#include "llfloaterproperties.h" #include "llfloatertools.h" #include "llfollowcam.h" #include "llhudtext.h" @@ -3486,9 +3485,6 @@ void LLViewerObject::doInventoryCallback() void LLViewerObject::removeInventory(const LLUUID& item_id) { - // close any associated floater properties - LLFloaterReg::hideInstance("properties", item_id); - LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_RemoveTaskInventory); msg->nextBlockFast(_PREHASH_AgentData); @@ -3610,6 +3606,17 @@ LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id) return rv; } +LLInventoryItem* LLViewerObject::getInventoryItem(const LLUUID& item_id) +{ + LLInventoryObject* iobj = getInventoryObject(item_id); + if (!iobj || iobj->getType() == LLAssetType::AT_CATEGORY) + { + return NULL; + } + LLInventoryItem* item = dynamic_cast(iobj); + return item; +} + void LLViewerObject::getInventoryContents(LLInventoryObject::object_list_t& objects) { if(mInventory) @@ -6232,8 +6239,7 @@ LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 para } default: { - llassert(false); // invalid parameter type - LL_INFOS() << "Unknown param type." << LL_ENDL; + LL_INFOS_ONCE() << "Unknown param type: " << param_type << LL_ENDL; break; } }; diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 3665c64965..898b21e1ae 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -494,6 +494,7 @@ public: void updateInventoryLocal(LLInventoryItem* item, U8 key); // Update without messaging. void updateMaterialInventory(LLViewerInventoryItem* item, U8 key, bool is_new); LLInventoryObject* getInventoryObject(const LLUUID& item_id); + LLInventoryItem* getInventoryItem(const LLUUID& item_id); // Get content except for root category void getInventoryContents(LLInventoryObject::object_list_t& objects); diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index ce4f9b7e64..5bc7523be1 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -369,7 +369,7 @@ LLViewerObject* LLViewerObjectList::processObjectUpdateFromCache(LLVOCacheEntry* if (!objectp) { LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL; - recorder.objectUpdateFailure(entry->getLocalID(), OUT_FULL_CACHED, 0); + recorder.objectUpdateFailure(); return NULL; } justCreated = true; @@ -393,7 +393,6 @@ LLViewerObject* LLViewerObjectList::processObjectUpdateFromCache(LLVOCacheEntry* objectp->setLastUpdateType(OUT_FULL_COMPRESSED); //newly cached objectp->setLastUpdateCached(TRUE); } - recorder.log(0.2f); LLVOAvatar::cullAvatarsByPixelArea(); return objectp; @@ -472,18 +471,14 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, for (i = 0; i < num_objects; i++) { - // timer is unused? - LLTimer update_timer; BOOL justCreated = FALSE; - S32 msg_size = 0; bool update_cache = false; //update object cache if it is a full-update or terse update if (compressed) { - S32 uncompressed_length = 2048; compressed_dp.reset(); - uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); + S32 uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data); LL_DEBUGS("ObjectUpdate") << "got binary data from message to compressed_dpbuffer" << LL_ENDL; mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i, 2048); compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length); @@ -505,7 +500,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, << " Flags: " << flags << " Region: " << regionp->getName() << " Region id: " << regionp->getRegionID() << LL_ENDL; - recorder.objectUpdateFailure(local_id, update_type, msg_size); + recorder.objectUpdateFailure(); continue; } else if ((flags & FLAGS_TEMPORARY_ON_REZ) == 0) @@ -533,7 +528,6 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, else if (update_type != OUT_FULL) // !compressed, !OUT_FULL ==> OUT_FULL_CACHED only? { mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); - msg_size += sizeof(U32); getUUIDFromLocal(fullid, local_id, @@ -554,8 +548,6 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, update_cache = true; mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i); mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i); - msg_size += sizeof(LLUUID); - msg_size += sizeof(U32); LL_DEBUGS("ObjectUpdate") << "Full Update, obj " << local_id << ", global ID " << fullid << " from " << mesgsys->getSender() << LL_ENDL; } objectp = findObject(fullid); @@ -616,7 +608,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, if (update_type == OUT_TERSE_IMPROVED) { // LL_INFOS() << "terse update for an unknown object (compressed):" << fullid << LL_ENDL; - recorder.objectUpdateFailure(local_id, update_type, msg_size); + recorder.objectUpdateFailure(); continue; } } @@ -625,12 +617,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, if (update_type != OUT_FULL) { //LL_INFOS() << "terse update for an unknown object:" << fullid << LL_ENDL; - recorder.objectUpdateFailure(local_id, update_type, msg_size); + recorder.objectUpdateFailure(); continue; } mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_PCode, pcode, i); - msg_size += sizeof(U8); } #ifdef IGNORE_DEAD @@ -638,7 +629,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, { mNumDeadObjectUpdates++; //LL_INFOS() << "update for a dead object:" << fullid << LL_ENDL; - recorder.objectUpdateFailure(local_id, update_type, msg_size); + recorder.objectUpdateFailure(); continue; } #endif @@ -651,7 +642,7 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, if (!objectp) { LL_INFOS() << "createObject failure for object: " << fullid << LL_ENDL; - recorder.objectUpdateFailure(local_id, update_type, msg_size); + recorder.objectUpdateFailure(); continue; } @@ -681,11 +672,11 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, if(!(flags & FLAGS_TEMPORARY_ON_REZ)) { - bCached = true; + bCached = true; LLViewerRegion::eCacheUpdateResult result = objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp, flags); - recorder.cacheFullUpdate(local_id, update_type, result, objectp, msg_size); + recorder.cacheFullUpdate(result); + } } - } #endif } else @@ -696,12 +687,10 @@ void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, } processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated); } - recorder.objectUpdateEvent(local_id, update_type, objectp, msg_size); + recorder.objectUpdateEvent(update_type); objectp->setLastUpdateType(update_type); } - recorder.log(0.2f); - LLVOAvatar::cullAvatarsByPixelArea(); } @@ -734,28 +723,26 @@ void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys, for (S32 i = 0; i < num_objects; i++) { - S32 msg_size = 0; U32 id; U32 crc; U32 flags; mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, id, i); mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i); mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i); - msg_size += sizeof(U32) * 2; LL_DEBUGS("ObjectUpdate") << "got probe for id " << id << " crc " << crc << LL_ENDL; dumpStack("ObjectUpdateStack"); // Lookup data packer and add this id to cache miss lists if necessary. U8 cache_miss_type = LLViewerRegion::CACHE_MISS_TYPE_NONE; - if(!regionp->probeCache(id, crc, flags, cache_miss_type)) - { - // Cache Miss. + if (regionp->probeCache(id, crc, flags, cache_miss_type)) + { // Cache Hit + recorder.cacheHitEvent(); + } + else + { // Cache Miss LL_DEBUGS("ObjectUpdate") << "cache miss for id " << id << " crc " << crc << " miss type " << (S32) cache_miss_type << LL_ENDL; - - recorder.cacheMissEvent(id, update_type, cache_miss_type, msg_size); - - continue; // no data packer, skip this object + recorder.cacheMissEvent(cache_miss_type); } } @@ -773,7 +760,6 @@ void LLViewerObjectList::dirtyAllObjectInventory() void LLViewerObjectList::updateApparentAngles(LLAgent &agent) { S32 i; - S32 num_objects = 0; LLViewerObject *objectp; S32 num_updates, max_value; @@ -795,8 +781,6 @@ void LLViewerObjectList::updateApparentAngles(LLAgent &agent) objectp = mObjects[i]; if (!objectp->isDead()) { - num_objects++; - // Update distance & gpw objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area objectp->updateTextures(); // Update the image levels of textures for this object. diff --git a/indra/newview/llvieweroctree.cpp b/indra/newview/llvieweroctree.cpp index a2d8d30fb2..9d63241300 100644 --- a/indra/newview/llvieweroctree.cpp +++ b/indra/newview/llvieweroctree.cpp @@ -627,13 +627,17 @@ void LLViewerOctreeGroup::handleRemoval(const TreeNode* node, LLViewerOctreeEntr //virtual void LLViewerOctreeGroup::handleDestruction(const TreeNode* node) { + if (isDead()) + { + return; + } + setState(DEAD); for (OctreeNode::element_iter i = mOctreeNode->getDataBegin(); i != mOctreeNode->getDataEnd(); ++i) { LLViewerOctreeEntry* obj = *i; if (obj && obj->getGroup() == this) { obj->nullGroup(); - //obj->setGroup(NULL); } } mOctreeNode = NULL; diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 97dc916bfe..15accd0547 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -72,12 +72,16 @@ #include "llenvironment.h" -const F32 PARCEL_COLLISION_DRAW_SECS = 1.f; +const F32 PARCEL_BAN_LINES_DRAW_SECS_ON_COLLISION = 10.f; +const F32 PARCEL_COLLISION_DRAW_SECS_ON_PROXIMITY = 1.f; // Globals U8* LLViewerParcelMgr::sPackedOverlay = NULL; +S32 LLViewerParcelMgr::PARCEL_BAN_LINES_HIDE = 0; +S32 LLViewerParcelMgr::PARCEL_BAN_LINES_ON_COLLISION = 1; +S32 LLViewerParcelMgr::PARCEL_BAN_LINES_ON_PROXIMITY = 2; LLUUID gCurrentMovieID = LLUUID::null; @@ -892,13 +896,18 @@ void LLViewerParcelMgr::render() void LLViewerParcelMgr::renderParcelCollision() { + static LLCachedControl ban_lines_mode(gSavedSettings , "ShowBanLines" , PARCEL_BAN_LINES_ON_COLLISION); + // check for expiration - if (mCollisionTimer.getElapsedTimeF32() > PARCEL_COLLISION_DRAW_SECS) + F32 expiration = (ban_lines_mode == PARCEL_BAN_LINES_ON_PROXIMITY) + ? PARCEL_COLLISION_DRAW_SECS_ON_PROXIMITY + : PARCEL_BAN_LINES_DRAW_SECS_ON_COLLISION; + if (mCollisionTimer.getElapsedTimeF32() > expiration) { - mRenderCollision = FALSE; + mRenderCollision = false; } - if (mRenderCollision && gSavedSettings.getBOOL("ShowBanLines")) + if (mRenderCollision && ban_lines_mode != PARCEL_BAN_LINES_HIDE) { LLViewerRegion* regionp = gAgent.getRegion(); if (regionp) @@ -1842,8 +1851,11 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID) { // We're about to collide with this parcel - parcel_mgr.mRenderCollision = TRUE; - parcel_mgr.mCollisionTimer.reset(); + static LLCachedControl ban_lines_mode(gSavedSettings , "ShowBanLines" , PARCEL_BAN_LINES_ON_COLLISION); + if (ban_lines_mode == PARCEL_BAN_LINES_ON_PROXIMITY) + { + parcel_mgr.resetCollisionTimer(); + } // Differentiate this parcel if we are banned from it. if (sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID) diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 6ce389ab88..56dacd3efd 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -205,6 +205,11 @@ public: void renderHighlightSegments(const U8* segments, LLViewerRegion* regionp); void renderCollisionSegments(U8* segments, BOOL use_pass, LLViewerRegion* regionp); + static S32 PARCEL_BAN_LINES_HIDE; + static S32 PARCEL_BAN_LINES_ON_COLLISION; + static S32 PARCEL_BAN_LINES_ON_PROXIMITY; + void resetCollisionTimer(); // Ban lines visibility timer + void sendParcelGodForceOwner(const LLUUID& owner_id); // make the selected parcel a content parcel. @@ -361,7 +366,7 @@ private: // If it's coming, draw the parcel's boundaries. LLParcel* mCollisionParcel; U8* mCollisionSegments; - BOOL mRenderCollision; + bool mRenderCollision; BOOL mRenderSelection; S32 mCollisionBanned; LLFrameTimer mCollisionTimer; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 9d3e63333e..d54ce2234d 100755 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -105,6 +105,9 @@ S32 LLViewerRegion::sLastCameraUpdated = 0; S32 LLViewerRegion::sNewObjectCreationThrottle = -1; LLViewerRegion::vocache_entry_map_t LLViewerRegion::sRegionCacheCleanup; +const std::string LLViewerRegion::IL_MODE_DEFAULT = "default"; +const std::string LLViewerRegion::IL_MODE_360 = "360"; + typedef std::map CapabilityMap; static void log_capabilities(const CapabilityMap &capmap); @@ -132,8 +135,8 @@ class LLRegionHandler : public LLCommandHandler public: // requests will be throttled from a non-trusted browser LLRegionHandler() : LLCommandHandler("region", UNTRUSTED_THROTTLE) {} - - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { // make sure that we at least have a region name int num_params = params.size(); @@ -144,6 +147,10 @@ public: // build a secondlife://{PLACE} SLurl from this SLapp std::string url = "secondlife://"; + if (!grid.empty()) + { + url += grid + "/secondlife/"; + } boost::regex name_rx("[A-Za-z0-9()_%]+"); boost::regex coord_rx("[0-9]+"); for (int i = 0; i < num_params; i++) @@ -215,7 +222,7 @@ public: LLVOCacheEntry::vocache_entry_set_t mVisibleEntries; //must-be-created visible entries wait for objects creation. LLVOCacheEntry::vocache_entry_priority_list_t mWaitingList; //transient list storing sorted visible entries waiting for object creation. std::set mNonCacheableCreatedList; //list of local ids of all non-cacheable objects - LLVOCacheEntry::vocache_gltf_overrides_map_t mGLTFOverridesJson; // for materials + LLVOCacheEntry::vocache_gltf_overrides_map_t mGLTFOverridesLLSD; // for materials // time? // LRU info? @@ -644,7 +651,8 @@ LLViewerRegion::LLViewerRegion(const U64 &handle, mInvisibilityCheckHistory(-1), mPaused(FALSE), mRegionCacheHitCount(0), - mRegionCacheMissCount(0) + mRegionCacheMissCount(0), + mInterestListMode(IL_MODE_DEFAULT) { mWidth = region_width_meters; mImpl->mOriginGlobal = from_region_handle(handle); @@ -787,7 +795,7 @@ void LLViewerRegion::loadObjectCache() { LLVOCache & vocache = LLVOCache::instance(); vocache.readFromCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap); - vocache.readGenericExtrasFromCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesJson); + vocache.readGenericExtrasFromCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesLLSD); if (mImpl->mCacheMap.empty()) { @@ -817,7 +825,7 @@ void LLViewerRegion::saveObjectCache() LLVOCache & instance = LLVOCache::instance(); instance.writeToCache(mHandle, mImpl->mCacheID, mImpl->mCacheMap, mCacheDirty, removal_enabled); - instance.writeGenericExtrasToCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesJson, mCacheDirty, removal_enabled); + instance.writeGenericExtrasToCache(mHandle, mImpl->mCacheID, mImpl->mGLTFOverridesLLSD, mCacheDirty, removal_enabled); mCacheDirty = FALSE; } @@ -2656,7 +2664,7 @@ LLViewerRegion::eCacheUpdateResult LLViewerRegion::cacheFullUpdate(LLViewerObjec void LLViewerRegion::cacheFullUpdateGLTFOverride(const LLGLTFOverrideCacheEntry &override_data) { U32 local_id = override_data.mLocalId; - mImpl->mGLTFOverridesJson[local_id] = override_data; + mImpl->mGLTFOverridesLLSD[local_id] = override_data; } LLVOCacheEntry* LLViewerRegion::getCacheEntryForOctree(U32 local_id) @@ -2685,14 +2693,10 @@ LLVOCacheEntry* LLViewerRegion::getCacheEntry(U32 local_id, bool valid) return NULL; } -void LLViewerRegion::addCacheMiss(U32 id, LLViewerRegion::eCacheMissType miss_type) +void LLViewerRegion::addCacheMiss(U32 id, LLViewerRegion::eCacheMissType cache_miss_type) { mRegionCacheMissCount++; -#if 0 - mCacheMissList.insert(CacheMissItem(id, miss_type)); -#else - mCacheMissList.push_back(CacheMissItem(id, miss_type)); -#endif + mCacheMissList.push_back(CacheMissItem(id, cache_miss_type)); } //check if a non-cacheable object is already created. @@ -2771,10 +2775,10 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss } } else - { + { // Total miss, don't have the object in cache // LL_INFOS() << "Cache miss for " << local_id << LL_ENDL; - addCacheMiss(local_id, CACHE_MISS_TYPE_FULL); - cache_miss_type = CACHE_MISS_TYPE_FULL; + addCacheMiss(local_id, CACHE_MISS_TYPE_TOTAL); + cache_miss_type = CACHE_MISS_TYPE_TOTAL; } return false; @@ -2782,7 +2786,7 @@ bool LLViewerRegion::probeCache(U32 local_id, U32 crc, U32 flags, U8 &cache_miss void LLViewerRegion::addCacheMissFull(const U32 local_id) { - addCacheMiss(local_id, CACHE_MISS_TYPE_FULL); + addCacheMiss(local_id, CACHE_MISS_TYPE_TOTAL); } void LLViewerRegion::requestCacheMisses() @@ -2833,7 +2837,6 @@ void LLViewerRegion::requestCacheMisses() mCacheDirty = TRUE ; // LL_INFOS() << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << LL_ENDL; LLViewerStatsRecorder::instance().requestCacheMissesEvent(mCacheMissList.size()); - LLViewerStatsRecorder::instance().log(0.2f); mCacheMissList.clear(); } @@ -3095,6 +3098,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("InterestList"); + capabilityNames.append("InventoryThumbnailUpload"); capabilityNames.append("GetDisplayNames"); capabilityNames.append("GetExperiences"); capabilityNames.append("AgentExperiences"); @@ -3358,6 +3362,9 @@ void LLViewerRegion::setCapabilitiesReceived(bool received) // This is a single-shot signal. Forget callbacks to save resources. mCapabilitiesReceivedSignal.disconnect_all_slots(); + + // Set the region to the desired interest list mode + setInterestListMode(gAgent.getInterestListMode()); } } @@ -3376,7 +3383,111 @@ void LLViewerRegion::logActiveCapabilities() const log_capabilities(mImpl->mCapabilities); } -LLSpatialPartition* LLViewerRegion::getSpatialPartition(U32 type) + +bool LLViewerRegion::requestPostCapability(const std::string &capName, LLSD &postData, httpCallback_t cbSuccess, httpCallback_t cbFailure) +{ + std::string url = getCapability(capName); + + if (url.empty()) + { + LL_WARNS("Region") << "Could not retrieve region " << getRegionID() + << " POST capability \"" << capName << "\"" << LL_ENDL; + return false; + } + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpPost(url, gAgent.getAgentPolicy(), postData, cbSuccess, cbFailure); + return true; +} + +bool LLViewerRegion::requestGetCapability(const std::string &capName, httpCallback_t cbSuccess, httpCallback_t cbFailure) +{ + std::string url; + + url = getCapability(capName); + + if (url.empty()) + { + LL_WARNS("Region") << "Could not retrieve region " << getRegionID() + << " GET capability \"" << capName << "\"" << LL_ENDL; + return false; + } + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpGet(url, gAgent.getAgentPolicy(), cbSuccess, cbFailure); + return true; +} + +bool LLViewerRegion::requestDelCapability(const std::string &capName, httpCallback_t cbSuccess, httpCallback_t cbFailure) +{ + std::string url; + + url = getCapability(capName); + + if (url.empty()) + { + LL_WARNS("Region") << "Could not retrieve region " << getRegionID() << " DEL capability \"" << capName << "\"" << LL_ENDL; + return false; + } + + LLCoreHttpUtil::HttpCoroutineAdapter::callbackHttpDel(url, gAgent.getAgentPolicy(), cbSuccess, cbFailure); + return true; +} + +void LLViewerRegion::setInterestListMode(const std::string &new_mode) +{ + if (new_mode != mInterestListMode) + { + mInterestListMode = new_mode; + + if (mInterestListMode != std::string(IL_MODE_DEFAULT) && mInterestListMode != std::string(IL_MODE_360)) + { + LL_WARNS("360Capture") << "Region " << getRegionID() << " setInterestListMode() invalid interest list mode: " + << mInterestListMode << ", setting to default" << LL_ENDL; + mInterestListMode = IL_MODE_DEFAULT; + } + + LLSD body; + body["mode"] = mInterestListMode; + if (requestPostCapability("InterestList", body, + [](const LLSD &response) { + LL_DEBUGS("360Capture") << "InterestList capability responded: \n" + << ll_pretty_print_sd(response) << LL_ENDL; + })) + { + LL_DEBUGS("360Capture") << "Region " << getRegionID() + << " Successfully posted an InterestList capability request with payload: \n" + << ll_pretty_print_sd(body) << LL_ENDL; + } + else + { + LL_WARNS("360Capture") << "Region " << getRegionID() + << " Unable to post an InterestList capability request with payload: \n" + << ll_pretty_print_sd(body) << LL_ENDL; + } + } + else + { + LL_DEBUGS("360Capture") << "Region " << getRegionID() << "No change, skipping Interest List mode POST to " + << new_mode << " mode" << LL_ENDL; + } +} + + +void LLViewerRegion::resetInterestList() +{ + if (requestDelCapability("InterestList", [](const LLSD &response) { + LL_DEBUGS("360Capture") << "InterestList capability DEL responded: \n" << ll_pretty_print_sd(response) << LL_ENDL; + })) + { + LL_DEBUGS("360Capture") << "Region " << getRegionID() << " Successfully reset InterestList capability" << LL_ENDL; + } + else + { + LL_WARNS("360Capture") << "Region " << getRegionID() << " Unable to DEL InterestList capability request" << LL_ENDL; + } +} + + +LLSpatialPartition *LLViewerRegion::getSpatialPartition(U32 type) { if (type < mImpl->mObjectPartition.size() && type < PARTITION_VO_CACHE) { @@ -3546,8 +3657,8 @@ std::string LLViewerRegion::getSimHostName() void LLViewerRegion::loadCacheMiscExtras(U32 local_id) { - auto iter = mImpl->mGLTFOverridesJson.find(local_id); - if (iter != mImpl->mGLTFOverridesJson.end()) + auto iter = mImpl->mGLTFOverridesLLSD.find(local_id); + if (iter != mImpl->mGLTFOverridesLLSD.end()) { LLGLTFMaterialList::loadCacheOverrides(iter->second); } @@ -3559,8 +3670,8 @@ void LLViewerRegion::applyCacheMiscExtras(LLViewerObject* obj) llassert(obj); U32 local_id = obj->getLocalID(); - auto iter = mImpl->mGLTFOverridesJson.find(local_id); - if (iter != mImpl->mGLTFOverridesJson.end()) + auto iter = mImpl->mGLTFOverridesLLSD.find(local_id); + if (iter != mImpl->mGLTFOverridesLLSD.end()) { llassert(iter->second.mGLTFMaterial.size() == iter->second.mSides.size()); diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h index fe0fdfb930..a409d837a4 100644 --- a/indra/newview/llviewerregion.h +++ b/indra/newview/llviewerregion.h @@ -32,6 +32,7 @@ #include #include +#include "llcorehttputil.h" #include "llwind.h" #include "v3dmath.h" #include "llstring.h" @@ -280,6 +281,16 @@ public: static bool isSpecialCapabilityName(const std::string &name); void logActiveCapabilities() const; + // Utilities to post and get via + // HTTP using the agent's policy settings and headers. + typedef LLCoreHttpUtil::HttpCoroutineAdapter::completionCallback_t httpCallback_t; + bool requestPostCapability(const std::string &capName, + LLSD &postData, + httpCallback_t cbSuccess = NULL, + httpCallback_t cbFailure = NULL); + bool requestGetCapability(const std::string &capName, httpCallback_t cbSuccess = NULL, httpCallback_t cbFailure = NULL); + bool requestDelCapability(const std::string &capName, httpCallback_t cbSuccess = NULL, httpCallback_t cbFailure = NULL); + /// implements LLCapabilityProvider /*virtual*/ const LLHost& getHost() const; const U64 &getHandle() const { return mHandle; } @@ -334,9 +345,9 @@ public: typedef enum { - CACHE_MISS_TYPE_FULL = 0, - CACHE_MISS_TYPE_CRC, - CACHE_MISS_TYPE_NONE + CACHE_MISS_TYPE_TOTAL = 0, // total cache miss - object not in cache + CACHE_MISS_TYPE_CRC, // object in cache, but CRC doesn't match + CACHE_MISS_TYPE_NONE // not a miss: cache hit } eCacheMissType; typedef enum @@ -489,7 +500,15 @@ public: }; typedef std::set region_priority_list_t; -private: + void setInterestListMode(const std::string & new_mode); + const std::string & getInterestListMode() const { return mInterestListMode; } + + void resetInterestList(); + + static const std::string IL_MODE_DEFAULT; + static const std::string IL_MODE_360; + + private: static S32 sNewObjectCreationThrottle; LLViewerRegionImpl * mImpl; LLFrameTimer mRegionTimer; @@ -563,10 +582,10 @@ private: class CacheMissItem { public: - CacheMissItem(U32 id, LLViewerRegion::eCacheMissType miss_type) : mID(id), mType(miss_type){} + CacheMissItem(U32 id, LLViewerRegion::eCacheMissType miss_type) : mID(id), mType(miss_type) {} - U32 mID; //local object id - LLViewerRegion::eCacheMissType mType; //cache miss type + U32 mID; //local object id + LLViewerRegion::eCacheMissType mType; // cache miss type typedef std::list cache_miss_list_t; }; @@ -587,6 +606,9 @@ private: LLFrameTimer mRenderInfoRequestTimer; LLFrameTimer mRenderInfoReportTimer; + // how the server interest list works + std::string mInterestListMode; + // list of reflection maps being managed by this llviewer region std::vector > mReflectionMaps; diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index d545ef97fd..9bd5814ed4 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -420,9 +420,6 @@ void LLViewerShaderMgr::setShaders() gViewerWindow->setCursor(UI_CURSOR_WAIT); } - // Lighting - gPipeline.setLightingDetail(-1); - // Shaders LL_INFOS("ShaderLoading") << "\n~~~~~~~~~~~~~~~~~~\n Loading Shaders:\n~~~~~~~~~~~~~~~~~~" << LL_ENDL; LL_INFOS("ShaderLoading") << llformat("Using GLSL %d.%d", gGLManager.mGLSLVersionMajor, gGLManager.mGLSLVersionMinor) << LL_ENDL; @@ -587,21 +584,6 @@ std::string LLViewerShaderMgr::loadBasicShaders() S32 sum_lights_class = 3; - // class one cards will get the lower sum lights - // class zero we're not going to think about - // since a class zero card COULD be a ridiculous new card - // and old cards should have the features masked - if(LLFeatureManager::getInstance()->getGPUClass() == GPU_CLASS_1) - { - sum_lights_class = 2; - } - - // If we have sun and moon only checked, then only sum those lights. - if (gPipeline.getLightingDetail() == 0) - { - sum_lights_class = 1; - } - #if LL_DARWIN // Work around driver crashes on older Macs when using deferred rendering // NORSPEC-59 @@ -643,30 +625,12 @@ std::string LLViewerShaderMgr::loadBasicShaders() attribs["MAX_JOINTS_PER_MESH_OBJECT"] = boost::lexical_cast(LLSkinningUtil::getMaxJointCount()); - BOOL ambient_kill = gSavedSettings.getBOOL("AmbientDisable"); - BOOL sunlight_kill = gSavedSettings.getBOOL("SunlightDisable"); - BOOL local_light_kill = gSavedSettings.getBOOL("LocalLightDisable"); BOOL ssr = gSavedSettings.getBOOL("RenderScreenSpaceReflections"); bool has_reflection_probes = gSavedSettings.getBOOL("RenderReflectionsEnabled") && gGLManager.mGLVersion > 3.99f; S32 probe_level = llclamp(gSavedSettings.getS32("RenderReflectionProbeLevel"), 0, 3); - if (ambient_kill) - { - attribs["AMBIENT_KILL"] = "1"; - } - - if (sunlight_kill) - { - attribs["SUNLIGHT_KILL"] = "1"; - } - - if (local_light_kill) - { - attribs["LOCAL_LIGHT_KILL"] = "1"; - } - S32 shadow_detail = gSavedSettings.getS32("RenderShadowDetail"); if (shadow_detail >= 1) @@ -915,11 +879,20 @@ BOOL LLViewerShaderMgr::loadShadersEffects() if (success) { - gGlowExtractProgram.mName = "Glow Extract Shader (Post)"; + const bool use_glow_noise = gSavedSettings.getBOOL("RenderGlowNoise"); + const std::string glow_noise_label = use_glow_noise ? " (+Noise)" : ""; + + gGlowExtractProgram.mName = llformat("Glow Extract Shader (Post)%s", glow_noise_label.c_str()); gGlowExtractProgram.mShaderFiles.clear(); gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractV.glsl", GL_VERTEX_SHADER)); gGlowExtractProgram.mShaderFiles.push_back(make_pair("effects/glowExtractF.glsl", GL_FRAGMENT_SHADER)); gGlowExtractProgram.mShaderLevel = mShaderLevel[SHADER_EFFECT]; + + if (use_glow_noise) + { + gGlowExtractProgram.addPermutation("HAS_NOISE", "1"); + } + success = gGlowExtractProgram.createShader(NULL, NULL); if (!success) { @@ -937,10 +910,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() bool use_sun_shadow = mShaderLevel[SHADER_DEFERRED] > 1 && gSavedSettings.getS32("RenderShadowDetail") > 0; - BOOL ambient_kill = gSavedSettings.getBOOL("AmbientDisable"); - BOOL sunlight_kill = gSavedSettings.getBOOL("SunlightDisable"); - BOOL local_light_kill = gSavedSettings.getBOOL("LocalLightDisable"); - if (mShaderLevel[SHADER_DEFERRED] == 0) { gDeferredTreeProgram.unload(); @@ -1171,21 +1140,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredMaterialProgram[i].addPermutation("HAS_SPECULAR_MAP", "1"); } - if (ambient_kill) - { - gDeferredMaterialProgram[i].addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredMaterialProgram[i].addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredMaterialProgram[i].addPermutation("LOCAL_LIGHT_KILL", "1"); - } - gDeferredMaterialProgram[i].addPermutation("DIFFUSE_ALPHA_MODE", llformat("%d", alpha_mode)); if (alpha_mode != 0) @@ -1274,21 +1228,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() } gDeferredMaterialWaterProgram[i].addPermutation("WATER_FOG","1"); - if (ambient_kill) - { - gDeferredMaterialWaterProgram[i].addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredMaterialWaterProgram[i].addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredMaterialWaterProgram[i].addPermutation("LOCAL_LIGHT_KILL", "1"); - } - gDeferredMaterialWaterProgram[i].mFeatures.hasReflectionProbes = true; gDeferredMaterialWaterProgram[i].mFeatures.hasWaterFog = true; gDeferredMaterialWaterProgram[i].mFeatures.hasSrgb = true; @@ -1602,21 +1541,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredLightProgram.clearPermutations(); - if (ambient_kill) - { - gDeferredLightProgram.addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredLightProgram.addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredLightProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); - } - success = gDeferredLightProgram.createShader(NULL, NULL); llassert(success); } @@ -1637,21 +1561,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredMultiLightProgram[i].mShaderLevel = mShaderLevel[SHADER_DEFERRED]; gDeferredMultiLightProgram[i].addPermutation("LIGHT_COUNT", llformat("%d", i+1)); - if (ambient_kill) - { - gDeferredMultiLightProgram[i].addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredMultiLightProgram[i].addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredMultiLightProgram[i].addPermutation("LOCAL_LIGHT_KILL", "1"); - } - success = gDeferredMultiLightProgram[i].createShader(NULL, NULL); llassert(success); } @@ -1670,21 +1579,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/spotLightF.glsl", GL_FRAGMENT_SHADER)); gDeferredSpotLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - if (ambient_kill) - { - gDeferredSpotLightProgram.addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredSpotLightProgram.addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredSpotLightProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); - } - success = gDeferredSpotLightProgram.createShader(NULL, NULL); llassert(success); } @@ -1697,16 +1591,12 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredMultiSpotLightProgram.mFeatures.hasShadows = true; gDeferredMultiSpotLightProgram.clearPermutations(); + gDeferredMultiSpotLightProgram.addPermutation("MULTI_SPOTLIGHT", "1"); gDeferredMultiSpotLightProgram.mShaderFiles.clear(); gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/multiPointLightV.glsl", GL_VERTEX_SHADER)); - gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/multiSpotLightF.glsl", GL_FRAGMENT_SHADER)); + gDeferredMultiSpotLightProgram.mShaderFiles.push_back(make_pair("deferred/spotLightF.glsl", GL_FRAGMENT_SHADER)); gDeferredMultiSpotLightProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; - if (local_light_kill) - { - gDeferredMultiSpotLightProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); - } - success = gDeferredMultiSpotLightProgram.createShader(NULL, NULL); llassert(success); } @@ -1812,21 +1702,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() shader->addPermutation("HAS_SUN_SHADOW", "1"); } - if (ambient_kill) - { - shader->addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - shader->addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - shader->addPermutation("LOCAL_LIGHT_KILL", "1"); - } - if (rigged) { shader->addPermutation("HAS_SKIN", "1"); @@ -1950,21 +1825,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() shader[i]->addPermutation("HAS_SUN_SHADOW", "1"); } - if (ambient_kill) - { - shader[i]->addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - shader[i]->addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - shader[i]->addPermutation("LOCAL_LIGHT_KILL", "1"); - } - if (i == 1) { // rigged variant shader[i]->mFeatures.hasObjectSkinning = true; @@ -2260,21 +2120,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredSoftenProgram.addPermutation("HAS_SUN_SHADOW", "1"); } - if (ambient_kill) - { - gDeferredSoftenProgram.addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredSoftenProgram.addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredSoftenProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); - } - if (gSavedSettings.getBOOL("RenderDeferredSSAO")) { //if using SSAO, take screen space light map into account as if shadows are enabled gDeferredSoftenProgram.mShaderLevel = llmax(gDeferredSoftenProgram.mShaderLevel, 2); @@ -2310,25 +2155,10 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredSoftenWaterProgram.addPermutation("HAS_SUN_SHADOW", "1"); } - if (ambient_kill) - { - gDeferredSoftenWaterProgram.addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredSoftenWaterProgram.addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredSoftenWaterProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); - gDeferredSoftenWaterProgram.addPermutation("HAS_SSAO", "1"); - } - if (gSavedSettings.getBOOL("RenderDeferredSSAO")) { //if using SSAO, take screen space light map into account as if shadows are enabled gDeferredSoftenWaterProgram.mShaderLevel = llmax(gDeferredSoftenWaterProgram.mShaderLevel, 2); + gDeferredSoftenWaterProgram.addPermutation("HAS_SSAO", "1"); } success = gDeferredSoftenWaterProgram.createShader(NULL, NULL); @@ -2533,20 +2363,6 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarAlphaProgram.addPermutation("HAS_SUN_SHADOW", "1"); } - if (ambient_kill) - { - gDeferredAvatarAlphaProgram.addPermutation("AMBIENT_KILL", "1"); - } - - if (sunlight_kill) - { - gDeferredAvatarAlphaProgram.addPermutation("SUNLIGHT_KILL", "1"); - } - - if (local_light_kill) - { - gDeferredAvatarAlphaProgram.addPermutation("LOCAL_LIGHT_KILL", "1"); - } gDeferredAvatarAlphaProgram.mShaderLevel = mShaderLevel[SHADER_DEFERRED]; success = gDeferredAvatarAlphaProgram.createShader(NULL, NULL); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 37999e58ff..6ac94fe4c4 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -171,7 +171,7 @@ SimMeasurement<> SIM_TIME_DILATION("simtimedilation", "Simulator time scale", SIM_MAIN_AGENTS("simmainagents", "Number of avatars in current region", LL_SIM_STAT_NUMAGENTMAIN), SIM_CHILD_AGENTS("simchildagents", "Number of avatars in neighboring regions", LL_SIM_STAT_NUMAGENTCHILD), SIM_OBJECTS("simobjects", "", LL_SIM_STAT_NUMTASKS), - SIM_ACTIVE_OBJECTS("simactiveobjects", "Number of scripted and/or mocing objects", LL_SIM_STAT_NUMTASKSACTIVE), + SIM_ACTIVE_OBJECTS("simactiveobjects", "Number of scripted and/or moving objects", LL_SIM_STAT_NUMTASKSACTIVE), SIM_ACTIVE_SCRIPTS("simactivescripts", "Number of scripted objects", LL_SIM_STAT_NUMSCRIPTSACTIVE), SIM_IN_PACKETS_PER_SEC("siminpps", "", LL_SIM_STAT_INPPS), SIM_OUT_PACKETS_PER_SEC("simoutpps", "", LL_SIM_STAT_OUTPPS), @@ -192,7 +192,6 @@ LLTrace::SampleStatHandle<> FPS_SAMPLE("fpssample"), NUM_OBJECTS("numobjectsstat"), NUM_ACTIVE_OBJECTS("numactiveobjectsstat"), ENABLE_VBO("enablevbo", "Vertex Buffers Enabled"), - LIGHTING_DETAIL("lightingdetail", "Lighting Detail"), VISIBLE_AVATARS("visibleavatars", "Visible Avatars"), SHADER_OBJECTS("shaderobjects", "Object Shaders"), DRAW_DISTANCE("drawdistance", "Draw Distance"), @@ -402,7 +401,6 @@ void update_statistics() record(LLStatViewer::TRIANGLES_DRAWN_PER_FRAME, last_frame_recording.getSum(LLStatViewer::TRIANGLES_DRAWN)); sample(LLStatViewer::ENABLE_VBO, (F64)gSavedSettings.getBOOL("RenderVBOEnable")); - sample(LLStatViewer::LIGHTING_DETAIL, (F64)gPipeline.getLightingDetail()); sample(LLStatViewer::DRAW_DISTANCE, (F64)gSavedSettings.getF32("RenderFarClip")); sample(LLStatViewer::CHAT_BUBBLES, gSavedSettings.getBOOL("UseChatBubbles")); diff --git a/indra/newview/llviewerstatsrecorder.cpp b/indra/newview/llviewerstatsrecorder.cpp index b5ccf4ffa0..6372679a07 100644 --- a/indra/newview/llviewerstatsrecorder.cpp +++ b/indra/newview/llviewerstatsrecorder.cpp @@ -28,24 +28,25 @@ #include "llviewerstatsrecorder.h" +#include "llcontrol.h" #include "llfile.h" #include "llviewerregion.h" #include "llviewerobject.h" +#include "llworld.h" - -// To do - something using region name or global position -#if LL_WINDOWS - static const std::string STATS_FILE_NAME("C:\\ViewerObjectCacheStats.csv"); -#else - static const std::string STATS_FILE_NAME("/tmp/viewerstats.csv"); -#endif +extern LLControlGroup gSavedSettings; LLViewerStatsRecorder* LLViewerStatsRecorder::sInstance = NULL; LLViewerStatsRecorder::LLViewerStatsRecorder() : - mObjectCacheFile(NULL), + mStatsFile(NULL), mTimer(), - mStartTime(0.0), - mLastSnapshotTime(0.0) + mFileOpenTime(0.0), + mLastSnapshotTime(0.0), + mEnableStatsRecording(false), + mEnableStatsLogging(false), + mInterval(0.2), + mMaxDuration(300.f), + mSkipSaveIfZeros(false) { if (NULL != sInstance) { @@ -57,79 +58,70 @@ LLViewerStatsRecorder::LLViewerStatsRecorder() : LLViewerStatsRecorder::~LLViewerStatsRecorder() { - if (mObjectCacheFile != NULL) + if (mStatsFile) { - // last chance snapshot - writeToLog(0.f); - LLFile::close(mObjectCacheFile); - mObjectCacheFile = NULL; + writeToLog(0.f); // Save last data + closeStatsFile(); } } void LLViewerStatsRecorder::clearStats() { mObjectCacheHitCount = 0; - mObjectCacheHitSize = 0; mObjectCacheMissFullCount = 0; - mObjectCacheMissFullSize = 0; mObjectCacheMissCrcCount = 0; - mObjectCacheMissCrcSize = 0; mObjectFullUpdates = 0; - mObjectFullUpdatesSize = 0; mObjectTerseUpdates = 0; - mObjectTerseUpdatesSize = 0; mObjectCacheMissRequests = 0; - mObjectCacheMissResponses = 0; - mObjectCacheMissResponsesSize = 0; mObjectCacheUpdateDupes = 0; mObjectCacheUpdateChanges = 0; mObjectCacheUpdateAdds = 0; mObjectCacheUpdateReplacements = 0; mObjectUpdateFailures = 0; - mObjectUpdateFailuresSize = 0; - mTextureFetchSize = 0; + mTextureFetchCount = 0; + mMeshLoadedCount = 0; + mObjectKills = 0; } -void LLViewerStatsRecorder::recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size) +void LLViewerStatsRecorder::enableObjectStatsRecording(bool enable, bool logging /* false */) { - mObjectUpdateFailures++; - mObjectUpdateFailuresSize += msg_size; + mEnableStatsRecording = enable; + + // if logging is stopping, close the file + if (mStatsFile && !logging) + { + writeToLog(0.f); // Save last data + closeStatsFile(); + } + mEnableStatsLogging = logging; } -void LLViewerStatsRecorder::recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size) + + +void LLViewerStatsRecorder::recordCacheMissEvent(U8 cache_miss_type) { - if (LLViewerRegion::CACHE_MISS_TYPE_FULL == cache_miss_type) + if (LLViewerRegion::CACHE_MISS_TYPE_TOTAL == cache_miss_type) { mObjectCacheMissFullCount++; - mObjectCacheMissFullSize += msg_size; } else { mObjectCacheMissCrcCount++; - mObjectCacheMissCrcSize += msg_size; } } -void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size) + +void LLViewerStatsRecorder::recordObjectUpdateEvent(const EObjectUpdateType update_type) { - switch (update_type) + switch (update_type) { case OUT_FULL: - mObjectFullUpdates++; - mObjectFullUpdatesSize += msg_size; + case OUT_FULL_COMPRESSED: + mObjectFullUpdates++; break; case OUT_TERSE_IMPROVED: mObjectTerseUpdates++; - mObjectTerseUpdatesSize += msg_size; - break; - case OUT_FULL_COMPRESSED: - mObjectCacheMissResponses++; - mObjectCacheMissResponsesSize += msg_size; - break; - case OUT_FULL_CACHED: - mObjectCacheHitCount++; - mObjectCacheHitSize += msg_size; break; default: LL_WARNS() << "Unknown update_type" << LL_ENDL; @@ -137,9 +129,9 @@ void LLViewerStatsRecorder::recordObjectUpdateEvent(U32 local_id, const EObjectU }; } -void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size) +void LLViewerStatsRecorder::recordCacheFullUpdate(LLViewerRegion::eCacheUpdateResult update_result) { - switch (update_result) + switch (update_result) { case LLViewerRegion::CACHE_UPDATE_DUPE: mObjectCacheUpdateDupes++; @@ -154,124 +146,176 @@ void LLViewerStatsRecorder::recordCacheFullUpdate(U32 local_id, const EObjectUpd mObjectCacheUpdateReplacements++; break; default: - LL_WARNS() << "Unknown update_result type" << LL_ENDL; + LL_WARNS() << "Unknown update_result type " << (S32) update_result << LL_ENDL; break; }; } -void LLViewerStatsRecorder::recordRequestCacheMissesEvent(S32 count) -{ - mObjectCacheMissRequests += count; -} - void LLViewerStatsRecorder::writeToLog( F32 interval ) { + if (!mEnableStatsLogging || !mEnableStatsRecording) + { + return; + } + size_t data_size = 0; - F64 delta_time = LLTimer::getTotalSeconds() - mLastSnapshotTime; - S32 total_objects = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheMissResponses + mObjectCacheUpdateDupes + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; + F64 delta_time = LLFrameTimer::getTotalSeconds() - mLastSnapshotTime; + if (delta_time < interval) + return; - if ( delta_time < interval || total_objects == 0) return; + if (mSkipSaveIfZeros) + { + S32 total_events = mObjectCacheHitCount + mObjectCacheMissCrcCount + mObjectCacheMissFullCount + mObjectFullUpdates + + mObjectTerseUpdates + mObjectCacheMissRequests + mObjectCacheUpdateDupes + + mObjectCacheUpdateChanges + mObjectCacheUpdateAdds + mObjectCacheUpdateReplacements + mObjectUpdateFailures; + if (total_events == 0) + { + LL_DEBUGS("ILXZeroData") << "ILX: not saving zero data" << LL_ENDL; + return; + } + } - mLastSnapshotTime = LLTimer::getTotalSeconds(); - LL_DEBUGS() << "ILX: " + mLastSnapshotTime = LLFrameTimer::getTotalSeconds(); + LL_DEBUGS("ILX") << "ILX: " << mObjectCacheHitCount << " hits, " << mObjectCacheMissFullCount << " full misses, " << mObjectCacheMissCrcCount << " crc misses, " << mObjectFullUpdates << " full updates, " << mObjectTerseUpdates << " terse updates, " << mObjectCacheMissRequests << " cache miss requests, " - << mObjectCacheMissResponses << " cache miss responses, " << mObjectCacheUpdateDupes << " cache update dupes, " << mObjectCacheUpdateChanges << " cache update changes, " << mObjectCacheUpdateAdds << " cache update adds, " - << mObjectCacheUpdateReplacements << " cache update replacements, " - << mObjectUpdateFailures << " update failures" + << mObjectCacheUpdateReplacements << " cache update replacements," + << mObjectUpdateFailures << " update failures," + << mTextureFetchCount << " texture fetches, " + << mMeshLoadedCount << " mesh loads, " + << mObjectKills << " object kills" << LL_ENDL; - - if (mObjectCacheFile == NULL) + + if (mStatsFile == NULL) { - mStartTime = LLTimer::getTotalSeconds(); - mObjectCacheFile = LLFile::fopen(STATS_FILE_NAME, "wb"); - if (mObjectCacheFile) - { // Write column headers - std::ostringstream data_msg; - data_msg << "EventTime(ms)\t" - << "Cache Hits\t" - << "Cache Full Misses\t" - << "Cache Crc Misses\t" - << "Full Updates\t" - << "Terse Updates\t" - << "Cache Miss Requests\t" - << "Cache Miss Responses\t" - << "Cache Update Dupes\t" - << "Cache Update Changes\t" - << "Cache Update Adds\t" - << "Cache Update Replacements\t" - << "Update Failures\t" - << "Cache Hits bps\t" - << "Cache Full Misses bps\t" - << "Cache Crc Misses bps\t" - << "Full Updates bps\t" - << "Terse Updates bps\t" - << "Cache Miss Responses bps\t" - << "Texture Fetch bps\t" - << "\n"; + // Refresh settings + mInterval = gSavedSettings.getF32("StatsReportFileInterval"); + mSkipSaveIfZeros = gSavedSettings.getBOOL("StatsReportSkipZeroDataSaves"); + mMaxDuration = gSavedSettings.getF32("StatsReportMaxDuration"); - data_size = data_msg.str().size(); - if (fwrite(data_msg.str().c_str(), 1, data_size, mObjectCacheFile ) != data_size) + // Open the data file + makeStatsFileName(); + mStatsFile = LLFile::fopen(mStatsFileName, "wb"); + + if (mStatsFile) + { + LL_INFOS("ILX") << "ILX: Writing update information to " << mStatsFileName << LL_ENDL; + + mFileOpenTime = LLFrameTimer::getTotalSeconds(); + + // Write column headers + std::ostringstream col_headers; + col_headers << "Time (sec)," + << "Regions," + << "Active Cached Objects," + << "Cache Hits," + << "Cache Full Misses," + << "Cache Crc Misses," + << "Full Updates," + << "Terse Updates," + << "Cache Miss Requests," // Normally results in a Full Update from simulator + << "Cache Update Dupes," + << "Cache Update Changes," + << "Cache Update Adds," + << "Cache Update Replacements," + << "Update Failures," + << "Texture Count," + << "Mesh Load Count," + << "Object Kills" + << "\n"; + + data_size = col_headers.str().size(); + if (fwrite(col_headers.str().c_str(), 1, data_size, mStatsFile ) != data_size) { - LL_WARNS() << "failed to write full headers to " << STATS_FILE_NAME << LL_ENDL; + LL_WARNS() << "failed to write full headers to " << mStatsFileName << LL_ENDL; + // Close the file and turn off stats logging + closeStatsFile(); + return; } } else - { - //LL_WARNS() << "Couldn't open " << STATS_FILE_NAME << " for logging." << LL_ENDL; + { // Failed to open file + LL_WARNS() << "Couldn't open " << mStatsFileName << " for logging, turning off stats recording." << LL_ENDL; + mEnableStatsLogging = false; return; } } - std::ostringstream data_msg; + std::ostringstream stats_data; - data_msg << getTimeSinceStart() - << "\t " << mObjectCacheHitCount - << "\t" << mObjectCacheMissFullCount - << "\t" << mObjectCacheMissCrcCount - << "\t" << mObjectFullUpdates - << "\t" << mObjectTerseUpdates - << "\t" << mObjectCacheMissRequests - << "\t" << mObjectCacheMissResponses - << "\t" << mObjectCacheUpdateDupes - << "\t" << mObjectCacheUpdateChanges - << "\t" << mObjectCacheUpdateAdds - << "\t" << mObjectCacheUpdateReplacements - << "\t" << mObjectUpdateFailures - << "\t" << (mObjectCacheHitSize * 8 / delta_time) - << "\t" << (mObjectCacheMissFullSize * 8 / delta_time) - << "\t" << (mObjectCacheMissCrcSize * 8 / delta_time) - << "\t" << (mObjectFullUpdatesSize * 8 / delta_time) - << "\t" << (mObjectTerseUpdatesSize * 8 / delta_time) - << "\t" << (mObjectCacheMissResponsesSize * 8 / delta_time) - << "\t" << (mTextureFetchSize * 8 / delta_time) + stats_data << getTimeSinceStart() + << "," << LLWorld::getInstance()->getRegionList().size() + << "," << LLWorld::getInstance()->getNumOfActiveCachedObjects() + << "," << mObjectCacheHitCount + << "," << mObjectCacheMissFullCount + << "," << mObjectCacheMissCrcCount + << "," << mObjectFullUpdates + << "," << mObjectTerseUpdates + << "," << mObjectCacheMissRequests + << "," << mObjectCacheUpdateDupes + << "," << mObjectCacheUpdateChanges + << "," << mObjectCacheUpdateAdds + << "," << mObjectCacheUpdateReplacements + << "," << mObjectUpdateFailures + << "," << mTextureFetchCount + << "," << mMeshLoadedCount + << "," << mObjectKills << "\n"; - data_size = data_msg.str().size(); - if ( data_size != fwrite(data_msg.str().c_str(), 1, data_size, mObjectCacheFile )) + data_size = stats_data.str().size(); + if ( data_size != fwrite(stats_data.str().c_str(), 1, data_size, mStatsFile )) { - LL_WARNS() << "Unable to write complete column data to " << STATS_FILE_NAME << LL_ENDL; - } + LL_WARNS() << "Unable to write complete column data to " << mStatsFileName << LL_ENDL; + closeStatsFile(); + } clearStats(); + + if (getTimeSinceStart() >= mMaxDuration) + { // If file recording has been running for too long, stop it. + closeStatsFile(); + } +} + +void LLViewerStatsRecorder::closeStatsFile() +{ + if (mStatsFile) + { + LL_INFOS("ILX") << "ILX: Stopped writing update information to " << mStatsFileName << " after " << getTimeSinceStart() + << " seconds." << LL_ENDL; + LLFile::close(mStatsFile); + mStatsFile = NULL; + } + mEnableStatsLogging = false; +} + +void LLViewerStatsRecorder::makeStatsFileName() +{ + // Create filename - tbd: use pid? +#if LL_WINDOWS + std::string stats_file_name("SLViewerStats-"); +#else + std::string stats_file_name("slviewerstats-"); +#endif + + F64 now = LLFrameTimer::getTotalSeconds(); + std::string date_str = LLDate(now).asString(); + std::replace(date_str.begin(), date_str.end(), ':', '-'); // Make it valid for a filename + stats_file_name.append(date_str); + stats_file_name.append(".csv"); + mStatsFileName = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, stats_file_name); } F32 LLViewerStatsRecorder::getTimeSinceStart() { - return (F32) (LLTimer::getTotalSeconds() - mStartTime); -} - -void LLViewerStatsRecorder::recordTextureFetch( S32 msg_size ) -{ - mTextureFetchSize += msg_size; + return (F32) (LLFrameTimer::getTotalSeconds() - mFileOpenTime); } - diff --git a/indra/newview/llviewerstatsrecorder.h b/indra/newview/llviewerstatsrecorder.h index c974bea49d..b9fe02e54d 100644 --- a/indra/newview/llviewerstatsrecorder.h +++ b/indra/newview/llviewerstatsrecorder.h @@ -31,10 +31,6 @@ // This is a diagnostic class used to record information from the viewer // for analysis. -// This is normally 0. Set to 1 to enable viewer stats recording -#define LL_RECORD_VIEWER_STATS 0 - - #include "llframetimer.h" #include "llviewerobject.h" #include "llviewerregion.h" @@ -49,97 +45,132 @@ class LLViewerStatsRecorder : public LLSingleton ~LLViewerStatsRecorder(); public: - void objectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size) - { -#if LL_RECORD_VIEWER_STATS - recordObjectUpdateFailure(local_id, update_type, msg_size); -#endif + // Enable/disable stats recording. This is broken down into two + // flags so we can record stats without writing them to the log + // file. This is useful to analyzing updates for scene loading. + void enableObjectStatsRecording(bool enable, bool logging = false); + + bool isEnabled() const { return mEnableStatsRecording; } + bool isLogging() const { return mEnableStatsLogging; } + + void objectUpdateFailure() + { + if (mEnableStatsRecording) + { + mObjectUpdateFailures++; + } } - void cacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size) + void cacheMissEvent(U8 cache_miss_type) { -#if LL_RECORD_VIEWER_STATS - recordCacheMissEvent(local_id, update_type, cache_miss_type, msg_size); -#endif + if (mEnableStatsRecording) + { + recordCacheMissEvent(cache_miss_type); + } + } + + void cacheHitEvent() + { + if (mEnableStatsRecording) + { + mObjectCacheHitCount++; + } + } + + void objectUpdateEvent(const EObjectUpdateType update_type) + { + if (mEnableStatsRecording) + { + recordObjectUpdateEvent(update_type); + } } - void objectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size) - { -#if LL_RECORD_VIEWER_STATS - recordObjectUpdateEvent(local_id, update_type, objectp, msg_size); -#endif - } - - void cacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size) - { -#if LL_RECORD_VIEWER_STATS - recordCacheFullUpdate(local_id, update_type, update_result, objectp, msg_size); -#endif + void cacheFullUpdate(LLViewerRegion::eCacheUpdateResult update_result) + { + if (mEnableStatsRecording) + { + recordCacheFullUpdate(update_result); + } } void requestCacheMissesEvent(S32 count) { -#if LL_RECORD_VIEWER_STATS - recordRequestCacheMissesEvent(count); -#endif + if (mEnableStatsRecording) + { + mObjectCacheMissRequests += count; + } } - void textureFetch(S32 msg_size) + void textureFetch() { -#if LL_RECORD_VIEWER_STATS - recordTextureFetch(msg_size); -#endif + if (mEnableStatsRecording) + { + mTextureFetchCount += 1; + } } - void log(F32 interval) + void meshLoaded() { -#if LL_RECORD_VIEWER_STATS - writeToLog(interval); -#endif + if (mEnableStatsRecording) + { + mMeshLoadedCount += 1; + } + } + + void recordObjectKills(S32 num_objects) + { + if (mEnableStatsRecording) + { + mObjectKills += num_objects; + } + } + + void idle() + { + writeToLog(mInterval); } F32 getTimeSinceStart(); private: - void recordObjectUpdateFailure(U32 local_id, const EObjectUpdateType update_type, S32 msg_size); - void recordCacheMissEvent(U32 local_id, const EObjectUpdateType update_type, U8 cache_miss_type, S32 msg_size); - void recordObjectUpdateEvent(U32 local_id, const EObjectUpdateType update_type, LLViewerObject * objectp, S32 msg_size); - void recordCacheFullUpdate(U32 local_id, const EObjectUpdateType update_type, LLViewerRegion::eCacheUpdateResult update_result, LLViewerObject* objectp, S32 msg_size); - void recordRequestCacheMissesEvent(S32 count); - void recordTextureFetch(S32 msg_size); + void recordCacheMissEvent(U8 cache_miss_type); + void recordObjectUpdateEvent(const EObjectUpdateType update_type); + void recordCacheFullUpdate(LLViewerRegion::eCacheUpdateResult update_result); void writeToLog(F32 interval); + void closeStatsFile(); + void makeStatsFileName(); static LLViewerStatsRecorder* sInstance; - LLFILE * mObjectCacheFile; // File to write data into + LLFILE * mStatsFile; // File to write data into + std::string mStatsFileName; + LLFrameTimer mTimer; - F64 mStartTime; + F64 mFileOpenTime; F64 mLastSnapshotTime; + F32 mInterval; // Interval between data log writes + F32 mMaxDuration; // Time limit on file + + bool mEnableStatsRecording; // Set to true to enable recording stats data + bool mEnableStatsLogging; // Set true to write stats to log file + bool mSkipSaveIfZeros; // Set true to skip saving stats if all values are zero S32 mObjectCacheHitCount; - S32 mObjectCacheHitSize; S32 mObjectCacheMissFullCount; - S32 mObjectCacheMissFullSize; S32 mObjectCacheMissCrcCount; - S32 mObjectCacheMissCrcSize; S32 mObjectFullUpdates; - S32 mObjectFullUpdatesSize; S32 mObjectTerseUpdates; - S32 mObjectTerseUpdatesSize; S32 mObjectCacheMissRequests; - S32 mObjectCacheMissResponses; - S32 mObjectCacheMissResponsesSize; S32 mObjectCacheUpdateDupes; S32 mObjectCacheUpdateChanges; S32 mObjectCacheUpdateAdds; S32 mObjectCacheUpdateReplacements; S32 mObjectUpdateFailures; - S32 mObjectUpdateFailuresSize; - S32 mTextureFetchSize; - + S32 mTextureFetchCount; + S32 mMeshLoadedCount; + S32 mObjectKills; void clearStats(); }; #endif // LLVIEWERSTATSRECORDER_H - diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 1353eea2f2..fa6b6dc156 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -36,6 +36,7 @@ #include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" #include "llfocusmgr.h" +#include "llinspecttexture.h" #include "llinventorybridge.h" #include "llinventorydefines.h" #include "llinventorymodel.h" @@ -246,6 +247,21 @@ public: } virtual BOOL handleToolTip(S32 x, S32 y, MASK mask ) { + if (mItem->getThumbnailUUID().notNull()) + { + LLSD params; + params["inv_type"] = mItem->getInventoryType(); + params["thumbnail_id"] = mItem->getThumbnailUUID(); + params["asset_id"] = mItem->getAssetUUID(); + + LLToolTipMgr::instance().show(LLToolTip::Params() + .message(mToolTip) + .create_callback(boost::bind(&LLInspectTextureUtil::createInventoryToolTip, _1)) + .create_params(params)); + + return TRUE; + } + if (!mToolTip.empty()) { LLToolTipMgr::instance().show(mToolTip); @@ -1200,7 +1216,11 @@ void LLViewerTextEditor::openEmbeddedLandmark( LLPointer item_p void LLViewerTextEditor::openEmbeddedCallingcard( LLInventoryItem* item, llwchar wc ) { - if(item && !item->getCreatorUUID().isNull()) + if (item && !item->getDescription().empty()) + { + LLAvatarActions::showProfile(LLUUID(item->getDescription())); + } + else if (item && !item->getCreatorUUID().isNull()) { LLAvatarActions::showProfile(item->getCreatorUUID()); } diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index cb58588848..9336d99555 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -534,9 +534,12 @@ void LLViewerTexture::updateClass() static LLCachedControl max_vram_budget(gSavedSettings, "RenderMaxVRAMBudget", 0); + F64 texture_bytes_alloc = LLImageGL::getTextureBytesAllocated() / 1024.0 / 512.0; + F64 vertex_bytes_alloc = LLVertexBuffer::getBytesAllocated() / 1024.0 / 512.0; + // get an estimate of how much video memory we're using // NOTE: our metrics miss about half the vram we use, so this biases high but turns out to typically be within 5% of the real number - F32 used = (LLImageGL::getTextureBytesAllocated() + LLVertexBuffer::getBytesAllocated()) / 1024 / 512; + F32 used = (F32)ll_round(texture_bytes_alloc + vertex_bytes_alloc); F32 budget = max_vram_budget == 0 ? gGLManager.mVRAM : max_vram_budget; @@ -2096,7 +2099,8 @@ bool LLViewerFetchedTexture::updateFetch() } } - llassert_always(mRawImage.notNull() || (!mNeedsCreateTexture && !mIsRawImageValid)); + llassert_always(mRawImage.notNull() || !mIsRawImageValid); + llassert_always(mRawImage.notNull() || !mNeedsCreateTexture); return mIsFetching ? true : false; } diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 9c4474f1d3..9ee6f88183 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -222,6 +222,8 @@ void LLViewerTextureList::doPrefetchImages() LLViewerTextureManager::getFetchedTexture(IMG_SHOT); LLViewerTextureManager::getFetchedTexture(IMG_SMOKE_POOF); + LLViewerFetchedTexture::sSmokeImagep = LLViewerTextureManager::getFetchedTexture(IMG_SMOKE, FTT_DEFAULT, TRUE, LLGLTexture::BOOST_UI); + LLViewerFetchedTexture::sSmokeImagep->setNoDelete(); LLStandardBumpmap::addstandard(); @@ -1241,11 +1243,52 @@ void LLViewerTextureList::decodeAllImages(F32 max_time) << LL_ENDL; } +bool LLViewerTextureList::createUploadFile(LLPointer raw_image, + const std::string& out_filename, + const S32 max_image_dimentions, + const S32 min_image_dimentions) +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; + + // make a copy, since convertToUploadFile scales raw image + LLPointer scale_image = new LLImageRaw( + raw_image->getData(), + raw_image->getWidth(), + raw_image->getHeight(), + raw_image->getComponents()); + + LLPointer compressedImage = LLViewerTextureList::convertToUploadFile(scale_image, max_image_dimentions); + if (compressedImage->getWidth() < min_image_dimentions || compressedImage->getHeight() < min_image_dimentions) + { + std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", + min_image_dimentions, + min_image_dimentions, + compressedImage->getWidth(), + compressedImage->getHeight()); + compressedImage->setLastError(reason); + return false; + } + if (compressedImage.isNull()) + { + compressedImage->setLastError("Couldn't convert the image to jpeg2000."); + LL_INFOS() << "Couldn't convert to j2c, file : " << out_filename << LL_ENDL; + return false; + } + if (!compressedImage->save(out_filename)) + { + compressedImage->setLastError("Couldn't create the jpeg2000 image for upload."); + LL_INFOS() << "Couldn't create output file : " << out_filename << LL_ENDL; + return false; + } + return true; +} BOOL LLViewerTextureList::createUploadFile(const std::string& filename, const std::string& out_filename, const U8 codec, - const S32 max_image_dimentions) + const S32 max_image_dimentions, + const S32 min_image_dimentions, + bool force_square) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; // Load the image @@ -1273,8 +1316,18 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, image->setLastError("Image files with less than 3 or more than 4 components are not supported."); return FALSE; } + if (image->getWidth() < min_image_dimentions || image->getHeight() < min_image_dimentions) + { + std::string reason = llformat("Images below %d x %d pixels are not allowed. Actual size: %d x %dpx", + min_image_dimentions, + min_image_dimentions, + image->getWidth(), + image->getHeight()); + image->setLastError(reason); + return FALSE; + } // Convert to j2c (JPEG2000) and save the file locally - LLPointer compressedImage = convertToUploadFile(raw_image, max_image_dimentions); + LLPointer compressedImage = convertToUploadFile(raw_image, max_image_dimentions, force_square); if (compressedImage.isNull()) { image->setLastError("Couldn't convert the image to jpeg2000."); @@ -1299,10 +1352,20 @@ BOOL LLViewerTextureList::createUploadFile(const std::string& filename, } // note: modifies the argument raw_image!!!! -LLPointer LLViewerTextureList::convertToUploadFile(LLPointer raw_image, const S32 max_image_dimentions, bool force_lossless) +LLPointer LLViewerTextureList::convertToUploadFile(LLPointer raw_image, const S32 max_image_dimentions, bool force_square, bool force_lossless) { LL_PROFILE_ZONE_SCOPED_CATEGORY_TEXTURE; - raw_image->biasedScaleToPowerOfTwo(max_image_dimentions); + if (force_square) + { + S32 biggest_side = llmax(raw_image->getWidth(), raw_image->getHeight()); + S32 square_size = raw_image->biasedDimToPowerOfTwo(biggest_side, max_image_dimentions); + + raw_image->scale(square_size, square_size); + } + else + { + raw_image->biasedScaleToPowerOfTwo(max_image_dimentions); + } LLPointer compressedImage = new LLImageJ2C(); if (force_lossless || diff --git a/indra/newview/llviewertexturelist.h b/indra/newview/llviewertexturelist.h index 1b740d8c76..e8dd96daee 100644 --- a/indra/newview/llviewertexturelist.h +++ b/indra/newview/llviewertexturelist.h @@ -92,11 +92,20 @@ class LLViewerTextureList friend class LLLocalBitmap; public: + static bool createUploadFile(LLPointer raw_image, + const std::string& out_filename, + const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT, + const S32 min_image_dimentions = 0); 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, bool force_lossless = false); + const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT, + const S32 min_image_dimentions = 0, + bool force_square = false); + static LLPointer convertToUploadFile(LLPointer raw_image, + const S32 max_image_dimentions = LLViewerFetchedTexture::MAX_IMAGE_SIZE_DEFAULT, + bool force_square = false, + bool force_lossless = false); static void processImageNotInDatabase( LLMessageSystem *msg, void **user_data ); public: diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp index 2729253d18..20390a316a 100644 --- a/indra/newview/llviewerthrottle.cpp +++ b/indra/newview/llviewerthrottle.cpp @@ -84,7 +84,7 @@ LLViewerThrottleGroup::LLViewerThrottleGroup() } -LLViewerThrottleGroup::LLViewerThrottleGroup(const F32 settings[]) +LLViewerThrottleGroup::LLViewerThrottleGroup(const F32 settings[TC_EOF]) { mThrottleTotal = 0.f; S32 i; diff --git a/indra/newview/llviewerwearable.h b/indra/newview/llviewerwearable.h index cc99f6af2f..24b1323b2b 100644 --- a/indra/newview/llviewerwearable.h +++ b/indra/newview/llviewerwearable.h @@ -93,7 +93,6 @@ public: // the wearable was worn. make sure the name of the wearable object matches the LLViewerInventoryItem, // not the wearable asset itself. void refreshName(); - /*virtual*/void addToBakedTextureHash(LLMD5& hash) const {} protected: LLAssetID mAssetID; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 8516bbc7ec..ba2b6e1c7c 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -996,7 +996,7 @@ void LLViewerWindow::handlePieMenu(S32 x, S32 y, MASK mask) } } -BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) +BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down, bool& is_toolmgr_action) { const char* buttonname = ""; const char* buttonstatestr = ""; @@ -1145,6 +1145,7 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK m if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) ) { LLViewerEventRecorder::instance().clear_xui(); + is_toolmgr_action = true; return TRUE; } @@ -3343,7 +3344,7 @@ void LLViewerWindow::updateUI() if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_RAYCAST)) { gDebugRaycastFaceHit = -1; - gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, + gDebugRaycastObject = cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, FALSE, &gDebugRaycastFaceHit, &gDebugRaycastIntersection, &gDebugRaycastTexCoord, @@ -4218,7 +4219,8 @@ void LLViewerWindow::pickAsync( S32 x, void (*callback)(const LLPickInfo& info), BOOL pick_transparent, BOOL pick_rigged, - BOOL pick_unselectable) + BOOL pick_unselectable, + BOOL pick_reflection_probes) { // "Show Debug Alpha" means no object actually transparent BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); @@ -4228,7 +4230,7 @@ void LLViewerWindow::pickAsync( S32 x, pick_transparent = TRUE; } - LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, pick_rigged, FALSE, TRUE, pick_unselectable, callback); + LLPickInfo pick_info(LLCoordGL(x, y_from_bot), mask, pick_transparent, pick_rigged, FALSE, pick_reflection_probes, pick_unselectable, TRUE, callback); schedulePick(pick_info); } @@ -4284,7 +4286,7 @@ void LLViewerWindow::returnEmptyPicks() } // Performs the GL object/land pick. -LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_particle, BOOL pick_unselectable) +LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transparent, BOOL pick_rigged, BOOL pick_particle, BOOL pick_unselectable, BOOL pick_reflection_probe) { BOOL in_build_mode = LLFloaterReg::instanceVisible("build"); if ((in_build_mode && gSavedSettings.getBOOL("SelectInvisibleObjects")) || LLDrawPoolAlpha::sShowDebugAlpha) @@ -4296,7 +4298,7 @@ LLPickInfo LLViewerWindow::pickImmediate(S32 x, S32 y_from_bot, BOOL pick_transp // shortcut queueing in mPicks and just update mLastPick in place MASK key_mask = gKeyboard->currentMask(TRUE); - mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), key_mask, pick_transparent, pick_rigged, pick_particle, TRUE, FALSE, NULL); + mLastPick = LLPickInfo(LLCoordGL(x, y_from_bot), key_mask, pick_transparent, pick_rigged, pick_particle, pick_reflection_probe, TRUE, FALSE, NULL); mLastPick.fetchResults(); return mLastPick; @@ -4334,6 +4336,7 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de BOOL pick_transparent, BOOL pick_rigged, BOOL pick_unselectable, + BOOL pick_reflection_probe, S32* face_hit, LLVector4a *intersection, LLVector2 *uv, @@ -4412,7 +4415,8 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de } else // is a world object { - if (this_object->lineSegmentIntersect(mw_start, mw_end, this_face, pick_transparent, pick_rigged, pick_unselectable, + if ((pick_reflection_probe || !this_object->isReflectionProbe()) + && this_object->lineSegmentIntersect(mw_start, mw_end, this_face, pick_transparent, pick_rigged, pick_unselectable, face_hit, intersection, uv, normal, tangent)) { found = this_object; @@ -4426,7 +4430,7 @@ LLViewerObject* LLViewerWindow::cursorIntersect(S32 mouse_x, S32 mouse_y, F32 de if (!found) // if not found in HUD, look in world: { - found = gPipeline.lineSegmentIntersectInWorld(mw_start, mw_end, pick_transparent, pick_rigged, pick_unselectable, + found = gPipeline.lineSegmentIntersectInWorld(mw_start, mw_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, face_hit, intersection, uv, normal, tangent); if (found && !pick_transparent) { @@ -6084,30 +6088,32 @@ LLPickInfo::LLPickInfo() { } -LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos, - MASK keyboard_mask, - BOOL pick_transparent, - BOOL pick_rigged, - BOOL pick_particle, - BOOL pick_uv_coords, - BOOL pick_unselectable, - void (*pick_callback)(const LLPickInfo& pick_info)) - : mMousePt(mouse_pos), - mKeyMask(keyboard_mask), - mPickCallback(pick_callback), - mPickType(PICK_INVALID), - mWantSurfaceInfo(pick_uv_coords), - mObjectFace(-1), - mUVCoords(-1.f, -1.f), - mSTCoords(-1.f, -1.f), - mXYCoords(-1, -1), - mNormal(), - mTangent(), - mBinormal(), - mHUDIcon(NULL), - mPickTransparent(pick_transparent), - mPickRigged(pick_rigged), - mPickParticle(pick_particle), +LLPickInfo::LLPickInfo(const LLCoordGL& mouse_pos, + MASK keyboard_mask, + BOOL pick_transparent, + BOOL pick_rigged, + BOOL pick_particle, + BOOL pick_reflection_probe, + BOOL pick_uv_coords, + BOOL pick_unselectable, + void (*pick_callback)(const LLPickInfo& pick_info)) + : mMousePt(mouse_pos), + mKeyMask(keyboard_mask), + mPickCallback(pick_callback), + mPickType(PICK_INVALID), + mWantSurfaceInfo(pick_uv_coords), + mObjectFace(-1), + mUVCoords(-1.f, -1.f), + mSTCoords(-1.f, -1.f), + mXYCoords(-1, -1), + mNormal(), + mTangent(), + mBinormal(), + mHUDIcon(NULL), + mPickTransparent(pick_transparent), + mPickRigged(pick_rigged), + mPickParticle(pick_particle), + mPickReflectionProbe(pick_reflection_probe), mPickUnselectable(pick_unselectable) { } @@ -6138,7 +6144,7 @@ void LLPickInfo::fetchResults() } LLViewerObject* hit_object = gViewerWindow->cursorIntersect(mMousePt.mX, mMousePt.mY, 512.f, - NULL, -1, mPickTransparent, mPickRigged, mPickUnselectable, &face_hit, + NULL, -1, mPickTransparent, mPickRigged, mPickUnselectable, mPickReflectionProbe, &face_hit, &intersection, &uv, &normal, &tangent, &start, &end); mPickPt = mMousePt; @@ -6283,7 +6289,7 @@ void LLPickInfo::getSurfaceInfo() if (objectp) { if (gViewerWindow->cursorIntersect(ll_round((F32)mMousePt.mX), ll_round((F32)mMousePt.mY), 1024.f, - objectp, -1, mPickTransparent, mPickRigged, mPickUnselectable, + objectp, -1, mPickTransparent, mPickRigged, mPickUnselectable, mPickReflectionProbe, &mObjectFace, &intersection, &mSTCoords, diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 586ea2bee7..6e8a5b2f4e 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -94,6 +94,7 @@ public: BOOL pick_transparent, BOOL pick_rigged, BOOL pick_particle, + BOOL pick_reflection_probe, BOOL pick_surface_info, BOOL pick_unselectable, void (*pick_callback)(const LLPickInfo& pick_info)); @@ -130,6 +131,7 @@ public: BOOL mPickRigged; BOOL mPickParticle; BOOL mPickUnselectable; + BOOL mPickReflectionProbe = FALSE; void getSurfaceInfo(); private: @@ -181,7 +183,7 @@ public: void reshapeStatusBarContainer(); - BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); + BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down, bool &is_toolmgr_action); // // LLWindowCallback interface implementation @@ -406,8 +408,9 @@ public: void (*callback)(const LLPickInfo& pick_info), BOOL pick_transparent = FALSE, BOOL pick_rigged = FALSE, - BOOL pick_unselectable = FALSE); - LLPickInfo pickImmediate(S32 x, S32 y, BOOL pick_transparent, BOOL pick_rigged = FALSE, BOOL pick_particle = FALSE, BOOL pick_unselectable = TRUE); + BOOL pick_unselectable = FALSE, + BOOL pick_reflection_probes = FALSE); + LLPickInfo pickImmediate(S32 x, S32 y, BOOL pick_transparent, BOOL pick_rigged = FALSE, BOOL pick_particle = FALSE, BOOL pick_unselectable = TRUE, BOOL pick_reflection_probe = FALSE); LLHUDIcon* cursorIntersectIcon(S32 mouse_x, S32 mouse_y, F32 depth, LLVector4a* intersection); @@ -417,6 +420,7 @@ public: BOOL pick_transparent = FALSE, BOOL pick_rigged = FALSE, BOOL pick_unselectable = TRUE, + BOOL pick_reflection_probe = TRUE, S32* face_hit = NULL, LLVector4a *intersection = NULL, LLVector2 *uv = NULL, diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 22cd9f71b3..35e45c6cd9 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -5232,11 +5232,6 @@ U32 LLVOAvatar::renderRigid() { return 0; } - - if (!mIsBuilt) - { - return 0; - } if (isTextureVisible(TEX_EYES_BAKED) || (getOverallAppearance() == AOA_JELLYDOLL && !isControlAvatar()) || isUIAvatar()) { @@ -9469,7 +9464,14 @@ void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys ) // RequestAgentUpdateAppearanceResponder::onRequestRequested() // assumes that cof version is only updated with server-bake // appearance messages. - LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL; + if (isSelf()) + { + LL_INFOS("Avatar") << "Processing appearance message version " << thisAppearanceVersion << LL_ENDL; + } + else + { + LL_INFOS("Avatar") << "Processing appearance message for " << getID() << ", version " << thisAppearanceVersion << LL_ENDL; + } // Note: // locally the COF is maintained via LLInventoryModel::accountForUpdate diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp index a4070a513c..dd5b9f9fd5 100644 --- a/indra/newview/llvocache.cpp +++ b/indra/newview/llvocache.cpp @@ -85,40 +85,24 @@ bool LLGLTFOverrideCacheEntry::fromLLSD(const LLSD& data) // message should be interpreted thusly: /// sides is a list of face indices - // gltf_json is a list of corresponding json + // gltf_llsd is a list of corresponding GLTF override LLSD // any side not represented in "sides" has no override - if (data.has("sides") && data.has("gltf_json")) + if (data.has("sides") && data.has("gltf_llsd")) { LLSD const& sides = data.get("sides"); - LLSD const& gltf_json = data.get("gltf_json"); + LLSD const& gltf_llsd = data.get("gltf_llsd"); - if (sides.isArray() && gltf_json.isArray() && + if (sides.isArray() && gltf_llsd.isArray() && sides.size() != 0 && - sides.size() == gltf_json.size()) + sides.size() == gltf_llsd.size()) { for (int i = 0; i < sides.size(); ++i) { S32 side_idx = sides[i].asInteger(); - std::string gltf_json_str = gltf_json[i].asString(); - mSides[side_idx] = gltf_json_str; + mSides[side_idx] = gltf_llsd[i]; LLGLTFMaterial* override_mat = new LLGLTFMaterial(); - std::string error, warn; - if (override_mat->fromJSON(gltf_json_str, warn, error)) - { - mGLTFMaterial[side_idx] = override_mat; - } - else - { - LL_WARNS() << "Invalid GLTF string: \n" << gltf_json_str << LL_ENDL; - if (!error.empty()) - { - LL_WARNS() << "Error: " << error << LL_ENDL; - } - if (!warn.empty()) - { - LL_WARNS() << "Warning: " << warn << LL_ENDL; - } - } + override_mat->applyOverrideLLSD(gltf_llsd[i]); + mGLTFMaterial[side_idx] = override_mat; } } else @@ -157,7 +141,7 @@ LLSD LLGLTFOverrideCacheEntry::toLLSD() const // check that mSides and mGLTFMaterial have exactly the same keys present llassert(mGLTFMaterial.count(side.first) == 1); data["sides"].append(LLSD::Integer(side.first)); - data["gltf_json"].append(side.second); + data["gltf_llsd"].append(side.second); } return data; @@ -1199,6 +1183,8 @@ void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version) readCacheHeader(); + LL_INFOS() << "Viewer Object Cache Versions - expected: " << cache_version << " found: " << mMetaInfo.mVersion << LL_ENDL; + if( mMetaInfo.mVersion != cache_version || mMetaInfo.mAddressSize != expected_address) { @@ -1209,7 +1195,8 @@ void LLVOCache::initCache(ELLPath location, U32 size, U32 cache_version) clearCacheInMemory(); } else //delete the current cache if the format does not match. - { + { + LL_INFOS() << "Viewer Object Cache Versions unmatched. clearing cache." << LL_ENDL; removeCache(); } } diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h index ec0df31828..8525edd121 100644 --- a/indra/newview/llvocache.h +++ b/indra/newview/llvocache.h @@ -48,7 +48,7 @@ public: LLUUID mObjectId; U32 mLocalId = 0; - std::unordered_map mSides; //json per side + std::unordered_map mSides; //override LLSD per side std::unordered_map > mGLTFMaterial; //GLTF material per side U64 mRegionHandle = 0; }; diff --git a/indra/newview/llvoicecallhandler.cpp b/indra/newview/llvoicecallhandler.cpp index 1e993d1384..95e11abd82 100644 --- a/indra/newview/llvoicecallhandler.cpp +++ b/indra/newview/llvoicecallhandler.cpp @@ -38,7 +38,7 @@ public: { } - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (!LLUI::getInstance()->mSettingGroups["config"]->getBOOL("EnableVoiceCall")) { diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 150b556284..68d9f4ffab 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -53,7 +53,7 @@ public: // requests will be throttled from a non-trusted browser LLVoiceHandler() : LLCommandHandler("voice", UNTRUSTED_THROTTLE) {} - bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (params[0].asString() == "effects") { diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index c73f96da2d..3725510b6a 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -276,13 +276,13 @@ static void killGateway() /////////////////////////////////////////////////////////////////////////////////////////////// bool LLVivoxVoiceClient::sShuttingDown = false; +bool LLVivoxVoiceClient::sConnected = false; +LLPumpIO *LLVivoxVoiceClient::sPump = nullptr; LLVivoxVoiceClient::LLVivoxVoiceClient() : mSessionTerminateRequested(false), mRelogRequested(false), - mConnected(false), mTerminateDaemon(false), - mPump(NULL), mSpatialJoiningNum(0), mTuningMode(false), @@ -350,7 +350,11 @@ LLVivoxVoiceClient::LLVivoxVoiceClient() : mIsProcessingChannels(false), mIsCoroutineActive(false), mVivoxPump("vivoxClientPump") -{ +{ + sShuttingDown = false; + sConnected = false; + sPump = nullptr; + mSpeakerVolume = scale_speaker_volume(0); mVoiceVersion.serverVersion = ""; @@ -392,7 +396,7 @@ LLVivoxVoiceClient::~LLVivoxVoiceClient() void LLVivoxVoiceClient::init(LLPumpIO *pump) { // constructor will set up LLVoiceClient::getInstance() - mPump = pump; + sPump = pump; // LLCoros::instance().launch("LLVivoxVoiceClient::voiceControlCoro", // boost::bind(&LLVivoxVoiceClient::voiceControlCoro, LLVivoxVoiceClient::getInstance())); @@ -413,10 +417,10 @@ void LLVivoxVoiceClient::terminate() logoutOfVivox(false); } - if(mConnected) + if(sConnected) { breakVoiceConnection(false); - mConnected = false; + sConnected = false; } else { @@ -425,7 +429,7 @@ void LLVivoxVoiceClient::terminate() } sShuttingDown = true; - mPump = NULL; + sPump = NULL; } //--------------------------------------------------- @@ -471,7 +475,7 @@ bool LLVivoxVoiceClient::writeString(const std::string &str) bool result = false; LL_DEBUGS("LowVoice") << "sending:\n" << str << LL_ENDL; - if(mConnected) + if(sConnected) { apr_status_t err; apr_size_t size = (apr_size_t)str.size(); @@ -1051,7 +1055,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() int retryCount(0); LLVoiceVivoxStats::getInstance()->reset(); - while (!mConnected && !sShuttingDown && retryCount++ <= DAEMON_CONNECT_RETRY_MAX) + while (!sConnected && !sShuttingDown && retryCount++ <= DAEMON_CONNECT_RETRY_MAX) { LLVoiceVivoxStats::getInstance()->connectionAttemptStart(); LL_DEBUGS("Voice") << "Attempting to connect to vivox daemon: " << mDaemonHost << LL_ENDL; @@ -1061,23 +1065,23 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); } - mConnected = mSocket->blockingConnect(mDaemonHost); - LLVoiceVivoxStats::getInstance()->connectionAttemptEnd(mConnected); - if (!mConnected) + sConnected = mSocket->blockingConnect(mDaemonHost); + LLVoiceVivoxStats::getInstance()->connectionAttemptEnd(sConnected); + if (!sConnected) { llcoro::suspendUntilTimeout(DAEMON_CONNECT_THROTTLE_SECONDS); } } //--------------------------------------------------------------------- - if (sShuttingDown && !mConnected) + if (sShuttingDown && !sConnected) { return false; } llcoro::suspendUntilTimeout(UPDATE_THROTTLE_SECONDS); - while (!mPump && !sShuttingDown) + while (!sPump && !sShuttingDown) { // Can't use the pump until we have it available. llcoro::suspend(); } @@ -1099,7 +1103,7 @@ bool LLVivoxVoiceClient::startAndLaunchDaemon() readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); - mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); + sPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); //--------------------------------------------------------------------- @@ -1321,9 +1325,9 @@ bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait) // the message, yet we need to receive "connector shutdown response". // Either wait a bit and emulate it or check gMessageSystem for specific message _sleep(1000); - if (mConnected) + if (sConnected) { - mConnected = false; + sConnected = false; LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false))); mVivoxPump.post(vivoxevent); } @@ -1335,7 +1339,7 @@ bool LLVivoxVoiceClient::breakVoiceConnection(bool corowait) LL_DEBUGS("Voice") << "closing SLVoice socket" << LL_ENDL; closeSocket(); // Need to do this now -- bad things happen if the destructor does it later. cleanUp(); - mConnected = false; + sConnected = false; return retval; } @@ -2516,7 +2520,7 @@ bool LLVivoxVoiceClient::performMicTuning() void LLVivoxVoiceClient::closeSocket(void) { mSocket.reset(); - mConnected = false; + sConnected = false; mConnectorEstablished = false; mAccountLoggedIn = false; } @@ -3017,7 +3021,7 @@ bool LLVivoxVoiceClient::deviceSettingsAvailable() { bool result = true; - if(!mConnected) + if(!sConnected) result = false; if(mRenderDevices.empty()) @@ -3816,7 +3820,7 @@ void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string & // Should this ever fail? do we care if it does? } - mConnected = false; + sConnected = false; mShutdownComplete = true; LLSD vivoxevent(LLSDMap("connector", LLSD::Boolean(false))); @@ -7389,7 +7393,7 @@ LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; - if(!LLVivoxVoiceClient::getInstance()->mConnected) + if(!LLVivoxVoiceClient::sConnected) { // If voice has been disabled, we just want to close the socket. This does so. LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h index 0a785401c1..e3ab99c675 100644 --- a/indra/newview/llvoicevivox.h +++ b/indra/newview/llvoicevivox.h @@ -666,12 +666,10 @@ private: LLHost mDaemonHost; LLSocket::ptr_t mSocket; - bool mConnected; // We should kill the voice daemon in case of connection alert bool mTerminateDaemon; - LLPumpIO *mPump; friend class LLVivoxProtocolParser; std::string mAccountName; @@ -916,7 +914,10 @@ private: bool mIsProcessingChannels; bool mIsCoroutineActive; - static bool sShuttingDown; // corutines can last longer than vivox so we need a static variable as a shutdown flag + // This variables can last longer than vivox in coroutines so we need them as static + static bool sShuttingDown; + static bool sConnected; + static LLPumpIO* sPump; LLEventMailDrop mVivoxPump; }; diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 3ac04cf665..99874b8185 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -572,7 +572,7 @@ void LLVOPartGroup::getGeometry(const LLViewerPart& part, up.setCross3(right, at); up.normalize3fast(); - if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK) + if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK && !part.mVelocity.isExactlyZero()) { LLVector4a normvel; normvel.load3(part.mVelocity.mV); diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index feb1e26d57..9544450a69 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -391,7 +391,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, LLStrider &indicesp, U32 &index_offset) { - S32 vertex_count = 0; S32 i, x, y; S32 num_vertices; @@ -426,7 +425,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, normalsp++; texCoords0p++; texCoords1p++; - vertex_count++; } // North patch @@ -439,7 +437,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, normalsp++; texCoords0p++; texCoords1p++; - vertex_count++; } @@ -476,7 +473,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, normalsp++; texCoords0p++; texCoords1p++; - vertex_count++; } // Iterate through the north patch's points @@ -490,7 +486,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, normalsp++; texCoords0p++; texCoords1p++; - vertex_count++; } @@ -534,7 +529,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, normalsp++; texCoords0p++; texCoords1p++; - vertex_count++; } // Iterate through the north patch's points @@ -548,7 +542,6 @@ void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep, normalsp++; texCoords0p++; texCoords1p++; - vertex_count++; } for (i = 0; i < length; i++) diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 08d2e99c40..ec2f490742 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -619,8 +619,18 @@ void LLVOVolume::animateTextures() continue; } - LLGLTFMaterial *gltf_mat = te->getGLTFRenderMaterial(); - const bool is_pbr = gltf_mat != nullptr; + if (!(result & LLViewerTextureAnim::ROTATE)) + { + te->getRotation(&rot); + } + if (!(result & LLViewerTextureAnim::TRANSLATE)) + { + te->getOffset(&off_s,&off_t); + } + if (!(result & LLViewerTextureAnim::SCALE)) + { + te->getScale(&scale_s, &scale_t); + } if (!facep->mTextureMatrix) { @@ -629,80 +639,22 @@ void LLVOVolume::animateTextures() LLMatrix4& tex_mat = *facep->mTextureMatrix; tex_mat.setIdentity(); + LLVector3 trans ; - if (!is_pbr) - { - if (!(result & LLViewerTextureAnim::ROTATE)) - { - te->getRotation(&rot); - } - if (!(result & LLViewerTextureAnim::TRANSLATE)) - { - te->getOffset(&off_s,&off_t); - } - if (!(result & LLViewerTextureAnim::SCALE)) - { - te->getScale(&scale_s, &scale_t); - } + trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); + tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); - LLVector3 trans ; + LLVector3 scale(scale_s, scale_t, 1.f); + LLQuaternion quat; + quat.setQuat(rot, 0, 0, -1.f); + + tex_mat.rotate(quat); - trans.set(LLVector3(off_s+0.5f, off_t+0.5f, 0.f)); - tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); - - LLVector3 scale(scale_s, scale_t, 1.f); - LLQuaternion quat; - quat.setQuat(rot, 0, 0, -1.f); - - tex_mat.rotate(quat); - - LLMatrix4 mat; - mat.initAll(scale, LLQuaternion(), LLVector3()); - tex_mat *= mat; - - tex_mat.translate(trans); - } - else - { - if (!(result & LLViewerTextureAnim::ROTATE)) - { - rot = 0.0f; - } - if (!(result & LLViewerTextureAnim::TRANSLATE)) - { - off_s = 0.0f; - off_t = 0.0f; - } - if (!(result & LLViewerTextureAnim::SCALE)) - { - scale_s = 1.0f; - scale_t = 1.0f; - } - - // For PBR materials, use Blinn-Phong rotation as hint for - // translation direction. In a Blinn-Phong material, the - // translation direction would be a byproduct the texture - // transform. - F32 rot_frame; - te->getRotation(&rot_frame); - - tex_mat.translate(LLVector3(-0.5f, -0.5f, 0.f)); - - LLQuaternion quat; - quat.setQuat(rot, 0, 0, -1.f); - tex_mat.rotate(quat); - - LLMatrix4 mat; - LLVector3 scale(scale_s, scale_t, 1.f); - mat.initAll(scale, LLQuaternion(), LLVector3()); - tex_mat *= mat; - - LLVector3 off(off_s, off_t, 0.f); - off.rotVec(rot_frame, 0, 0, 1.f); - tex_mat.translate(off); - - tex_mat.translate(LLVector3(0.5f, 0.5f, 0.f)); - } + LLMatrix4 mat; + mat.initAll(scale, LLQuaternion(), LLVector3()); + tex_mat *= mat; + + tex_mat.translate(trans); } } else @@ -4617,7 +4569,7 @@ BOOL LLVOVolume::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& if (!pick_unselectable) { - if (!LLSelectMgr::instance().canSelectObject(this)) + if (!LLSelectMgr::instance().canSelectObject(this, TRUE)) { return FALSE; } @@ -5102,6 +5054,30 @@ LLControlAVBridge::LLControlAVBridge(LLDrawable* drawablep, LLViewerRegion* regi mPartitionType = LLViewerRegion::PARTITION_CONTROL_AV; } +void LLControlAVBridge::updateSpatialExtents() +{ + LL_PROFILE_ZONE_SCOPED_CATEGORY_DRAWABLE + + LLSpatialGroup* root = (LLSpatialGroup*)mOctree->getListener(0); + + bool rootWasDirty = root->isDirty(); + + super::updateSpatialExtents(); // root becomes non-dirty here + + // SL-18251 "On-screen animesh characters using pelvis offset animations + // disappear when root goes off-screen" + // + // Expand extents to include Control Avatar placed outside of the bounds + LLControlAvatar* controlAvatar = getVObj() ? getVObj()->getControlAvatar() : NULL; + if (controlAvatar + && controlAvatar->mDrawable + && controlAvatar->mDrawable->getEntry() + && (rootWasDirty || controlAvatar->mPlaying)) + { + root->expandExtents(controlAvatar->mDrawable->getSpatialExtents(), *mDrawable->getXform()); + } +} + bool can_batch_texture(LLFace* facep) { if (facep->getTextureEntry()->getBumpmap()) @@ -5322,13 +5298,14 @@ void LLVolumeGeometryManager::registerFace(LLSpatialGroup* group, LLFace* facep, if (mat) { + BOOL is_alpha = (facep->getPoolType() == LLDrawPool::POOL_ALPHA) || (facep->getTextureEntry()->getColor().mV[3] < 0.999f) ? TRUE : FALSE; if (type == LLRenderPass::PASS_ALPHA) { - shader_mask = mat->getShaderMask(LLMaterial::DIFFUSE_ALPHA_MODE_BLEND); + shader_mask = mat->getShaderMask(LLMaterial::DIFFUSE_ALPHA_MODE_BLEND, is_alpha); } else { - shader_mask = mat->getShaderMask(); + shader_mask = mat->getShaderMask(LLMaterial::DIFFUSE_ALPHA_MODE_DEFAULT, is_alpha); } } @@ -5831,15 +5808,29 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } else { - if (te->getColor().mV[3] > 0.f || te->getGlow() > 0.f) - { //only treat as alpha in the pipeline if < 100% transparent - drawablep->setState(LLDrawable::HAS_ALPHA); - add_face(sAlphaFaces, alpha_count, facep); - } - else if (LLDrawPoolAlpha::sShowDebugAlpha) + F32 alpha; + if (is_pbr) { - add_face(sAlphaFaces, alpha_count, facep); + alpha = gltf_mat ? gltf_mat->mBaseColor.mV[3] : 1.0; } + else + { + alpha = te->getColor().mV[3]; + } + if (alpha > 0.f || te->getGlow() > 0.f) + { //only treat as alpha in the pipeline if < 100% transparent + drawablep->setState(LLDrawable::HAS_ALPHA); + add_face(sAlphaFaces, alpha_count, facep); + } + else if (LLDrawPoolAlpha::sShowDebugAlpha || + (gPipeline.sRenderHighlight && !drawablep->getParent() && + //only root objects are highlighted with red color in this case + drawablep->getVObj() && drawablep->getVObj()->flagScripted() && + (LLPipeline::getRenderScriptedBeacons() || + (LLPipeline::getRenderScriptedTouchBeacons() && drawablep->getVObj()->flagHandleTouch())))) + { //draw the transparent face for debugging purposes using a custom texture + add_face(sAlphaFaces, alpha_count, facep); + } } } else @@ -6052,12 +6043,18 @@ void LLVolumeGeometryManager::rebuildMesh(LLSpatialGroup* group) LLVertexBuffer* buff = face->getVertexBuffer(); if (buff) { - if (!face->getGeometryVolume(*volume, face->getTEOffset(), - vobj->getRelativeXform(), vobj->getRelativeXformInvTrans(), face->getGeomIndex())) - { //something's gone wrong with the vertex buffer accounting, rebuild this group - group->dirtyGeom(); - gPipeline.markRebuild(group); - } + if (!face->getGeometryVolume(*volume, // volume + face->getTEOffset(), // face_index + vobj->getRelativeXform(), // mat_vert_in + vobj->getRelativeXformInvTrans(), // mat_norm_in + face->getGeomIndex(), // index_offset + false, // force_rebuild + true)) // no_debug_assert + { // Something's gone wrong with the vertex buffer accounting, + // rebuild this group with no debug assert because MESH_DIRTY + group->dirtyGeom(); + gPipeline.markRebuild(group); + } buff->unmapBuffer(); } @@ -6190,7 +6187,6 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace LLSpatialGroup::buffer_map_t buffer_map; LLViewerTexture* last_tex = NULL; - S32 buffer_index = 0; S32 texture_index_channels = 1; @@ -6202,8 +6198,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace if (distance_sort) { texture_index_channels = gDeferredAlphaProgram.mFeatures.mIndexedTextureChannels; - buffer_index = -1; - } + } texture_index_channels = LLGLSLShader::sIndexedTextureChannels; @@ -6223,14 +6218,9 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace tex = NULL; } - if (last_tex == tex) - { - buffer_index++; - } - else + if (last_tex != tex) { last_tex = tex; - buffer_index = 0; } bool bake_sunlight = LLPipeline::sBakeSunlight && facep->getDrawable()->isStatic(); @@ -6484,12 +6474,15 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace } } - F32 te_alpha = te->getColor().mV[3]; + F32 blinn_phong_alpha = te->getColor().mV[3]; bool use_legacy_bump = te->getBumpmap() && (te->getBumpmap() < 18) && (!mat || mat->getNormalID().isNull()); - bool opaque = te_alpha >= 0.999f; - bool transparent = te_alpha < 0.999f; + bool blinn_phong_opaque = blinn_phong_alpha >= 0.999f; + bool blinn_phong_transparent = blinn_phong_alpha < 0.999f; - is_alpha = (is_alpha || transparent) ? TRUE : FALSE; + if (!gltf_mat) + { + is_alpha = (is_alpha || blinn_phong_transparent) ? TRUE : FALSE; + } if (gltf_mat || (mat && !hud_group)) { @@ -6519,7 +6512,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace { if (mat->getDiffuseAlphaMode() == LLMaterial::DIFFUSE_ALPHA_MODE_MASK) { - if (opaque) + if (blinn_phong_opaque) { registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT_ALPHA_MASK); } @@ -6540,7 +6533,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace } else { - if (opaque) + if (blinn_phong_opaque) { registerFace(group, facep, LLRenderPass::PASS_FULLBRIGHT); } @@ -6551,7 +6544,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace } } } - else if (transparent) + else if (blinn_phong_transparent) { registerFace(group, facep, LLRenderPass::PASS_ALPHA); } @@ -6593,7 +6586,7 @@ U32 LLVolumeGeometryManager::genDrawInfo(LLSpatialGroup* group, U32 mask, LLFace { // HACK - this should never happen, but sometimes we get a material that thinks it has alpha blending when it ought not alpha_mode = LLMaterial::DIFFUSE_ALPHA_MODE_NONE; } - U32 mask = mat->getShaderMask(alpha_mode); + U32 mask = mat->getShaderMask(alpha_mode, is_alpha); U32 vb_mask = facep->getVertexBuffer()->getTypeMask(); diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index 89b74ae962..8681f7c14e 100644 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -104,8 +104,6 @@ BOOL LLPanelWearableOutfitItem::postBuild() { LLPanelWearableListItem::postBuild(); - LLViewerInventoryItem* inv_item = getItem(); - mShowWidgets &= (inv_item->getType() != LLAssetType::AT_BODYPART); if(mShowWidgets) { addWidgetToRightSide("add_wearable"); @@ -208,7 +206,12 @@ void LLPanelWearableOutfitItem::updateItem(const std::string& name, if(mShowWidgets) { setShowWidget("add_wearable", !is_worn); - setShowWidget("remove_wearable", is_worn); + + // Body parts can't be removed, only replaced + LLViewerInventoryItem* inv_item = getItem(); + bool show_remove = is_worn && inv_item && (inv_item->getType() != LLAssetType::AT_BODYPART); + setShowWidget("remove_wearable", show_remove); + if(mHovered) { setWidgetsVisible(true); diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 7a8f97fa68..cc947c87d5 100755 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -50,6 +50,7 @@ #include "lltextureview.h" #include "lltracker.h" #include "llviewercamera.h" +#include "llviewernetwork.h" #include "llviewertexture.h" #include "llviewertexturelist.h" #include "llviewerregion.h" @@ -1779,7 +1780,7 @@ BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask ) // Invoke the event details floater if someone is clicking on an event. LLSD params(LLSD::emptyArray()); params.append(event_id); - LLCommandDispatcher::dispatch("event", params, LLSD(), NULL, LLCommandHandler::NAV_TYPE_CLICKED, true); + LLCommandDispatcher::dispatch("event", params, LLSD(), LLGridManager::getInstance()->getGrid(), NULL, LLCommandHandler::NAV_TYPE_CLICKED, true); break; } case MAP_ITEM_LAND_FOR_SALE: diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 0c767e7767..9266c84540 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -130,7 +130,6 @@ S32 LLPipeline::RenderShadowDetail; S32 LLPipeline::RenderShadowSplits; bool LLPipeline::RenderDeferredSSAO; F32 LLPipeline::RenderShadowResolutionScale; -bool LLPipeline::RenderLocalLights; bool LLPipeline::RenderDelayCreation; bool LLPipeline::RenderAnimateRes; bool LLPipeline::FreezeTime; @@ -157,6 +156,7 @@ S32 LLPipeline::RenderGlowResolutionPow; S32 LLPipeline::RenderGlowIterations; F32 LLPipeline::RenderGlowWidth; F32 LLPipeline::RenderGlowStrength; +bool LLPipeline::RenderGlowNoise; bool LLPipeline::RenderDepthOfField; bool LLPipeline::RenderDepthOfFieldInEditMode; F32 LLPipeline::CameraFocusTransitionTime; @@ -348,8 +348,7 @@ LLPipeline::LLPipeline() : mResetVertexBuffers(false), mLastRebuildPool(NULL), mLightMask(0), - mLightMovingMask(0), - mLightingDetail(0) + mLightMovingMask(0) { mNoiseMap = 0; mTrueNoiseMap = 0; @@ -470,8 +469,6 @@ void LLPipeline::init() mScreenTriangleVB->unmapBuffer(); } - setLightingDetail(-1); - // // Update all settings to trigger a cached settings refresh // @@ -490,7 +487,6 @@ void LLPipeline::init() connectRefreshCachedSettingsSafe("RenderShadowSplits"); connectRefreshCachedSettingsSafe("RenderDeferredSSAO"); connectRefreshCachedSettingsSafe("RenderShadowResolutionScale"); - connectRefreshCachedSettingsSafe("RenderLocalLights"); connectRefreshCachedSettingsSafe("RenderDelayCreation"); connectRefreshCachedSettingsSafe("RenderAnimateRes"); connectRefreshCachedSettingsSafe("FreezeTime"); @@ -517,6 +513,7 @@ void LLPipeline::init() connectRefreshCachedSettingsSafe("RenderGlowIterations"); connectRefreshCachedSettingsSafe("RenderGlowWidth"); connectRefreshCachedSettingsSafe("RenderGlowStrength"); + connectRefreshCachedSettingsSafe("RenderGlowNoise"); connectRefreshCachedSettingsSafe("RenderDepthOfField"); connectRefreshCachedSettingsSafe("RenderDepthOfFieldInEditMode"); connectRefreshCachedSettingsSafe("CameraFocusTransitionTime"); @@ -840,7 +837,9 @@ bool LLPipeline::allocateScreenBuffer(U32 resX, U32 resY, U32 samples) mSceneMap.allocate(resX, resY, GL_RGB, true); } - mPostMap.allocate(resX, resY, GL_RGBA); + const bool post_hdr = gSavedSettings.getBOOL("RenderPostProcessingHDR"); + const U32 post_color_fmt = post_hdr ? GL_RGBA16F : GL_RGBA; + mPostMap.allocate(resX, resY, post_color_fmt); //HACK make screenbuffer allocations start failing after 30 seconds if (gSavedSettings.getBOOL("SimulateFBOFailure")) @@ -978,7 +977,6 @@ void LLPipeline::refreshCachedSettings() RenderShadowSplits = gSavedSettings.getS32("RenderShadowSplits"); RenderDeferredSSAO = gSavedSettings.getBOOL("RenderDeferredSSAO"); RenderShadowResolutionScale = gSavedSettings.getF32("RenderShadowResolutionScale"); - RenderLocalLights = gSavedSettings.getBOOL("RenderLocalLights"); RenderDelayCreation = gSavedSettings.getBOOL("RenderDelayCreation"); RenderAnimateRes = gSavedSettings.getBOOL("RenderAnimateRes"); FreezeTime = gSavedSettings.getBOOL("FreezeTime"); @@ -1005,6 +1003,7 @@ void LLPipeline::refreshCachedSettings() RenderGlowIterations = gSavedSettings.getS32("RenderGlowIterations"); RenderGlowWidth = gSavedSettings.getF32("RenderGlowWidth"); RenderGlowStrength = gSavedSettings.getF32("RenderGlowStrength"); + RenderGlowNoise = gSavedSettings.getBOOL("RenderGlowNoise"); RenderDepthOfField = gSavedSettings.getBOOL("RenderDepthOfField"); RenderDepthOfFieldInEditMode = gSavedSettings.getBOOL("RenderDepthOfFieldInEditMode"); CameraFocusTransitionTime = gSavedSettings.getF32("CameraFocusTransitionTime"); @@ -1163,9 +1162,11 @@ void LLPipeline::createGLBuffers() // allocate screen space glow buffers const U32 glow_res = llmax(1, llmin(512, 1 << gSavedSettings.getS32("RenderGlowResolutionPow"))); + const bool glow_hdr = gSavedSettings.getBOOL("RenderGlowHDR"); + const U32 glow_color_fmt = glow_hdr ? GL_RGBA16F : GL_RGBA; for (U32 i = 0; i < 3; i++) { - mGlow[i].allocate(512, glow_res, GL_RGBA); + mGlow[i].allocate(512, glow_res, glow_color_fmt); } allocateScreenBuffer(resX, resY); @@ -1349,39 +1350,6 @@ void LLPipeline::enableShadows(const bool enable_shadows) //should probably do something here to wrangle shadows.... } -S32 LLPipeline::getMaxLightingDetail() const -{ - /*if (mShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS) - { - return 3; - } - else*/ - { - return 1; - } -} - -S32 LLPipeline::setLightingDetail(S32 level) -{ - refreshCachedSettings(); - - if (level < 0) - { - if (RenderLocalLights) - { - level = 1; - } - else - { - level = 0; - } - } - level = llclamp(level, 0, getMaxLightingDetail()); - mLightingDetail = level; - - return mLightingDetail; -} - class LLOctreeDirtyTexture : public OctreeTraveler { public: @@ -2459,8 +2427,11 @@ void LLPipeline::doOcclusion(LLCamera& camera) for (LLCullResult::sg_iterator iter = sCull->beginOcclusionGroups(); iter != sCull->endOcclusionGroups(); ++iter) { LLSpatialGroup* group = *iter; - group->doOcclusion(&camera); - group->clearOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); + if (!group->isDead()) + { + group->doOcclusion(&camera); + group->clearOcclusionState(LLSpatialGroup::ACTIVE_OCCLUSION); + } } //apply occlusion culling to object cache tree @@ -2905,6 +2876,10 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) for (LLCullResult::sg_iterator iter = sCull->beginDrawableGroups(); iter != sCull->endDrawableGroups(); ++iter) { LLSpatialGroup* group = *iter; + if (group->isDead()) + { + continue; + } group->checkOcclusion(); if (sUseOcclusion > 1 && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) { @@ -2964,6 +2939,10 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) for (LLCullResult::sg_iterator iter = sCull->beginVisibleGroups(); iter != sCull->endVisibleGroups(); ++iter) { LLSpatialGroup* group = *iter; + if (group->isDead()) + { + continue; + } group->checkOcclusion(); if (sUseOcclusion > 1 && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) { @@ -3123,7 +3102,12 @@ void forAllDrawables(LLCullResult::sg_iterator begin, { for (LLCullResult::sg_iterator i = begin; i != end; ++i) { - for (LLSpatialGroup::element_iter j = (*i)->getDataBegin(); j != (*i)->getDataEnd(); ++j) + LLSpatialGroup* group = *i; + if (group->isDead()) + { + continue; + } + for (LLSpatialGroup::element_iter j = group->getDataBegin(); j != group->getDataEnd(); ++j) { if((*j)->hasDrawable()) { @@ -3332,6 +3316,10 @@ void LLPipeline::postSort(LLCamera &camera) for (LLCullResult::sg_iterator i = sCull->beginDrawableGroups(); i != sCull->endDrawableGroups(); ++i) { LLSpatialGroup *group = *i; + if (group->isDead()) + { + continue; + } if (!sUseOcclusion || !group->isOcclusionState(LLSpatialGroup::OCCLUDED)) { group->rebuildGeom(); @@ -3350,6 +3338,12 @@ void LLPipeline::postSort(LLCamera &camera) for (LLCullResult::sg_iterator i = sCull->beginVisibleGroups(); i != sCull->endVisibleGroups(); ++i) { LLSpatialGroup *group = *i; + + if (group->isDead()) + { + continue; + } + if ((sUseOcclusion && group->isOcclusionState(LLSpatialGroup::OCCLUDED)) || (RenderAutoHideSurfaceAreaLimit > 0.f && group->mSurfaceArea > RenderAutoHideSurfaceAreaLimit * llmax(group->mObjectBoxSize, 10.f))) @@ -5218,7 +5212,9 @@ void LLPipeline::calcNearbyLights(LLCamera& camera) return; } - if (mLightingDetail >= 1) + static LLCachedControl local_light_count(gSavedSettings, "RenderLocalLightCount", 256); + + if (local_light_count >= 1) { // mNearbyLight (and all light_set_t's) are sorted such that // begin() == the closest light and rbegin() == the farthest light @@ -5485,7 +5481,9 @@ void LLPipeline::setupHWLights() mLightMovingMask = 0; - if (mLightingDetail >= 1) + static LLCachedControl local_light_count(gSavedSettings, "RenderLocalLightCount", 256); + + if (local_light_count >= 1) { for (light_set_t::iterator iter = mNearbyLights.begin(); iter != mNearbyLights.end(); ++iter) @@ -5628,10 +5626,6 @@ void LLPipeline::enableLights(U32 mask) { assertInitialized(); - if (mLightingDetail == 0) - { - mask &= 0xf003; // sun and backlight only (and fullbright bit) - } if (mLightMask != mask) { stop_glerror(); @@ -5659,28 +5653,13 @@ void LLPipeline::enableLights(U32 mask) } } -void LLPipeline::enableLightsStatic() -{ - assertInitialized(); - U32 mask = 0x01; // Sun - if (mLightingDetail >= 2) - { - mask |= mLightMovingMask; // Hardware moving lights - } - else - { - mask |= 0xff & (~2); // Hardware local lights - } - enableLights(mask); -} - void LLPipeline::enableLightsDynamic() { assertInitialized(); U32 mask = 0xff & (~2); // Local lights enableLights(mask); - if (isAgentAvatarValid() && getLightingDetail() <= 0) + if (isAgentAvatarValid()) { if (gAgentAvatarp->mSpecialRenderMode == 0) // normal { @@ -6216,7 +6195,7 @@ LLVOPartGroup* LLPipeline::lineSegmentIntersectParticle(const LLVector4a& start, LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_PARTICLE); if (part && hasRenderType(part->mDrawableType)) { - LLDrawable* hit = part->lineSegmentIntersect(start, local_end, TRUE, FALSE, TRUE, face_hit, &position, NULL, NULL, NULL); + LLDrawable* hit = part->lineSegmentIntersect(start, local_end, TRUE, FALSE, TRUE, FALSE, face_hit, &position, NULL, NULL, NULL); if (hit) { drawable = hit; @@ -6245,6 +6224,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, bool pick_transparent, bool pick_rigged, bool pick_unselectable, + bool pick_reflection_probe, S32* face_hit, LLVector4a* intersection, // return the intersection point LLVector2* tex_coord, // return the texture coordinates of the intersection point @@ -6278,7 +6258,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, LLSpatialPartition* part = region->getSpatialPartition(j); if (part && hasRenderType(part->mDrawableType)) { - LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, face_hit, &position, tex_coord, normal, tangent); + LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, face_hit, &position, tex_coord, normal, tangent); if (hit) { drawable = hit; @@ -6335,7 +6315,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInWorld(const LLVector4a& start, LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_AVATAR); if (part && hasRenderType(part->mDrawableType)) { - LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, face_hit, &position, tex_coord, normal, tangent); + LLDrawable* hit = part->lineSegmentIntersect(start, local_end, pick_transparent, pick_rigged, pick_unselectable, pick_reflection_probe, face_hit, &position, tex_coord, normal, tangent); if (hit) { LLVector4a delta; @@ -6423,7 +6403,7 @@ LLViewerObject* LLPipeline::lineSegmentIntersectInHUD(const LLVector4a& start, c LLSpatialPartition* part = region->getSpatialPartition(LLViewerRegion::PARTITION_HUD); if (part) { - LLDrawable* hit = part->lineSegmentIntersect(start, end, pick_transparent, FALSE, TRUE, face_hit, intersection, tex_coord, normal, tangent); + LLDrawable* hit = part->lineSegmentIntersect(start, end, pick_transparent, FALSE, TRUE, FALSE, face_hit, intersection, tex_coord, normal, tangent); if (hit) { drawable = hit; @@ -6487,6 +6467,25 @@ void LLPipeline::renderObjects(U32 type, bool texture, bool batch_texture, bool gGLLastMatrix = NULL; } +void LLPipeline::renderGLTFObjects(U32 type, bool texture, bool rigged) +{ + assertInitialized(); + gGL.loadMatrix(gGLModelView); + gGLLastMatrix = NULL; + + if (rigged) + { + mSimplePool->pushRiggedGLTFBatches(type + 1, texture); + } + else + { + mSimplePool->pushGLTFBatches(type, texture); + } + + gGL.loadMatrix(gGLModelView); + gGLLastMatrix = NULL; +} + // Currently only used for shadows -Cosmic,2023-04-19 void LLPipeline::renderAlphaObjects(bool rigged) { @@ -6882,6 +6881,19 @@ void LLPipeline::generateGlow(LLRenderTarget* src) warmthWeights.mV[2]); gGlowExtractProgram.uniform1f(LLShaderMgr::GLOW_WARMTH_AMOUNT, warmthAmount); + if (RenderGlowNoise) + { + S32 channel = gGlowExtractProgram.enableTexture(LLShaderMgr::GLOW_NOISE_MAP); + if (channel > -1) + { + gGL.getTexUnit(channel)->bindManual(LLTexUnit::TT_TEXTURE, mTrueNoiseMap); + gGL.getTexUnit(channel)->setTextureFilteringOption(LLTexUnit::TFO_POINT); + } + gGlowExtractProgram.uniform2f(LLShaderMgr::DEFERRED_SCREEN_RES, + mGlow[2].getWidth(), + mGlow[2].getHeight()); + } + { LLGLEnable blend_on(GL_BLEND); @@ -7126,7 +7138,7 @@ void LLPipeline::renderDoF(LLRenderTarget* src, LLRenderTarget* dst) LLVector4a result; result.clear(); - gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, NULL, &result); + gViewerWindow->cursorIntersect(-1, -1, 512.f, NULL, -1, FALSE, FALSE, TRUE, TRUE, NULL, &result); focus_point.set(result.getF32ptr()); } @@ -7664,7 +7676,17 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, LLRenderTarget* light_ shader.uniformMatrix4fv(LLShaderMgr::DEFERRED_NORM_MATRIX, 1, FALSE, norm_mat.m); } - shader.uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, mSunDiffuse.mV); + // auto adjust legacy sun color if needed + static LLCachedControl should_auto_adjust(gSavedSettings, "RenderSkyAutoAdjustLegacy", true); + static LLCachedControl auto_adjust_sun_color_scale(gSavedSettings, "RenderSkyAutoAdjustSunColorScale", 1.f); + LLSettingsSky::ptr_t psky = LLEnvironment::instance().getCurrentSky(); + LLColor3 sun_diffuse(mSunDiffuse.mV); + if (should_auto_adjust && psky->canAutoAdjust()) + { + sun_diffuse *= auto_adjust_sun_color_scale; + } + + shader.uniform3fv(LLShaderMgr::SUNLIGHT_COLOR, 1, sun_diffuse.mV); shader.uniform3fv(LLShaderMgr::MOONLIGHT_COLOR, 1, mMoonDiffuse.mV); shader.uniform1f(LLShaderMgr::REFLECTION_PROBE_MAX_LOD, mReflectionMapManager.mMaxProbeLOD); @@ -7885,9 +7907,9 @@ void LLPipeline::renderDeferredLighting() unbindDeferredShader(LLPipeline::sUnderWaterRender ? gDeferredSoftenWaterProgram : gDeferredSoftenProgram); } - bool render_local = RenderLocalLights; // && !gCubeSnapshot; + static LLCachedControl local_light_count(gSavedSettings, "RenderLocalLightCount", 256); - if (render_local) + if (local_light_count > 0) { gGL.setSceneBlendType(LLRender::BT_ADD); std::list fullscreen_lights; @@ -7922,8 +7944,15 @@ void LLPipeline::renderDeferredLighting() // mNearbyLights already includes distance calculation and excludes muted avatars. // It is calculated from mLights // mNearbyLights also provides fade value to gracefully fade-out out of range lights + S32 count = 0; for (light_set_t::iterator iter = mNearbyLights.begin(); iter != mNearbyLights.end(); ++iter) { + count++; + if (count > local_light_count) + { //stop collecting lights once we hit the limit + break; + } + LLDrawable * drawablep = iter->drawable; LLVOVolume * volume = drawablep->getVOVolume(); if (!volume) @@ -7970,24 +7999,20 @@ void LLPipeline::renderDeferredLighting() camera->getOrigin().mV[1] > c[1] + s + 0.2f || camera->getOrigin().mV[1] < c[1] - s - 0.2f || camera->getOrigin().mV[2] > c[2] + s + 0.2f || camera->getOrigin().mV[2] < c[2] - s - 0.2f) { // draw box if camera is outside box - if (render_local) + if (volume->isLightSpotlight()) { - if (volume->isLightSpotlight()) - { - drawablep->getVOVolume()->updateSpotLightPriority(); - spot_lights.push_back(drawablep); - continue; - } - - gDeferredLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, c); - gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s); - gDeferredLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); - gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_FALLOFF, volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF)); - gGL.syncMatrices(); - - mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, center)); - stop_glerror(); + drawablep->getVOVolume()->updateSpotLightPriority(); + spot_lights.push_back(drawablep); + continue; } + + gDeferredLightProgram.uniform3fv(LLShaderMgr::LIGHT_CENTER, 1, c); + gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_SIZE, s); + gDeferredLightProgram.uniform3fv(LLShaderMgr::DIFFUSE_COLOR, 1, col.mV); + gDeferredLightProgram.uniform1f(LLShaderMgr::LIGHT_FALLOFF, volume->getLightFalloff(DEFERRED_LIGHT_FALLOFF)); + gGL.syncMatrices(); + + mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, center)); } else { @@ -8581,8 +8606,7 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera LLRenderPass::PASS_NORMMAP, LLRenderPass::PASS_NORMMAP_EMISSIVE, LLRenderPass::PASS_NORMSPEC, - LLRenderPass::PASS_NORMSPEC_EMISSIVE, - LLRenderPass::PASS_GLTF_PBR + LLRenderPass::PASS_NORMSPEC_EMISSIVE }; LLGLEnable cull(GL_CULL_FACE); @@ -8645,6 +8669,8 @@ void LLPipeline::renderShadow(glh::matrix4f& view, glh::matrix4f& proj, LLCamera renderObjects(type, false, false, rigged); } + renderGLTFObjects(LLRenderPass::PASS_GLTF_PBR, false, rigged); + gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); } diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index 961a55330a..d4c6432e55 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -205,6 +205,7 @@ public: bool pick_transparent, bool pick_rigged, bool pick_unselectable, + bool pick_reflection_probe, S32* face_hit, // return the face hit LLVector4a* intersection = NULL, // return the intersection point LLVector2* tex_coord = NULL, // return the texture coordinates of the intersection point @@ -238,12 +239,6 @@ public: void releaseSunShadowTargets(); void releaseSunShadowTarget(U32 index); -// void setLocalLighting(const bool local_lighting); -// bool isLocalLightingEnabled() const; - S32 setLightingDetail(S32 level); - S32 getLightingDetail() const { return mLightingDetail; } - S32 getMaxLightingDetail() const; - bool shadersLoaded(); bool canUseWindLightShaders() const; bool canUseAntiAliasing() const; @@ -284,7 +279,8 @@ public: void forAllVisibleDrawables(void (*func)(LLDrawable*)); void renderObjects(U32 type, bool texture = true, bool batch_texture = false, bool rigged = false); - + void renderGLTFObjects(U32 type, bool texture = true, bool rigged = false); + void renderAlphaObjects(bool rigged = false); void renderMaskedObjects(U32 type, bool texture = true, bool batch_texture = false, bool rigged = false); void renderFullbrightMaskedObjects(U32 type, bool texture = true, bool batch_texture = false, bool rigged = false); @@ -346,7 +342,6 @@ public: void setupHWLights(); void setupAvatarLights(bool for_edit = false); void enableLights(U32 mask); - void enableLightsStatic(); void enableLightsDynamic(); void enableLightsAvatar(); void enableLightsPreview(); @@ -943,7 +938,6 @@ protected: U32 mLightMask; U32 mLightMovingMask; - S32 mLightingDetail; static bool sRenderPhysicalBeacons; static bool sRenderMOAPBeacons; @@ -973,7 +967,6 @@ public: static S32 RenderShadowSplits; static bool RenderDeferredSSAO; static F32 RenderShadowResolutionScale; - static bool RenderLocalLights; static bool RenderDelayCreation; static bool RenderAnimateRes; static bool FreezeTime; @@ -1001,6 +994,7 @@ public: static S32 RenderGlowIterations; static F32 RenderGlowWidth; static F32 RenderGlowStrength; + static bool RenderGlowNoise; static bool RenderDepthOfField; static bool RenderDepthOfFieldInEditMode; static F32 CameraFocusTransitionTime; diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 6efe302f30..92f63a1820 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -407,7 +407,7 @@ value="0.43 0.06 0.06 1" /> + value="0.3 0.82 1 1" /> diff --git a/indra/newview/skins/default/html/ja/help-offline/index.html b/indra/newview/skins/default/html/ja/help-offline/index.html new file mode 100644 index 0000000000..aeaafe8216 --- /dev/null +++ b/indra/newview/skins/default/html/ja/help-offline/index.html @@ -0,0 +1,42 @@ + + + + + オフラインヘルプ + + + +
+

Second Lifeオフラインヘルプ

+

+ オンラインではなく、リモートでヘルプを取得しないように構成されています。 +
+ これは、さらに作業が完了するまで利用できるすべてのヘルプです。うん。 +

+
+ + diff --git a/indra/newview/skins/default/html/ja/loading-error/index.html b/indra/newview/skins/default/html/ja/loading-error/index.html index d969c03098..27d4425016 100644 --- a/indra/newview/skins/default/html/ja/loading-error/index.html +++ b/indra/newview/skins/default/html/ja/loading-error/index.html @@ -1,25 +1,99 @@ - - - -接続できません - - -

-

Second Lifeによるログインサーバーへの接続が確立できません。 -

-

インターネット接続を確認してください。 お使いのコンピュータやネットワークがファイヤウォールまたはプロキシにより保護されている場合は、Second Lifeによるネットワークへのアクセスが許可されていることを確認してください。 -

-
- - + + + + + 接続できません + + + +
+ unabletoconnect +
+

Second Lifeによるログインサーバーへの接続が確立できません。

+

+ インターネット接続を確認してください。 + お使いのコンピュータやネットワークがファイヤウォールまたはプロキシにより保護されている場合は、 + Second + Lifeによるネットワークへのアクセスが許可されていることを確認してください。 +

+
+ +
+
+ + diff --git a/indra/newview/skins/default/html/ja/loading/loading.html b/indra/newview/skins/default/html/ja/loading/loading.html index 35cf74a35f..ea576ed082 100644 --- a/indra/newview/skins/default/html/ja/loading/loading.html +++ b/indra/newview/skins/default/html/ja/loading/loading.html @@ -1,10 +1,22 @@ - - - - - - -
-
   ロード中... -
- + + + + + 読み込んでいます… + + + + loading +
+

   読み込んでいます…

+ + diff --git a/indra/newview/skins/default/textures/icons/copy_clipboard.png b/indra/newview/skins/default/textures/icons/copy_clipboard.png new file mode 100644 index 0000000000..bb1ceff1ce Binary files /dev/null and b/indra/newview/skins/default/textures/icons/copy_clipboard.png differ diff --git a/indra/newview/skins/default/textures/icons/delete_icon.png b/indra/newview/skins/default/textures/icons/delete_icon.png new file mode 100644 index 0000000000..37ce374653 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/delete_icon.png differ diff --git a/indra/newview/skins/default/textures/icons/file_upload.png b/indra/newview/skins/default/textures/icons/file_upload.png new file mode 100644 index 0000000000..58f2757136 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/file_upload.png differ diff --git a/indra/newview/skins/default/textures/icons/multi_folder_mode.png b/indra/newview/skins/default/textures/icons/multi_folder_mode.png new file mode 100644 index 0000000000..8cda3efc36 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/multi_folder_mode.png differ diff --git a/indra/newview/skins/default/textures/icons/paste_clipboard.png b/indra/newview/skins/default/textures/icons/paste_clipboard.png new file mode 100644 index 0000000000..e1589ab098 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/paste_clipboard.png differ diff --git a/indra/newview/skins/default/textures/icons/profile_badge_beta.png b/indra/newview/skins/default/textures/icons/profile_badge_beta.png new file mode 100644 index 0000000000..7c8a723c47 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/profile_badge_beta.png differ diff --git a/indra/newview/skins/default/textures/icons/profile_badge_beta_lifetime.png b/indra/newview/skins/default/textures/icons/profile_badge_beta_lifetime.png new file mode 100644 index 0000000000..7c38e9e2ae Binary files /dev/null and b/indra/newview/skins/default/textures/icons/profile_badge_beta_lifetime.png differ diff --git a/indra/newview/skins/default/textures/icons/profile_badge_lifetime.png b/indra/newview/skins/default/textures/icons/profile_badge_lifetime.png new file mode 100644 index 0000000000..475edd080e Binary files /dev/null and b/indra/newview/skins/default/textures/icons/profile_badge_lifetime.png differ diff --git a/indra/newview/skins/default/textures/icons/profile_badge_linden.png b/indra/newview/skins/default/textures/icons/profile_badge_linden.png new file mode 100644 index 0000000000..1b6ac03e86 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/profile_badge_linden.png differ diff --git a/indra/newview/skins/default/textures/icons/profile_badge_pplus_lifetime.png b/indra/newview/skins/default/textures/icons/profile_badge_pplus_lifetime.png new file mode 100644 index 0000000000..4286995202 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/profile_badge_pplus_lifetime.png differ diff --git a/indra/newview/skins/default/textures/icons/profile_badge_premium_lifetime.png b/indra/newview/skins/default/textures/icons/profile_badge_premium_lifetime.png new file mode 100644 index 0000000000..47e93c4fac Binary files /dev/null and b/indra/newview/skins/default/textures/icons/profile_badge_premium_lifetime.png differ diff --git a/indra/newview/skins/default/textures/icons/single_folder_back.png b/indra/newview/skins/default/textures/icons/single_folder_back.png new file mode 100644 index 0000000000..b614e9ef9b Binary files /dev/null and b/indra/newview/skins/default/textures/icons/single_folder_back.png differ diff --git a/indra/newview/skins/default/textures/icons/single_folder_forward.png b/indra/newview/skins/default/textures/icons/single_folder_forward.png new file mode 100644 index 0000000000..c7bee3522d Binary files /dev/null and b/indra/newview/skins/default/textures/icons/single_folder_forward.png differ diff --git a/indra/newview/skins/default/textures/icons/single_folder_mode.png b/indra/newview/skins/default/textures/icons/single_folder_mode.png new file mode 100644 index 0000000000..f70b754123 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/single_folder_mode.png differ diff --git a/indra/newview/skins/default/textures/icons/single_folder_up.png b/indra/newview/skins/default/textures/icons/single_folder_up.png new file mode 100644 index 0000000000..651b2b1af1 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/single_folder_up.png differ diff --git a/indra/newview/skins/default/textures/icons/snapshot_icon.png b/indra/newview/skins/default/textures/icons/snapshot_icon.png new file mode 100644 index 0000000000..41d524678f Binary files /dev/null and b/indra/newview/skins/default/textures/icons/snapshot_icon.png differ diff --git a/indra/newview/skins/default/textures/icons/texture_icon.png b/indra/newview/skins/default/textures/icons/texture_icon.png new file mode 100644 index 0000000000..278760a5b0 Binary files /dev/null and b/indra/newview/skins/default/textures/icons/texture_icon.png differ diff --git a/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png b/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png new file mode 100644 index 0000000000..8d5ca624af Binary files /dev/null and b/indra/newview/skins/default/textures/icons/thumbnail_fallback_icon.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 61257466aa..2126db32df 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -253,10 +253,16 @@ with the same filename but different name + + + + + + @@ -269,10 +275,16 @@ with the same filename but different name + + + + + + @@ -512,7 +524,13 @@ with the same filename but different name - + + + + + + + @@ -680,6 +698,7 @@ with the same filename but different name + + + + + + + diff --git a/indra/newview/skins/default/xui/da/floater_animation_preview.xml b/indra/newview/skins/default/xui/da/floater_animation_preview.xml deleted file mode 100644 index adf96841c0..0000000000 --- a/indra/newview/skins/default/xui/da/floater_animation_preview.xml +++ /dev/null @@ -1,187 +0,0 @@ - - - - Fejlede at starte bevægelse - - - Animations filen er [LENGTH] sekunder lang. - -Maksimal animations længde er [MAX_LENGTH] sekunder. - - - Kan ikke læse animations fil. - -[STATUS] - - - OK - - - Fil afsluttet for tidligt. - - - Kan ikke læse "constraint definition". - - - Kan ikke åbne BVH fil. - - - Ugyldig header i HIERARCHY. - - - Kan ikke finde "ROOT" eller "JOINT". - - - Kan ikke finde JOINT navn. - - - Kan ikke finde OFFSET. - - - Kan ikke finde CHANNELS. - - - Kan ikke læse "rotation order". - - - Kan ikke finde rotationsakser. - - - Kan ikke finde MOTION. - - - Kan ikke læse antal "frames". - - - Kan ikke læse "frame time". - - - Kan ikke læse positionsværdier. - - - Kan ikke læse rotationsværdier. - - - kan ikke åbne "translation file". - - - Kan ikke læse "translation header. - - - Kan ikke aflæse "translation" navne. - - - Kan ikke læse "translation ignore" værdi. - - - Kan ikke læse "translation relative" værdi. - - - Kan ikke læse "translation outname" værdi. - - - Kan ikke læse "translation matrix". - - - Kan ikke læse "mergechild" navn. - - - Kan ikke læse "mergeparent" navn. - - - Kan ikke finde prioritetsværdi. - - - Kan ikke læse "loop" værdi. - - - kan ikke læse "easeIn" værdier. - - - Kan ikke læse "easeOut" værdier. - - - Kan ikke læse "hand morph" værdi. - - - kan ikke læse "emote" navn. - - - Incorrect root joint name, use "hip". - - - Navn: - - - Beskrivelse: - - - - - - - Hånd posering - - - - - - - - - - - - - - - - - - Ansigtsudtryk - - - - - - - - - - - - - - - - - - - - - - - - - Vis mens - - - - - - - - - - + diff --git a/indra/newview/skins/default/xui/en/floater_translation_settings.xml b/indra/newview/skins/default/xui/en/floater_translation_settings.xml index a212ce7889..3f3331b468 100644 --- a/indra/newview/skins/default/xui/en/floater_translation_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_translation_settings.xml @@ -1,7 +1,7 @@ - Bing appID not verified. Please try again. - Google API key not verified. Please try again. + Azure service identifier not verified. Status: [STATUS]. Please check your settings and try again. + Google API key not verified. Status: [STATUS]. Please check your key and try again. + DeepL Auth Key key not verified. Status: [STATUS]. Please check your key and try again. - Bing appID verified. + Azure service identifier verified. Google API key verified. + DeepL API key verified. + name="azure" /> + top_pad="115" /> + + + [https://learn.microsoft.com/en-us/azure/cognitive-services/translator/create-translator-resource Setup] + + + + Endpoint: + + + + + + + + + - Bing [http://www.bing.com/developers/createapp.aspx AppID]: + Azure Key: - + + + + + 名前タグ: + + + + + + + diff --git a/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml b/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml new file mode 100644 index 0000000000..a031cd7176 --- /dev/null +++ b/indra/newview/skins/default/xui/ja/panel_performance_preferences.xml @@ -0,0 +1,120 @@ + + +