diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 3ceca8c98b..655cee4f2c 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -8056,17 +8056,6 @@ Value 0 - FSPoserAdvancedWindowState - - Comment - Whether the 'advanced' pane is shown when opening the Avatar/Animesh Poser. - Persist - 1 - Type - Boolean - Value - 0 - FSPoserSaveExternalFileAlso Comment @@ -8089,6 +8078,17 @@ Value 0 + FSPoserOnSaveConfirmOverwrite + + Comment + Whether to confirm overwriting a save file. + Persist + 1 + Type + Boolean + Value + 0 + FSPoserStopPosingWhenClosed Comment diff --git a/indra/newview/fsfloaterposer.cpp b/indra/newview/fsfloaterposer.cpp index 07e66081e3..bd3bfb70a6 100644 --- a/indra/newview/fsfloaterposer.cpp +++ b/indra/newview/fsfloaterposer.cpp @@ -61,6 +61,7 @@ constexpr std::string_view POSER_TRACKPAD_SENSITIVITY_SAVE_KEY = "FSPoserTrackpa constexpr std::string_view POSER_STOPPOSINGWHENCLOSED_SAVE_KEY = "FSPoserStopPosingWhenClosed"; constexpr std::string_view POSER_RESETBASEROTONEDIT_SAVE_KEY = "FSPoserResetBaseRotationOnEdit"; constexpr std::string_view POSER_SAVEEXTERNALFORMAT_SAVE_KEY = "FSPoserSaveExternalFileAlso"; +constexpr std::string_view POSER_SAVECONFIRMREQUIRED_SAVE_KEY = "FSPoserOnSaveConfirmOverwrite"; } // namespace /// @@ -178,6 +179,7 @@ bool FSFloaterPoser::postBuild() mBrowserFolderBtn = getChild("open_poseDir_button"); mLoadPosesBtn = getChild("load_poses_button"); mSavePosesBtn = getChild("save_poses_button"); + mSavePosesBtn->setMouseLeaveCallback([this](LLUICtrl*, const LLSD&) { onMouseLeaveSavePoseBtn(); }); mFlipPoseBtn = getChild("FlipPose_avatar"); mFlipJointBtn = getChild("FlipJoint_avatar"); @@ -356,16 +358,22 @@ void FSFloaterPoser::onPoseFileSelect() mPoseSaveNameEditor->setText(name); bool isDeltaSave = !poseFileStartsFromTeePose(name); - if (isDeltaSave) + if (isDeltaSave && hasString("LoadDiffLabel")) mLoadPosesBtn->setLabel(getString("LoadDiffLabel")); - else + else if (hasString("LoadPoseLabel")) mLoadPosesBtn->setLabel(getString("LoadPoseLabel")); } void FSFloaterPoser::onClickPoseSave() { std::string filename = mPoseSaveNameEditor->getValue().asString(); - if (filename.empty()) + if (filename.empty() && hasString("icon_save_failed_button")) + { + mSavePosesBtn->setImageOverlay(getString("icon_save_failed_button"), mSavePosesBtn->getImageOverlayHAlign()); + return; + } + + if (confirmFileOverwrite(filename)) return; LLVOAvatar* avatar = getUiSelectedAvatar(); @@ -381,8 +389,49 @@ void FSFloaterPoser::onClickPoseSave() if (getSavingToBvh()) savePoseToBvh(avatar, filename); - // TODO: provide feedback for save + if (hasString("icon_rotation_is_own_work")) + mSavePosesBtn->setImageOverlay(getString("icon_rotation_is_own_work"), mSavePosesBtn->getImageOverlayHAlign()); + + setSavePosesButtonText(!mPoserAnimator.allBaseRotationsAreZero(avatar)); } + else + { + if (hasString("icon_save_failed_button")) + mSavePosesBtn->setImageOverlay(getString("icon_save_failed_button"), mSavePosesBtn->getImageOverlayHAlign()); + } +} + +bool FSFloaterPoser::confirmFileOverwrite(std::string fileName) +{ + if (fileName.empty()) + return false; + + if (!gSavedSettings.getBOOL(POSER_SAVECONFIRMREQUIRED_SAVE_KEY)) + return false; + + if (!hasString("icon_save_query")) + return false; + + if (mSavePosesBtn->getImageOverlay().notNull() && mSavePosesBtn->getImageOverlay()->getName() == getString("icon_save_query")) + return false; + + std::string fullSavePath = + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, POSE_SAVE_SUBDIRECTORY, fileName + POSE_INTERNAL_FORMAT_FILE_EXT); + + if (!gDirUtilp->fileExists(fullSavePath)) + return false; + + mSavePosesBtn->setImageOverlay(getString("icon_save_query"), mSavePosesBtn->getImageOverlayHAlign()); + if (hasString("OverWriteLabel")) + mSavePosesBtn->setLabel(getString("OverWriteLabel")); + + return true; +} + +void FSFloaterPoser::onMouseLeaveSavePoseBtn() +{ + if (hasString("icon_save_button")) + mSavePosesBtn->setImageOverlay(getString("icon_save_button"), mSavePosesBtn->getImageOverlayHAlign()); } void FSFloaterPoser::createUserPoseDirectoryIfNeeded() @@ -2305,7 +2354,8 @@ void FSFloaterPoser::refreshTextHighlightingOnJointScrollLists() void FSFloaterPoser::setSavePosesButtonText(bool setAsSaveDiff) { - setAsSaveDiff ? mSavePosesBtn->setLabel("Save Diff") : mSavePosesBtn->setLabel("Save Pose"); + if (hasString("SavePoseLabel") && hasString("SaveDiffLabel")) + setAsSaveDiff ? mSavePosesBtn->setLabel(getString("SaveDiffLabel")) : mSavePosesBtn->setLabel(getString("SavePoseLabel")); } void FSFloaterPoser::addBoldToScrollList(LLScrollListCtrl* list, LLVOAvatar* avatar) @@ -2600,8 +2650,8 @@ S32 FSFloaterPoser::getBvhJointNegation(const std::string& jointName) const return result; } - bool FSFloaterPoser::getWhetherToResetBaseRotationOnEdit() { return gSavedSettings.getBOOL(POSER_RESETBASEROTONEDIT_SAVE_KEY); } + void FSFloaterPoser::onClickSetBaseRotZero() { mAlsoSaveBvhCbx->setEnabled(getWhetherToResetBaseRotationOnEdit()); } bool FSFloaterPoser::getSavingToBvh() diff --git a/indra/newview/fsfloaterposer.h b/indra/newview/fsfloaterposer.h index 0affb3adb9..93788d57b3 100644 --- a/indra/newview/fsfloaterposer.h +++ b/indra/newview/fsfloaterposer.h @@ -220,6 +220,7 @@ public: void createUserPoseDirectoryIfNeeded(); void onToggleLoadSavePanel(); void onClickPoseSave(); + void onMouseLeaveSavePoseBtn(); void onPoseFileSelect(); bool savePoseToXml(LLVOAvatar* avatar, const std::string& posePath); bool savePoseToBvh(LLVOAvatar* avatar, const std::string& posePath); @@ -229,6 +230,7 @@ public: bool poseFileStartsFromTeePose(const std::string& poseFileName); void setPoseSaveFileTextBoxToUiSelectedAvatarSaveFileName(); void setUiSelectedAvatarSaveFileName(const std::string& saveFileName); + bool confirmFileOverwrite(std::string fileName); void startPosingSelf(); void stopPosingAllAvatars(); // visual manipulators control diff --git a/indra/newview/fsposeranimator.h b/indra/newview/fsposeranimator.h index 2276a9a69f..8056ba7785 100644 --- a/indra/newview/fsposeranimator.h +++ b/indra/newview/fsposeranimator.h @@ -504,13 +504,13 @@ public: /// /// Symmetrizes the rotations of the joints from one side of the supplied avatar to the other. /// - /// The avatar whose joints to symmetrizet. + /// The avatar to symmetrize. /// Whether to symmetrize rotations from right to left, otherwise symmetrize left to right. void symmetrizeLeftToRightOrRightToLeft(LLVOAvatar* avatar, bool rightToLeft); /// /// Recaptures the rotation, position and scale state of the supplied joint for the supplied avatar. - /// AsDelta variant retians the original base and creates a delta relative to it. + /// AsDelta variant retains the original base and creates a delta relative to it. /// /// The avatar whose joint is to be recaptured. /// The joint to recapture. @@ -533,14 +533,11 @@ public: void setAllAvatarStartingRotationsToZero(LLVOAvatar* avatar); /// - /// Determines if the kind of save to perform should be a 'delta' save, or a complete save. + /// Determines if the supplied joint has a base rotation of zero. /// - /// The avatar whose pose-rotations are being considered for saving. - /// True if the save should save only 'deltas' to the rotation, otherwise false. - /// - /// A save of the rotation 'deltas' facilitates a user saving their changes to an existing animation. - /// Thus the save represents 'nothing other than the changes the user made', to some other pose which they may have limited rights to. - /// + /// The avatar owning the supplied joint. + /// The joint to query. + /// True if the supplied joint has a 'base' rotation of zero (thus user-supplied change only), otherwise false. bool baseRotationIsZero(LLVOAvatar* avatar, const FSPoserJoint& joint) const; /// diff --git a/indra/newview/skins/default/xui/en/floater_fs_poser.xml b/indra/newview/skins/default/xui/en/floater_fs_poser.xml index 1cd3cb1ef1..2cca78dc29 100644 --- a/indra/newview/skins/default/xui/en/floater_fs_poser.xml +++ b/indra/newview/skins/default/xui/en/floater_fs_poser.xml @@ -11,6 +11,9 @@ width="430"> Inv_Object Check_Mark + Icon_Dock_Foreground + Parcel_Exp_Color + Info tool_tip="When you save your pose, also write a BVH file, which can be uploaded via the 'Build > Upload > Animation' to pose yourself or others in-world. This needs joints to reset their 'base' to zero, because BVH requires original work." top_pad="2" width="134" /> +