mirror of
https://github.com/FirestormViewer/phoenix-firestorm.git
synced 2026-08-14 08:53:53 +00:00
Merge branch 'master' of https://github.com/FirestormViewer/phoenix-firestorm-202602
This commit is contained in:
@@ -386,7 +386,7 @@ jobs:
|
||||
tar -xJf "${{ needs.build.outputs.viewer_channel }}.sym.tar.xz" -C _artifacts
|
||||
- name: Post Windows symbols
|
||||
if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
|
||||
uses: BugSplat-Git/symbol-upload@095d163ae9ceb006d286a731dcd35cf6a1b458c8
|
||||
uses: BugSplat-Git/symbol-upload@f56c6c85e5d04890d5358fb378fa782d3e3800d8
|
||||
with:
|
||||
clientId: "${{ env.SYMBOL_UPLOAD_CLIENT_ID }}"
|
||||
clientSecret: "${{ env.SYMBOL_UPLOAD_CLIENT_SECRET }}"
|
||||
@@ -414,7 +414,7 @@ jobs:
|
||||
name: macOS-symbols
|
||||
- name: Post Mac symbols
|
||||
if: env.BUGSPLAT_DATABASE && env.SYMBOL_UPLOAD_CLIENT_ID
|
||||
uses: BugSplat-Git/symbol-upload@095d163ae9ceb006d286a731dcd35cf6a1b458c8
|
||||
uses: BugSplat-Git/symbol-upload@f56c6c85e5d04890d5358fb378fa782d3e3800d8
|
||||
with:
|
||||
clientId: "${{ env.SYMBOL_UPLOAD_CLIENT_ID }}"
|
||||
clientSecret: "${{ env.SYMBOL_UPLOAD_CLIENT_SECRET }}"
|
||||
|
||||
@@ -440,9 +440,9 @@ jobs:
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -r "indra/newview/VIEWER_VERSION.txt" ]
|
||||
if [ -r "indra/newview/VIEWER_VERSION_FS.txt" ]
|
||||
then
|
||||
viewer_version="$(<"indra/newview/VIEWER_VERSION.txt")"
|
||||
viewer_version="$(<"indra/newview/VIEWER_VERSION_FS.txt")"
|
||||
echo "viewer_version=$viewer_version" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
viewer_build=$(git rev-list --count HEAD)
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
run: pip install discord-webhook
|
||||
|
||||
- name: Download Build Artifacts
|
||||
uses: dawidd6/action-download-artifact@v16
|
||||
uses: dawidd6/action-download-artifact@v19
|
||||
id: download
|
||||
with:
|
||||
workflow: build_viewer.yml
|
||||
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
setup_files: ${{ steps.get-files.outputs.setup_files }}
|
||||
steps:
|
||||
- name: Download Build Artifacts
|
||||
uses: dawidd6/action-download-artifact@v16
|
||||
uses: dawidd6/action-download-artifact@v19
|
||||
id: download
|
||||
with:
|
||||
workflow: build_viewer.yml
|
||||
@@ -115,7 +115,7 @@ jobs:
|
||||
# wait-for-completion: true
|
||||
# output-artifact-directory: 'application-signed'
|
||||
- name: Azure Trusted Signing
|
||||
uses: azure/trusted-signing-action@v1.1.0
|
||||
uses: azure/trusted-signing-action@v1.2.0
|
||||
with:
|
||||
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
echo "build_run_number=${{ github.event.inputs.build_run_number }}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Download Build Artifacts
|
||||
uses: dawidd6/action-download-artifact@v16
|
||||
uses: dawidd6/action-download-artifact@v19
|
||||
id: download_build_info
|
||||
with:
|
||||
workflow: build_viewer.yml
|
||||
|
||||
+2
-2
@@ -2724,11 +2724,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||
<key>archive</key>
|
||||
<map>
|
||||
<key>hash</key>
|
||||
<string>52bfa9057a5d34cd841e93624c03a0cdcdf7b12a</string>
|
||||
<string>05e30110611a8fd71c79eebe1fc33f429d98efb0</string>
|
||||
<key>hash_algorithm</key>
|
||||
<string>sha1</string>
|
||||
<key>url</key>
|
||||
<string>https://3p.firestormviewer.org/viewer_fonts-1.0.0.242151216-common-242151216.tar.bz2</string>
|
||||
<string>https://3p.firestormviewer.org/viewer_fonts-1.1.2-common-260701806.tar.bz2</string>
|
||||
</map>
|
||||
<key>name</key>
|
||||
<string>common</string>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# Construct the viewer version number based on the indra/VIEWER_VERSION file
|
||||
|
||||
if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/newview/
|
||||
set(VIEWER_VERSION_BASE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/newview/VIEWER_VERSION.txt")
|
||||
# <FS:Ansariel> Use own version file to avoid LL's year-as-major-version thing
|
||||
# set(VIEWER_VERSION_BASE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/newview/VIEWER_VERSION.txt")
|
||||
set(VIEWER_VERSION_BASE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/newview/VIEWER_VERSION_FS.txt")
|
||||
|
||||
if ( EXISTS ${VIEWER_VERSION_BASE_FILE} )
|
||||
file(STRINGS ${VIEWER_VERSION_BASE_FILE} VIEWER_SHORT_VERSION REGEX "^[0-9]+\\.[0-9]+\\.[0-9]+")
|
||||
@@ -47,7 +49,7 @@ if (NOT DEFINED VIEWER_SHORT_VERSION) # will be true in indra/, false in indra/n
|
||||
endif ("${VIEWER_VERSION_REVISION}" STREQUAL "")
|
||||
|
||||
# <FS:PP>
|
||||
set(VIEWER_VERSION_LL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/newview/VIEWER_VERSION_LL.txt")
|
||||
set(VIEWER_VERSION_LL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/newview/VIEWER_VERSION.txt")
|
||||
if (EXISTS ${VIEWER_VERSION_LL_FILE})
|
||||
file(STRINGS ${VIEWER_VERSION_LL_FILE} VIEWER_VERSION_LL LIMIT_COUNT 1)
|
||||
string(STRIP "${VIEWER_VERSION_LL}" VIEWER_VERSION_LL)
|
||||
|
||||
@@ -1629,6 +1629,7 @@ bool LLModel::loadDecomposition(LLSD& header, std::istream& is)
|
||||
}
|
||||
|
||||
LLMeshSkinInfo::LLMeshSkinInfo():
|
||||
mBindShapeMatrix(LLMatrix4a::identity()), // <FS:Beq/> Keep LLMeshSkinInfo default state in one place
|
||||
mPelvisOffset(0.0),
|
||||
mLockScaleIfJointPosition(false),
|
||||
mInvalidJointsScrubbed(false),
|
||||
@@ -1636,22 +1637,34 @@ LLMeshSkinInfo::LLMeshSkinInfo():
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
LLMeshSkinInfo::LLMeshSkinInfo(LLSD& skin):
|
||||
mPelvisOffset(0.0),
|
||||
mLockScaleIfJointPosition(false),
|
||||
mInvalidJointsScrubbed(false),
|
||||
mJointNumsInitialized(false)
|
||||
// <FS:Beq> Keep LLMeshSkinInfo default state in one place
|
||||
// mPelvisOffset(0.0),
|
||||
// mLockScaleIfJointPosition(false),
|
||||
// mInvalidJointsScrubbed(false),
|
||||
// mJointNumsInitialized(false)
|
||||
// {
|
||||
// mBindShapeMatrix = LLMatrix4a::identity();
|
||||
LLMeshSkinInfo()
|
||||
{
|
||||
// </FS:Beq>
|
||||
fromLLSD(skin);
|
||||
}
|
||||
|
||||
LLMeshSkinInfo::LLMeshSkinInfo(const LLUUID& mesh_id, LLSD& skin) :
|
||||
mMeshID(mesh_id),
|
||||
mPelvisOffset(0.0),
|
||||
mLockScaleIfJointPosition(false),
|
||||
mInvalidJointsScrubbed(false),
|
||||
mJointNumsInitialized(false)
|
||||
// <FS:Beq> Keep LLMeshSkinInfo default state in one place
|
||||
// mMeshID(mesh_id),
|
||||
// mPelvisOffset(0.0),
|
||||
// mLockScaleIfJointPosition(false),
|
||||
// mInvalidJointsScrubbed(false),
|
||||
// mJointNumsInitialized(false)
|
||||
// {
|
||||
// mBindShapeMatrix = LLMatrix4a::identity();
|
||||
LLMeshSkinInfo()
|
||||
{
|
||||
// </FS:Beq>
|
||||
mMeshID = mesh_id;
|
||||
fromLLSD(skin);
|
||||
}
|
||||
|
||||
@@ -2388,4 +2401,3 @@ bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const
|
||||
|
||||
return mFullbright < rhs.mFullbright;
|
||||
}
|
||||
|
||||
|
||||
@@ -443,6 +443,7 @@ void LLFlatListView::sort()
|
||||
|
||||
mItemPairs.sort(ComparatorAdaptor(*mItemComparator));
|
||||
rearrangeItems();
|
||||
notifyParentItemsRectChanged(); // <FS:PP> FIRE-36479 Multiple nested scrollbars in Appearance window
|
||||
}
|
||||
|
||||
bool LLFlatListView::updateValue(const LLSD& old_value, const LLSD& new_value)
|
||||
|
||||
@@ -113,6 +113,7 @@ public:
|
||||
virtual bool isText() const { return false; }
|
||||
virtual bool needsToolTip() const { return ! mToolTip.empty(); }
|
||||
virtual void setColor(const LLColor4&) {}
|
||||
virtual void setUseColor(bool) {} // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
virtual void onCommit() {};
|
||||
|
||||
virtual bool handleClick() { return false; }
|
||||
@@ -151,6 +152,7 @@ public:
|
||||
/*virtual*/ void highlightText(S32 offset, S32 num_chars);
|
||||
|
||||
/*virtual*/ void setColor(const LLColor4&);
|
||||
/*virtual*/ void setUseColor(bool use_color) { mUseColor = use_color; } // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
/*virtual*/ bool isText() const;
|
||||
/*virtual*/ const std::string & getToolTip() const;
|
||||
/*virtual*/ bool needsToolTip() const;
|
||||
|
||||
@@ -124,6 +124,7 @@ set(viewer_SOURCE_FILES
|
||||
fsfloaterexport.cpp
|
||||
fsfloatergroup.cpp
|
||||
fsfloatergrouptitles.cpp
|
||||
fsgrouptitleregionmgr.cpp
|
||||
fsfloaterimport.cpp
|
||||
fsfloaterim.cpp
|
||||
fsfloaterimcontainer.cpp
|
||||
@@ -163,6 +164,7 @@ set(viewer_SOURCE_FILES
|
||||
fspanelface.cpp
|
||||
fspanelimcontrolpanel.cpp
|
||||
fspanellogin.cpp
|
||||
fspanelpreferenceuisounds.cpp
|
||||
fspanelprefs.cpp
|
||||
fspanelradar.cpp
|
||||
fsparticipantlist.cpp
|
||||
@@ -198,7 +200,9 @@ set(viewer_SOURCE_FILES
|
||||
llagentwearablesfetch.cpp
|
||||
vjlocalmesh.cpp
|
||||
vjfloaterlocalmesh.cpp
|
||||
fslocalmeshimportbase.cpp
|
||||
vjlocalmeshimportdae.cpp
|
||||
fslocalmeshimportgltf.cpp
|
||||
|
||||
gltfscenemanager.cpp
|
||||
# <FS:Ansariel> Group GLTF files into their subfolders
|
||||
@@ -976,6 +980,7 @@ set(viewer_HEADER_FILES
|
||||
fsfloaterexport.h
|
||||
fsfloatergroup.h
|
||||
fsfloatergrouptitles.h
|
||||
fsgrouptitleregionmgr.h
|
||||
fsfloaterimport.h
|
||||
fsfloaterim.h
|
||||
fsfloaterimcontainer.h
|
||||
@@ -1016,6 +1021,7 @@ set(viewer_HEADER_FILES
|
||||
fspanelface.h
|
||||
fspanelimcontrolpanel.h
|
||||
fspanellogin.h
|
||||
fspanelpreferenceuisounds.h
|
||||
fspanelprefs.h
|
||||
fspanelradar.h
|
||||
fsparticipantlist.h
|
||||
@@ -1051,7 +1057,9 @@ set(viewer_HEADER_FILES
|
||||
omnifilterengine.h
|
||||
vjlocalmesh.h
|
||||
vjfloaterlocalmesh.h
|
||||
fslocalmeshimportbase.h
|
||||
vjlocalmeshimportdae.h
|
||||
fslocalmeshimportgltf.h
|
||||
|
||||
gltfscenemanager.h
|
||||
groupchatlistener.h
|
||||
@@ -3140,4 +3148,3 @@ if (LL_TESTS)
|
||||
endif (LL_TESTS)
|
||||
|
||||
check_message_template(${VIEWER_BINARY_NAME})
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
7.2.4
|
||||
@@ -1 +0,0 @@
|
||||
7.2.3
|
||||
@@ -229,10 +229,8 @@
|
||||
icon="Command_Search_Icon"
|
||||
label_ref="Command_Search_Label"
|
||||
tooltip_ref="Command_Search_Tooltip"
|
||||
execute_function="Floater.ToggleOrBringToFront"
|
||||
execute_parameters="search"
|
||||
is_running_function="Floater.IsOpen"
|
||||
is_running_parameters="search"
|
||||
execute_function="Avatar.ToggleSearch"
|
||||
is_running_function="Avatar.SearchVisible"
|
||||
/>
|
||||
<command name="snapshot"
|
||||
available_in_toybox="true"
|
||||
|
||||
@@ -24,6 +24,17 @@
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>FSEventPollCoreRetries</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>DEBUG: EventQueueGet llcorehttp retry count. 0 keeps current 7.2.3 behavior; 5 matches 7.2.2 behaviour.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>U32</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSUseV2Friends</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -465,6 +476,17 @@
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSContactSetsColorizeFriends</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Whether to color a friends list entry based on their friends groups</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSMuteAllGroups</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -3284,6 +3306,28 @@
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSCloseChatOnReturnInMouselook</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>When CloseChatOnReturn is enabled, only deselect nearby chat in mouselook</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>FSCloseChatOnReturnOnlyForNearbyChatControl</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>When CloseChatOnReturn is enabled, only deselect nearby chat bar</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>CmdLineChannel</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -9819,7 +9863,7 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>PlaySoundChatMention</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Plays a sound when got mentioned in a chat</string>
|
||||
<string>Plays a sound when got mentioned in a chat - UNUSED IN FS - see PlayModeUISndChatMention</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
@@ -16689,6 +16733,17 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>Value</key>
|
||||
<string>4174f859-0d3d-c517-c424-72923dc21f65</string>
|
||||
</map>
|
||||
<key>UISndNearbyChat</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Sound file for nearby chat messages from other avatars (uuid for sound asset)</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>String</string>
|
||||
<key>Value</key>
|
||||
<string>a3f48b85-c29f-1f97-ebb6-644b7c053512</string>
|
||||
</map>
|
||||
<key>UISndNewIncomingIMSession</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -18210,7 +18265,7 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>Type</key>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>WaterEditPresets</key>
|
||||
<map>
|
||||
@@ -22329,6 +22384,17 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>PlayModeUISndChatMention</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Holds state for Prefs > Sound/Media > UI Sounds - UISndChatMention.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>PlayModeUISndClick</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -22505,6 +22571,17 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>PlayModeUISndNearbyChat</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Holds state for Prefs > Sound/Media > UI Sounds - UISndNearbyChat.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>PlayModeUISndNewIncomingIMSession</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -26656,6 +26733,28 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSLocalMeshAutoReload</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Automatically reload local mesh files when they change on disk.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>FSLocalMeshAutoReloadPeriod</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>How often, in seconds, the viewer should scan local mesh files for changes.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>F32</string>
|
||||
<key>Value</key>
|
||||
<real>3.0</real>
|
||||
</map>
|
||||
<key>FSShowEmojiButton</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
@@ -27389,7 +27488,7 @@ Change of this parameter will affect the layout of buttons in notification toast
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
</map>
|
||||
</llsd>
|
||||
|
||||
@@ -999,6 +999,17 @@
|
||||
<key>Value</key>
|
||||
<string>0</string>
|
||||
</map>
|
||||
<key>FSGesturesEnabled</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Enable gesture playback and trigger handling viewer-wide - for your own gestures</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<key>UseLSLFlightAssist</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
||||
@@ -39,7 +39,26 @@ FSFloaterAddToContactSet::FSFloaterAddToContactSet(const LLSD& target)
|
||||
: LLFloater(target),
|
||||
mContactSetsCombo(nullptr)
|
||||
{
|
||||
if (target.isArray())
|
||||
if (target.isMap())
|
||||
{
|
||||
mIsMoveOperation = (target["operation"].asString() == "move");
|
||||
mSourceSet = target["source_set"].asString();
|
||||
const LLSD ids = target["ids"];
|
||||
if (ids.isArray())
|
||||
{
|
||||
mHasMultipleAgents = true;
|
||||
for (LLSD::array_const_iterator it = ids.beginArray(); it != ids.endArray(); ++it)
|
||||
{
|
||||
mAgentIDs.push_back((*it).asUUID());
|
||||
}
|
||||
}
|
||||
else if (target.has("id"))
|
||||
{
|
||||
mHasMultipleAgents = false;
|
||||
mAgentID = target["id"].asUUID();
|
||||
}
|
||||
}
|
||||
else if (target.isArray())
|
||||
{
|
||||
mHasMultipleAgents = true;
|
||||
for (LLSD::array_const_iterator it = target.beginArray(); it != target.endArray(); ++it)
|
||||
@@ -92,22 +111,41 @@ bool FSFloaterAddToContactSet::postBuild()
|
||||
void FSFloaterAddToContactSet::onClickAdd()
|
||||
{
|
||||
const std::string set = mContactSetsCombo->getSimple();
|
||||
if (set.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mHasMultipleAgents)
|
||||
{
|
||||
if (mAgentID.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
mAgentIDs.push_back(mAgentID);
|
||||
}
|
||||
else if (mAgentIDs.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LGGContactSets::instance().addToSet(mAgentIDs, set);
|
||||
if (mIsMoveOperation && set != mSourceSet)
|
||||
{
|
||||
for (const LLUUID& id : mAgentIDs)
|
||||
{
|
||||
LGGContactSets::instance().removeFriendFromSet(id, mSourceSet);
|
||||
}
|
||||
}
|
||||
|
||||
if (mHasMultipleAgents)
|
||||
if (!mIsMoveOperation && mHasMultipleAgents)
|
||||
{
|
||||
LLSD args;
|
||||
args["COUNT"] = llformat("%d", mAgentIDs.size());
|
||||
args["SET"] = set;
|
||||
LLNotificationsUtil::add("AddToContactSetMultipleSuccess", args);
|
||||
}
|
||||
else
|
||||
else if (!mIsMoveOperation)
|
||||
{
|
||||
LLSD args;
|
||||
args["NAME"] = LLSLURL("agent", mAgentID, "inspect").getSLURLString();
|
||||
@@ -144,6 +182,7 @@ void FSFloaterAddToContactSet::populateContactSets()
|
||||
|
||||
mContactSetsCombo->clearRows();
|
||||
std::vector<std::string> contact_sets = LGGContactSets::getInstance()->getAllContactSets();
|
||||
bool isEmptySet = true;
|
||||
if (contact_sets.empty())
|
||||
{
|
||||
mContactSetsCombo->add(getString("no_sets"), LLSD("No Set"));
|
||||
@@ -152,8 +191,13 @@ void FSFloaterAddToContactSet::populateContactSets()
|
||||
{
|
||||
for (auto const& set_name : contact_sets)
|
||||
{
|
||||
if (mIsMoveOperation && set_name == mSourceSet)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
mContactSetsCombo->add(set_name);
|
||||
isEmptySet = false;
|
||||
}
|
||||
}
|
||||
getChild<LLButton>("add_btn")->setEnabled(!contact_sets.empty());
|
||||
getChild<LLButton>("add_btn")->setEnabled(!isEmptySet);
|
||||
}
|
||||
|
||||
@@ -52,9 +52,11 @@ private:
|
||||
void updateSets(LGGContactSets::EContactSetUpdate type);
|
||||
boost::signals2::connection mContactSetChangedConnection;
|
||||
|
||||
bool mHasMultipleAgents;
|
||||
bool mHasMultipleAgents{ false };
|
||||
bool mIsMoveOperation{ false };
|
||||
LLUUID mAgentID;
|
||||
uuid_vec_t mAgentIDs;
|
||||
std::string mSourceSet;
|
||||
|
||||
LLComboBox* mContactSetsCombo;
|
||||
};
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "lltooldraganddrop.h"
|
||||
#include "llviewermenu.h"
|
||||
#include "llvoiceclient.h"
|
||||
#include "lggcontactsets.h"
|
||||
// [RLVa:KB] - @pay
|
||||
#include "rlvactions.h"
|
||||
// [/RLVa:KB]
|
||||
@@ -73,6 +74,7 @@ FSFloaterContacts::FSFloaterContacts(const LLSD& seed)
|
||||
LLAvatarTracker::instance().addObserver(this);
|
||||
// For notification when SIP online status changes.
|
||||
LLVoiceClient::addObserver(this);
|
||||
mContactSetChangedConnection = LGGContactSets::getInstance()->setContactSetChangeCallback(boost::bind(&FSFloaterContacts::onContactSetsChanged, this, _1));
|
||||
}
|
||||
|
||||
FSFloaterContacts::~FSFloaterContacts()
|
||||
@@ -81,6 +83,11 @@ FSFloaterContacts::~FSFloaterContacts()
|
||||
LLVoiceClient::removeObserver(this);
|
||||
LLAvatarTracker::instance().removeObserver(this);
|
||||
|
||||
if (mContactSetChangedConnection.connected())
|
||||
{
|
||||
mContactSetChangedConnection.disconnect();
|
||||
}
|
||||
|
||||
if (mRlvBehaviorCallbackConnection.connected())
|
||||
{
|
||||
mRlvBehaviorCallbackConnection.disconnect();
|
||||
@@ -181,6 +188,7 @@ bool FSFloaterContacts::postBuild()
|
||||
gSavedSettings.getControl("FSFriendListColumnShowDisplayName")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, "FSFriendListColumnShowDisplayName"));
|
||||
gSavedSettings.getControl("FSFriendListColumnShowFullName")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, "FSFriendListColumnShowFullName"));
|
||||
gSavedSettings.getControl("FSFriendListColumnShowPermissions")->getSignal()->connect(boost::bind(&FSFloaterContacts::onColumnDisplayModeChanged, this, std::string()));
|
||||
gSavedSettings.getControl("FSContactSetsColorizeFriends")->getSignal()->connect(boost::bind(&FSFloaterContacts::onDisplayNameChanged, this));
|
||||
onColumnDisplayModeChanged();
|
||||
|
||||
LLAvatarNameCache::getInstance()->addUseDisplayNamesCallback(boost::bind(&FSFloaterContacts::onDisplayNameChanged, this));
|
||||
@@ -266,7 +274,7 @@ void FSFloaterContacts::onOpen(const LLSD& key)
|
||||
// first set the tear-off host to the conversations container
|
||||
setHost(floater_container);
|
||||
// clear the tear-off host right after, the "last host used" will still stick
|
||||
setHost(NULL);
|
||||
setHost(nullptr);
|
||||
// reparent to floater view
|
||||
gFloaterView->addChild(this);
|
||||
}
|
||||
@@ -381,7 +389,7 @@ void FSFloaterContacts::onDeleteFriendButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
bool FSFloaterContacts::isItemsFreeOfFriends(const uuid_vec_t& uuids)
|
||||
bool FSFloaterContacts::isItemsFreeOfFriends(const uuid_vec_t& uuids) const
|
||||
{
|
||||
const LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
|
||||
for (const auto& id : uuids)
|
||||
@@ -715,7 +723,7 @@ void FSFloaterContacts::addFriend(const LLUUID& agent_id)
|
||||
update_gen_column["column"] = "friend_last_update_generation";
|
||||
update_gen_column["value"] = relationInfo->getChangeSerialNum();
|
||||
|
||||
mFriendsList->addElement(element, ADD_BOTTOM);
|
||||
updateFriendItemColor(mFriendsList->addElement(element, ADD_BOTTOM), agent_id);
|
||||
}
|
||||
|
||||
void FSFloaterContacts::onMapButtonClicked()
|
||||
@@ -767,6 +775,7 @@ void FSFloaterContacts::updateFriendItem(const LLUUID& agent_id, const LLRelatio
|
||||
itemp->getColumn(LIST_FRIEND_USER_NAME)->setValue(av_name.getUserNameForDisplay());
|
||||
itemp->getColumn(LIST_FRIEND_DISPLAY_NAME)->setValue(av_name.getDisplayName());
|
||||
itemp->getColumn(LIST_FRIEND_NAME)->setValue(getFullName(av_name));
|
||||
updateFriendItemColor(itemp, agent_id);
|
||||
|
||||
// render name of online friends in bold text
|
||||
LLFontGL::StyleFlags font_style = ((isOnline || isOnlineSIP) ? LLFontGL::BOLD : LLFontGL::NORMAL);
|
||||
@@ -786,6 +795,30 @@ void FSFloaterContacts::updateFriendItem(const LLUUID& agent_id, const LLRelatio
|
||||
itemp->setEnabled(true);
|
||||
}
|
||||
|
||||
void FSFloaterContacts::updateFriendItemColor(LLScrollListItem* item, const LLUUID& agent_id) const
|
||||
{
|
||||
LLColor4 name_color;
|
||||
const bool has_contact_set_color = LGGContactSets::getInstance()->hasFriendColorThatShouldShow(agent_id, ContactSetType::FRIENDS, name_color);
|
||||
LLScrollListCell* user_name_cell = item->getColumn(LIST_FRIEND_USER_NAME);
|
||||
LLScrollListCell* display_name_cell = item->getColumn(LIST_FRIEND_DISPLAY_NAME);
|
||||
LLScrollListCell* full_name_cell = item->getColumn(LIST_FRIEND_NAME);
|
||||
if (has_contact_set_color)
|
||||
{
|
||||
user_name_cell->setColor(name_color);
|
||||
display_name_cell->setColor(name_color);
|
||||
full_name_cell->setColor(name_color);
|
||||
user_name_cell->setUseColor(true);
|
||||
display_name_cell->setUseColor(true);
|
||||
full_name_cell->setUseColor(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
user_name_cell->setUseColor(false);
|
||||
display_name_cell->setUseColor(false);
|
||||
full_name_cell->setUseColor(false);
|
||||
}
|
||||
}
|
||||
|
||||
void FSFloaterContacts::updateFriendItem(const LLUUID& agent_id, const LLRelationship* relationship, const LLUUID& request_id)
|
||||
{
|
||||
disconnectAvatarNameCacheConnection(request_id);
|
||||
@@ -899,7 +932,7 @@ void FSFloaterContacts::onSelectName()
|
||||
applyRightsToFriends();
|
||||
}
|
||||
|
||||
void FSFloaterContacts::confirmModifyRights(rights_map_t& ids, EGrantRevoke command)
|
||||
void FSFloaterContacts::confirmModifyRights(const rights_map_t& ids, EGrantRevoke command)
|
||||
{
|
||||
if (ids.empty())
|
||||
{
|
||||
@@ -907,48 +940,44 @@ void FSFloaterContacts::confirmModifyRights(rights_map_t& ids, EGrantRevoke comm
|
||||
}
|
||||
|
||||
LLSD args;
|
||||
if (ids.size() > 0)
|
||||
{
|
||||
rights_map_t* rights = new rights_map_t(ids);
|
||||
rights_map_t* rights = new rights_map_t(ids);
|
||||
|
||||
// for single friend, show their name
|
||||
if (ids.size() == 1)
|
||||
// for single friend, show their name
|
||||
if (ids.size() == 1)
|
||||
{
|
||||
args["NAME"] = LLSLURL("agent", ids.begin()->first, "completename").getSLURLString();
|
||||
if (command == GRANT)
|
||||
{
|
||||
args["NAME"] = LLSLURL("agent", ids.begin()->first, "completename").getSLURLString();
|
||||
if (command == GRANT)
|
||||
{
|
||||
LLNotificationsUtil::add("GrantModifyRights",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
else
|
||||
{
|
||||
LLNotificationsUtil::add("RevokeModifyRights",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
LLNotificationsUtil::add("GrantModifyRights",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (command == GRANT)
|
||||
{
|
||||
LLNotificationsUtil::add("GrantModifyRightsMultiple",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
else
|
||||
{
|
||||
LLNotificationsUtil::add("RevokeModifyRightsMultiple",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
LLNotificationsUtil::add("RevokeModifyRights",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (command == GRANT)
|
||||
{
|
||||
LLNotificationsUtil::add("GrantModifyRightsMultiple",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
else
|
||||
{
|
||||
LLNotificationsUtil::add("RevokeModifyRightsMultiple",
|
||||
args,
|
||||
LLSD(),
|
||||
boost::bind(&FSFloaterContacts::modifyRightsConfirmation, this, _1, _2, rights));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool FSFloaterContacts::modifyRightsConfirmation(const LLSD& notification, const LLSD& response, rights_map_t* rights)
|
||||
@@ -1076,7 +1105,7 @@ void FSFloaterContacts::applyRightsToFriends()
|
||||
}
|
||||
}
|
||||
|
||||
void FSFloaterContacts::sendRightsGrant(rights_map_t& ids)
|
||||
void FSFloaterContacts::sendRightsGrant(const rights_map_t& ids)
|
||||
{
|
||||
if (ids.empty())
|
||||
{
|
||||
@@ -1264,6 +1293,7 @@ void FSFloaterContacts::onDisplayNameChanged()
|
||||
item->getColumn(LIST_FRIEND_USER_NAME)->setValue(av_name.getUserNameForDisplay());
|
||||
item->getColumn(LIST_FRIEND_DISPLAY_NAME)->setValue(av_name.getDisplayName());
|
||||
item->getColumn(LIST_FRIEND_NAME)->setValue(getFullName(av_name));
|
||||
updateFriendItemColor(item, item->getUUID());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1274,7 +1304,7 @@ void FSFloaterContacts::onDisplayNameChanged()
|
||||
mFriendsList->setNeedsSort();
|
||||
}
|
||||
|
||||
std::string FSFloaterContacts::getFullName(const LLAvatarName& av_name)
|
||||
std::string FSFloaterContacts::getFullName(const LLAvatarName& av_name) const
|
||||
{
|
||||
if (av_name.isDisplayNameDefault() || !gSavedSettings.getBOOL("UseDisplayNames"))
|
||||
{
|
||||
@@ -1376,6 +1406,18 @@ void FSFloaterContacts::onGroupFilterEdit(const std::string& search_string)
|
||||
mGroupList->setNameFilter(search_string);
|
||||
}
|
||||
|
||||
void FSFloaterContacts::onContactSetsChanged(LGGContactSets::EContactSetUpdate type)
|
||||
{
|
||||
if (!mFriendsList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (type == LGGContactSets::UPDATED_LISTS || type == LGGContactSets::UPDATED_MEMBERS)
|
||||
{
|
||||
onDisplayNameChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// <FS:TJ> [FIRE-35804] Allow the IM floater to have separate transparency
|
||||
// This is specifically for making the filter editors such as mFriendFilter and mGroupFilter always active opacity when the IM floater is focused
|
||||
// Otherwise if they aren't active, it will use either the IM opacity, or inactive opacity, whatever is smaller
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "llavatarnamecache.h"
|
||||
#include "llcallingcard.h"
|
||||
#include "llfloater.h"
|
||||
#include "llscrolllistcolumn.h"
|
||||
#include "lggcontactsets.h"
|
||||
#include "rlvhandler.h"
|
||||
|
||||
class FSScrollListCtrl;
|
||||
@@ -66,16 +66,16 @@ public:
|
||||
void openTab(std::string_view name);
|
||||
LLPanel* getPanelByName(std::string_view panel_name);
|
||||
|
||||
void sortFriendList();
|
||||
void onDisplayNameChanged();
|
||||
void resetFriendFilter();
|
||||
void sortFriendList();
|
||||
void onDisplayNameChanged();
|
||||
void resetFriendFilter();
|
||||
|
||||
// <FS:TJ> [FIRE-35804] Allow the IM floater to have separate transparency
|
||||
F32 onGetFilterOpacityCallback(ETypeTransparency type, F32 alpha);
|
||||
// </FS:TJ>
|
||||
|
||||
private:
|
||||
typedef std::vector<LLScrollListItem*> listitem_vec_t;
|
||||
using listitem_vec_t = std::vector<LLScrollListItem*>;
|
||||
|
||||
std::string getActiveTabName() const;
|
||||
LLUUID getCurrentItemID() const;
|
||||
@@ -96,7 +96,7 @@ private:
|
||||
LIST_FRIEND_UPDATE_GEN
|
||||
};
|
||||
|
||||
typedef std::map<LLUUID, S32> rights_map_t;
|
||||
using rights_map_t = std::map<LLUUID, S32>;
|
||||
void refreshRightsChangeList();
|
||||
void refreshUI();
|
||||
void updateFriendCount();
|
||||
@@ -105,18 +105,19 @@ private:
|
||||
void addFriend(const LLUUID& agent_id);
|
||||
void updateFriendItem(const LLUUID& agent_id, const LLRelationship* relationship);
|
||||
void updateFriendItem(const LLUUID& agent_id, const LLRelationship* relationship, const LLUUID& request_id);
|
||||
void updateFriendItemColor(LLScrollListItem* item, const LLUUID& agent_id) const;
|
||||
|
||||
typedef enum
|
||||
enum EGrantRevoke
|
||||
{
|
||||
GRANT,
|
||||
REVOKE
|
||||
} EGrantRevoke;
|
||||
void confirmModifyRights(rights_map_t& ids, EGrantRevoke command);
|
||||
void sendRightsGrant(rights_map_t& ids);
|
||||
};
|
||||
void confirmModifyRights(const rights_map_t& ids, EGrantRevoke command);
|
||||
void sendRightsGrant(const rights_map_t& ids);
|
||||
bool modifyRightsConfirmation(const LLSD& notification, const LLSD& response, rights_map_t* rights);
|
||||
|
||||
|
||||
bool isItemsFreeOfFriends(const uuid_vec_t& uuids);
|
||||
bool isItemsFreeOfFriends(const uuid_vec_t& uuids) const;
|
||||
|
||||
// misc callbacks
|
||||
static void onAvatarPicked(const uuid_vec_t& ids, const std::vector<LLAvatarName>& names);
|
||||
@@ -128,6 +129,7 @@ private:
|
||||
std::string& tooltip_msg);
|
||||
void onFriendFilterEdit(const std::string& search_string);
|
||||
void onGroupFilterEdit(const std::string& search_string);
|
||||
void onContactSetsChanged(LGGContactSets::EContactSetUpdate type);
|
||||
|
||||
// friend buttons
|
||||
void onViewProfileButtonClicked();
|
||||
@@ -196,12 +198,13 @@ private:
|
||||
|
||||
void updateRlvRestrictions(ERlvBehaviour behavior);
|
||||
boost::signals2::connection mRlvBehaviorCallbackConnection{};
|
||||
boost::signals2::connection mContactSetChangedConnection{};
|
||||
|
||||
std::string getFullName(const LLAvatarName& av_name);
|
||||
std::string getFullName(const LLAvatarName& av_name) const;
|
||||
|
||||
void setDirtyNames(const LLUUID& request_id);
|
||||
|
||||
typedef std::map<LLUUID, LLAvatarNameCache::callback_connection_t> avatar_name_cb_t;
|
||||
using avatar_name_cb_t = std::map<LLUUID, LLAvatarNameCache::callback_connection_t>;
|
||||
avatar_name_cb_t mAvatarNameCacheConnections;
|
||||
void disconnectAvatarNameCacheConnection(const LLUUID& request_id);
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "llcolorswatch.h"
|
||||
#include "lllineeditor.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "lltexteditor.h"
|
||||
#include "llviewercontrol.h"
|
||||
|
||||
FSFloaterContactSetConfiguration::FSFloaterContactSetConfiguration(const LLSD& target_set)
|
||||
@@ -67,6 +68,24 @@ bool FSFloaterContactSetConfiguration::postBuild()
|
||||
|
||||
mNotificationCheckBox = getChild<LLCheckBoxCtrl>("show_set_notifications");
|
||||
mNotificationCheckBox->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetNotifications, this));
|
||||
|
||||
mSortByOnlineStatusCheckBox = getChild<LLCheckBoxCtrl>("sort_by_online_status");
|
||||
mSortByOnlineStatusCheckBox->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetSortByOnlineStatus, this));
|
||||
|
||||
mAutoresponseBusyEnabledCheckBox = getChild<LLCheckBoxCtrl>("set_autoresponse_busy_enabled");
|
||||
mAutoresponseBusyEnabledCheckBox->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetAutoresponseBusy, this));
|
||||
mAutoresponseBusyEditor = getChild<LLTextEditor>("set_autoresponse_busy");
|
||||
mAutoresponseBusyEditor->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetAutoresponseBusy, this));
|
||||
|
||||
mAutoresponseModeEnabledCheckBox = getChild<LLCheckBoxCtrl>("set_autoresponse_mode_enabled");
|
||||
mAutoresponseModeEnabledCheckBox->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetAutoresponseMode, this));
|
||||
mAutoresponseModeEditor = getChild<LLTextEditor>("set_autoresponse_mode");
|
||||
mAutoresponseModeEditor->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetAutoresponseMode, this));
|
||||
|
||||
mAutoresponseNonFriendsEnabledCheckBox = getChild<LLCheckBoxCtrl>("set_autoresponse_nonfriends_enabled");
|
||||
mAutoresponseNonFriendsEnabledCheckBox->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetAutoresponseNonFriends, this));
|
||||
mAutoresponseNonFriendsEditor = getChild<LLTextEditor>("set_autoresponse_nonfriends");
|
||||
mAutoresponseNonFriendsEditor->setCommitCallback(boost::bind(&FSFloaterContactSetConfiguration::onCommitSetAutoresponseNonFriends, this));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,9 +98,26 @@ void FSFloaterContactSetConfiguration::draw()
|
||||
|
||||
void FSFloaterContactSetConfiguration::onOpen(const LLSD& target_set)
|
||||
{
|
||||
bool enabled = false;
|
||||
std::string response;
|
||||
|
||||
mSetSwatch->set(LGGContactSets::getInstance()->getSetColor(mContactSet), true);
|
||||
mGlobalSwatch->set(LGGContactSets::getInstance()->getDefaultColor(), true);
|
||||
mNotificationCheckBox->set(LGGContactSets::getInstance()->getNotifyForSet(mContactSet));
|
||||
mSortByOnlineStatusCheckBox->set(LGGContactSets::getInstance()->getSortByOnlineStatusForSet(mContactSet));
|
||||
|
||||
LGGContactSets::getInstance()->getAutoresponseForSet(mContactSet, ContactSetAutoresponseMode::BUSY, enabled, response);
|
||||
mAutoresponseBusyEnabledCheckBox->set(enabled);
|
||||
mAutoresponseBusyEditor->setText(response);
|
||||
|
||||
LGGContactSets::getInstance()->getAutoresponseForSet(mContactSet, ContactSetAutoresponseMode::AUTORESPONSE, enabled, response);
|
||||
mAutoresponseModeEnabledCheckBox->set(enabled);
|
||||
mAutoresponseModeEditor->setText(response);
|
||||
|
||||
LGGContactSets::getInstance()->getAutoresponseForSet(mContactSet, ContactSetAutoresponseMode::AUTORESPONSE_NONFRIENDS, enabled, response);
|
||||
mAutoresponseNonFriendsEnabledCheckBox->set(enabled);
|
||||
mAutoresponseNonFriendsEditor->setText(response);
|
||||
updateAutoresponseEditorEnabledState();
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onCommitSetColor()
|
||||
@@ -94,11 +130,34 @@ void FSFloaterContactSetConfiguration::onCommitSetNotifications()
|
||||
LGGContactSets::getInstance()->setNotifyForSet(mContactSet, mNotificationCheckBox->getValue().asBoolean());
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onCommitSetSortByOnlineStatus()
|
||||
{
|
||||
LGGContactSets::getInstance()->setSortByOnlineStatusForSet(mContactSet, mSortByOnlineStatusCheckBox->getValue().asBoolean());
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onCommitDefaultColor()
|
||||
{
|
||||
LGGContactSets::getInstance()->setDefaultColor(mGlobalSwatch->get());
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onCommitSetAutoresponseBusy()
|
||||
{
|
||||
LGGContactSets::getInstance()->setAutoresponseForSet(mContactSet, ContactSetAutoresponseMode::BUSY, mAutoresponseBusyEnabledCheckBox->getValue().asBoolean(), mAutoresponseBusyEditor->getText());
|
||||
updateAutoresponseEditorEnabledState();
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onCommitSetAutoresponseMode()
|
||||
{
|
||||
LGGContactSets::getInstance()->setAutoresponseForSet(mContactSet, ContactSetAutoresponseMode::AUTORESPONSE, mAutoresponseModeEnabledCheckBox->getValue().asBoolean(), mAutoresponseModeEditor->getText());
|
||||
updateAutoresponseEditorEnabledState();
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onCommitSetAutoresponseNonFriends()
|
||||
{
|
||||
LGGContactSets::getInstance()->setAutoresponseForSet(mContactSet, ContactSetAutoresponseMode::AUTORESPONSE_NONFRIENDS, mAutoresponseNonFriendsEnabledCheckBox->getValue().asBoolean(), mAutoresponseNonFriendsEditor->getText());
|
||||
updateAutoresponseEditorEnabledState();
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::onRenameSet()
|
||||
{
|
||||
std::string new_name = mSetName->getText();
|
||||
@@ -132,3 +191,10 @@ void FSFloaterContactSetConfiguration::setFrustumOrigin(LLView* frustumOrigin)
|
||||
mFrustumOrigin = frustumOrigin->getHandle();
|
||||
}
|
||||
}
|
||||
|
||||
void FSFloaterContactSetConfiguration::updateAutoresponseEditorEnabledState()
|
||||
{
|
||||
mAutoresponseBusyEditor->setEnabled(mAutoresponseBusyEnabledCheckBox->getValue().asBoolean());
|
||||
mAutoresponseModeEditor->setEnabled(mAutoresponseModeEnabledCheckBox->getValue().asBoolean());
|
||||
mAutoresponseNonFriendsEditor->setEnabled(mAutoresponseNonFriendsEnabledCheckBox->getValue().asBoolean());
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
class LLColorSwatchCtrl;
|
||||
class LLCheckBoxCtrl;
|
||||
class LLLineEditor;
|
||||
class LLTextEditor;
|
||||
class LLButton;
|
||||
|
||||
class FSFloaterContactSetConfiguration : public LLFloater
|
||||
@@ -50,8 +51,13 @@ private:
|
||||
~FSFloaterContactSetConfiguration() = default;
|
||||
void onCommitSetColor();
|
||||
void onCommitSetNotifications();
|
||||
void onCommitSetSortByOnlineStatus();
|
||||
void onCommitDefaultColor();
|
||||
void onCommitSetAutoresponseBusy();
|
||||
void onCommitSetAutoresponseMode();
|
||||
void onCommitSetAutoresponseNonFriends();
|
||||
void onRenameSet();
|
||||
void updateAutoresponseEditorEnabledState();
|
||||
|
||||
void updateTitle();
|
||||
|
||||
@@ -59,6 +65,13 @@ private:
|
||||
std::string mContactSet;
|
||||
|
||||
LLCheckBoxCtrl* mNotificationCheckBox;
|
||||
LLCheckBoxCtrl* mSortByOnlineStatusCheckBox;
|
||||
LLCheckBoxCtrl* mAutoresponseBusyEnabledCheckBox;
|
||||
LLTextEditor* mAutoresponseBusyEditor;
|
||||
LLCheckBoxCtrl* mAutoresponseModeEnabledCheckBox;
|
||||
LLTextEditor* mAutoresponseModeEditor;
|
||||
LLCheckBoxCtrl* mAutoresponseNonFriendsEnabledCheckBox;
|
||||
LLTextEditor* mAutoresponseNonFriendsEditor;
|
||||
LLColorSwatchCtrl* mSetSwatch;
|
||||
LLColorSwatchCtrl* mGlobalSwatch;
|
||||
LLLineEditor* mSetName;
|
||||
|
||||
@@ -28,10 +28,14 @@
|
||||
|
||||
#include "fsfloatergrouptitles.h"
|
||||
#include "fscommon.h"
|
||||
#include "fsgrouptitleregionmgr.h"
|
||||
#include "llcheckboxctrl.h"
|
||||
#include "llfiltereditor.h"
|
||||
#include "llgroupactions.h"
|
||||
#include "llmenugl.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "lltrans.h"
|
||||
#include "llviewermenu.h"
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// FSGroupTitlesObserver class
|
||||
@@ -79,6 +83,17 @@ FSFloaterGroupTitles::~FSFloaterGroupTitles()
|
||||
gAgent.removeListener(this);
|
||||
LLGroupMgr::getInstance()->removeObserver(this);
|
||||
|
||||
if (mAssignmentsChangedConnection.connected())
|
||||
{
|
||||
mAssignmentsChangedConnection.disconnect();
|
||||
}
|
||||
|
||||
if (LLContextMenu* menu = mClearRegionMenuHandle.get())
|
||||
{
|
||||
gMenuHolder->removeChild(menu);
|
||||
delete menu;
|
||||
}
|
||||
|
||||
// Clean up still registered observers
|
||||
clearObservers();
|
||||
}
|
||||
@@ -88,16 +103,30 @@ bool FSFloaterGroupTitles::postBuild()
|
||||
mActivateButton = getChild<LLButton>("btnActivate");
|
||||
mRefreshButton = getChild<LLButton>("btnRefresh");
|
||||
mInfoButton = getChild<LLButton>("btnInfo");
|
||||
mSetRegionButton = getChild<LLButton>("btnSetRegion");
|
||||
mSetRegionManualButton = getChild<LLButton>("btnSetRegionManual");
|
||||
mClearRegionButton = getChild<LLButton>("btnClearRegion");
|
||||
mNoneOnUnassigned = getChild<LLCheckBoxCtrl>("none_on_unassigned");
|
||||
mTitleList = getChild<LLScrollListCtrl>("title_list");
|
||||
mFilterEditor = getChild<LLFilterEditor>("filter_input");
|
||||
|
||||
mActivateButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::activateGroupTitle, this));
|
||||
mRefreshButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::refreshGroupTitles, this));
|
||||
mInfoButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::openGroupInfo, this));
|
||||
mSetRegionButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onSetRegion, this));
|
||||
mSetRegionManualButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onSetRegionManual, this));
|
||||
mClearRegionButton->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onClearRegion, this));
|
||||
mNoneOnUnassigned->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onNoneOnUnassignedToggle, this));
|
||||
mTitleList->setDoubleClickCallback(boost::bind(&FSFloaterGroupTitles::activateGroupTitle, this));
|
||||
mTitleList->setCommitCallback(boost::bind(&FSFloaterGroupTitles::selectedTitleChanged, this));
|
||||
mFilterEditor->setCommitCallback(boost::bind(&FSFloaterGroupTitles::onFilterEdit, this, _2));
|
||||
|
||||
mNoneOnUnassigned->set(FSGroupTitleRegionMgr::getInstance()->getNoneOnUnassigned());
|
||||
mSetRegionButton->setEnabled(false);
|
||||
mSetRegionManualButton->setEnabled(false);
|
||||
mClearRegionButton->setEnabled(false);
|
||||
mAssignmentsChangedConnection = FSGroupTitleRegionMgr::getInstance()->setAssignmentsChangedCallback([this]() { updateRegionColumn(); });
|
||||
|
||||
mTitleList->sortByColumn("title_sort_column", true);
|
||||
mTitleList->setFilterColumn(0);
|
||||
|
||||
@@ -162,6 +191,7 @@ void FSFloaterGroupTitles::addListItem(const LLUUID& group_id, const LLUUID& rol
|
||||
const std::string& group_name, bool is_active, bool is_group)
|
||||
{
|
||||
std::string font_style = (is_active ? "BOLD" : "NORMAL");
|
||||
std::string region_name = FSGroupTitleRegionMgr::getInstance()->getRegionForTitle(group_id, role_id);
|
||||
|
||||
LLSD item;
|
||||
item["id"] = group_id.asString() + role_id.asString(); // Only combination of group id and role id is unique!
|
||||
@@ -173,18 +203,22 @@ void FSFloaterGroupTitles::addListItem(const LLUUID& group_id, const LLUUID& rol
|
||||
item["columns"][1]["type"] = "text";
|
||||
item["columns"][1]["font"]["style"] = font_style;
|
||||
item["columns"][1]["value"] = group_name;
|
||||
item["columns"][2]["column"] = "role_id";
|
||||
item["columns"][2]["column"] = "regionname";
|
||||
item["columns"][2]["type"] = "text";
|
||||
item["columns"][2]["value"] = role_id;
|
||||
item["columns"][3]["column"] = "group_id";
|
||||
item["columns"][2]["font"]["style"] = font_style;
|
||||
item["columns"][2]["value"] = region_name;
|
||||
item["columns"][3]["column"] = "role_id";
|
||||
item["columns"][3]["type"] = "text";
|
||||
item["columns"][3]["value"] = group_id;
|
||||
item["columns"][4]["column"] = "title_sort_column";
|
||||
item["columns"][3]["value"] = role_id;
|
||||
item["columns"][4]["column"] = "group_id";
|
||||
item["columns"][4]["type"] = "text";
|
||||
item["columns"][4]["value"] = (is_group ? ("1_" + title) : "0");
|
||||
item["columns"][5]["column"] = "name_sort_column";
|
||||
item["columns"][4]["value"] = group_id;
|
||||
item["columns"][5]["column"] = "title_sort_column";
|
||||
item["columns"][5]["type"] = "text";
|
||||
item["columns"][5]["value"] = (is_group ? ("1_" + group_name) : "0");
|
||||
item["columns"][5]["value"] = (is_group ? ("1_" + title) : "0");
|
||||
item["columns"][6]["column"] = "name_sort_column";
|
||||
item["columns"][6]["type"] = "text";
|
||||
item["columns"][6]["value"] = (is_group ? ("1_" + group_name) : "0");
|
||||
|
||||
mTitleList->addElement(item);
|
||||
|
||||
@@ -268,7 +302,18 @@ void FSFloaterGroupTitles::selectedTitleChanged()
|
||||
if (selected_item)
|
||||
{
|
||||
LLUUID group_id = selected_item->getColumn(mTitleList->getColumn("group_id")->mIndex)->getValue().asUUID();
|
||||
LLUUID role_id = selected_item->getColumn(mTitleList->getColumn("role_id")->mIndex)->getValue().asUUID();
|
||||
mInfoButton->setEnabled(group_id.notNull());
|
||||
const bool has_region = !FSGroupTitleRegionMgr::getInstance()->getRegionForTitle(group_id, role_id).empty();
|
||||
mSetRegionButton->setEnabled(true);
|
||||
mSetRegionManualButton->setEnabled(true);
|
||||
mClearRegionButton->setEnabled(has_region);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSetRegionButton->setEnabled(false);
|
||||
mSetRegionManualButton->setEnabled(false);
|
||||
mClearRegionButton->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,3 +345,119 @@ void FSFloaterGroupTitles::onFilterEdit(const std::string& search_string)
|
||||
// Apply new filter.
|
||||
mTitleList->setFilterString(mFilterSubStringOrig);
|
||||
}
|
||||
|
||||
void FSFloaterGroupTitles::onSetRegion()
|
||||
{
|
||||
auto* selected_item = mTitleList->getFirstSelected();
|
||||
if (!selected_item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto group_id = selected_item->getColumn(mTitleList->getColumn("group_id")->mIndex)->getValue().asUUID();
|
||||
const auto role_id = selected_item->getColumn(mTitleList->getColumn("role_id")->mIndex)->getValue().asUUID();
|
||||
FSGroupTitleRegionMgr::getInstance()->setAssignmentForCurrentRegion(group_id, role_id);
|
||||
selectedTitleChanged();
|
||||
}
|
||||
|
||||
void FSFloaterGroupTitles::onSetRegionManual()
|
||||
{
|
||||
auto* selected_item = mTitleList->getFirstSelected();
|
||||
if (!selected_item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto group_id = selected_item->getColumn(mTitleList->getColumn("group_id")->mIndex)->getValue().asUUID();
|
||||
const auto role_id = selected_item->getColumn(mTitleList->getColumn("role_id")->mIndex)->getValue().asUUID();
|
||||
FSGroupTitleRegionMgr::getInstance()->showRegionInputDialog(group_id, role_id);
|
||||
}
|
||||
|
||||
void FSFloaterGroupTitles::onClearRegion()
|
||||
{
|
||||
auto* selected_item = mTitleList->getFirstSelected();
|
||||
if (!selected_item)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto group_id = selected_item->getColumn(mTitleList->getColumn("group_id")->mIndex)->getValue().asUUID();
|
||||
const auto role_id = selected_item->getColumn(mTitleList->getColumn("role_id")->mIndex)->getValue().asUUID();
|
||||
auto* mgr = FSGroupTitleRegionMgr::getInstance();
|
||||
std::vector<std::string> regions = mgr->getRegionDisplayNamesForTitle(group_id, role_id);
|
||||
|
||||
if (regions.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (regions.size() == 1)
|
||||
{
|
||||
mgr->clearAssignmentByRegion(regions[0]);
|
||||
selectedTitleChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiple regions, build a popup menu
|
||||
if (LLContextMenu* old_menu = mClearRegionMenuHandle.get())
|
||||
{
|
||||
gMenuHolder->removeChild(old_menu);
|
||||
delete old_menu;
|
||||
}
|
||||
|
||||
LLContextMenu::Params menu_params;
|
||||
menu_params.name("clear_region_menu");
|
||||
menu_params.visible(false);
|
||||
LLContextMenu* menu = LLUICtrlFactory::create<LLContextMenu>(menu_params);
|
||||
|
||||
for (const auto& region : regions)
|
||||
{
|
||||
LLMenuItemCallGL::Params item_params;
|
||||
item_params.label(region);
|
||||
item_params.name(region);
|
||||
item_params.on_click.function(
|
||||
[mgr, region](LLUICtrl*, const LLSD&)
|
||||
{
|
||||
mgr->clearAssignmentByRegion(region);
|
||||
});
|
||||
menu->addChild(LLUICtrlFactory::create<LLMenuItemCallGL>(item_params));
|
||||
}
|
||||
|
||||
menu->addSeparator();
|
||||
|
||||
LLMenuItemCallGL::Params clear_all_params;
|
||||
clear_all_params.label(getString("ClearAllRegions"));
|
||||
clear_all_params.name("clear_all_regions");
|
||||
clear_all_params.on_click.function(
|
||||
[mgr, group_id, role_id](LLUICtrl*, const LLSD&)
|
||||
{
|
||||
mgr->clearAssignment(group_id, role_id);
|
||||
});
|
||||
menu->addChild(LLUICtrlFactory::create<LLMenuItemCallGL>(clear_all_params));
|
||||
|
||||
mClearRegionMenuHandle = menu->getHandle();
|
||||
gMenuHolder->addChild(menu);
|
||||
S32 screen_x, screen_y;
|
||||
mClearRegionButton->localPointToScreen(0, mClearRegionButton->getRect().getHeight(), &screen_x, &screen_y);
|
||||
menu->show(screen_x, screen_y, mClearRegionButton);
|
||||
}
|
||||
|
||||
void FSFloaterGroupTitles::onNoneOnUnassignedToggle()
|
||||
{
|
||||
FSGroupTitleRegionMgr::getInstance()->setNoneOnUnassigned(mNoneOnUnassigned->get());
|
||||
}
|
||||
|
||||
void FSFloaterGroupTitles::updateRegionColumn()
|
||||
{
|
||||
const auto region_col_idx = mTitleList->getColumn("regionname")->mIndex;
|
||||
const auto group_id_col_idx = mTitleList->getColumn("group_id")->mIndex;
|
||||
const auto role_id_col_idx = mTitleList->getColumn("role_id")->mIndex;
|
||||
auto* mgr = FSGroupTitleRegionMgr::getInstance();
|
||||
for (auto* item : mTitleList->getAllData())
|
||||
{
|
||||
const auto group_id = item->getColumn(group_id_col_idx)->getValue().asUUID();
|
||||
const auto role_id = item->getColumn(role_id_col_idx)->getValue().asUUID();
|
||||
|
||||
if (auto* cell = item->getColumn(region_col_idx))
|
||||
{
|
||||
cell->setValue(mgr->getRegionForTitle(group_id, role_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,11 @@
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llgroupmgr.h"
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
class FSFloaterGroupTitles;
|
||||
class LLCheckBoxCtrl;
|
||||
class LLContextMenu;
|
||||
class LLFilterEditor;
|
||||
class LLScrollListCtrl;
|
||||
|
||||
@@ -79,15 +82,28 @@ private:
|
||||
void openGroupInfo();
|
||||
void onFilterEdit(const std::string& search_string);
|
||||
|
||||
void onSetRegion();
|
||||
void onSetRegionManual();
|
||||
void onClearRegion();
|
||||
void onNoneOnUnassignedToggle();
|
||||
void updateRegionColumn();
|
||||
|
||||
LLButton* mActivateButton;
|
||||
LLButton* mRefreshButton;
|
||||
LLButton* mInfoButton;
|
||||
LLButton* mSetRegionButton;
|
||||
LLButton* mSetRegionManualButton;
|
||||
LLButton* mClearRegionButton;
|
||||
LLCheckBoxCtrl* mNoneOnUnassigned;
|
||||
LLScrollListCtrl* mTitleList;
|
||||
LLFilterEditor* mFilterEditor;
|
||||
|
||||
std::string mFilterSubString;
|
||||
std::string mFilterSubStringOrig;
|
||||
|
||||
boost::signals2::connection mAssignmentsChangedConnection;
|
||||
LLHandle<LLContextMenu> mClearRegionMenuHandle;
|
||||
|
||||
typedef std::map<LLUUID, FSGroupTitlesObserver*> observer_map_t;
|
||||
observer_map_t mGroupTitleObserverMap;
|
||||
};
|
||||
|
||||
@@ -1529,7 +1529,7 @@ void FSFloaterIM::sessionInitReplyReceived(const LLUUID& im_session_id)
|
||||
// updating "Call" button from group/ad-hoc control panel here to enable it without placing into draw() (EXT-4796)
|
||||
if (LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(im_session_id); session)
|
||||
{
|
||||
if ((session->isGroupSessionType() && gAgent.isInGroup(im_session_id)) || session->isAdHocSessionType())
|
||||
if ((session->isGroupSessionType() && gAgent.isInGroup(im_session_id)) || session->isAdHocSessionType() || session->isP2PSessionType())
|
||||
{
|
||||
updateCallButton();
|
||||
}
|
||||
|
||||
@@ -365,8 +365,15 @@ void FSFloaterIMContainer::addFloater(LLFloater* floaterp,
|
||||
// [/SL:KB]
|
||||
}
|
||||
|
||||
LLFloater* active_floater = getActiveFloater();
|
||||
|
||||
LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
|
||||
|
||||
if (!select_added_floater && active_floater)
|
||||
{
|
||||
selectFloater(active_floater);
|
||||
}
|
||||
|
||||
LLUUID session_id = floaterp->getKey();
|
||||
mSessions[session_id] = floaterp;
|
||||
floaterp->mCloseSignal.connect(boost::bind(&FSFloaterIMContainer::onCloseFloater, this, session_id));
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "fsfloaterimcontainer.h"
|
||||
#include "fsnearbychathub.h"
|
||||
#include "llagent.h" // gAgent
|
||||
#include "llagentcamera.h" // gAgentCamera
|
||||
#include "llanimationstates.h" // ANIM_AGENT_WHISPER, ANIM_AGENT_TALK, ANIM_AGENT_SHOUT
|
||||
#include "llautoreplace.h"
|
||||
#include "llavatarnamecache.h"
|
||||
@@ -923,7 +924,11 @@ void FSFloaterNearbyChat::sendChat( EChatType type )
|
||||
|
||||
// If the user wants to stop chatting on hitting return, lose focus
|
||||
// and go out of chat mode.
|
||||
if (gSavedSettings.getBOOL("CloseChatOnReturn") && gSavedSettings.getBOOL("FSUnfocusChatHistoryOnReturn"))
|
||||
const bool in_mouselook = gAgentCamera.cameraMouselook();
|
||||
const bool closeChatOnReturn = gSavedSettings.getBOOL("CloseChatOnReturn")
|
||||
&& !(!in_mouselook && gSavedSettings.getBOOL("FSCloseChatOnReturnInMouselook"))
|
||||
&& !gSavedSettings.getBOOL("FSCloseChatOnReturnOnlyBar");
|
||||
if (closeChatOnReturn && gSavedSettings.getBOOL("FSUnfocusChatHistoryOnReturn"))
|
||||
{
|
||||
stopChat();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,560 @@
|
||||
/**
|
||||
* @file fsgrouptitleregionmgr.cpp
|
||||
* @brief Per-region automatic group title switching manager
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Copyright (c) 2026 The Phoenix Firestorm Project, 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
|
||||
*
|
||||
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
|
||||
* http://www.firestormviewer.org
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "fsgrouptitleregionmgr.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "lldir.h"
|
||||
#include "llgroupactions.h"
|
||||
#include "llgroupmgr.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llviewerregion.h"
|
||||
#include "llworldmap.h"
|
||||
#include "llworldmapmessage.h"
|
||||
#include "rlvactions.h"
|
||||
|
||||
static constexpr char REGION_GROUP_TITLES_FILE[] = "region_group_titles.xml";
|
||||
static constexpr S32 MAX_REGION_NAME_LENGTH = 63;
|
||||
static constexpr F32 REGION_VALIDATION_TIMEOUT = 10.f;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Construction / destruction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FSGroupTitleRegionMgr::FSGroupTitleRegionMgr()
|
||||
{
|
||||
mRegionChangedConnection = gAgent.addRegionChangedCallback([this]() { onRegionChanged(); });
|
||||
}
|
||||
|
||||
FSGroupTitleRegionMgr::~FSGroupTitleRegionMgr()
|
||||
{
|
||||
cancelPendingValidation();
|
||||
|
||||
if (mLoginRetryTimer)
|
||||
{
|
||||
delete mLoginRetryTimer;
|
||||
mLoginRetryTimer = nullptr;
|
||||
}
|
||||
|
||||
if (mRegionChangedConnection.connected())
|
||||
{
|
||||
gAgent.removeRegionChangedCallback(mRegionChangedConnection);
|
||||
mRegionChangedConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Persistence
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
std::string FSGroupTitleRegionMgr::getFilename() const
|
||||
{
|
||||
auto path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "");
|
||||
if (!path.empty())
|
||||
{
|
||||
path = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, REGION_GROUP_TITLES_FILE);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::loadFromDisk()
|
||||
{
|
||||
if (mLoginRetryTimer)
|
||||
{
|
||||
delete mLoginRetryTimer;
|
||||
mLoginRetryTimer = nullptr;
|
||||
}
|
||||
|
||||
mAssignments.clear();
|
||||
mNoneOnUnassigned = false;
|
||||
mLastAppliedRegion.clear();
|
||||
|
||||
const auto filename = getFilename();
|
||||
if (filename.empty())
|
||||
{
|
||||
LL_WARNS() << "Could not determine per-account path for region group titles" << LL_ENDL;
|
||||
mDataLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
llifstream file(filename.c_str());
|
||||
if (!file.is_open())
|
||||
{
|
||||
LL_INFOS() << "No region group titles file found at: " << filename << LL_ENDL;
|
||||
mDataLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
LLSD data;
|
||||
if (LLSDSerialize::fromXML(data, file) == LLSDParser::PARSE_FAILURE)
|
||||
{
|
||||
LL_WARNS() << "Failed to parse region group titles file: " << filename << LL_ENDL;
|
||||
mDataLoaded = true;
|
||||
return;
|
||||
}
|
||||
file.close();
|
||||
|
||||
mNoneOnUnassigned = data["none_on_unassigned"].asBoolean();
|
||||
|
||||
if (data.has("assignments") && data["assignments"].isMap())
|
||||
{
|
||||
const auto& assignments = data["assignments"];
|
||||
for (auto it = assignments.beginMap(); it != assignments.endMap(); ++it)
|
||||
{
|
||||
FSRegionTitleAssignment assignment;
|
||||
assignment.mGroupID = it->second["group_id"].asUUID();
|
||||
assignment.mRoleID = it->second["role_id"].asUUID();
|
||||
assignment.mDisplayName = it->second["display_name"].asString();
|
||||
if (assignment.mDisplayName.empty())
|
||||
{
|
||||
assignment.mDisplayName = it->first;
|
||||
}
|
||||
mAssignments[it->first] = assignment;
|
||||
}
|
||||
}
|
||||
|
||||
mDataLoaded = true;
|
||||
LL_INFOS() << "Loaded " << mAssignments.size() << " region group title assignments" << LL_ENDL;
|
||||
|
||||
// The region changed callback likely already fired during login before data was loaded (and was ignored via the mDataLoaded guard).
|
||||
// Try now and schedule a deferred retry in case the region handshake hasn't finished yet (region name would be empty, causing an early return).
|
||||
onRegionChanged();
|
||||
|
||||
static constexpr F32 LOGIN_RETRY_DELAY = 5.f;
|
||||
mLoginRetryTimer = LLEventTimer::run_after(LOGIN_RETRY_DELAY, [this]()
|
||||
{
|
||||
mLoginRetryTimer = nullptr;
|
||||
mLastAppliedRegion.clear();
|
||||
onRegionChanged();
|
||||
});
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::saveToDisk()
|
||||
{
|
||||
const auto filename = getFilename();
|
||||
if (filename.empty())
|
||||
{
|
||||
LL_WARNS() << "Could not determine per-account path for saving region group titles" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
LLSD assignments;
|
||||
for (const auto& [region, assignment] : mAssignments)
|
||||
{
|
||||
LLSD entry;
|
||||
entry["group_id"] = assignment.mGroupID;
|
||||
entry["role_id"] = assignment.mRoleID;
|
||||
entry["display_name"] = assignment.mDisplayName;
|
||||
assignments[region] = entry;
|
||||
}
|
||||
|
||||
LLSD data;
|
||||
data["none_on_unassigned"] = mNoneOnUnassigned;
|
||||
data["assignments"] = assignments;
|
||||
|
||||
llofstream file(filename.c_str());
|
||||
if (!file.is_open())
|
||||
{
|
||||
LL_WARNS() << "Could not open file for writing: " << filename << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
LLSDSerialize::toPrettyXML(data, file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Assignment CRUD
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSGroupTitleRegionMgr::setAssignment(const LLUUID& group_id, const LLUUID& role_id, const std::string& region_name)
|
||||
{
|
||||
const auto normalized = normalizeRegionName(region_name);
|
||||
if (normalized.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto it = mAssignments.find(normalized); it != mAssignments.end())
|
||||
{
|
||||
if (it->second.mGroupID == group_id && it->second.mRoleID == role_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mAssignments.erase(it);
|
||||
}
|
||||
|
||||
mAssignments[normalized] = { group_id, role_id, sanitizeRegionName(region_name) };
|
||||
saveToDisk();
|
||||
mAssignmentsChangedSignal();
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::setAssignmentForCurrentRegion(const LLUUID& group_id, const LLUUID& role_id)
|
||||
{
|
||||
const auto* region = gAgent.getRegion();
|
||||
if (!region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
setAssignment(group_id, role_id, region->getName());
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::clearAssignment(const LLUUID& group_id, const LLUUID& role_id)
|
||||
{
|
||||
bool changed = false;
|
||||
for (auto it = mAssignments.begin(); it != mAssignments.end(); )
|
||||
{
|
||||
if (it->second.mGroupID == group_id && it->second.mRoleID == role_id)
|
||||
{
|
||||
it = mAssignments.erase(it);
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
saveToDisk();
|
||||
mAssignmentsChangedSignal();
|
||||
}
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::clearAssignmentByRegion(const std::string& region_name)
|
||||
{
|
||||
if (auto it = mAssignments.find(normalizeRegionName(region_name)); it != mAssignments.end())
|
||||
{
|
||||
mAssignments.erase(it);
|
||||
saveToDisk();
|
||||
mAssignmentsChangedSignal();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Lookups
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
std::string FSGroupTitleRegionMgr::getRegionForTitle(const LLUUID& group_id, const LLUUID& role_id) const
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& [region, assignment] : mAssignments)
|
||||
{
|
||||
if (assignment.mGroupID == group_id && assignment.mRoleID == role_id)
|
||||
{
|
||||
if (!result.empty())
|
||||
{
|
||||
result += ", ";
|
||||
}
|
||||
result += assignment.mDisplayName;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> FSGroupTitleRegionMgr::getRegionDisplayNamesForTitle(const LLUUID& group_id, const LLUUID& role_id) const
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
for (const auto& [region, assignment] : mAssignments)
|
||||
{
|
||||
if (assignment.mGroupID == group_id && assignment.mRoleID == role_id)
|
||||
{
|
||||
result.push_back(assignment.mDisplayName);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FSGroupTitleRegionMgr::getAssignmentForRegion(const std::string& region_name, LLUUID& group_id, LLUUID& role_id) const
|
||||
{
|
||||
if (auto it = mAssignments.find(normalizeRegionName(region_name)); it != mAssignments.end())
|
||||
{
|
||||
group_id = it->second.mGroupID;
|
||||
role_id = it->second.mRoleID;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Preferences
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSGroupTitleRegionMgr::setNoneOnUnassigned(bool enabled)
|
||||
{
|
||||
mNoneOnUnassigned = enabled;
|
||||
saveToDisk();
|
||||
}
|
||||
|
||||
boost::signals2::connection FSGroupTitleRegionMgr::setAssignmentsChangedCallback(const assignments_changed_signal_t::slot_type& cb)
|
||||
{
|
||||
return mAssignmentsChangedSignal.connect(cb);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Input sanitization / normalization
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
std::string FSGroupTitleRegionMgr::sanitizeRegionName(const std::string& input)
|
||||
{
|
||||
std::string result = input;
|
||||
LLStringUtil::trim(result);
|
||||
std::string collapsed;
|
||||
collapsed.reserve(result.size());
|
||||
bool prev_space = false;
|
||||
for (const auto c : result)
|
||||
{
|
||||
if (c == ' ')
|
||||
{
|
||||
if (!prev_space)
|
||||
{
|
||||
collapsed += c;
|
||||
}
|
||||
prev_space = true;
|
||||
}
|
||||
else if (static_cast<unsigned char>(c) >= 32)
|
||||
{
|
||||
collapsed += c;
|
||||
prev_space = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (static_cast<S32>(collapsed.size()) > MAX_REGION_NAME_LENGTH)
|
||||
{
|
||||
collapsed.resize(MAX_REGION_NAME_LENGTH);
|
||||
}
|
||||
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
std::string FSGroupTitleRegionMgr::normalizeRegionName(const std::string& name)
|
||||
{
|
||||
auto normalized = sanitizeRegionName(name);
|
||||
LLStringUtil::toLower(normalized);
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Region input dialog (offloaded from floater)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSGroupTitleRegionMgr::showRegionInputDialog(const LLUUID& group_id, const LLUUID& role_id)
|
||||
{
|
||||
LLSD payload;
|
||||
payload["group_id"] = group_id;
|
||||
payload["role_id"] = role_id;
|
||||
LLNotificationsUtil::add("FSSetTitleRegion", LLSD(), payload, &FSGroupTitleRegionMgr::onRegionInputCallback);
|
||||
}
|
||||
|
||||
bool FSGroupTitleRegionMgr::onRegionInputCallback(const LLSD& notification, const LLSD& response)
|
||||
{
|
||||
if (LLNotificationsUtil::getSelectedOption(notification, response) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto region_name = sanitizeRegionName(response["region_name"].asString());
|
||||
if (region_name.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto group_id = notification["payload"]["group_id"].asUUID();
|
||||
const auto role_id = notification["payload"]["role_id"].asUUID();
|
||||
FSGroupTitleRegionMgr::getInstance()->validateAndSetAssignment(group_id, role_id, region_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Async region name validation
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSGroupTitleRegionMgr::validateAndSetAssignment(const LLUUID& group_id, const LLUUID& role_id, const std::string& region_name)
|
||||
{
|
||||
const auto sanitized = sanitizeRegionName(region_name);
|
||||
if (sanitized.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cancelPendingValidation();
|
||||
mPendingGroupID = group_id;
|
||||
mPendingRoleID = role_id;
|
||||
mPendingRegionName = sanitized;
|
||||
mHasPendingValidation = true;
|
||||
LLWorldMapMessage::getInstance()->sendNamedRegionRequest(
|
||||
sanitized,
|
||||
[this](U64 region_handle, const std::string&, const LLUUID&, bool)
|
||||
{
|
||||
onValidationResult(region_handle);
|
||||
},
|
||||
"",
|
||||
false
|
||||
);
|
||||
|
||||
mValidationTimer = LLEventTimer::run_after(REGION_VALIDATION_TIMEOUT, [this]()
|
||||
{
|
||||
mValidationTimer = nullptr;
|
||||
onValidationTimeout();
|
||||
});
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::cancelPendingValidation()
|
||||
{
|
||||
mHasPendingValidation = false;
|
||||
if (mValidationTimer)
|
||||
{
|
||||
delete mValidationTimer;
|
||||
mValidationTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::onValidationResult(U64 region_handle)
|
||||
{
|
||||
if (!mHasPendingValidation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto* sim_info = LLWorldMap::getInstance()->simInfoFromHandle(region_handle);
|
||||
if (!sim_info)
|
||||
{
|
||||
cancelPendingValidation();
|
||||
LLSD args;
|
||||
args["REGION"] = mPendingRegionName;
|
||||
LLNotificationsUtil::add("FSSetTitleRegionNotFound", args);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto canonical_name = sim_info->getName();
|
||||
if (normalizeRegionName(canonical_name) != normalizeRegionName(mPendingRegionName))
|
||||
{
|
||||
cancelPendingValidation();
|
||||
LLSD args;
|
||||
args["REGION"] = mPendingRegionName;
|
||||
LLNotificationsUtil::add("FSSetTitleRegionNotFound", args);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto group_id = mPendingGroupID;
|
||||
const auto role_id = mPendingRoleID;
|
||||
cancelPendingValidation();
|
||||
setAssignment(group_id, role_id, canonical_name);
|
||||
}
|
||||
|
||||
void FSGroupTitleRegionMgr::onValidationTimeout()
|
||||
{
|
||||
if (!mHasPendingValidation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mHasPendingValidation = false;
|
||||
LLSD args;
|
||||
args["REGION"] = mPendingRegionName;
|
||||
LLNotificationsUtil::add("FSSetTitleRegionNotFound", args);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Region change handler
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSGroupTitleRegionMgr::onRegionChanged()
|
||||
{
|
||||
if (!mDataLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* region = gAgent.getRegion();
|
||||
if (!region)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (RlvActions::isRlvEnabled() && !RlvActions::canChangeActiveGroup())
|
||||
{
|
||||
LL_DEBUGS() << "Skipping region-based group title switch: RLVa @setgroup lock active" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto region_name = normalizeRegionName(region->getName());
|
||||
if (region_name.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (region_name == mLastAppliedRegion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mLastAppliedRegion = region_name;
|
||||
|
||||
LLUUID group_id;
|
||||
LLUUID role_id;
|
||||
if (getAssignmentForRegion(region_name, group_id, role_id))
|
||||
{
|
||||
if (group_id.notNull() && !gAgent.isInGroup(group_id))
|
||||
{
|
||||
LL_WARNS() << "Region '" << region->getName() << "' has a title assignment for a group we are no longer in, skipping" << LL_ENDL;
|
||||
return;
|
||||
}
|
||||
|
||||
bool title_already_active = false;
|
||||
if (group_id.notNull())
|
||||
{
|
||||
if (const auto* group_data = LLGroupMgr::getInstance()->getGroupData(group_id))
|
||||
{
|
||||
for (const auto& title : group_data->mTitles)
|
||||
{
|
||||
if (title.mRoleID == role_id && title.mSelected)
|
||||
{
|
||||
title_already_active = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_INFOS() << "Region '" << region->getName() << "', switching to assigned group title" << LL_ENDL;
|
||||
|
||||
if (group_id.notNull() && !title_already_active)
|
||||
{
|
||||
LLGroupMgr::getInstance()->sendGroupTitleUpdate(group_id, role_id);
|
||||
}
|
||||
|
||||
if (gAgent.getGroupID() != group_id)
|
||||
{
|
||||
LLGroupActions::activate(group_id);
|
||||
}
|
||||
}
|
||||
else if (mNoneOnUnassigned && gAgent.getGroupID().notNull())
|
||||
{
|
||||
LL_INFOS() << "Region '" << region->getName() << "' has no title preset, deactivating group" << LL_ENDL;
|
||||
LLGroupActions::activate(LLUUID::null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file fsgrouptitleregionmgr.h
|
||||
* @brief Per-region automatic group title switching manager
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Copyright (c) 2026 The Phoenix Firestorm Project, 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
|
||||
*
|
||||
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
|
||||
* http://www.firestormviewer.org
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef FS_GROUPTITLEREGIONMGR_H
|
||||
#define FS_GROUPTITLEREGIONMGR_H
|
||||
|
||||
#include "lleventtimer.h"
|
||||
#include "llsingleton.h"
|
||||
#include "lluuid.h"
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct FSRegionTitleAssignment
|
||||
{
|
||||
LLUUID mGroupID;
|
||||
LLUUID mRoleID;
|
||||
std::string mDisplayName;
|
||||
};
|
||||
|
||||
class FSGroupTitleRegionMgr : public LLSingleton<FSGroupTitleRegionMgr>
|
||||
{
|
||||
LOG_CLASS(FSGroupTitleRegionMgr);
|
||||
LLSINGLETON(FSGroupTitleRegionMgr);
|
||||
~FSGroupTitleRegionMgr();
|
||||
|
||||
public:
|
||||
void loadFromDisk();
|
||||
void saveToDisk();
|
||||
void setAssignment(const LLUUID& group_id, const LLUUID& role_id, const std::string& region_name);
|
||||
void setAssignmentForCurrentRegion(const LLUUID& group_id, const LLUUID& role_id);
|
||||
void clearAssignment(const LLUUID& group_id, const LLUUID& role_id);
|
||||
void clearAssignmentByRegion(const std::string& region_name);
|
||||
std::string getRegionForTitle(const LLUUID& group_id, const LLUUID& role_id) const;
|
||||
std::vector<std::string> getRegionDisplayNamesForTitle(const LLUUID& group_id, const LLUUID& role_id) const;
|
||||
bool getAssignmentForRegion(const std::string& region_name, LLUUID& group_id, LLUUID& role_id) const;
|
||||
void setNoneOnUnassigned(bool enabled);
|
||||
bool getNoneOnUnassigned() const { return mNoneOnUnassigned; }
|
||||
void showRegionInputDialog(const LLUUID& group_id, const LLUUID& role_id);
|
||||
static std::string sanitizeRegionName(const std::string& input);
|
||||
|
||||
using assignments_changed_signal_t = boost::signals2::signal<void()>;
|
||||
boost::signals2::connection setAssignmentsChangedCallback(const assignments_changed_signal_t::slot_type& cb);
|
||||
|
||||
private:
|
||||
void validateAndSetAssignment(const LLUUID& group_id, const LLUUID& role_id, const std::string& region_name);
|
||||
void onRegionChanged();
|
||||
void cancelPendingValidation();
|
||||
void onValidationResult(U64 region_handle);
|
||||
void onValidationTimeout();
|
||||
std::string getFilename() const;
|
||||
static std::string normalizeRegionName(const std::string& name);
|
||||
static bool onRegionInputCallback(const LLSD& notification, const LLSD& response);
|
||||
bool mNoneOnUnassigned = false;
|
||||
bool mDataLoaded = false;
|
||||
std::string mLastAppliedRegion;
|
||||
|
||||
using assignment_map_t = std::map<std::string, FSRegionTitleAssignment>;
|
||||
assignment_map_t mAssignments;
|
||||
|
||||
bool mHasPendingValidation = false;
|
||||
LLUUID mPendingGroupID;
|
||||
LLUUID mPendingRoleID;
|
||||
std::string mPendingRegionName;
|
||||
LLEventTimer* mValidationTimer = nullptr;
|
||||
LLEventTimer* mLoginRetryTimer = nullptr;
|
||||
|
||||
boost::signals2::connection mRegionChangedConnection;
|
||||
assignments_changed_signal_t mAssignmentsChangedSignal;
|
||||
};
|
||||
|
||||
#endif // FS_GROUPTITLEREGIONMGR_H
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* @file fslocalmeshimportbase.cpp
|
||||
* @brief Shared helpers for local mesh importers
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Local Mesh contribution source code
|
||||
* Copyright (C) 2026, The Phoenix Firestorm Project, 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
|
||||
*
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "fslocalmeshimportbase.h"
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llskinningutil.h"
|
||||
#include "llvoavatarself.h"
|
||||
|
||||
void FSLocalMeshImportBase::setLod(LLLocalMeshFileLOD lod)
|
||||
{
|
||||
mLod = lod;
|
||||
mLoadingLog.clear();
|
||||
}
|
||||
|
||||
void FSLocalMeshImportBase::pushLog(const std::string& who, const std::string& what, bool is_error)
|
||||
{
|
||||
std::string log_msg = "[ " + who + " ] ";
|
||||
if (is_error)
|
||||
{
|
||||
log_msg += "[ ERROR ] ";
|
||||
}
|
||||
|
||||
log_msg += what;
|
||||
mLoadingLog.push_back(log_msg);
|
||||
|
||||
if (mLogToInfo)
|
||||
{
|
||||
LL_INFOS("LocalMesh") << log_msg << LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void FSLocalMeshImportBase::postProcessObject(LLLocalMeshObject& object, const LLMatrix4& scene_transform, bool compute_bounds)
|
||||
{
|
||||
if (compute_bounds)
|
||||
{
|
||||
object.computeObjectBoundingBox();
|
||||
object.computeObjectTransform(scene_transform);
|
||||
}
|
||||
|
||||
object.normalizeFaceValues(mLod);
|
||||
}
|
||||
|
||||
bool FSLocalMeshImportBase::enforceRigJointLimit(const std::string& who,
|
||||
LLLocalMeshObject& object,
|
||||
LLPointer<LLMeshSkinInfo> skininfop,
|
||||
U32 recognized_joint_count)
|
||||
{
|
||||
const S32 max_joints = LLSkinningUtil::getMaxJointCount();
|
||||
if ((S32)recognized_joint_count <= max_joints)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string warning = "WARNING: Skinning disabled for object \"" + object.getObjectName()
|
||||
+ "\" due to too many joints: " + std::to_string(recognized_joint_count)
|
||||
+ ", maximum: " + std::to_string(max_joints) + ".";
|
||||
pushLog(who, warning);
|
||||
LL_WARNS("LocalMesh") << who << ": " << warning << LL_ENDL;
|
||||
|
||||
if (skininfop.notNull())
|
||||
{
|
||||
skininfop->mJointNames.clear();
|
||||
skininfop->mJointNums.clear();
|
||||
skininfop->mInvBindMatrix.clear();
|
||||
skininfop->mAlternateBindMatrix.clear();
|
||||
skininfop->mBindPoseMatrix.clear();
|
||||
skininfop->mBindShapeMatrix = LLMatrix4a::identity();
|
||||
skininfop->mInvalidJointsScrubbed = false;
|
||||
skininfop->mJointNumsInitialized = false;
|
||||
skininfop->updateHash();
|
||||
}
|
||||
|
||||
object.setObjectMeshSkinInfo(LLPointer<LLMeshSkinInfo>());
|
||||
|
||||
for (S32 lod = 0; lod < LOCAL_NUM_LODS; ++lod)
|
||||
{
|
||||
for (auto& face : object.getFaces(static_cast<LLLocalMeshFileLOD>(lod)))
|
||||
{
|
||||
face->getSkin().clear();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FSLocalMeshImportBase::JointMap FSLocalMeshImportBase::loadJointMap()
|
||||
{
|
||||
JointMap joint_map = gAgentAvatarp->getJointAliases();
|
||||
|
||||
// unfortunately getSortedJointNames clears the ref vector, and we need two extra lists.
|
||||
std::vector<std::string> extra_names, more_extra_names;
|
||||
gAgentAvatarp->getSortedJointNames(1, extra_names);
|
||||
gAgentAvatarp->getSortedJointNames(2, more_extra_names);
|
||||
extra_names.reserve(more_extra_names.size());
|
||||
extra_names.insert(extra_names.end(), more_extra_names.begin(), more_extra_names.end());
|
||||
|
||||
// add the extras to jointmap
|
||||
for (const auto& extra_name : extra_names)
|
||||
{
|
||||
joint_map[extra_name] = extra_name;
|
||||
}
|
||||
|
||||
return joint_map;
|
||||
}
|
||||
|
||||
LLMatrix4 FSLocalMeshImportBase::buildNormalizedTransformation(const LLLocalMeshObject& object)
|
||||
{
|
||||
LLVector4 inverse_translation = object.getObjectTranslation() * -1.f;
|
||||
LLVector4 object_size = object.getObjectSize();
|
||||
|
||||
LLMatrix4 normalized_transformation;
|
||||
LLMatrix4 mesh_scale;
|
||||
normalized_transformation.setTranslation(LLVector3(inverse_translation));
|
||||
mesh_scale.initScale(LLVector3(object_size));
|
||||
mesh_scale *= normalized_transformation;
|
||||
normalized_transformation = mesh_scale;
|
||||
return normalized_transformation;
|
||||
}
|
||||
|
||||
void FSLocalMeshImportBase::buildBindPoseMatrix(LLPointer<LLMeshSkinInfo> skininfop)
|
||||
{
|
||||
if (skininfop.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
skininfop->mBindPoseMatrix.resize(skininfop->mInvBindMatrix.size());
|
||||
for (U32 i = 0; i < skininfop->mInvBindMatrix.size(); ++i)
|
||||
{
|
||||
matMul(skininfop->mBindShapeMatrix, skininfop->mInvBindMatrix[i], skininfop->mBindPoseMatrix[i]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file fslocalmeshimportbase.h
|
||||
* @brief Shared helpers for local mesh importers
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Local Mesh contribution source code
|
||||
* Copyright (C) 2026, The Phoenix Firestorm Project, 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
|
||||
*
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "vjlocalmesh.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class FSLocalMeshImportBase
|
||||
{
|
||||
public:
|
||||
using JointMap = std::map<std::string, std::string, std::less<>>;
|
||||
using loadFile_return = std::pair<bool, std::vector<std::string>>;
|
||||
virtual ~FSLocalMeshImportBase() = default;
|
||||
|
||||
protected:
|
||||
void setLod(LLLocalMeshFileLOD lod);
|
||||
void pushLog(const std::string& who, const std::string& what, bool is_error = false);
|
||||
void postProcessObject(LLLocalMeshObject& object, const LLMatrix4& scene_transform, bool compute_bounds);
|
||||
bool enforceRigJointLimit(const std::string& who,
|
||||
LLLocalMeshObject& object,
|
||||
LLPointer<LLMeshSkinInfo> skininfop,
|
||||
U32 recognized_joint_count);
|
||||
|
||||
static JointMap loadJointMap();
|
||||
static LLMatrix4 buildNormalizedTransformation(const LLLocalMeshObject& object);
|
||||
static void buildBindPoseMatrix(LLPointer<LLMeshSkinInfo> skininfop);
|
||||
|
||||
protected:
|
||||
LLLocalMeshFileLOD mLod{ LLLocalMeshFileLOD::LOCAL_LOD_HIGH };
|
||||
std::vector<std::string> mLoadingLog;
|
||||
bool mLogToInfo{ true };
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* @file fslocalmeshimportgltf.h
|
||||
* @author Beq Janus
|
||||
* @brief Local Mesh glTF importer header
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Phoenix Firestorm Viewer Source Code
|
||||
* Copyright (C) 2026, Beq Janus.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fslocalmeshimportbase.h"
|
||||
|
||||
// GLM headers
|
||||
#include <glm/mat4x4.hpp>
|
||||
|
||||
// STL headers
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace LL
|
||||
{
|
||||
namespace GLTF
|
||||
{
|
||||
class Asset;
|
||||
class Primitive;
|
||||
class Node;
|
||||
}
|
||||
}
|
||||
|
||||
class FSLocalMeshImportGLTF : public FSLocalMeshImportBase
|
||||
{
|
||||
public:
|
||||
using loadFile_return = FSLocalMeshImportBase::loadFile_return;
|
||||
FSLocalMeshImportGLTF();
|
||||
loadFile_return loadFile(const std::string& filename,
|
||||
LLLocalMeshFileLOD lod,
|
||||
std::vector<std::unique_ptr<LLLocalMeshObject>>& object_vector);
|
||||
|
||||
private:
|
||||
bool processNodeMesh(const LL::GLTF::Asset& asset, const LL::GLTF::Node& node, LLLocalMeshObject* object);
|
||||
bool appendPrimitiveToObject(const LL::GLTF::Asset& asset,
|
||||
const LL::GLTF::Primitive& prim,
|
||||
const glm::mat4& mesh_transform,
|
||||
bool flip_winding,
|
||||
const std::vector<S32>& joint_index_remap,
|
||||
const std::vector<std::string>& skin_joint_names,
|
||||
LLLocalMeshObject* object,
|
||||
S32 skin_idx);
|
||||
bool initSkinInfo(const LL::GLTF::Asset& asset, S32 skin_idx, LLLocalMeshObject* object);
|
||||
void finalizeSkinInfo(LLLocalMeshObject* object) const;
|
||||
private:
|
||||
std::vector<S32> mParentMap;
|
||||
};
|
||||
@@ -134,6 +134,14 @@ void FSNearbyChatControl::setTextPadding(S32 left, S32 right)
|
||||
void FSNearbyChatControl::applyTextPadding()
|
||||
{
|
||||
LLRect base_rect = mScroller ? mScroller->getContentWindowRect() : getLocalRect();
|
||||
|
||||
const LLRect local_rect = getLocalRect();
|
||||
if (base_rect.getHeight() < local_rect.getHeight())
|
||||
{
|
||||
base_rect.mTop = local_rect.mTop;
|
||||
base_rect.mBottom = local_rect.mBottom;
|
||||
}
|
||||
|
||||
base_rect.mLeft = llmin(base_rect.mRight, base_rect.mLeft + mTextPadLeft);
|
||||
base_rect.mRight = llmax(base_rect.mLeft, base_rect.mRight - mTextPadRight);
|
||||
|
||||
@@ -144,16 +152,23 @@ void FSNearbyChatControl::applyTextPadding()
|
||||
}
|
||||
}
|
||||
|
||||
void FSNearbyChatControl::autohide()
|
||||
void FSNearbyChatControl::autohide(bool after_send)
|
||||
{
|
||||
if (isDefault())
|
||||
{
|
||||
if (gSavedSettings.getBOOL("CloseChatOnReturn"))
|
||||
const bool in_mouselook = gAgentCamera.cameraMouselook();
|
||||
const bool closeChatOnReturn = gSavedSettings.getBOOL("CloseChatOnReturn")
|
||||
&& !(!in_mouselook && gSavedSettings.getBOOL("FSCloseChatOnReturnInMouselook"));
|
||||
const bool autohideChatBar = gSavedSettings.getBOOL("AutohideChatBar");
|
||||
bool hide_chatbar = false;
|
||||
|
||||
if (closeChatOnReturn)
|
||||
{
|
||||
setFocus(false);
|
||||
hide_chatbar = autohideChatBar;
|
||||
}
|
||||
|
||||
if (gAgentCamera.cameraMouselook() || gSavedSettings.getBOOL("AutohideChatBar"))
|
||||
if (hide_chatbar || (!after_send && autohideChatBar))
|
||||
{
|
||||
FSNearbyChat::instance().showDefaultChatBar(false);
|
||||
}
|
||||
@@ -176,7 +191,7 @@ bool FSNearbyChatControl::handleKeyHere(KEY key, MASK mask)
|
||||
if (key == KEY_ESCAPE && mask == MASK_NONE)
|
||||
{
|
||||
// we let ESC key go through to the rest of the UI code, so don't set handled = true
|
||||
autohide();
|
||||
autohide(false);
|
||||
gAgent.stopTyping();
|
||||
}
|
||||
else if (KEY_RETURN == key)
|
||||
@@ -222,7 +237,7 @@ bool FSNearbyChatControl::handleKeyHere(KEY key, MASK mask)
|
||||
FSNearbyChat::instance().sendChat(getConvertedText(), type);
|
||||
|
||||
setText(LLStringExplicit(""));
|
||||
autohide();
|
||||
autohide(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
void applyTextPadding();
|
||||
|
||||
// Unfocus and autohide chat bar accordingly if we are the default chat bar
|
||||
void autohide();
|
||||
void autohide(bool after_send);
|
||||
|
||||
void updateRlvRestrictions(ERlvBehaviour behavior);
|
||||
void updateEmojiHelperSetting(const LLSD& data);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "llcallingcard.h"
|
||||
#include "llfloateravatarpicker.h"
|
||||
#include "llfloaterreg.h"
|
||||
#include "llfiltereditor.h"
|
||||
#include "llnotificationsutil.h"
|
||||
#include "llpanelpeoplemenus.h"
|
||||
#include "llslurl.h"
|
||||
@@ -45,13 +46,38 @@
|
||||
constexpr U32 MAX_SELECTIONS = 20;
|
||||
static LLPanelInjector<FSPanelContactSets> t_panel_contact_sets("contact_sets_panel");
|
||||
|
||||
class FSAvatarItemOnlineStatusComparator : public LLAvatarItemComparator
|
||||
{
|
||||
public:
|
||||
virtual ~FSAvatarItemOnlineStatusComparator() = default;
|
||||
|
||||
protected:
|
||||
bool doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const override
|
||||
{
|
||||
const bool online_1 = LLAvatarTracker::instance().isBuddyOnline(avatar_item1->getAvatarId());
|
||||
const bool online_2 = LLAvatarTracker::instance().isBuddyOnline(avatar_item2->getAvatarId());
|
||||
if (online_1 != online_2)
|
||||
{
|
||||
return online_1;
|
||||
}
|
||||
|
||||
std::string name1 = getComparableName(avatar_item1);
|
||||
std::string name2 = getComparableName(avatar_item2);
|
||||
return name1 < name2;
|
||||
}
|
||||
};
|
||||
|
||||
static const FSAvatarItemOnlineStatusComparator FS_ONLINE_STATUS_COMPARATOR;
|
||||
|
||||
FSPanelContactSets::FSPanelContactSets() : LLPanel()
|
||||
{
|
||||
LLAvatarTracker::instance().addObserver(this);
|
||||
mContactSetChangedConnection = LGGContactSets::getInstance()->setContactSetChangeCallback(boost::bind(&FSPanelContactSets::updateSets, this, _1));
|
||||
}
|
||||
|
||||
FSPanelContactSets::~FSPanelContactSets()
|
||||
{
|
||||
LLAvatarTracker::instance().removeObserver(this);
|
||||
if (mContactSetChangedConnection.connected())
|
||||
{
|
||||
mContactSetChangedConnection.disconnect();
|
||||
@@ -64,6 +90,7 @@ bool FSPanelContactSets::postBuild()
|
||||
childSetAction("remove_set_btn", boost::bind(&FSPanelContactSets::onClickRemoveSet, this));
|
||||
childSetAction("config_btn", boost::bind(&FSPanelContactSets::onClickConfigureSet, this, _1));
|
||||
childSetAction("add_btn", boost::bind(&FSPanelContactSets::onClickAddAvatar, this, _1));
|
||||
childSetAction("move_btn", boost::bind(&FSPanelContactSets::onClickMoveAvatar, this));
|
||||
childSetAction("remove_btn", boost::bind(&FSPanelContactSets::onClickRemoveAvatar, this));
|
||||
childSetAction("profile_btn", boost::bind(&FSPanelContactSets::onClickOpenProfile, this));
|
||||
childSetAction("start_im_btn", boost::bind(&FSPanelContactSets::onClickStartIM, this));
|
||||
@@ -76,7 +103,15 @@ bool FSPanelContactSets::postBuild()
|
||||
mContactSetCombo->setCommitCallback(boost::bind(&FSPanelContactSets::refreshSetList, this));
|
||||
refreshContactSets();
|
||||
|
||||
if (LLFilterEditor* filter = getChild<LLFilterEditor>("contact_sets_filter_input", false))
|
||||
{
|
||||
filter->setCommitCallback(boost::bind(&FSPanelContactSets::onFilterEdit, this, _2));
|
||||
}
|
||||
|
||||
mAvatarList = getChild<LLAvatarList>("contact_list");
|
||||
mAvatarList->setUseContactSetColors(true);
|
||||
mAvatarList->setUseContactSetListStyle(true);
|
||||
mAvatarList->setAvatarDropCallback(boost::bind(&FSPanelContactSets::handleAvatarDrop, this, _1, _2));
|
||||
mAvatarList->setCommitCallback(boost::bind(&FSPanelContactSets::onSelectAvatar, this));
|
||||
mAvatarList->setItemDoubleClickCallback(boost::bind(&FSPanelContactSets::onClickStartIM, this));
|
||||
mAvatarList->setNoItemsCommentText(getString("empty_list"));
|
||||
@@ -107,17 +142,7 @@ void FSPanelContactSets::generateAvatarList(const std::string& contact_set)
|
||||
|
||||
if (contact_set == CS_SET_ALL_SETS)
|
||||
{
|
||||
avatars = LGGContactSets::getInstance()->getListOfNonFriends();
|
||||
|
||||
// "All sets" includes buddies
|
||||
LLAvatarTracker::buddy_map_t all_buddies;
|
||||
LLAvatarTracker::instance().copyBuddyList(all_buddies);
|
||||
for (LLAvatarTracker::buddy_map_t::const_iterator buddy = all_buddies.begin();
|
||||
buddy != all_buddies.end();
|
||||
++buddy)
|
||||
{
|
||||
avatars.push_back(buddy->first);
|
||||
}
|
||||
avatars = LGGContactSets::getInstance()->getFriendsInAnySet();
|
||||
}
|
||||
else if (contact_set == CS_SET_NO_SETS)
|
||||
{
|
||||
@@ -151,10 +176,19 @@ void FSPanelContactSets::generateAvatarList(const std::string& contact_set)
|
||||
}
|
||||
}
|
||||
getChild<LLTextBox>("member_count")->setTextArg("[COUNT]", llformat("%d", avatars.size()));
|
||||
updateAvatarListSorting();
|
||||
mAvatarList->setDirty();
|
||||
resetControls();
|
||||
}
|
||||
|
||||
void FSPanelContactSets::changed(U32 changed_mask)
|
||||
{
|
||||
if ((changed_mask & LLFriendObserver::ONLINE) && mAvatarList && shouldSortByOnlineStatus())
|
||||
{
|
||||
mAvatarList->sort();
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelContactSets::resetControls()
|
||||
{
|
||||
bool mutable_set = (!LGGContactSets::getInstance()->isInternalSetName(mContactSetCombo->getValue().asString()));
|
||||
@@ -164,11 +198,12 @@ void FSPanelContactSets::resetControls()
|
||||
childSetEnabled("remove_set_btn", mutable_set);
|
||||
childSetEnabled("config_btn", mutable_set);
|
||||
childSetEnabled("add_btn", mutable_set);
|
||||
childSetEnabled("move_btn", (mutable_set && has_selection));
|
||||
childSetEnabled("remove_btn", (mutable_set && has_selection));
|
||||
childSetEnabled("profile_btn", has_selection);
|
||||
childSetEnabled("start_im_btn", has_selection);
|
||||
childSetEnabled("offer_teleport_btn", has_selection); // Should probably check if they're online...
|
||||
childSetEnabled("set_pseudonym_btn", (mAvatarSelections.size() == 1));
|
||||
childSetEnabled("set_pseudonym_btn", has_selection);
|
||||
childSetEnabled("remove_pseudonym_btn", (has_selection
|
||||
&& LGGContactSets::getInstance()->hasPseudonym(mAvatarSelections)));
|
||||
childSetEnabled("remove_displayname_btn", (has_selection
|
||||
@@ -218,6 +253,62 @@ void FSPanelContactSets::refreshSetList()
|
||||
resetControls();
|
||||
}
|
||||
|
||||
void FSPanelContactSets::updateAvatarListSorting()
|
||||
{
|
||||
if (!mAvatarList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldSortByOnlineStatus())
|
||||
{
|
||||
mAvatarList->setComparator(&FS_ONLINE_STATUS_COMPARATOR);
|
||||
mAvatarList->sort();
|
||||
}
|
||||
else
|
||||
{
|
||||
mAvatarList->sortByName();
|
||||
}
|
||||
}
|
||||
|
||||
bool FSPanelContactSets::shouldSortByOnlineStatus() const
|
||||
{
|
||||
if (!mContactSetCombo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string selected_set = mContactSetCombo->getValue().asString();
|
||||
if (LGGContactSets::getInstance()->isInternalSetName(selected_set))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return LGGContactSets::getInstance()->getSortByOnlineStatusForSet(selected_set);
|
||||
}
|
||||
|
||||
bool FSPanelContactSets::handleAvatarDrop(const LLUUID& avatar_id, bool drop)
|
||||
{
|
||||
if (!mContactSetCombo || avatar_id.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string set_name = mContactSetCombo->getValue().asString();
|
||||
if (LGGContactSets::getInstance()->isInternalSetName(set_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drop)
|
||||
{
|
||||
uuid_vec_t ids{ avatar_id };
|
||||
LGGContactSets::instance().addToSet(ids, set_name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FSPanelContactSets::onClickAddAvatar(LLUICtrl* ctrl)
|
||||
{
|
||||
LLFloater* root_floater = gFloaterView->getParentFloater(this);
|
||||
@@ -259,6 +350,22 @@ void FSPanelContactSets::onClickRemoveAvatar()
|
||||
LLNotificationsUtil::add((selected_size > 1 ? "RemoveContactsFromSet" : "RemoveContactFromSet"), args, payload, &LGGContactSets::handleRemoveAvatarFromSetCallback);
|
||||
}
|
||||
|
||||
void FSPanelContactSets::onClickMoveAvatar()
|
||||
{
|
||||
if (!(mContactSetCombo && !mAvatarSelections.empty()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string set = mContactSetCombo->getValue().asString();
|
||||
if (LGGContactSets::getInstance()->isInternalSetName(set))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLAvatarActions::moveToContactSet(mAvatarSelections, set);
|
||||
}
|
||||
|
||||
void FSPanelContactSets::onClickAddSet()
|
||||
{
|
||||
LLNotificationsUtil::add("AddNewContactSet", LLSD(), LLSD(), &LGGContactSets::handleAddContactSetCallback);
|
||||
@@ -311,10 +418,26 @@ void FSPanelContactSets::onClickOfferTeleport()
|
||||
|
||||
void FSPanelContactSets::onClickSetPseudonym()
|
||||
{
|
||||
if (mAvatarSelections.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLSD payload, args;
|
||||
args["AVATAR"] = LLSLURL("agent", mAvatarSelections.front(), "about").getSLURLString();
|
||||
payload["id"] = mAvatarSelections.front();
|
||||
LLNotificationsUtil::add("SetAvatarPseudonym", args, payload, &LGGContactSets::handleSetAvatarPseudonymCallback);
|
||||
if (mAvatarSelections.size() == 1)
|
||||
{
|
||||
args["AVATAR"] = LLSLURL("agent", mAvatarSelections.front(), "about").getSLURLString();
|
||||
payload["id"] = mAvatarSelections.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
args["COUNT"] = llformat("%d", static_cast<S32>(mAvatarSelections.size()));
|
||||
for (const LLUUID& id : mAvatarSelections)
|
||||
{
|
||||
payload["ids"].append(id);
|
||||
}
|
||||
}
|
||||
LLNotificationsUtil::add((mAvatarSelections.size() > 1 ? "SetAvatarPseudonymMultiple" : "SetAvatarPseudonym"), args, payload, &LGGContactSets::handleSetAvatarPseudonymCallback);
|
||||
}
|
||||
|
||||
void FSPanelContactSets::onClickRemovePseudonym()
|
||||
@@ -340,3 +463,22 @@ void FSPanelContactSets::onClickRemoveDisplayName()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelContactSets::onFilterEdit(const std::string& search_string)
|
||||
{
|
||||
std::string trimmed_search = search_string;
|
||||
LLStringUtil::trimHead(trimmed_search);
|
||||
|
||||
std::string search_upper = trimmed_search;
|
||||
LLStringUtil::toUpper(search_upper);
|
||||
if (mFilterSubString == search_upper)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mFilterSubString = search_upper;
|
||||
if (mAvatarList)
|
||||
{
|
||||
mAvatarList->setNameFilter(trimmed_search);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,17 +32,19 @@
|
||||
|
||||
#include "lggcontactsets.h"
|
||||
#include "llavatarlist.h"
|
||||
#include "llcallingcard.h"
|
||||
#include "llcombobox.h"
|
||||
#include "llpanel.h"
|
||||
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
class FSPanelContactSets : public LLPanel
|
||||
class FSPanelContactSets : public LLPanel, public LLFriendObserver
|
||||
{
|
||||
public:
|
||||
FSPanelContactSets();
|
||||
bool postBuild() override;
|
||||
void refreshSetList();
|
||||
void changed(U32 changed_mask) override;
|
||||
|
||||
private:
|
||||
~FSPanelContactSets();
|
||||
@@ -51,6 +53,7 @@ private:
|
||||
void generateAvatarList(const std::string& contact_set);
|
||||
void onClickAddAvatar(LLUICtrl* ctrl);
|
||||
void handlePickerCallback(const uuid_vec_t& ids, const std::string& set);
|
||||
void onClickMoveAvatar();
|
||||
void onClickRemoveAvatar();
|
||||
void onClickOpenProfile();
|
||||
void onClickStartIM();
|
||||
@@ -61,9 +64,13 @@ private:
|
||||
void onClickRemoveDisplayName();
|
||||
void onClickSetPseudonym();
|
||||
void onClickRemovePseudonym();
|
||||
void onFilterEdit(const std::string& search_string);
|
||||
bool handleAvatarDrop(const LLUUID& avatar_id, bool drop);
|
||||
|
||||
void refreshContactSets();
|
||||
void resetControls();
|
||||
void updateAvatarListSorting();
|
||||
bool shouldSortByOnlineStatus() const;
|
||||
|
||||
void updateSets(LGGContactSets::EContactSetUpdate type);
|
||||
boost::signals2::connection mContactSetChangedConnection;
|
||||
@@ -72,6 +79,7 @@ private:
|
||||
|
||||
LLComboBox* mContactSetCombo{ nullptr };
|
||||
LLAvatarList* mAvatarList{ nullptr };
|
||||
std::string mFilterSubString{};
|
||||
};
|
||||
|
||||
#endif // FS_PANELCONTACTSETS_H
|
||||
|
||||
@@ -923,6 +923,9 @@ void FSPanelLogin::loadLoginPage()
|
||||
// login page (web) content version
|
||||
params["login_content_version"] = gSavedSettings.getString("LoginContentVersion");
|
||||
|
||||
// skin
|
||||
params["skin"] = gSavedSettings.getString("FSInternalSkinCurrent") + " " + gSavedSettings.getString("FSInternalSkinCurrentTheme");
|
||||
|
||||
// No version popup
|
||||
if (gSavedSettings.getBOOL("FSNoVersionPopup"))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,629 @@
|
||||
/**
|
||||
* @file fspanelpreferenceuisounds.cpp
|
||||
* @brief FSPanelPreferenceUISounds class: UI Sounds preferences tab
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Copyright (C) 2026, Firestorm Viewer Project
|
||||
*
|
||||
* 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.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
|
||||
#include "fspanelpreferenceuisounds.h"
|
||||
|
||||
#include "llcheckboxctrl.h"
|
||||
#include "llclipboard.h"
|
||||
#include "llcombobox.h"
|
||||
#include "llcontrol.h"
|
||||
#include "llfiltereditor.h"
|
||||
#include "lllineeditor.h"
|
||||
#include "llmenugl.h"
|
||||
#include "llscrolllistcell.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "lltextbox.h"
|
||||
#include "llui.h"
|
||||
#include "llviewercontrol.h"
|
||||
#include "llviewermenu.h"
|
||||
|
||||
#ifdef OPENSIM
|
||||
#include "llviewernetwork.h"
|
||||
#endif
|
||||
|
||||
static LLPanelInjector<FSPanelPreferenceUISounds> t_pref_ui_sounds("fs_panel_preference_ui_sounds");
|
||||
|
||||
static constexpr S32 COL_LABEL = 0;
|
||||
static constexpr S32 COL_CHECK = 1;
|
||||
static constexpr S32 COL_STATUS = 2;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FSPanelPreferenceUISounds::FSPanelPreferenceUISounds()
|
||||
: LLPanelPreference()
|
||||
{
|
||||
mCommitCallbackRegistrar.add("Pref.SelectUISound", boost::bind(&FSPanelPreferenceUISounds::onSelectSound, this));
|
||||
mCommitCallbackRegistrar.add("Pref.UpdateUISoundFilter", boost::bind(&FSPanelPreferenceUISounds::onUpdateFilter, this));
|
||||
mCommitCallbackRegistrar.add("Pref.CommitUISoundUUID", boost::bind(&FSPanelPreferenceUISounds::onCommitUUID, this));
|
||||
mCommitCallbackRegistrar.add("Pref.CommitUISoundPlayCheck", boost::bind(&FSPanelPreferenceUISounds::onCommitPlayCheck, this));
|
||||
mCommitCallbackRegistrar.add("Pref.CommitUISoundPlayCombo", boost::bind(&FSPanelPreferenceUISounds::onCommitPlayCombo, this));
|
||||
mCommitCallbackRegistrar.add("Pref.PreviewSelectedUISound", boost::bind(&FSPanelPreferenceUISounds::onPreviewSound, this));
|
||||
mCommitCallbackRegistrar.add("Pref.ResetSelectedUISound", boost::bind(&FSPanelPreferenceUISounds::onResetSound, this));
|
||||
}
|
||||
|
||||
bool FSPanelPreferenceUISounds::postBuild()
|
||||
{
|
||||
mSoundsList = getChild<LLScrollListCtrl>("ui_sounds_list");
|
||||
mFilter = getChild<LLFilterEditor>("ui_sound_filter");
|
||||
mUUIDEditor = getChild<LLLineEditor>("ui_sound_uuid");
|
||||
mPlayCheck = getChild<LLCheckBoxCtrl>("ui_sound_play_checkbox");
|
||||
mPlayCombo = getChild<LLComboBox>("ui_sound_play_combo");
|
||||
mSelectedLabel = getChild<LLTextBox>("ui_sound_selected_label");
|
||||
mSettingName = getChild<LLTextBox>("ui_sound_setting_name");
|
||||
|
||||
mSoundsList->setFilterColumn(COL_LABEL);
|
||||
mSoundsList->setDoubleClickCallback(boost::bind(&FSPanelPreferenceUISounds::onDoubleClick, this));
|
||||
mSoundsList->setRightMouseDownCallback(boost::bind(&FSPanelPreferenceUISounds::onRightClick, this, _1, _2, _3, _4));
|
||||
|
||||
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
|
||||
registrar.add("UISounds.CopyUUID", boost::bind(&FSPanelPreferenceUISounds::onCopyUUID, this));
|
||||
|
||||
LLContextMenu::Params menu_params;
|
||||
menu_params.name("ui_sounds_context_menu");
|
||||
menu_params.visible(false);
|
||||
LLContextMenu* menu = LLUICtrlFactory::create<LLContextMenu>(menu_params);
|
||||
|
||||
LLMenuItemCallGL::Params item_params;
|
||||
item_params.label("Copy sound UUID");
|
||||
item_params.name("copy_uuid");
|
||||
item_params.on_click.function_name = "UISounds.CopyUUID";
|
||||
menu->addChild(LLUICtrlFactory::create<LLMenuItemCallGL>(item_params));
|
||||
|
||||
mContextMenuHandle = menu->getHandle();
|
||||
gMenuHolder->addChild(menu);
|
||||
|
||||
applyLocalizedLabels();
|
||||
buildList();
|
||||
refreshEditor();
|
||||
|
||||
return LLPanelPreference::postBuild();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSPanelPreferenceUISounds::refreshList()
|
||||
{
|
||||
applyLocalizedLabels();
|
||||
buildList();
|
||||
refreshEditor();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// List building
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FSPanelPreferenceUISounds::UISoundEntry FSPanelPreferenceUISounds::makeEntry(
|
||||
std::string_view sound,
|
||||
const std::optional<std::string_view>& label_override,
|
||||
bool combo,
|
||||
bool inverted)
|
||||
{
|
||||
UISoundEntry e;
|
||||
e.sound_setting = sound;
|
||||
e.playmode_setting = "PlayMode" + e.sound_setting;
|
||||
e.label_control = label_override.value_or("textFS" + e.sound_setting.substr(5));
|
||||
e.uses_combo = combo;
|
||||
e.inverted_bool = inverted;
|
||||
return e;
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::buildList()
|
||||
{
|
||||
if (!mSoundsList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const S32 prev_sel = getSelectedIndex();
|
||||
|
||||
if (mEntries.empty())
|
||||
{
|
||||
mEntries = {
|
||||
makeEntry("UISndAlert"),
|
||||
makeEntry("UISndBadKeystroke"),
|
||||
makeEntry("UISndClick"),
|
||||
makeEntry("UISndClickRelease"),
|
||||
makeEntry("UISndHealthReductionF"),
|
||||
makeEntry("UISndHealthReductionM"),
|
||||
makeEntry("UISndMoneyChangeDown"),
|
||||
makeEntry("UISndMoneyChangeUp"),
|
||||
makeEntry("UISndNearbyChat"),
|
||||
makeEntry("UISndNewIncomingIMSession", std::nullopt, true),
|
||||
makeEntry("UISndNewIncomingGroupIMSession", std::nullopt, true),
|
||||
makeEntry("UISndNewIncomingConfIMSession", std::nullopt, true),
|
||||
makeEntry("UISndStartIM"),
|
||||
makeEntry("UISndChatMention"),
|
||||
makeEntry("UISndObjectCreate"),
|
||||
makeEntry("UISndObjectDelete"),
|
||||
makeEntry("UISndObjectRezIn"),
|
||||
makeEntry("UISndObjectRezOut"),
|
||||
makeEntry("UISndSnapshot", std::nullopt, false, true),
|
||||
makeEntry("UISndTeleportOut"),
|
||||
makeEntry("UISndPieMenuAppear"),
|
||||
makeEntry("UISndPieMenuHide"),
|
||||
makeEntry("UISndPieMenuSliceHighlight0"),
|
||||
makeEntry("UISndPieMenuSliceHighlight1"),
|
||||
makeEntry("UISndPieMenuSliceHighlight2"),
|
||||
makeEntry("UISndPieMenuSliceHighlight3"),
|
||||
makeEntry("UISndPieMenuSliceHighlight4"),
|
||||
makeEntry("UISndPieMenuSliceHighlight5"),
|
||||
makeEntry("UISndPieMenuSliceHighlight6"),
|
||||
makeEntry("UISndPieMenuSliceHighlight7"),
|
||||
makeEntry("UISndTyping"),
|
||||
makeEntry("UISndWindowClose"),
|
||||
makeEntry("UISndWindowOpen"),
|
||||
makeEntry("UISndScriptFloaterOpen"),
|
||||
makeEntry("UISndScriptFloaterClose"),
|
||||
makeEntry("UISndFriendOnline"),
|
||||
makeEntry("UISndFriendOffline"),
|
||||
makeEntry("UISndFriendshipOffer"),
|
||||
makeEntry("UISndTeleportOffer"),
|
||||
makeEntry("UISndInventoryOffer"),
|
||||
makeEntry("UISndIncomingVoiceCall"),
|
||||
makeEntry("UISndGroupInvitation"),
|
||||
makeEntry("UISndGroupNotice"),
|
||||
makeEntry("UISndQuestionExperience"),
|
||||
makeEntry("UISndInvalidOp"),
|
||||
makeEntry("UISndMovelockToggle"),
|
||||
makeEntry("UISndFootsteps"),
|
||||
makeEntry("UISndTrackerBeacon"),
|
||||
makeEntry("UISndMicToggle"),
|
||||
makeEntry("UISndRestart"),
|
||||
makeEntry("UISndRestartOpenSim"),
|
||||
};
|
||||
}
|
||||
|
||||
mSoundsList->deleteAllItems();
|
||||
|
||||
for (size_t i = 0; i < mEntries.size(); ++i)
|
||||
{
|
||||
UISoundEntry& entry = mEntries[i];
|
||||
|
||||
#ifdef OPENSIM
|
||||
const bool show_opensim = LLGridManager::instance().isInOpenSim();
|
||||
#else
|
||||
const bool show_opensim = false;
|
||||
#endif
|
||||
if (entry.sound_setting == "UISndRestartOpenSim" && !show_opensim)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.display_label = resolveEntryLabel(entry);
|
||||
|
||||
LLSD row;
|
||||
row["id"] = LLSD::Integer(i);
|
||||
row["columns"][COL_LABEL]["column"] = "ui_sound_label";
|
||||
row["columns"][COL_LABEL]["value"] = entry.display_label;
|
||||
|
||||
if (entry.uses_combo)
|
||||
{
|
||||
row["columns"][COL_CHECK]["column"] = "ui_sound_check";
|
||||
row["columns"][COL_CHECK]["value"] = "";
|
||||
row["columns"][COL_STATUS]["column"] = "ui_sound_status";
|
||||
row["columns"][COL_STATUS]["value"] = getModeLabel(entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool val = gSavedSettings.getBOOL(entry.playmode_setting);
|
||||
bool is_enabled = entry.inverted_bool ? !val : val;
|
||||
row["columns"][COL_CHECK]["type"] = "checkbox";
|
||||
row["columns"][COL_CHECK]["column"] = "ui_sound_check";
|
||||
row["columns"][COL_CHECK]["value"] = is_enabled;
|
||||
row["columns"][COL_STATUS]["column"] = "ui_sound_status";
|
||||
row["columns"][COL_STATUS]["value"] = getModeLabel(entry);
|
||||
}
|
||||
|
||||
mSoundsList->addElement(row);
|
||||
}
|
||||
|
||||
if (prev_sel >= 0)
|
||||
{
|
||||
for (LLScrollListItem* row : mSoundsList->getAllData())
|
||||
{
|
||||
if (row && row->getValue().asInteger() == prev_sel)
|
||||
{
|
||||
mSoundsList->selectNthItem(mSoundsList->getItemIndex(row));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSoundsList->getItemCount() > 0 && !mSoundsList->getFirstSelected())
|
||||
{
|
||||
mSoundsList->selectFirstItem();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Editor refresh
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSPanelPreferenceUISounds::refreshEditor()
|
||||
{
|
||||
if (!mSelectedLabel || !mSettingName || !mUUIDEditor || !mPlayCheck || !mPlayCombo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto entry = getSelectedEntry();
|
||||
const bool has_sel = entry.has_value();
|
||||
|
||||
mUUIDEditor->setEnabled(has_sel);
|
||||
|
||||
if (!has_sel)
|
||||
{
|
||||
mSelectedLabel->setValue(getPanelString("ui_sound_select_prompt"));
|
||||
mSettingName->setValue(LLStringUtil::null);
|
||||
mUUIDEditor->setValue(LLSD());
|
||||
mPlayCheck->setVisible(false);
|
||||
mPlayCombo->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
mSelectedLabel->setValue(entry->display_label.empty() ? entry->sound_setting : entry->display_label);
|
||||
mSettingName->setValue(entry->sound_setting);
|
||||
mUUIDEditor->setValue(gSavedSettings.getString(entry->sound_setting));
|
||||
|
||||
if (LLControlVariable* ctrl = gSavedSettings.getControl(entry->sound_setting))
|
||||
{
|
||||
mUUIDEditor->setToolTip(ctrl->getDefault().asString());
|
||||
}
|
||||
|
||||
if (entry->uses_combo)
|
||||
{
|
||||
mPlayCheck->setVisible(false);
|
||||
mPlayCombo->setVisible(true);
|
||||
mPlayCombo->setValue(static_cast<int>(gSavedSettings.getU32(entry->playmode_setting)));
|
||||
}
|
||||
else
|
||||
{
|
||||
mPlayCombo->setVisible(false);
|
||||
mPlayCheck->setVisible(true);
|
||||
bool val = gSavedSettings.getBOOL(entry->playmode_setting);
|
||||
mPlayCheck->setValue(entry->inverted_bool ? !val : val);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Callbacks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSPanelPreferenceUISounds::onSelectSound()
|
||||
{
|
||||
if (!mSoundsList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LLScrollListItem* selected = mSoundsList->getFirstSelected())
|
||||
{
|
||||
S32 idx = selected->getValue().asInteger();
|
||||
if (idx >= 0 && idx < static_cast<S32>(mEntries.size()))
|
||||
{
|
||||
const UISoundEntry& entry = mEntries[idx];
|
||||
if (!entry.uses_combo)
|
||||
{
|
||||
bool checked = selected->getColumn(COL_CHECK)->getValue().asBoolean();
|
||||
bool val = gSavedSettings.getBOOL(entry.playmode_setting);
|
||||
bool was_enabled = entry.inverted_bool ? !val : val;
|
||||
if (checked != was_enabled)
|
||||
{
|
||||
gSavedSettings.setBOOL(entry.playmode_setting, entry.inverted_bool ? !checked : checked);
|
||||
if (LLScrollListText* text_cell = dynamic_cast<LLScrollListText*>(selected->getColumn(COL_STATUS)))
|
||||
{
|
||||
text_cell->setText(getModeLabel(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshEditor();
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onUpdateFilter()
|
||||
{
|
||||
if (mSoundsList && mFilter)
|
||||
{
|
||||
mSoundsList->setFilterString(mFilter->getValue().asString());
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onCommitUUID()
|
||||
{
|
||||
const auto entry = getSelectedEntry();
|
||||
if (!entry || !mUUIDEditor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
gSavedSettings.setString(entry->sound_setting, mUUIDEditor->getValue().asString());
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onCommitPlayCheck()
|
||||
{
|
||||
const auto entry = getSelectedEntry();
|
||||
if (!entry || entry->uses_combo || !mPlayCheck)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool checked = mPlayCheck->getValue().asBoolean();
|
||||
gSavedSettings.setBOOL(entry->playmode_setting, entry->inverted_bool ? !checked : checked);
|
||||
|
||||
LLScrollListItem* selected = mSoundsList ? mSoundsList->getFirstSelected() : nullptr;
|
||||
if (selected)
|
||||
{
|
||||
selected->getColumn(COL_CHECK)->setValue(checked);
|
||||
if (LLScrollListText* text_cell = dynamic_cast<LLScrollListText*>(selected->getColumn(COL_STATUS)))
|
||||
{
|
||||
text_cell->setText(getModeLabel(*entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onCommitPlayCombo()
|
||||
{
|
||||
const auto entry = getSelectedEntry();
|
||||
if (!entry || !entry->uses_combo || !mPlayCombo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
U32 mode = static_cast<U32>(mPlayCombo->getValue().asInteger());
|
||||
gSavedSettings.setU32(entry->playmode_setting, mode);
|
||||
|
||||
LLScrollListItem* selected = mSoundsList ? mSoundsList->getFirstSelected() : nullptr;
|
||||
if (selected)
|
||||
{
|
||||
if (LLScrollListText* text_cell = dynamic_cast<LLScrollListText*>(selected->getColumn(COL_STATUS)))
|
||||
{
|
||||
text_cell->setText(getModeLabel(*entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onPreviewSound()
|
||||
{
|
||||
const auto entry = getSelectedEntry();
|
||||
if (entry)
|
||||
{
|
||||
make_ui_sound(entry->sound_setting.c_str(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onResetSound()
|
||||
{
|
||||
const auto entry = getSelectedEntry();
|
||||
if (!entry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LLControlVariable* ctrl = gSavedSettings.getControl(entry->sound_setting))
|
||||
{
|
||||
ctrl->resetToDefault();
|
||||
}
|
||||
if (LLControlVariable* ctrl = gSavedSettings.getControl(entry->playmode_setting))
|
||||
{
|
||||
ctrl->resetToDefault();
|
||||
}
|
||||
|
||||
LLScrollListItem* selected = mSoundsList ? mSoundsList->getFirstSelected() : nullptr;
|
||||
if (selected)
|
||||
{
|
||||
if (entry->uses_combo)
|
||||
{
|
||||
if (LLScrollListText* text_cell = dynamic_cast<LLScrollListText*>(selected->getColumn(COL_STATUS)))
|
||||
{
|
||||
text_cell->setText(getModeLabel(*entry));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool val = gSavedSettings.getBOOL(entry->playmode_setting);
|
||||
bool is_enabled = entry->inverted_bool ? !val : val;
|
||||
selected->getColumn(COL_CHECK)->setValue(is_enabled);
|
||||
if (LLScrollListText* text_cell = dynamic_cast<LLScrollListText*>(selected->getColumn(COL_STATUS)))
|
||||
{
|
||||
text_cell->setText(getModeLabel(*entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshEditor();
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onDoubleClick()
|
||||
{
|
||||
onPreviewSound();
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onRightClick(LLUICtrl* ctrl, S32 x, S32 y, MASK mask)
|
||||
{
|
||||
mSoundsList->selectItemAt(x, y, mask);
|
||||
refreshEditor();
|
||||
|
||||
const auto entry = getSelectedEntry();
|
||||
if (!entry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLContextMenu* menu = mContextMenuHandle.get();
|
||||
if (menu)
|
||||
{
|
||||
S32 screen_x, screen_y;
|
||||
mSoundsList->localPointToScreen(x, y, &screen_x, &screen_y);
|
||||
menu->show(screen_x, screen_y, mSoundsList);
|
||||
}
|
||||
}
|
||||
|
||||
void FSPanelPreferenceUISounds::onCopyUUID()
|
||||
{
|
||||
const auto entry = getSelectedEntry();
|
||||
if (!entry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string uuid = gSavedSettings.getString(entry->sound_setting);
|
||||
LLClipboard::instance().copyToClipboard(utf8str_to_wstring(uuid), 0, static_cast<S32>(uuid.size()));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void FSPanelPreferenceUISounds::applyLocalizedLabels()
|
||||
{
|
||||
if (mPlayCheck)
|
||||
{
|
||||
std::string lbl = getPanelString("ui_sound_play_this");
|
||||
if (!lbl.empty()) mPlayCheck->setLabel(lbl);
|
||||
}
|
||||
|
||||
LLContextMenu* menu = mContextMenuHandle.get();
|
||||
if (menu)
|
||||
{
|
||||
std::string lbl = getPanelString("ui_sound_copy_uuid");
|
||||
if (!lbl.empty())
|
||||
{
|
||||
if (LLMenuItemGL* item = menu->findChild<LLMenuItemGL>("copy_uuid"))
|
||||
{
|
||||
item->setLabel(lbl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mPlayCombo)
|
||||
{
|
||||
static const struct { const char* value; const char* key; } rows[] = {
|
||||
{"1", "ui_sound_playmode_new_session"},
|
||||
{"2", "ui_sound_playmode_every_message"},
|
||||
{"3", "ui_sound_playmode_not_focus"},
|
||||
{"0", "ui_sound_playmode_mute"},
|
||||
};
|
||||
|
||||
for (const auto& r : rows)
|
||||
{
|
||||
LLScrollListItem* item = mPlayCombo->getItemByValue(LLSD(r.value));
|
||||
if (!item)
|
||||
continue;
|
||||
|
||||
LLScrollListCell* cell = item->getColumn(0);
|
||||
if (LLScrollListText* tc = dynamic_cast<LLScrollListText*>(cell))
|
||||
{
|
||||
std::string s = getPanelString(r.key);
|
||||
if (!s.empty()) tc->setText(s);
|
||||
}
|
||||
}
|
||||
mPlayCombo->updateLabel();
|
||||
}
|
||||
}
|
||||
|
||||
std::string FSPanelPreferenceUISounds::getPanelString(std::string_view name) const
|
||||
{
|
||||
if (hasString(name))
|
||||
{
|
||||
return getString(name);
|
||||
}
|
||||
LLView* ancestor = getParent();
|
||||
while (ancestor)
|
||||
{
|
||||
LLPanel* panel = dynamic_cast<LLPanel*>(ancestor);
|
||||
if (panel && panel->hasString(name))
|
||||
{
|
||||
return panel->getString(name);
|
||||
}
|
||||
ancestor = ancestor->getParent();
|
||||
}
|
||||
return LLStringUtil::null;
|
||||
}
|
||||
|
||||
std::string FSPanelPreferenceUISounds::resolveEntryLabel(const UISoundEntry& entry) const
|
||||
{
|
||||
std::string label = getPanelString(entry.label_control);
|
||||
if (label.empty())
|
||||
{
|
||||
label = entry.sound_setting;
|
||||
}
|
||||
LLStringUtil::trim(label);
|
||||
if (!label.empty() && label.back() == ':')
|
||||
{
|
||||
label.pop_back();
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
std::string FSPanelPreferenceUISounds::getModeLabel(const UISoundEntry& entry) const
|
||||
{
|
||||
if (entry.uses_combo)
|
||||
{
|
||||
const U32 mode = gSavedSettings.getU32(entry.playmode_setting);
|
||||
switch (mode)
|
||||
{
|
||||
case 1:
|
||||
return getPanelString("ui_sound_playmode_new_session");
|
||||
case 2:
|
||||
return getPanelString("ui_sound_playmode_every_message");
|
||||
case 3:
|
||||
return getPanelString("ui_sound_playmode_not_focus");
|
||||
case 0:
|
||||
default:
|
||||
return getPanelString("ui_sound_playmode_mute");
|
||||
}
|
||||
}
|
||||
|
||||
const bool enabled = gSavedSettings.getBOOL(entry.playmode_setting);
|
||||
if (entry.inverted_bool)
|
||||
{
|
||||
return enabled ? getPanelString("ui_sound_playmode_mute") : getPanelString("ui_sound_play_this");
|
||||
}
|
||||
|
||||
return enabled ? getPanelString("ui_sound_play_this") : getPanelString("ui_sound_playmode_mute");
|
||||
}
|
||||
|
||||
S32 FSPanelPreferenceUISounds::getSelectedIndex() const
|
||||
{
|
||||
if (!mSoundsList)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
LLScrollListItem* selected = mSoundsList->getFirstSelected();
|
||||
if (!selected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return selected->getValue().asInteger();
|
||||
}
|
||||
|
||||
std::optional<FSPanelPreferenceUISounds::UISoundEntry> FSPanelPreferenceUISounds::getSelectedEntry() const
|
||||
{
|
||||
S32 idx = getSelectedIndex();
|
||||
if (idx < 0 || idx >= static_cast<S32>(mEntries.size()))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return mEntries[idx];
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @file fspanelpreferenceuisounds.h
|
||||
* @brief FSPanelPreferenceUISounds class — UI Sounds preferences tab
|
||||
*
|
||||
* $LicenseInfo:firstyear=2026&license=viewerlgpl$
|
||||
* Copyright (C) 2026, Firestorm Viewer Project
|
||||
*
|
||||
* 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.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#ifndef FS_PANEL_PREFERENCE_UI_SOUNDS_H
|
||||
#define FS_PANEL_PREFERENCE_UI_SOUNDS_H
|
||||
|
||||
#include "llfloaterpreference.h"
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
class LLCheckBoxCtrl;
|
||||
class LLContextMenu;
|
||||
class LLScrollListCtrl;
|
||||
class LLFilterEditor;
|
||||
class LLLineEditor;
|
||||
class LLComboBox;
|
||||
class LLTextBox;
|
||||
|
||||
class FSPanelPreferenceUISounds : public LLPanelPreference
|
||||
{
|
||||
public:
|
||||
FSPanelPreferenceUISounds();
|
||||
bool postBuild() override;
|
||||
|
||||
void refreshList();
|
||||
|
||||
private:
|
||||
struct UISoundEntry
|
||||
{
|
||||
std::string sound_setting;
|
||||
std::string playmode_setting;
|
||||
std::string label_control;
|
||||
std::string display_label;
|
||||
bool uses_combo = false;
|
||||
bool inverted_bool = false;
|
||||
};
|
||||
|
||||
void buildList();
|
||||
static UISoundEntry makeEntry(std::string_view sound, const std::optional<std::string_view>& label_override = std::nullopt, bool combo = false, bool inverted = false);
|
||||
void refreshEditor();
|
||||
void onSelectSound();
|
||||
void onUpdateFilter();
|
||||
void onCommitUUID();
|
||||
void onCommitPlayCheck();
|
||||
void onCommitPlayCombo();
|
||||
void onPreviewSound();
|
||||
void onResetSound();
|
||||
void onDoubleClick();
|
||||
void onRightClick(LLUICtrl* ctrl, S32 x, S32 y, MASK mask);
|
||||
void onCopyUUID();
|
||||
void applyLocalizedLabels();
|
||||
|
||||
std::string resolveEntryLabel(const UISoundEntry& entry) const;
|
||||
std::string getModeLabel(const UISoundEntry& entry) const;
|
||||
std::string getPanelString(std::string_view name) const;
|
||||
|
||||
S32 getSelectedIndex() const;
|
||||
std::optional<UISoundEntry> getSelectedEntry() const;
|
||||
|
||||
LLScrollListCtrl* mSoundsList = nullptr;
|
||||
LLFilterEditor* mFilter = nullptr;
|
||||
LLLineEditor* mUUIDEditor = nullptr;
|
||||
LLCheckBoxCtrl* mPlayCheck = nullptr;
|
||||
LLComboBox* mPlayCombo = nullptr;
|
||||
LLTextBox* mSelectedLabel = nullptr;
|
||||
LLTextBox* mSettingName = nullptr;
|
||||
std::vector<UISoundEntry> mEntries;
|
||||
LLHandle<LLContextMenu> mContextMenuHandle;
|
||||
|
||||
LOG_CLASS(FSPanelPreferenceUISounds);
|
||||
};
|
||||
|
||||
#endif // FS_PANEL_PREFERENCE_UI_SOUNDS_H
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
// libs
|
||||
#include "llfiltereditor.h"
|
||||
#include "llmenugl.h"
|
||||
#include "lluictrlfactory.h"
|
||||
#include "llmenubutton.h"
|
||||
#include "lltoggleablemenu.h"
|
||||
@@ -39,6 +38,7 @@
|
||||
#include "fscommon.h"
|
||||
#include "fsradarmenu.h"
|
||||
#include "llavataractions.h"
|
||||
#include "llcallingcard.h"
|
||||
#include "llnetmap.h"
|
||||
#include "llpanelblockedlist.h"
|
||||
#include "llviewercontrol.h" // for gSavedSettings
|
||||
@@ -59,14 +59,13 @@ public:
|
||||
LLAvatarTracker::instance().addObserver(this);
|
||||
}
|
||||
|
||||
~FSButtonsUpdater()
|
||||
~FSButtonsUpdater() override
|
||||
{
|
||||
LLAvatarTracker::instance().removeObserver(this);
|
||||
}
|
||||
|
||||
/*virtual*/ void changed(U32 mask)
|
||||
void changed(U32) override
|
||||
{
|
||||
(void) mask;
|
||||
update();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
#include "fsradar.h"
|
||||
#include "fsradarlistctrl.h"
|
||||
#include "llcallingcard.h" // for avatar tracker
|
||||
|
||||
class LLButton;
|
||||
class LLFilterEditor;
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
#include "lltracker.h"
|
||||
#include "lltrans.h"
|
||||
#include "llviewercontrol.h" // for gSavedSettings
|
||||
#include "llviewermenu.h" // for gMenuHolder
|
||||
#include "llviewerparcelmgr.h"
|
||||
#include "llvoavatar.h"
|
||||
#include "llvoiceclient.h"
|
||||
@@ -60,13 +59,14 @@
|
||||
#include "rlvactions.h"
|
||||
#include "rlvhandler.h"
|
||||
|
||||
constexpr F32 FS_RADAR_LIST_UPDATE_INTERVAL = 1.f;
|
||||
|
||||
/**
|
||||
* Periodically updates the nearby people list while the Nearby tab is active.
|
||||
*
|
||||
* The period is defined by FS_RADAR_LIST_UPDATE_INTERVAL constant.
|
||||
*/
|
||||
|
||||
constexpr F32 FS_RADAR_LIST_UPDATE_INTERVAL = 1.f;
|
||||
|
||||
class FSRadarListUpdater : public FSRadar::Updater, public LLEventTimer
|
||||
{
|
||||
LOG_CLASS(FSRadarListUpdater);
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
mEventTimer.start();
|
||||
}
|
||||
|
||||
/*virtual*/ bool tick()
|
||||
bool tick() override
|
||||
{
|
||||
update();
|
||||
return false;
|
||||
|
||||
@@ -86,13 +86,12 @@ public:
|
||||
class Updater
|
||||
{
|
||||
public:
|
||||
typedef std::function<void()> callback_t;
|
||||
using callback_t = std::function<void()>;
|
||||
Updater(callback_t cb)
|
||||
: mCallback(cb)
|
||||
{ }
|
||||
|
||||
virtual ~Updater()
|
||||
{ }
|
||||
virtual ~Updater() = default;
|
||||
|
||||
protected:
|
||||
void update()
|
||||
|
||||
@@ -551,6 +551,8 @@ bool FSVirtualTrackpad::handleMouseDown(S32 x, S32 y, MASK mask)
|
||||
{
|
||||
if (isPointInTouchArea(x, y))
|
||||
{
|
||||
mWheelClicksSinceLastDelta = 0;
|
||||
|
||||
determineThumbClickError(x, y);
|
||||
updateClickErrorIfInfiniteScrolling();
|
||||
gFocusMgr.setMouseCapture(this);
|
||||
@@ -605,6 +607,9 @@ bool FSVirtualTrackpad::handleScrollWheel(S32 x, S32 y, S32 clicks)
|
||||
|
||||
mWheelClicksSinceLastDelta = -1 * clicks * changeAmount;
|
||||
|
||||
mValueDeltaX = mValueDeltaY = mValueDeltaZ = 0;
|
||||
mPinchValueDeltaX = mPinchValueDeltaY = mPinchValueDeltaZ = 0;
|
||||
|
||||
if (mDoingPinchMode)
|
||||
{
|
||||
if ((mask & (MASK_SHIFT | MASK_ALT)) == MASK_ALT)
|
||||
|
||||
@@ -43,6 +43,48 @@
|
||||
constexpr F32 COLOR_DAMPENING = 0.8f;
|
||||
constexpr char CONTACT_SETS_FILE[] = "settings_friends_groups.xml";
|
||||
|
||||
namespace
|
||||
{
|
||||
struct contact_set_autoresponse_ref_t
|
||||
{
|
||||
bool* enabled;
|
||||
std::string* message;
|
||||
};
|
||||
struct const_contact_set_autoresponse_ref_t
|
||||
{
|
||||
const bool* enabled;
|
||||
const std::string* message;
|
||||
};
|
||||
contact_set_autoresponse_ref_t get_autoresponse_ref(LGGContactSets::ContactSet* set, ContactSetAutoresponseMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ContactSetAutoresponseMode::BUSY:
|
||||
return { &set->mAutoresponseBusyEnabled, &set->mAutoresponseBusy };
|
||||
case ContactSetAutoresponseMode::AUTORESPONSE:
|
||||
return { &set->mAutoresponseModeEnabled, &set->mAutoresponseMode };
|
||||
case ContactSetAutoresponseMode::AUTORESPONSE_NONFRIENDS:
|
||||
return { &set->mAutoresponseNonFriendsEnabled, &set->mAutoresponseNonFriends };
|
||||
}
|
||||
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
const_contact_set_autoresponse_ref_t get_autoresponse_ref(const LGGContactSets::ContactSet* set, ContactSetAutoresponseMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ContactSetAutoresponseMode::BUSY:
|
||||
return { &set->mAutoresponseBusyEnabled, &set->mAutoresponseBusy };
|
||||
case ContactSetAutoresponseMode::AUTORESPONSE:
|
||||
return { &set->mAutoresponseModeEnabled, &set->mAutoresponseMode };
|
||||
case ContactSetAutoresponseMode::AUTORESPONSE_NONFRIENDS:
|
||||
return { &set->mAutoresponseNonFriendsEnabled, &set->mAutoresponseNonFriends };
|
||||
}
|
||||
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
}
|
||||
|
||||
LGGContactSets::LGGContactSets()
|
||||
{
|
||||
}
|
||||
@@ -110,6 +152,13 @@ LLSD LGGContactSets::exportContactSet(std::string_view set_name)
|
||||
ret["groupname"] = set->mName;
|
||||
ret["color"] = set->mColor.getValue();
|
||||
ret["notices"] = set->mNotify;
|
||||
ret["sort_by_online_status"] = set->mSortByOnlineStatus;
|
||||
ret["autoresponse_busy_enabled"] = set->mAutoresponseBusyEnabled;
|
||||
ret["autoresponse_busy"] = set->mAutoresponseBusy;
|
||||
ret["autoresponse_mode_enabled"] = set->mAutoresponseModeEnabled;
|
||||
ret["autoresponse_mode"] = set->mAutoresponseMode;
|
||||
ret["autoresponse_nonfriends_enabled"] = set->mAutoresponseNonFriendsEnabled;
|
||||
ret["autoresponse_nonfriends"] = set->mAutoresponseNonFriends;
|
||||
for (const auto& friend_id : set->mFriends)
|
||||
{
|
||||
ret["friends"][friend_id.asString()] = "";
|
||||
@@ -212,6 +261,13 @@ LLSD LGGContactSets::exportToLLSD()
|
||||
{
|
||||
output[name]["color"] = set->mColor.getValue();
|
||||
output[name]["notify"] = set->mNotify;
|
||||
output[name]["sort_by_online_status"] = set->mSortByOnlineStatus;
|
||||
output[name]["autoresponse_busy_enabled"] = set->mAutoresponseBusyEnabled;
|
||||
output[name]["autoresponse_busy"] = set->mAutoresponseBusy;
|
||||
output[name]["autoresponse_mode_enabled"] = set->mAutoresponseModeEnabled;
|
||||
output[name]["autoresponse_mode"] = set->mAutoresponseMode;
|
||||
output[name]["autoresponse_nonfriends_enabled"] = set->mAutoresponseNonFriendsEnabled;
|
||||
output[name]["autoresponse_nonfriends"] = set->mAutoresponseNonFriends;
|
||||
for (const auto& friend_id : set->mFriends)
|
||||
{
|
||||
output[name]["friends"][friend_id.asString()] = "";
|
||||
@@ -276,6 +332,46 @@ void LGGContactSets::importFromLLSD(const LLSD& data)
|
||||
}
|
||||
new_set->mNotify = notify;
|
||||
|
||||
bool sort_by_online_status = false;
|
||||
if (set_data.has("sort_by_online_status"))
|
||||
{
|
||||
sort_by_online_status = set_data["sort_by_online_status"].asBoolean();
|
||||
}
|
||||
new_set->mSortByOnlineStatus = sort_by_online_status;
|
||||
|
||||
bool autoresponse_busy_enabled = false;
|
||||
if (set_data.has("autoresponse_busy_enabled"))
|
||||
{
|
||||
autoresponse_busy_enabled = set_data["autoresponse_busy_enabled"].asBoolean();
|
||||
}
|
||||
new_set->mAutoresponseBusyEnabled = autoresponse_busy_enabled;
|
||||
if (set_data.has("autoresponse_busy"))
|
||||
{
|
||||
new_set->mAutoresponseBusy = LLStringFn::strip_invalid_xml(set_data["autoresponse_busy"].asString());
|
||||
}
|
||||
|
||||
bool autoresponse_mode_enabled = false;
|
||||
if (set_data.has("autoresponse_mode_enabled"))
|
||||
{
|
||||
autoresponse_mode_enabled = set_data["autoresponse_mode_enabled"].asBoolean();
|
||||
}
|
||||
new_set->mAutoresponseModeEnabled = autoresponse_mode_enabled;
|
||||
if (set_data.has("autoresponse_mode"))
|
||||
{
|
||||
new_set->mAutoresponseMode = LLStringFn::strip_invalid_xml(set_data["autoresponse_mode"].asString());
|
||||
}
|
||||
|
||||
bool autoresponse_nonfriends_enabled = false;
|
||||
if (set_data.has("autoresponse_nonfriends_enabled"))
|
||||
{
|
||||
autoresponse_nonfriends_enabled = set_data["autoresponse_nonfriends_enabled"].asBoolean();
|
||||
}
|
||||
new_set->mAutoresponseNonFriendsEnabled = autoresponse_nonfriends_enabled;
|
||||
if (set_data.has("autoresponse_nonfriends"))
|
||||
{
|
||||
new_set->mAutoresponseNonFriends = LLStringFn::strip_invalid_xml(set_data["autoresponse_nonfriends"].asString());
|
||||
}
|
||||
|
||||
if (set_data.has("friends"))
|
||||
{
|
||||
for (const auto& [av_key, av_value] : llsd::inMap(set_data["friends"]))
|
||||
@@ -503,6 +599,7 @@ bool LGGContactSets::hasFriendColorThatShouldShow(const LLUUID& friend_id, Conta
|
||||
static LLCachedControl<bool> fsContactSetsColorizeTag(gSavedSettings,"FSContactSetsColorizeNameTag", false);
|
||||
static LLCachedControl<bool> fsContactSetsColorizeRadar(gSavedSettings,"FSContactSetsColorizeRadar", false);
|
||||
static LLCachedControl<bool> fsContactSetsColorizeMiniMap(gSavedSettings,"FSContactSetsColorizeMiniMap", false);
|
||||
static LLCachedControl<bool> fsContactSetsColorizeFriends(gSavedSettings,"FSContactSetsColorizeFriends", false);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
@@ -523,6 +620,10 @@ bool LGGContactSets::hasFriendColorThatShouldShow(const LLUUID& friend_id, Conta
|
||||
if (!fsContactSetsColorizeMiniMap)
|
||||
return false;
|
||||
break;
|
||||
case ContactSetType::FRIENDS:
|
||||
if (!fsContactSetsColorizeFriends)
|
||||
return false;
|
||||
break;
|
||||
};
|
||||
|
||||
/// don't show friend color if they are no longer a friend
|
||||
@@ -753,7 +854,7 @@ uuid_vec_t LGGContactSets::getListOfPseudonymAvs() const
|
||||
|
||||
for (const auto& [id, pseudonym] : mPseudonyms)
|
||||
{
|
||||
pseudonyms.emplace_back(pseudonym);
|
||||
pseudonyms.emplace_back(id);
|
||||
}
|
||||
|
||||
return pseudonyms;
|
||||
@@ -929,8 +1030,15 @@ void LGGContactSets::addSet(std::string_view set_name)
|
||||
{
|
||||
ContactSet* set = new ContactSet();
|
||||
set->mName = set_name;
|
||||
set->mColor = LLColor4::red;
|
||||
set->mColor = getDefaultColor();
|
||||
set->mNotify = false;
|
||||
set->mSortByOnlineStatus = false;
|
||||
set->mAutoresponseBusyEnabled = false;
|
||||
set->mAutoresponseBusy.clear();
|
||||
set->mAutoresponseModeEnabled = false;
|
||||
set->mAutoresponseMode.clear();
|
||||
set->mAutoresponseNonFriendsEnabled = false;
|
||||
set->mAutoresponseNonFriends.clear();
|
||||
mContactSets[set_name.data()] = set;
|
||||
saveToDisk();
|
||||
mChangedSignal(UPDATED_LISTS);
|
||||
@@ -1002,6 +1110,86 @@ bool LGGContactSets::getNotifyForSet(std::string_view set_name) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void LGGContactSets::setSortByOnlineStatusForSet(std::string_view set_name, bool sort_by_online_status)
|
||||
{
|
||||
ContactSet* set = getContactSet(set_name);
|
||||
if (set)
|
||||
{
|
||||
set->mSortByOnlineStatus = sort_by_online_status;
|
||||
saveToDisk();
|
||||
mChangedSignal(UPDATED_MEMBERS);
|
||||
}
|
||||
}
|
||||
|
||||
bool LGGContactSets::getSortByOnlineStatusForSet(std::string_view set_name) const
|
||||
{
|
||||
if (ContactSet* set = getContactSet(set_name); set)
|
||||
{
|
||||
return set->mSortByOnlineStatus;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LGGContactSets::setAutoresponseForSet(std::string_view set_name, ContactSetAutoresponseMode mode, bool enabled, std::string_view response)
|
||||
{
|
||||
if (ContactSet* set = getContactSet(set_name); set)
|
||||
{
|
||||
contact_set_autoresponse_ref_t autoresponse = get_autoresponse_ref(set, mode);
|
||||
if (autoresponse.enabled && autoresponse.message)
|
||||
{
|
||||
*autoresponse.enabled = enabled;
|
||||
*autoresponse.message = LLStringFn::strip_invalid_xml(std::string(response));
|
||||
saveToDisk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LGGContactSets::getAutoresponseForSet(std::string_view set_name, ContactSetAutoresponseMode mode, bool& enabled, std::string& response) const
|
||||
{
|
||||
enabled = false;
|
||||
response.clear();
|
||||
if (const ContactSet* set = getContactSet(set_name); set)
|
||||
{
|
||||
const_contact_set_autoresponse_ref_t autoresponse = get_autoresponse_ref(set, mode);
|
||||
if (autoresponse.enabled && autoresponse.message)
|
||||
{
|
||||
enabled = *autoresponse.enabled;
|
||||
response = *autoresponse.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LGGContactSets::getAutoresponseForFriend(const LLUUID& friend_id, ContactSetAutoresponseMode mode, std::string& response) const
|
||||
{
|
||||
response.clear();
|
||||
U32 best_set_size = U32_MAX;
|
||||
bool found = false;
|
||||
for (const auto& set_name : getFriendSets(friend_id))
|
||||
{
|
||||
const ContactSet* set = getContactSet(set_name);
|
||||
if (!set)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const_contact_set_autoresponse_ref_t autoresponse = get_autoresponse_ref(set, mode);
|
||||
if (!autoresponse.enabled || !autoresponse.message || !(*autoresponse.enabled) || autoresponse.message->empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const U32 set_size = static_cast<U32>(set->mFriends.size());
|
||||
if (!found || set_size < best_set_size)
|
||||
{
|
||||
response = *autoresponse.message;
|
||||
best_set_size = set_size;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void LGGContactSets::setSetColor(std::string_view set_name, const LLColor4& color)
|
||||
{
|
||||
if (ContactSet* set = getContactSet(set_name); set)
|
||||
@@ -1100,8 +1288,20 @@ bool LGGContactSets::handleSetAvatarPseudonymCallback(const LLSD& notification,
|
||||
if (S32 option = LLNotificationsUtil::getSelectedOption(notification, response); option == 0)
|
||||
{
|
||||
const std::string pseudonym(response["message"].asString());
|
||||
const LLUUID id(notification["payload"]["id"].asUUID());
|
||||
LGGContactSets::getInstance()->setPseudonym(id, pseudonym);
|
||||
LGGContactSets* contact_sets = LGGContactSets::getInstance();
|
||||
if (notification["payload"].has("ids"))
|
||||
{
|
||||
for (const auto& item : llsd::inArray(notification["payload"]["ids"]))
|
||||
{
|
||||
const LLUUID id(item.asUUID());
|
||||
contact_sets->setPseudonym(id, pseudonym);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const LLUUID id(notification["payload"]["id"].asUUID());
|
||||
contact_sets->setPseudonym(id, pseudonym);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,15 @@ enum class ContactSetType
|
||||
IM,
|
||||
TAG,
|
||||
RADAR,
|
||||
MINIMAP
|
||||
MINIMAP,
|
||||
FRIENDS
|
||||
};
|
||||
|
||||
enum class ContactSetAutoresponseMode
|
||||
{
|
||||
BUSY,
|
||||
AUTORESPONSE,
|
||||
AUTORESPONSE_NONFRIENDS
|
||||
};
|
||||
|
||||
constexpr char CS_SET_ALL_SETS[] = "All Sets";
|
||||
@@ -93,12 +101,18 @@ public:
|
||||
void removeNonFriendFromList(const LLUUID& non_friend_id, bool save_changes = true);
|
||||
bool isNonFriend(const LLUUID& non_friend_id) const;
|
||||
bool isFriendInAnySet(const LLUUID& friend_id) const;
|
||||
uuid_vec_t getFriendsInAnySet() const;
|
||||
uuid_vec_t getListOfNonFriends() const;
|
||||
uuid_vec_t getListOfPseudonymAvs() const;
|
||||
|
||||
bool notifyForFriend(const LLUUID& friend_id) const;
|
||||
void setNotifyForSet(std::string_view set_name, bool notify);
|
||||
bool getNotifyForSet(std::string_view set_name) const;
|
||||
void setSortByOnlineStatusForSet(std::string_view set_name, bool sort_by_online_status);
|
||||
bool getSortByOnlineStatusForSet(std::string_view set_name) const;
|
||||
void setAutoresponseForSet(std::string_view set_name, ContactSetAutoresponseMode mode, bool enabled, std::string_view response);
|
||||
void getAutoresponseForSet(std::string_view set_name, ContactSetAutoresponseMode mode, bool& enabled, std::string& response) const;
|
||||
bool getAutoresponseForFriend(const LLUUID& friend_id, ContactSetAutoresponseMode mode, std::string& response) const;
|
||||
|
||||
bool callbackAliasReset(const LLSD& notification, const LLSD& response);
|
||||
|
||||
@@ -116,7 +130,14 @@ public:
|
||||
std::string mName;
|
||||
uuid_set_t mFriends;
|
||||
bool mNotify;
|
||||
bool mSortByOnlineStatus;
|
||||
LLColor4 mColor;
|
||||
bool mAutoresponseBusyEnabled;
|
||||
std::string mAutoresponseBusy;
|
||||
bool mAutoresponseModeEnabled;
|
||||
std::string mAutoresponseMode;
|
||||
bool mAutoresponseNonFriendsEnabled;
|
||||
std::string mAutoresponseNonFriends;
|
||||
};
|
||||
ContactSet* getContactSet(std::string_view set_name) const;
|
||||
|
||||
@@ -142,7 +163,6 @@ public:
|
||||
private:
|
||||
void toneDownColor(LLColor4& color) const;
|
||||
uuid_vec_t getFriendsInSet(std::string_view set_name) const;
|
||||
uuid_vec_t getFriendsInAnySet() const;
|
||||
|
||||
void setPseudonym(const LLUUID& friend_id, std::string_view pseudonym);
|
||||
bool hasVisuallyDifferentPseudonym(const LLUUID& friend_id) const;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Enhanced Firestorm Viewer Installation Script
|
||||
# Features:
|
||||
# - Creates a timestamped backup of existing installation
|
||||
# - Allows installation with custom suffixes for parallel versions (e.g., beta, release, debug)
|
||||
# - Supports both system-wide and user-specific installations
|
||||
# - Provides command-line options for flexibility
|
||||
# Firestorm Viewer Installation Script
|
||||
|
||||
# ANSI color codes for styling
|
||||
VT102_STYLE_NORMAL='\E[0m'
|
||||
@@ -20,8 +15,8 @@ tarball_path="${RUN_PATH}"
|
||||
DEFAULT_SYSTEM_INSTALL_DIR="/opt/firestorm"
|
||||
DEFAULT_USER_INSTALL_DIR="$HOME/firestorm"
|
||||
|
||||
# Backup directory base
|
||||
BACKUP_BASE_DIR="${DEFAULT_SYSTEM_INSTALL_DIR}_backups"
|
||||
# Default number of retained backups
|
||||
DEFAULT_RETAIN_BACKUPS=5
|
||||
|
||||
# Function to display usage information
|
||||
usage() {
|
||||
@@ -30,33 +25,12 @@ usage() {
|
||||
echo "Options:"
|
||||
echo " --suffix <suffix> Append a suffix to the installation directory (e.g., beta, release, debug)"
|
||||
echo " --install-dir <directory> Specify a custom installation directory"
|
||||
echo " --retain <count> Number of backup versions to keep (default: ${DEFAULT_RETAIN_BACKUPS})"
|
||||
echo " --yes, -y Non-interactive mode (accept defaults)"
|
||||
echo " --help, -h Display this help message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Function to prompt the user with a yes/no question
|
||||
prompt() {
|
||||
local prompt_message="$1"
|
||||
local input
|
||||
|
||||
echo -n "$prompt_message"
|
||||
|
||||
while read -r input; do
|
||||
case "$input" in
|
||||
[Yy]* )
|
||||
return 1
|
||||
;;
|
||||
[Nn]* )
|
||||
return 0
|
||||
;;
|
||||
* )
|
||||
echo "Please enter yes or no."
|
||||
echo -n "$prompt_message"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Function to display warning messages in red
|
||||
warn() {
|
||||
echo -e "${VT102_COLOR_RED}$1${VT102_STYLE_NORMAL}"
|
||||
@@ -79,6 +53,38 @@ backup_previous_installation() {
|
||||
echo " - Backup created successfully."
|
||||
}
|
||||
|
||||
# Function to remove old backups beyond configured retention
|
||||
prune_old_backups() {
|
||||
local install_dir="$1"
|
||||
local retain="$2"
|
||||
local backups=()
|
||||
local i
|
||||
local path
|
||||
|
||||
shopt -s nullglob
|
||||
for path in "${install_dir}"-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]_[0-9][0-9][0-9][0-9][0-9][0-9]; do
|
||||
[[ -e "$path" ]] || continue
|
||||
backups+=("$path")
|
||||
done
|
||||
shopt -u nullglob
|
||||
|
||||
if (( ${#backups[@]} > 0 )); then
|
||||
mapfile -t backups < <(printf '%s\n' "${backups[@]}" | sort -r)
|
||||
fi
|
||||
|
||||
echo " - Backup retention: found ${#backups[@]} backups, retaining ${retain}"
|
||||
|
||||
# Nothing to prune.
|
||||
if (( ${#backups[@]} <= retain )); then
|
||||
return
|
||||
fi
|
||||
|
||||
for (( i=retain; i<${#backups[@]}; i++ )); do
|
||||
echo " - Removing old backup: ${backups[i]}"
|
||||
rm -rf -- "${backups[i]}" || die "Failed to remove old backup: ${backups[i]}"
|
||||
done
|
||||
}
|
||||
|
||||
# Function to install Firestorm Viewer to the specified directory
|
||||
install_to_prefix() {
|
||||
local install_prefix="$1"
|
||||
@@ -88,9 +94,18 @@ install_to_prefix() {
|
||||
backup_previous_installation "$install_prefix"
|
||||
fi
|
||||
|
||||
# Run retention exactly once per invocation.
|
||||
# This still cleans pre-existing backup clutter when no current install exists.
|
||||
prune_old_backups "$install_prefix" "$RETAIN_BACKUPS"
|
||||
|
||||
# Create the installation directory
|
||||
mkdir -p "$install_prefix" || die "Failed to create installation directory: $install_prefix"
|
||||
|
||||
# Prevent recursive copy if install_prefix is inside tarball_path
|
||||
if [[ "$(readlink -f "$install_prefix")" == "$(readlink -f "$tarball_path")"* ]]; then
|
||||
die "Cannot install into a subdirectory of the source package."
|
||||
fi
|
||||
|
||||
echo " - Installing Firestorm Viewer to $install_prefix"
|
||||
|
||||
# Copy all files from the tarball to the installation directory
|
||||
@@ -106,15 +121,21 @@ homedir_install() {
|
||||
warn "this script as the root user, or with the 'sudo' command."
|
||||
echo
|
||||
|
||||
local install_prefix="$DEFAULT_USER_INSTALL_DIR"
|
||||
if [[ -n "$SUFFIX" ]]; then
|
||||
install_prefix="${install_prefix}_${SUFFIX}"
|
||||
local install_prefix
|
||||
if [[ -n "$CUSTOM_INSTALL_DIR" ]]; then
|
||||
install_prefix="$CUSTOM_INSTALL_DIR"
|
||||
elif [[ -n "$SUFFIX" ]]; then
|
||||
install_prefix="${DEFAULT_USER_INSTALL_DIR}_${SUFFIX}"
|
||||
else
|
||||
install_prefix="$DEFAULT_USER_INSTALL_DIR"
|
||||
fi
|
||||
|
||||
echo -n "Enter the desired installation directory [${install_prefix}]: "
|
||||
read -r user_input
|
||||
if [[ -n "$user_input" ]]; then
|
||||
install_prefix="$user_input"
|
||||
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
echo -n "Enter the desired installation directory [${install_prefix}]: "
|
||||
read -r user_input
|
||||
if [[ -n "$user_input" ]]; then
|
||||
install_prefix="$user_input"
|
||||
fi
|
||||
fi
|
||||
|
||||
install_to_prefix "${install_prefix}"
|
||||
@@ -134,10 +155,12 @@ root_install() {
|
||||
local install_prefix="$default_prefix"
|
||||
fi
|
||||
|
||||
echo -n "Enter the desired installation directory [${install_prefix}]: "
|
||||
read -r user_input
|
||||
if [[ -n "$user_input" ]]; then
|
||||
install_prefix="$user_input"
|
||||
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||
echo -n "Enter the desired installation directory [${install_prefix}]: "
|
||||
read -r user_input
|
||||
if [[ -n "$user_input" ]]; then
|
||||
install_prefix="$user_input"
|
||||
fi
|
||||
fi
|
||||
|
||||
install_to_prefix "$install_prefix"
|
||||
@@ -168,6 +191,18 @@ parse_arguments() {
|
||||
die "Error: --install-dir requires a non-empty option argument."
|
||||
fi
|
||||
;;
|
||||
--retain)
|
||||
if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
|
||||
RETAIN_BACKUPS="$2"
|
||||
shift 2
|
||||
else
|
||||
die "Error: --retain requires a non-negative integer argument."
|
||||
fi
|
||||
;;
|
||||
--yes|-y)
|
||||
NON_INTERACTIVE="true"
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
usage
|
||||
;;
|
||||
@@ -178,43 +213,23 @@ parse_arguments() {
|
||||
done
|
||||
}
|
||||
|
||||
# Function to download Firestorm Viewer (optional enhancement)
|
||||
download_firestorm() {
|
||||
# Define Firestorm Viewer download URL
|
||||
# NOTE: Replace this URL with the actual download link as needed
|
||||
FIRESTORM_URL="https://firestormviewer.org/download/linux/firestorm.tar.gz"
|
||||
|
||||
# Temporary download location
|
||||
TEMP_DOWNLOAD="/tmp/firestorm.tar.gz"
|
||||
|
||||
echo "Downloading Firestorm Viewer from $FIRESTORM_URL..."
|
||||
wget -O "$TEMP_DOWNLOAD" "$FIRESTORM_URL" || die "Failed to download Firestorm Viewer!"
|
||||
echo "Download completed."
|
||||
|
||||
# Extract the tarball to the run path
|
||||
tar -xzf "$TEMP_DOWNLOAD" -C "$tarball_path" --strip-components=1 || die "Failed to extract Firestorm Viewer tarball!"
|
||||
echo "Extraction completed."
|
||||
|
||||
# Clean up the downloaded tarball
|
||||
rm "$TEMP_DOWNLOAD"
|
||||
}
|
||||
|
||||
# Main installation workflow
|
||||
main() {
|
||||
echo "Starting Firestorm Viewer installation script..."
|
||||
|
||||
RETAIN_BACKUPS="${DEFAULT_RETAIN_BACKUPS}"
|
||||
NON_INTERACTIVE="false"
|
||||
|
||||
# Parse command-line arguments
|
||||
parse_arguments "$@"
|
||||
|
||||
echo "Installation Options:"
|
||||
echo " Suffix: ${SUFFIX:-None}"
|
||||
echo " Custom Install Directory: ${CUSTOM_INSTALL_DIR:-None}"
|
||||
echo " Retained Backups: ${RETAIN_BACKUPS}"
|
||||
echo " Non-interactive: ${NON_INTERACTIVE}"
|
||||
echo
|
||||
|
||||
# Optionally, download Firestorm Viewer if not already present
|
||||
# Uncomment the following line if you want the script to handle downloading
|
||||
# download_firestorm
|
||||
|
||||
# Determine if the script is run as root
|
||||
if [[ "$EUID" -eq 0 ]]; then
|
||||
root_install
|
||||
|
||||
@@ -3562,6 +3562,9 @@ bool LLAppViewer::initConfiguration()
|
||||
// </FS:Ansariel>
|
||||
{
|
||||
// successfully handed off URL to existing instance, exit
|
||||
// <FS:PP> FIRE-36191: Mark this helper process for clean shutdown so platform startup code can exit without treating this path as a launch failure
|
||||
forceQuit();
|
||||
// </FS:PP>
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,12 @@ int main( int argc, char **argv )
|
||||
bool ok = viewer_app_ptr->init();
|
||||
if(!ok)
|
||||
{
|
||||
// <FS:PP> FIRE-36191: A secondary helper process can intentionally quit after handing an external SLURL to the already-running viewer instance
|
||||
if (LLApp::isQuitting())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// </FS:PP>
|
||||
LL_WARNS() << "Application init failed." << LL_ENDL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,13 @@ bool initViewer()
|
||||
bool ok = gViewerAppPtr->init();
|
||||
if(!ok)
|
||||
{
|
||||
LL_WARNS("InitOSX") << "Application init failed." << LL_ENDL;
|
||||
// <FS:PP> FIRE-36191: A secondary helper process can intentionally quit after handing an external SLURL to the already-running viewer instance
|
||||
// LL_WARNS("InitOSX") << "Application init failed." << LL_ENDL;
|
||||
if (!LLApp::isQuitting())
|
||||
{
|
||||
LL_WARNS("InitOSX") << "Application init failed." << LL_ENDL;
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
else if (!gHandleSLURL.empty())
|
||||
{
|
||||
|
||||
@@ -544,6 +544,12 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
|
||||
bool ok = viewer_app_ptr->init();
|
||||
if (!ok)
|
||||
{
|
||||
// <FS:PP> FIRE-36191: A secondary helper process can intentionally quit after handing an external SLURL to the already-running viewer instance
|
||||
if (LLApp::isQuitting())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// </FS:PP>
|
||||
LL_WARNS() << "Application init failed." << LL_ENDL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,22 @@
|
||||
|
||||
const char* LLAutoReplace::SETTINGS_FILE_NAME = "autoreplace.xml";
|
||||
|
||||
// <FS:PP> FIRE-36304 Allow some punctuation in autoreplace keywords
|
||||
namespace
|
||||
{
|
||||
bool isAutoReplaceKeywordChar(llwchar character)
|
||||
{
|
||||
return LLWStringUtil::isPartOfWord(character)
|
||||
|| character == L'('
|
||||
|| character == L')'
|
||||
|| character == L'.'
|
||||
|| character == L','
|
||||
|| character == L'-'
|
||||
|| character == L'_';
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement_length, LLWString& replacement_string, S32& cursor_pos, const LLWString& input_text)
|
||||
{
|
||||
// make sure these returned values are cleared in case there is no replacement
|
||||
@@ -46,7 +62,10 @@ void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement
|
||||
if (word_end < 0 ) { word_end = 0; }; // <FS:Beq/> FIRE-34765
|
||||
|
||||
bool at_space = (input_text[word_end] == ' ');
|
||||
bool have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
|
||||
// <FS:PP> FIRE-36304 Allow some punctuation in autoreplace keywords
|
||||
// bool have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
|
||||
bool have_word = isAutoReplaceKeywordChar(input_text[word_end]);
|
||||
// </FS:PP>
|
||||
|
||||
if (at_space || have_word)
|
||||
{
|
||||
@@ -54,7 +73,10 @@ void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement
|
||||
{
|
||||
// find out if this space immediately follows a word
|
||||
word_end--;
|
||||
have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
|
||||
// <FS:PP> FIRE-36304 Allow some punctuation in autoreplace keywords
|
||||
// have_word = (LLWStringUtil::isPartOfWord(input_text[word_end]));
|
||||
have_word = isAutoReplaceKeywordChar(input_text[word_end]);
|
||||
// </FS:PP>
|
||||
}
|
||||
if (have_word)
|
||||
{
|
||||
@@ -62,7 +84,10 @@ void LLAutoReplace::autoreplaceCallback(S32& replacement_start, S32& replacement
|
||||
std::string word;
|
||||
S32 word_start = word_end;
|
||||
for (S32 back_one = word_start - 1;
|
||||
back_one >= 0 && LLWStringUtil::isPartOfWord(input_text[back_one]);
|
||||
// <FS:PP> FIRE-36304 Allow some punctuation in autoreplace keywords
|
||||
// back_one >= 0 && LLWStringUtil::isPartOfWord(input_text[back_one]);
|
||||
back_one >= 0 && isAutoReplaceKeywordChar(input_text[back_one]);
|
||||
// </FS:PP>
|
||||
back_one--
|
||||
)
|
||||
{
|
||||
@@ -717,7 +742,10 @@ bool LLAutoReplaceSettings::addEntryToList(LLWString keyword, LLWString replacem
|
||||
bool isOneWord = true;
|
||||
for (S32 character = 0; isOneWord && character < keyword.size(); character++ )
|
||||
{
|
||||
if ( ! LLWStringUtil::isPartOfWord(keyword[character]) )
|
||||
// <FS:PP> FIRE-36304 Allow some punctuation in autoreplace keywords
|
||||
// if ( ! LLWStringUtil::isPartOfWord(keyword[character]) )
|
||||
if ( ! isAutoReplaceKeywordChar(keyword[character]) )
|
||||
// </FS:PP>
|
||||
{
|
||||
LL_WARNS("AutoReplace") << "keyword '" << wstring_to_utf8str(keyword) << "' not a single word (len "<<keyword.size()<<" '"<<character<<"')" << LL_ENDL;
|
||||
isOneWord = false;
|
||||
|
||||
@@ -1634,6 +1634,31 @@ void LLAvatarActions::addToContactSet(const uuid_vec_t& agent_ids)
|
||||
LLFloaterReg::showInstance("fs_add_contact", data, true);
|
||||
}
|
||||
}
|
||||
|
||||
void LLAvatarActions::moveToContactSet(const uuid_vec_t& agent_ids, const std::string& source_set)
|
||||
{
|
||||
if (agent_ids.empty() || source_set.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLSD data;
|
||||
data["operation"] = "move";
|
||||
data["source_set"] = source_set;
|
||||
if (agent_ids.size() == 1)
|
||||
{
|
||||
data["id"] = agent_ids.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const LLUUID& id : agent_ids)
|
||||
{
|
||||
data["ids"].append(id);
|
||||
}
|
||||
}
|
||||
|
||||
LLFloaterReg::showInstance("fs_add_contact", data, true);
|
||||
}
|
||||
// [/FS:CR] Add to contact set
|
||||
|
||||
//== private methods ========================================================================================
|
||||
|
||||
@@ -223,6 +223,7 @@ public:
|
||||
*/
|
||||
static void addToContactSet(const LLUUID& agent_id);
|
||||
static void addToContactSet(const uuid_vec_t& agent_ids);
|
||||
static void moveToContactSet(const uuid_vec_t& agent_ids, const std::string& source_set);
|
||||
|
||||
/**
|
||||
* Checks whether we can offer a teleport to the avatar, only offline friends
|
||||
|
||||
@@ -41,12 +41,14 @@
|
||||
#include "llavatarnamecache.h"
|
||||
#include "llcallingcard.h" // for LLAvatarTracker
|
||||
#include "llcachename.h"
|
||||
#include "llinventory.h" // <FS:PP> FIRE-31146 Contact Sets - drag-and-drop support
|
||||
#include "lllistcontextmenu.h"
|
||||
#include "llrecentpeople.h"
|
||||
#include "lluuid.h"
|
||||
#include "llvoiceclient.h"
|
||||
#include "llviewercontrol.h" // for gSavedSettings
|
||||
#include "lltooldraganddrop.h"
|
||||
#include "lggcontactsets.h" // <FS:PP> FIRE-36478: Ignore alias quotation marks when comparing names from contact sets
|
||||
// [RLVa:KB] - Checked: 2010-06-04 (RLVa-1.2.2a)
|
||||
#include "rlvhandler.h"
|
||||
#include "rlvactions.h"
|
||||
@@ -193,6 +195,8 @@ LLAvatarList::LLAvatarList(const Params& p)
|
||||
, mShowVoiceVolume(p.show_voice_volume)
|
||||
, mShowUsername(gSavedSettings.getBOOL("NameTagShowUsernames"))
|
||||
, mShowDisplayName(gSavedSettings.getBOOL("UseDisplayNames"))
|
||||
, mUseContactSetColors(false) // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
, mUseContactSetListStyle(false) // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
{
|
||||
setCommitOnSelectionChange(true);
|
||||
|
||||
@@ -211,10 +215,26 @@ LLAvatarList::LLAvatarList(const Params& p)
|
||||
// <FS:Ansariel> FIRE-1089: List needs to update also if we change the username setting
|
||||
gSavedSettings.getControl("NameTagShowUsernames")->getSignal()->connect(boost::bind(&LLAvatarList::handleDisplayNamesOptionChanged, this));
|
||||
|
||||
// <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
gSavedSettings.getControl("FSContactSetsColorizeFriends")->getSignal()->connect(boost::bind(&LLAvatarList::refreshNames, this));
|
||||
|
||||
// <FS:Ansariel> Update voice volume slider on RLVa shownames restriction update
|
||||
mRlvBehaviorCallbackConnection = gRlvHandler.setBehaviourCallback(boost::bind(&LLAvatarList::updateRlvRestrictions, this, _1, _2));
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
void LLAvatarList::setUseContactSetColors(bool use_colors)
|
||||
{
|
||||
mUseContactSetColors = use_colors;
|
||||
mNeedUpdateNames = true;
|
||||
}
|
||||
|
||||
void LLAvatarList::setUseContactSetListStyle(bool use_style)
|
||||
{
|
||||
mUseContactSetListStyle = use_style;
|
||||
mNeedUpdateNames = true;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLAvatarList::handleDisplayNamesOptionChanged()
|
||||
{
|
||||
@@ -494,6 +514,8 @@ void LLAvatarList::updateAvatarNames()
|
||||
for( std::vector<LLPanel*>::const_iterator it = items.begin(); it != items.end(); it++)
|
||||
{
|
||||
LLAvatarListItem* item = static_cast<LLAvatarListItem*>(*it);
|
||||
item->setUseContactSetColors(mUseContactSetColors); // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
item->setUseContactSetListStyle(mUseContactSetListStyle); // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
item->setShowCompleteName(mShowCompleteName, mForceCompleteName);
|
||||
item->updateAvatarName();
|
||||
}
|
||||
@@ -594,6 +616,8 @@ void LLAvatarList::addNewItem(const LLUUID& id, const std::string& name, bool is
|
||||
item->setShowPermissions(mShowPermissions);
|
||||
item->showUsername(mShowUsername);
|
||||
item->showDisplayName(mShowDisplayName);
|
||||
item->setUseContactSetColors(mUseContactSetColors); // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
item->setUseContactSetListStyle(mUseContactSetListStyle); // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
|
||||
item->setDoubleClickCallback(boost::bind(&LLAvatarList::onItemDoubleClicked, this, _1, _2, _3, _4));
|
||||
item->setMouseDownCallback(boost::bind(&LLAvatarList::onItemClicked, this, _1, _2, _3, _4));
|
||||
@@ -668,6 +692,46 @@ bool LLAvatarList::handleHover(S32 x, S32 y, MASK mask)
|
||||
return handled;
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-31146 Contact Sets - drag-and-drop support
|
||||
bool LLAvatarList::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg)
|
||||
{
|
||||
if (!mAvatarDropCallback)
|
||||
{
|
||||
return LLFlatListViewEx::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
|
||||
}
|
||||
|
||||
LLUUID avatar_id;
|
||||
if (cargo_type == DAD_PERSON)
|
||||
{
|
||||
if (cargo_data)
|
||||
{
|
||||
avatar_id = *static_cast<LLUUID*>(cargo_data);
|
||||
}
|
||||
}
|
||||
else if (cargo_type == DAD_CALLINGCARD)
|
||||
{
|
||||
if (LLInventoryItem* item = static_cast<LLInventoryItem*>(cargo_data); item)
|
||||
{
|
||||
avatar_id = item->getCreatorUUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*accept = ACCEPT_NO;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (avatar_id.isNull())
|
||||
{
|
||||
*accept = ACCEPT_NO;
|
||||
return true;
|
||||
}
|
||||
|
||||
*accept = mAvatarDropCallback(avatar_id, drop) ? ACCEPT_YES_MULTI : ACCEPT_NO;
|
||||
return true;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLAvatarList::setVisible(bool visible)
|
||||
{
|
||||
if (!visible && mContextMenu )
|
||||
@@ -763,14 +827,32 @@ bool LLAvatarItemComparator::compare(const LLPanel* item1, const LLPanel* item2)
|
||||
|
||||
bool LLAvatarItemNameComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
|
||||
{
|
||||
std::string name1 = avatar_item1->getAvatarName();
|
||||
std::string name2 = avatar_item2->getAvatarName();
|
||||
// <FS:PP> FIRE-36478: Ignore alias quotation marks when comparing names from contact sets
|
||||
// std::string name1 = avatar_item1->getAvatarName();
|
||||
// std::string name2 = avatar_item2->getAvatarName();
|
||||
|
||||
LLStringUtil::toUpper(name1);
|
||||
LLStringUtil::toUpper(name2);
|
||||
// LLStringUtil::toUpper(name1);
|
||||
// LLStringUtil::toUpper(name2);
|
||||
|
||||
std::string name1 = getComparableName(avatar_item1);
|
||||
std::string name2 = getComparableName(avatar_item2);
|
||||
// </FS:PP>
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-36478: Ignore alias quotation marks when comparing names from contact sets
|
||||
std::string LLAvatarItemComparator::getComparableName(const LLAvatarListItem* avatar_item)
|
||||
{
|
||||
std::string name = avatar_item->getAvatarName();
|
||||
if (LGGContactSets::getInstance()->hasPseudonym(avatar_item->getAvatarId()) && !name.empty() && (name.front() == '\'' || name.front() == '"'))
|
||||
{
|
||||
name.erase(0, 1);
|
||||
}
|
||||
LLStringUtil::toUpper(name);
|
||||
return name;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
bool LLAvatarItemAgentOnTopComparator::doCompare(const LLAvatarListItem* avatar_item1, const LLAvatarListItem* avatar_item2) const
|
||||
{
|
||||
//keep agent on top, if first is agent,
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#ifndef LL_LLAVATARLIST_H
|
||||
#define LL_LLAVATARLIST_H
|
||||
|
||||
#include <functional> // <FS:PP> FIRE-31146 Contact Sets - drag-and-drop support
|
||||
|
||||
#include "llflatlistview.h"
|
||||
#include "llavatarlistitem.h"
|
||||
#include "rlvdefines.h"
|
||||
@@ -48,6 +50,8 @@ class LLAvatarList : public LLFlatListViewEx
|
||||
{
|
||||
LOG_CLASS(LLAvatarList);
|
||||
public:
|
||||
using avatar_drop_cb_t = std::function<bool(const LLUUID&, bool)>; // <FS:PP> FIRE-31146 Contact Sets - drag-and-drop support
|
||||
|
||||
struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
|
||||
{
|
||||
Optional<bool> ignore_online_status, // show all items as online
|
||||
@@ -85,6 +89,8 @@ public:
|
||||
void showDisplayName(bool visible);
|
||||
void showUsername(bool visible);
|
||||
void showVoiceVolume(bool visible);
|
||||
void setUseContactSetColors(bool use_colors); // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
void setUseContactSetListStyle(bool use_style); // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
// <FS:Ansariel> [FS Communication UI]
|
||||
//void sortByName();
|
||||
void sortByName(bool agent_on_top = false);
|
||||
@@ -100,6 +106,11 @@ public:
|
||||
/*virtual*/ bool handleMouseUp(S32 x, S32 y, MASK mask);
|
||||
/*virtual*/ bool handleHover(S32 x, S32 y, MASK mask);
|
||||
|
||||
// <FS:PP> FIRE-31146 Contact Sets - drag-and-drop support
|
||||
bool handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop, EDragAndDropType cargo_type, void* cargo_data, EAcceptance* accept, std::string& tooltip_msg);
|
||||
void setAvatarDropCallback(const avatar_drop_cb_t& cb) { mAvatarDropCallback = cb; }
|
||||
// </FS:PP>
|
||||
|
||||
// Return true if filter has at least one match.
|
||||
bool filterHasMatches();
|
||||
|
||||
@@ -161,6 +172,8 @@ private:
|
||||
// [/RLVa:KB]
|
||||
bool mShowDisplayName;
|
||||
bool mShowUsername;
|
||||
bool mUseContactSetColors; // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
bool mUseContactSetListStyle; // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
|
||||
LLTimer* mLITUpdateTimer; // last interaction time update timer
|
||||
std::string mIconParamName;
|
||||
@@ -173,6 +186,7 @@ private:
|
||||
commit_signal_t mRefreshCompleteSignal;
|
||||
mouse_signal_t mItemDoubleClickSignal;
|
||||
mouse_signal_t mItemClickedSignal;
|
||||
avatar_drop_cb_t mAvatarDropCallback; // <FS:PP> FIRE-31146 Contact Sets - drag-and-drop support
|
||||
|
||||
// <FS:Ansariel> Update voice volume slider on RLVa shownames restriction update
|
||||
boost::signals2::connection mRlvBehaviorCallbackConnection;
|
||||
@@ -192,6 +206,7 @@ public:
|
||||
virtual bool compare(const LLPanel* item1, const LLPanel* item2) const;
|
||||
|
||||
protected:
|
||||
static std::string getComparableName(const LLAvatarListItem* avatar_item); // <FS:PP> FIRE-36478: Ignore alias quotation marks when comparing names from contact sets
|
||||
|
||||
/**
|
||||
* Returns true if avatar_item1 < avatar_item2, false otherwise
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
#include "lfsimfeaturehandler.h" // <FS:CR> Opensim
|
||||
#include "llavatarlist.h"
|
||||
#include "lggcontactsets.h" // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
|
||||
bool LLAvatarListItem::sStaticInitialized = false;
|
||||
S32 LLAvatarListItem::sLeftPadding = 0;
|
||||
@@ -299,6 +300,20 @@ void LLAvatarListItem::setAvatarName(const std::string& name)
|
||||
setNameInternal(name, mHighlihtSubstring);
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
void LLAvatarListItem::setUseContactSetColors(bool use_colors)
|
||||
{
|
||||
mUseContactSetColors = use_colors;
|
||||
setNameInternal(mAvatarName->getText(), mHighlihtSubstring);
|
||||
}
|
||||
|
||||
void LLAvatarListItem::setUseContactSetListStyle(bool use_style)
|
||||
{
|
||||
mUseContactSetListStyle = use_style;
|
||||
setNameInternal(mAvatarName->getText(), mHighlihtSubstring);
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLAvatarListItem::setAvatarToolTip(const std::string& tooltip)
|
||||
{
|
||||
mAvatarName->setToolTip(tooltip);
|
||||
@@ -586,7 +601,28 @@ void LLAvatarListItem::setNameInternal(const std::string& name, const std::strin
|
||||
//{
|
||||
// LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle, name, highlight);
|
||||
//}
|
||||
LLTextUtil::textboxSetHighlightedVal(mAvatarName, mAvatarNameStyle, name, highlight);
|
||||
LLStyle::Params avatar_name_style = mAvatarNameStyle;
|
||||
if (mUseContactSetListStyle)
|
||||
{
|
||||
const LLAvatarListItem::Params& params = LLUICtrlFactory::getDefaultParams<LLAvatarListItem>();
|
||||
if (mOnlineStatus == E_ONLINE && params.group_moderator_style.isProvided() && params.group_moderator_style().font.isProvided())
|
||||
{
|
||||
avatar_name_style.font = params.group_moderator_style().font();
|
||||
}
|
||||
if (params.online_style.isProvided() && params.online_style().color.isProvided())
|
||||
{
|
||||
avatar_name_style.color = params.online_style().color();
|
||||
}
|
||||
}
|
||||
if (mUseContactSetColors)
|
||||
{
|
||||
LLColor4 contact_set_color;
|
||||
if (LGGContactSets::getInstance()->hasFriendColorThatShouldShow(mAvatarId, ContactSetType::FRIENDS, contact_set_color))
|
||||
{
|
||||
avatar_name_style.color = contact_set_color;
|
||||
}
|
||||
}
|
||||
LLTextUtil::textboxSetHighlightedVal(mAvatarName, avatar_name_style, name, highlight);
|
||||
// </FS:Ansariel>
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ class LLOutputMonitorCtrl;
|
||||
class LLAvatarName;
|
||||
class LLIconCtrl;
|
||||
class LLUICtrl;
|
||||
enum class ContactSetType; // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
|
||||
class LLAvatarListItem : public LLPanel, public LLFriendObserver
|
||||
{
|
||||
@@ -117,6 +118,8 @@ public:
|
||||
void showLastInteractionTime(bool show);
|
||||
void setAvatarIconVisible(bool visible);
|
||||
void setShowCompleteName(bool show, bool force = false) { mShowCompleteName = show; mForceCompleteName = force;};
|
||||
void setUseContactSetColors(bool use_colors); // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
void setUseContactSetListStyle(bool use_style); // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
// [RLVa:KB] - Checked: RLVa-1.2.0
|
||||
void setRlvCheckShowNames(bool fRlvCheckShowNames);
|
||||
void updateRlvRestrictions();
|
||||
@@ -266,6 +269,8 @@ private:
|
||||
|
||||
bool mShowCompleteName;
|
||||
bool mForceCompleteName;
|
||||
bool mUseContactSetColors{ false }; // <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
bool mUseContactSetListStyle{ false }; // <FS:PP> FIRE-31733: Make contact sets lists more readable
|
||||
std::string mGreyOutUsername;
|
||||
|
||||
void fetchAvatarName();
|
||||
|
||||
@@ -655,7 +655,7 @@ bool LLRenderPass::uploadMatrixPalette(LLDrawInfo& params)
|
||||
}
|
||||
|
||||
//static
|
||||
bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo)
|
||||
bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, const LLMeshSkinInfo* skinInfo) // <FS:Beq/> be defensive about UAF with skinInfo during LocalMesh
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
|
||||
|
||||
@@ -682,7 +682,7 @@ bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinI
|
||||
|
||||
// Returns true if rendering should proceed
|
||||
//static
|
||||
bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, bool& skipLastSkin)
|
||||
bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, const LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, bool& skipLastSkin)// <FS:Beq/> be defensive about UAF with skinInfo during LocalMesh
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
|
||||
|
||||
@@ -719,7 +719,7 @@ bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinI
|
||||
|
||||
// Returns true if rendering should proceed
|
||||
//static
|
||||
bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, const LLGLSLShader*& lastAvatarShader, bool& skipLastSkin)
|
||||
bool LLRenderPass::uploadMatrixPalette(LLVOAvatar* avatar, const LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, const LLGLSLShader*& lastAvatarShader, bool& skipLastSkin)// <FS:Beq/> be defensive about UAF with skinInfo during LocalMesh
|
||||
{
|
||||
LL_PROFILE_ZONE_SCOPED_CATEGORY_AVATAR;
|
||||
|
||||
@@ -920,5 +920,4 @@ void LLRenderPass::pushUntexturedRiggedGLTFBatch(LLDrawInfo& params, const LLVOA
|
||||
{
|
||||
pushUntexturedGLTFBatch(params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -387,9 +387,14 @@ public:
|
||||
void pushUntexturedBatch(LLDrawInfo& params);
|
||||
void pushBumpBatch(LLDrawInfo& params, bool texture, bool batch_textures = false);
|
||||
static bool uploadMatrixPalette(LLDrawInfo& params);
|
||||
static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo);
|
||||
static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, bool& skipLastSkin);
|
||||
static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, const LLGLSLShader*& lastAvatarShader, bool& skipLastSkin);
|
||||
// <FS:Beq> be defensive about UAF with skinInfo during LocalMesh
|
||||
// static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo);
|
||||
// static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, bool& skipLastSkin);
|
||||
// static bool uploadMatrixPalette(LLVOAvatar* avatar, LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, const LLGLSLShader*& lastAvatarShader, bool& skipLastSkin);
|
||||
// </FS:Beq>
|
||||
static bool uploadMatrixPalette(LLVOAvatar* avatar, const LLMeshSkinInfo* skinInfo);
|
||||
static bool uploadMatrixPalette(LLVOAvatar* avatar, const LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, bool& skipLastSkin);
|
||||
static bool uploadMatrixPalette(LLVOAvatar* avatar, const LLMeshSkinInfo* skinInfo, const LLVOAvatar*& lastAvatar, U64& lastMeshId, const LLGLSLShader*& lastAvatarShader, bool& skipLastSkin);
|
||||
virtual void renderGroup(LLSpatialGroup* group, U32 type, bool texture = true);
|
||||
virtual void renderRiggedGroup(LLSpatialGroup* group, U32 type, bool texture = true);
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "lleventfilter.h"
|
||||
|
||||
#include "llsdutil.h" // <FS:ND/> for ll_pretty_print_sd
|
||||
#include "llviewercontrol.h" // <FS:Beq/> Debug setting for FIRE-36454 etc.
|
||||
#include "llviewernetwork.h"
|
||||
|
||||
namespace LLEventPolling
|
||||
@@ -76,9 +77,9 @@ namespace Details
|
||||
// We will wait RETRY_SECONDS + (errorCount * RETRY_SECONDS_INC) before retrying after an error.
|
||||
// This means we attempt to recover relatively quickly but back off giving more time to recover
|
||||
// until we finally give up after MAX_EVENT_POLL_HTTP_ERRORS attempts.
|
||||
constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS = 15.f; // ~ half of a normal timeout.
|
||||
constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 5.f; // ~ half of a normal timeout.
|
||||
constexpr S32 MAX_EVENT_POLL_HTTP_ERRORS = 10; // ~5 minutes, by the above rules.
|
||||
constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS = 1.f;
|
||||
constexpr F32 EVENT_POLL_ERROR_RETRY_SECONDS_INC = 3.f;
|
||||
constexpr S32 MAX_EVENT_POLL_ERRORS = 15; // ~5 minutes, by the above rules.
|
||||
constexpr F64 MIN_SECONDS_PASSED = 10.0; // Minimum time we expect the server to hold the request.
|
||||
|
||||
int LLEventPollImpl::sNextCounter = 1;
|
||||
@@ -161,6 +162,7 @@ namespace Details
|
||||
int errorCount = 0;
|
||||
int counter = mCounter; // saved on the stack for logging.
|
||||
LLTimer message_time;
|
||||
static LLCachedControl<U32> event_poll_core_retries(gSavedSettings, "FSEventPollCoreRetries", 0U); // <FS:Beq/> FIRE-36454 TP Disconnects in 7.2.3
|
||||
|
||||
LL_DEBUGS("LLEventPollImpl") << " <" << counter << "> entering coroutine." << LL_ENDL;
|
||||
|
||||
@@ -169,7 +171,13 @@ namespace Details
|
||||
// This is a loop with its own waitToRetry implementation,
|
||||
// so disable retries.
|
||||
LLCore::HttpOptions::ptr_t httpOpts = std::make_shared<LLCore::HttpOptions>();
|
||||
httpOpts->setRetries(0);
|
||||
// <FS:Beq> FIRE-36454 TP Disconnects in 7.2.3
|
||||
// httpOpts->setRetries(0);
|
||||
const U32 llcore_retries = event_poll_core_retries();
|
||||
httpOpts->setRetries(llcore_retries);
|
||||
LL_DEBUGS("LLEventPollImpl") << "<" << counter << "> Event poll llcore retries set to "
|
||||
<< llcore_retries << " for " << mSenderIp << LL_ENDL;
|
||||
// </FS:Beq>
|
||||
// <FS:Ansariel> Restore pre-coro behavior (60s timeout, no retries)
|
||||
#ifdef OPENSIM
|
||||
if (LLGridManager::instance().isInOpenSim())
|
||||
@@ -229,8 +237,14 @@ namespace Details
|
||||
{
|
||||
// Server is supposed to hold request for 20 to 30 seconds.
|
||||
// If it didn't hold the request at least for 10s, treat as an error.
|
||||
// <FS:Beq> FIRE-36454 TP Disconnects in 7.2.3
|
||||
// LL_WARNS("LLEventPollImpl") << "Response arrived too early, status: " << status.toTerseString()
|
||||
// << ", time passed: " << message_time.getElapsedSeconds() << LL_ENDL;
|
||||
LL_WARNS("LLEventPollImpl") << "Response arrived too early, status: " << status.toTerseString()
|
||||
<< ", time passed: " << message_time.getElapsedSeconds() << LL_ENDL;
|
||||
<< ", time passed: " << message_time.getElapsedSeconds()
|
||||
<< ", llcore retries: " << llcore_retries
|
||||
<< ", sender: " << mSenderIp << LL_ENDL;
|
||||
// </FS:Beq>
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -261,30 +275,45 @@ namespace Details
|
||||
// some cases the server gets ahead of the viewer and will
|
||||
// return a 404 error (Not Found) before the cancel event
|
||||
// comes back in the queue
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Canceling coroutine, status: " << status.toTerseString() << LL_ENDL;
|
||||
// <FS:Beq> FIRE-36454 TP Disconnects in 7.2.3
|
||||
// LL_WARNS("LLEventPollImpl") << "<" << counter << "> Canceling coroutine, status: " << status.toTerseString()
|
||||
// << ", time passed: " << message_time.getElapsedSeconds() << LL_ENDL;
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Canceling coroutine, status: " << status.toTerseString()
|
||||
<< ", time passed: " << message_time.getElapsedSeconds()
|
||||
<< ", llcore retries: " << llcore_retries
|
||||
<< ", sender: " << mSenderIp << LL_ENDL;
|
||||
// </FS:Beq>
|
||||
break;
|
||||
}
|
||||
else if (!status.isHttpStatus())
|
||||
{
|
||||
/// Some LLCore or LIBCurl error was returned. This is unlikely to be recoverable
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Critical error from poll request returned from libraries. Canceling coroutine." << LL_ENDL;
|
||||
break;
|
||||
// Some LLCore or LIBCurl error was returned.
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Error from poll request returned from libraries. " << status.toTerseString() << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Error result from LLCoreHttpUtil::HttpCoroHandler. Code "
|
||||
<< status.toTerseString() << ": '" << httpResults["message"] << "'" << LL_ENDL;
|
||||
}
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Error result from LLCoreHttpUtil::HttpCoroHandler. Code "
|
||||
<< status.toTerseString() << ": '" << httpResults["message"] << "'" << LL_ENDL;
|
||||
|
||||
if (errorCount < MAX_EVENT_POLL_HTTP_ERRORS)
|
||||
if (errorCount < MAX_EVENT_POLL_ERRORS)
|
||||
{ // An unanticipated error has been received from our poll
|
||||
// request. Calculate a timeout and wait for it to expire(sleep)
|
||||
// before trying again. The sleep time is increased by 5 seconds
|
||||
// before trying again. The sleep time is increased by 3 seconds
|
||||
// for each consecutive error.
|
||||
++errorCount;
|
||||
|
||||
F32 waitToRetry = EVENT_POLL_ERROR_RETRY_SECONDS
|
||||
+ errorCount * EVENT_POLL_ERROR_RETRY_SECONDS_INC;
|
||||
|
||||
++errorCount;
|
||||
|
||||
LL_WARNS("LLEventPollImpl") << "<" << counter << "> Retrying in " << waitToRetry <<
|
||||
" seconds, error count is now " << errorCount << LL_ENDL;
|
||||
// <FS:Beq> FIRE-36454 TP Disconnects in 7.2.3
|
||||
// " seconds, error count is now " << errorCount << LL_ENDL;
|
||||
" seconds, error count is now " << errorCount
|
||||
<< ", llcore retries: " << llcore_retries
|
||||
<< ", sender: " << mSenderIp << LL_ENDL;
|
||||
// </FS:Beq>
|
||||
|
||||
llcoro::suspendUntilTimeout(waitToRetry);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "llviewertexture.h"
|
||||
#include "lldrawable.h"
|
||||
#include "lljoint.h"
|
||||
#include "llmodel.h"// <FS:Beq/> be defensive about UAF with skinInfo during LocalMesh
|
||||
|
||||
class LLFacePool;
|
||||
class LLVolume;
|
||||
@@ -49,7 +50,7 @@ class LLVertexProgram;
|
||||
class LLViewerTexture;
|
||||
class LLGeometryManager;
|
||||
class LLDrawInfo;
|
||||
class LLMeshSkinInfo;
|
||||
// class LLMeshSkinInfo;// <FS:Beq/> be defensive about UAF with skinInfo during LocalMesh
|
||||
|
||||
const F32 MIN_ALPHA_SIZE = 1024.f;
|
||||
// <FS:minerjr>
|
||||
@@ -272,7 +273,7 @@ public:
|
||||
LLMatrix4* mNormalMapMatrix;
|
||||
LLDrawInfo* mDrawInfo;
|
||||
LLVOAvatar* mAvatar = nullptr;
|
||||
LLMeshSkinInfo* mSkinInfo = nullptr;
|
||||
LLConstPointer<LLMeshSkinInfo> mSkinInfo;// <FS:Beq/> be defensive about UAF with skinInfo during LocalMesh
|
||||
|
||||
// return mSkinInfo->mHash or 0 if mSkinInfo is null
|
||||
U64 getSkinHash();
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "llavatarnamecache.h" // IDEVO
|
||||
#include "llbutton.h"
|
||||
#include "llcachename.h"
|
||||
#include "llcombobox.h" // <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
#include "lllineeditor.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
#include "llscrolllistitem.h"
|
||||
@@ -62,6 +63,7 @@
|
||||
|
||||
#include "fsavatarsearchmenu.h"
|
||||
#include "fsscrolllistctrl.h"
|
||||
#include "lggcontactsets.h" // <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
#include "lltransientfloatermgr.h"
|
||||
|
||||
static const U32 AVATAR_PICKER_SEARCH_TIMEOUT = 180U;
|
||||
@@ -118,6 +120,7 @@ LLFloaterAvatarPicker::LLFloaterAvatarPicker(const LLSD& key)
|
||||
mNearMeListComplete(false),
|
||||
mCloseOnSelect(false),
|
||||
mExcludeAgentFromSearchResults(false),
|
||||
mAllowMultipleSelection(false), // <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
mContextConeOpacity (0.f),
|
||||
mContextConeInAlpha(CONTEXT_CONE_IN_ALPHA),
|
||||
mContextConeOutAlpha(CONTEXT_CONE_OUT_ALPHA),
|
||||
@@ -194,6 +197,11 @@ bool LLFloaterAvatarPicker::postBuild()
|
||||
getChild<LLPanel>("SearchPanelUUID")->setDefaultBtn("FindUUID");
|
||||
// </FS:Ansariel>
|
||||
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
getChild<LLComboBox>("ContactSetSelector")->setCommitCallback(boost::bind(&LLFloaterAvatarPicker::onContactSetSelected, this));
|
||||
populateContactSets();
|
||||
// </FS:PP>
|
||||
|
||||
setAllowMultiple(false);
|
||||
|
||||
center();
|
||||
@@ -333,38 +341,80 @@ void LLFloaterAvatarPicker::onBtnSelect()
|
||||
if(mSelectionCallback)
|
||||
{
|
||||
std::string acvtive_panel_name;
|
||||
LLScrollListCtrl* list = NULL;
|
||||
// LLScrollListCtrl* list = NULL; - <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
LLPanel* active_panel = getChild<LLTabContainer>("ResidentChooserTabs")->getCurrentPanel();
|
||||
if(active_panel)
|
||||
{
|
||||
acvtive_panel_name = active_panel->getName();
|
||||
}
|
||||
if(acvtive_panel_name == "SearchPanel")
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
uuid_vec_t avatar_ids;
|
||||
std::vector<LLAvatarName> avatar_names;
|
||||
if (acvtive_panel_name == "ContactSetsPanel")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("SearchResults");
|
||||
if (!mAllowMultipleSelection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
LLComboBox* contact_sets_combo = getChild<LLComboBox>("ContactSetSelector");
|
||||
if (contact_sets_combo)
|
||||
{
|
||||
const std::string set_name = contact_sets_combo->getSimple();
|
||||
if (LGGContactSets::ContactSet* contact_set = LGGContactSets::instance().getContactSet(set_name); contact_set)
|
||||
{
|
||||
avatar_ids.reserve(contact_set->mFriends.size());
|
||||
avatar_names.reserve(contact_set->mFriends.size());
|
||||
for (const LLUUID& avatar_id : contact_set->mFriends)
|
||||
{
|
||||
if (avatar_id.isNull())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
avatar_ids.push_back(avatar_id);
|
||||
LLAvatarName av_name;
|
||||
LLAvatarNameCache::get(avatar_id, &av_name);
|
||||
avatar_names.push_back(av_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!avatar_ids.empty())
|
||||
{
|
||||
mSelectionCallback(avatar_ids, avatar_names);
|
||||
}
|
||||
}
|
||||
else if(acvtive_panel_name == "NearMePanel")
|
||||
else
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("NearMe");
|
||||
}
|
||||
else if (acvtive_panel_name == "FriendsPanel")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("Friends");
|
||||
}
|
||||
// <FS:Ansariel> Search by UUID
|
||||
else if (acvtive_panel_name == "SearchPanelUUID")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("SearchResultsUUID");
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
LLScrollListCtrl* list = NULL;
|
||||
// </FS:PP>
|
||||
if(acvtive_panel_name == "SearchPanel")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("SearchResults");
|
||||
}
|
||||
else if(acvtive_panel_name == "NearMePanel")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("NearMe");
|
||||
}
|
||||
else if (acvtive_panel_name == "FriendsPanel")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("Friends");
|
||||
}
|
||||
// <FS:Ansariel> Search by UUID
|
||||
else if (acvtive_panel_name == "SearchPanelUUID")
|
||||
{
|
||||
list = getChild<LLScrollListCtrl>("SearchResultsUUID");
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
if(list)
|
||||
{
|
||||
uuid_vec_t avatar_ids;
|
||||
std::vector<LLAvatarName> avatar_names;
|
||||
getSelectedAvatarData(list, avatar_ids, avatar_names);
|
||||
mSelectionCallback(avatar_ids, avatar_names);
|
||||
}
|
||||
if(list)
|
||||
{
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
// uuid_vec_t avatar_ids;
|
||||
// std::vector<LLAvatarName> avatar_names;
|
||||
// </FS:PP>
|
||||
getSelectedAvatarData(list, avatar_ids, avatar_names);
|
||||
mSelectionCallback(avatar_ids, avatar_names);
|
||||
}
|
||||
} // <FS:PP> End bracket only; FIRE-34809 Contact Sets support in avatar picker
|
||||
}
|
||||
getChild<LLScrollListCtrl>("SearchResults")->deselectAllItems(true);
|
||||
getChild<LLScrollListCtrl>("NearMe")->deselectAllItems(true);
|
||||
@@ -516,6 +566,44 @@ void LLFloaterAvatarPicker::populateFriend()
|
||||
// </FS:Ansariel>
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
void LLFloaterAvatarPicker::populateContactSets()
|
||||
{
|
||||
LLComboBox* contact_sets_combo = getChild<LLComboBox>("ContactSetSelector");
|
||||
if (!contact_sets_combo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
contact_sets_combo->clearRows();
|
||||
|
||||
const LGGContactSets::string_vec_t contact_sets = LGGContactSets::instance().getAllContactSets();
|
||||
for (const std::string& set_name : contact_sets)
|
||||
{
|
||||
contact_sets_combo->add(set_name);
|
||||
}
|
||||
|
||||
contact_sets_combo->sortByName();
|
||||
|
||||
const bool has_contact_sets = !contact_sets.empty();
|
||||
contact_sets_combo->setEnabled(has_contact_sets);
|
||||
|
||||
if (!has_contact_sets)
|
||||
{
|
||||
onList();
|
||||
return;
|
||||
}
|
||||
|
||||
contact_sets_combo->selectFirstItem();
|
||||
onContactSetSelected();
|
||||
}
|
||||
|
||||
void LLFloaterAvatarPicker::onContactSetSelected()
|
||||
{
|
||||
onList();
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLFloaterAvatarPicker::drawFrustum()
|
||||
{
|
||||
static LLCachedControl<F32> max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f);
|
||||
@@ -567,6 +655,25 @@ bool LLFloaterAvatarPicker::visibleItemsSelected() const
|
||||
return getChild<LLScrollListCtrl>("SearchResultsUUID")->getFirstSelectedIndex() >= 0;
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
else if (active_panel == getChild<LLPanel>("ContactSetsPanel"))
|
||||
{
|
||||
if (!mAllowMultipleSelection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
LLComboBox* contact_sets_combo = getChild<LLComboBox>("ContactSetSelector");
|
||||
if (!contact_sets_combo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const std::string set_name = contact_sets_combo->getSimple();
|
||||
if (LGGContactSets::ContactSet* contact_set = LGGContactSets::instance().getContactSet(set_name); contact_set)
|
||||
{
|
||||
return !contact_set->mFriends.empty();
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -758,6 +865,24 @@ void LLFloaterAvatarPicker::setAllowMultiple(bool allow_multiple)
|
||||
getChild<LLScrollListCtrl>("Friends")->setAllowMultipleSelection(allow_multiple);
|
||||
// <FS:Ansariel> Search by UUID
|
||||
getChild<LLScrollListCtrl>("SearchResultsUUID")->setAllowMultipleSelection(allow_multiple);
|
||||
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
mAllowMultipleSelection = allow_multiple;
|
||||
LLTabContainer* tabs = getChild<LLTabContainer>("ResidentChooserTabs");
|
||||
LLPanel* contact_sets_panel = getChild<LLPanel>("ContactSetsPanel");
|
||||
if (tabs && contact_sets_panel)
|
||||
{
|
||||
const S32 tab_index = tabs->getIndexForPanel(contact_sets_panel);
|
||||
if (tab_index >= 0)
|
||||
{
|
||||
tabs->enableTabButton(tab_index, allow_multiple);
|
||||
if (!allow_multiple && tabs->getCurrentPanel() == contact_sets_panel)
|
||||
{
|
||||
tabs->selectTabByName("FriendsPanel");
|
||||
}
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
LLScrollListCtrl* LLFloaterAvatarPicker::getActiveList()
|
||||
@@ -1083,6 +1208,16 @@ bool LLFloaterAvatarPicker::isSelectBtnEnabled()
|
||||
list = getChild<LLScrollListCtrl>("SearchResultsUUID");
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
else if (acvtive_panel_name == "ContactSetsPanel")
|
||||
{
|
||||
if (!mAllowMultipleSelection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return mOkButtonValidateSignal.num_slots() ? mOkButtonValidateSignal(uuid_vec_t{}) : true;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
if(list)
|
||||
{
|
||||
|
||||
@@ -97,6 +97,11 @@ private:
|
||||
void populateFriend();
|
||||
bool visibleItemsSelected() const; // Returns true if any items in the current tab are selected.
|
||||
|
||||
// <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
void populateContactSets();
|
||||
void onContactSetSelected();
|
||||
// </FS:PP>
|
||||
|
||||
static void findByIdCoro(std::string url, LLUUID query_id, LLUUID agent_id, std::string floater_key);
|
||||
static void findByNameCoro(std::string url, LLUUID mQueryID, std::string mName);
|
||||
void find();
|
||||
@@ -112,6 +117,7 @@ private:
|
||||
bool mNearMeListComplete;
|
||||
bool mCloseOnSelect;
|
||||
bool mExcludeAgentFromSearchResults;
|
||||
bool mAllowMultipleSelection; // <FS:PP> FIRE-34809 Contact Sets support in avatar picker
|
||||
LLHandle <LLView> mFrustumOrigin;
|
||||
F32 mContextConeOpacity;
|
||||
F32 mContextConeInAlpha;
|
||||
|
||||
@@ -44,13 +44,18 @@
|
||||
#include "llpanelplaces.h"
|
||||
#include "llpanelprofile.h"
|
||||
|
||||
#include "lltabcontainer.h"
|
||||
|
||||
LLFloaterDirectory::LLFloaterDirectory(const std::string& name)
|
||||
: LLFloater(name),
|
||||
mPanelAvatarp(nullptr),
|
||||
mPanelGroupp(nullptr),
|
||||
mPanelPlacep(nullptr),
|
||||
mPanelClassifiedp(nullptr),
|
||||
mPanelEventp(nullptr)
|
||||
mPanelEventp(nullptr),
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
mOpenProfileBtn(nullptr),
|
||||
mDirectoryTabs(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -87,9 +92,45 @@ bool LLFloaterDirectory::postBuild()
|
||||
mPanelClassifiedp->setBackgroundVisible(false);
|
||||
mPanelEventp = findChild<LLPanelEventInfo>("panel_event_info");
|
||||
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
mOpenProfileBtn = getChild<LLButton>("open_profile_btn");
|
||||
mOpenProfileBtn->setCommitCallback([&](LLUICtrl*, const LLSD&) {
|
||||
auto* currentDirBrowserPanel = dynamic_cast<LLPanelDirBrowser*>(mDirectoryTabs->getCurrentPanel());
|
||||
if (currentDirBrowserPanel)
|
||||
currentDirBrowserPanel->openProfile();
|
||||
});
|
||||
mDirectoryTabs = getChild<LLTabContainer>("Directory Tabs");
|
||||
mDirectoryTabs->setCommitCallback([&](LLUICtrl*, const LLSD&) { updateProfileButtonVisibility(); });
|
||||
// </FS:Ansariel>
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
void LLFloaterDirectory::onOpen(const LLSD& key)
|
||||
{
|
||||
LLFloater::onOpen(key);
|
||||
|
||||
if (!key.has("query"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLPanelDirWeb* panel_dir_web = findChild<LLPanelDirWeb>("panel_dir_web");
|
||||
if (!panel_dir_web || !mDirectoryTabs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string category = key.has("category") ? key["category"].asString() : "standard";
|
||||
const std::string query = key["query"].asString();
|
||||
const std::string collection = key.has("collection") ? key["collection"].asString() : "";
|
||||
|
||||
mDirectoryTabs->selectTabByName("panel_dir_web");
|
||||
panel_dir_web->navigateToSearchPage(category, query, collection);
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLFloaterDirectory::hideAllDetailPanels()
|
||||
{
|
||||
if (mPanelAvatarp) mPanelAvatarp->setVisible(false);
|
||||
@@ -97,4 +138,19 @@ void LLFloaterDirectory::hideAllDetailPanels()
|
||||
if (mPanelPlacep) mPanelPlacep->setVisible(false);
|
||||
if (mPanelClassifiedp) mPanelClassifiedp->setVisible(false);
|
||||
if (mPanelEventp) mPanelEventp->setVisible(false);
|
||||
|
||||
mOpenProfileBtn->setVisible(false); // <FS:Ansariel> Add "open profile" button
|
||||
}
|
||||
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
void LLFloaterDirectory::updateProfileButtonVisibility()
|
||||
{
|
||||
std::string selected_tab = mDirectoryTabs->getCurrentPanel()->getName();
|
||||
if (selected_tab == "panel_dir_people")
|
||||
mOpenProfileBtn->setVisible(mPanelAvatarp->getVisible());
|
||||
else if (selected_tab == "panel_dir_groups")
|
||||
mOpenProfileBtn->setVisible(mPanelGroupp->getVisible());
|
||||
else
|
||||
mOpenProfileBtn->setVisible(false);
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
@@ -54,6 +54,9 @@ class LLPanelGroup;
|
||||
class LLPanelPlaces;
|
||||
class LLPanelClassifiedInfo;
|
||||
|
||||
class LLButton;
|
||||
class LLTabContainer;
|
||||
|
||||
// Floater to find people, places, things
|
||||
class LLFloaterDirectory : public LLFloater
|
||||
{
|
||||
@@ -64,6 +67,9 @@ public:
|
||||
void hideAllDetailPanels();
|
||||
|
||||
bool postBuild() override;
|
||||
void onOpen(const LLSD& key) override; // <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
|
||||
void updateProfileButtonVisibility(); // <FS:Ansariel> Add "open profile" button
|
||||
|
||||
public:
|
||||
LLPanelProfileSecondLife* mPanelAvatarp;
|
||||
@@ -72,6 +78,10 @@ public:
|
||||
LLPanelPlaces* mPanelPlacep;
|
||||
LLPanelClassifiedInfo* mPanelClassifiedp;
|
||||
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
LLButton* mOpenProfileBtn;
|
||||
LLTabContainer* mDirectoryTabs;
|
||||
|
||||
private:
|
||||
static LLFloaterDirectory *sInstance;
|
||||
};
|
||||
|
||||
@@ -56,6 +56,14 @@ bool item_name_precedes( LLInventoryItem* a, LLInventoryItem* b )
|
||||
return LLStringUtil::precedesDict( a->getName(), b->getName() );
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
static bool are_gestures_enabled()
|
||||
{
|
||||
static LLCachedControl<bool> gestures_enabled(gSavedPerAccountSettings, "FSGesturesEnabled", true);
|
||||
return gestures_enabled;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
class LLFloaterGestureObserver : public LLGestureManagerObserver
|
||||
{
|
||||
public:
|
||||
@@ -207,6 +215,7 @@ bool LLFloaterGesture::postBuild()
|
||||
getChild<LLButton>("activate_btn")->setClickedCallback(boost::bind(&LLFloaterGesture::onActivateBtnClick, this));
|
||||
|
||||
getChild<LLUICtrl>("FSShowOnlyActiveGestures")->setCommitCallback(boost::bind(&LLFloaterGesture::refreshForActiveSort, this)); // <FS:PP> FIRE-5646: Option to show only active gestures
|
||||
getChild<LLUICtrl>("FSGesturesEnabled")->setCommitCallback(boost::bind(&LLFloaterGesture::onGesturesEnabledChanged, this)); // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
getChild<LLUICtrl>("new_gesture_btn")->setCommitCallback(boost::bind(&LLFloaterGesture::onClickNew, this));
|
||||
getChild<LLButton>("del_btn")->setClickedCallback(boost::bind(&LLFloaterGesture::onDeleteSelected, this));
|
||||
@@ -236,6 +245,7 @@ bool LLFloaterGesture::postBuild()
|
||||
|
||||
// Update button labels
|
||||
onCommitList();
|
||||
updateGesturesEnabledState(); // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -465,6 +475,13 @@ void LLFloaterGesture::getSelectedIds(uuid_vec_t& ids)
|
||||
|
||||
bool LLFloaterGesture::isActionEnabled(const LLSD& command)
|
||||
{
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
if (!are_gestures_enabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// paste copy_uuid edit_gesture
|
||||
std::string command_name = command.asString();
|
||||
if("paste" == command_name)
|
||||
@@ -507,6 +524,8 @@ bool LLFloaterGesture::isActionEnabled(const LLSD& command)
|
||||
|
||||
void LLFloaterGesture::onClickPlay()
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
const LLUUID& item_id = mGestureList->getCurrentID();
|
||||
if(item_id.isNull()) return;
|
||||
|
||||
@@ -533,6 +552,8 @@ void LLFloaterGesture::onClickPlay()
|
||||
|
||||
void LLFloaterGesture::onClickNew()
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
LLPointer<LLInventoryCallback> cb = new GestureShowCallback();
|
||||
create_inventory_item(gAgent.getID(),
|
||||
gAgent.getSessionID(),
|
||||
@@ -549,6 +570,8 @@ void LLFloaterGesture::onClickNew()
|
||||
|
||||
void LLFloaterGesture::onActivateBtnClick()
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
uuid_vec_t ids;
|
||||
getSelectedIds(ids);
|
||||
if(ids.empty())
|
||||
@@ -626,6 +649,8 @@ void LLFloaterGesture::onGestureRename(const LLSD& notification, const LLSD& res
|
||||
|
||||
void LLFloaterGesture::onCopyPasteAction(const LLSD& command)
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
std::string command_name = command.asString();
|
||||
// Since we select this command, the inventory items must have already arrived
|
||||
if("copy_gesture" == command_name)
|
||||
@@ -676,6 +701,8 @@ void LLFloaterGesture::onCopyPasteAction(const LLSD& command)
|
||||
|
||||
void LLFloaterGesture::onClickEdit()
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
const LLUUID& item_id = mGestureList->getCurrentID();
|
||||
|
||||
LLInventoryItem* item = gInventory.getItem(item_id);
|
||||
@@ -703,10 +730,36 @@ void LLFloaterGesture::onCommitList()
|
||||
getChildView("play_btn")->setVisible( true);
|
||||
getChildView("stop_btn")->setVisible( false);
|
||||
}
|
||||
|
||||
updateGesturesEnabledState(); // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
void LLFloaterGesture::onGesturesEnabledChanged()
|
||||
{
|
||||
updateGesturesEnabledState();
|
||||
}
|
||||
|
||||
void LLFloaterGesture::updateGesturesEnabledState()
|
||||
{
|
||||
const bool gestures_enabled = are_gestures_enabled();
|
||||
|
||||
mGestureList->setEnabled(gestures_enabled);
|
||||
getChildView("edit_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("play_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("stop_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("new_gesture_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("del_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("activate_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("gear_btn")->setEnabled(gestures_enabled);
|
||||
getChildView("FSShowOnlyActiveGestures")->setEnabled(gestures_enabled);
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLFloaterGesture::onDeleteSelected()
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
uuid_vec_t ids;
|
||||
getSelectedIds(ids);
|
||||
if(ids.empty())
|
||||
@@ -771,15 +824,17 @@ void LLFloaterGesture::playGesture(LLUUID item_id)
|
||||
|
||||
// <FS:ND> Try to update an item that already exists. Return true on success, false if such an item does not exist yet.
|
||||
|
||||
#define COL_TRIGGER 0
|
||||
#define COL_SHORTCUT 1
|
||||
#define COL_KEY 2
|
||||
#define COL_NAME 3
|
||||
#define COL_ACTIVE 0
|
||||
#define COL_NAME 1
|
||||
#define COL_TRIGGER 2
|
||||
#define COL_KEY 3
|
||||
#define COL_SHORTCUT 4
|
||||
|
||||
#define UI_COL_NAME 0
|
||||
#define UI_COL_TRIGGER 1
|
||||
#define UI_COL_KEY 2
|
||||
#define UI_COL_SHORTCUT 3
|
||||
#define UI_COL_ACTIVE 0
|
||||
#define UI_COL_NAME 1
|
||||
#define UI_COL_TRIGGER 2
|
||||
#define UI_COL_KEY 3
|
||||
#define UI_COL_SHORTCUT 4
|
||||
|
||||
bool LLFloaterGesture::updateItem( LLUUID const &aItem, LLSD const &aData )
|
||||
{
|
||||
@@ -793,12 +848,15 @@ bool LLFloaterGesture::updateItem( LLUUID const &aItem, LLSD const &aData )
|
||||
if( !pItem )
|
||||
return false;
|
||||
|
||||
std::string sDummyShortcut = "---";
|
||||
std::string sDummyKey = "~~~";
|
||||
const std::string sDummyShortcut = "---";
|
||||
const std::string sDummyKey = "~~~";
|
||||
|
||||
if( aData[ "columns" ][ COL_NAME ][ "value" ] != "" )
|
||||
pItem->getColumn( UI_COL_NAME )->setValue( aData[ "columns" ][ COL_NAME ][ "value" ] );
|
||||
|
||||
if( aData[ "columns" ][ COL_TRIGGER ][ "value" ] != "" )
|
||||
pItem->getColumn( UI_COL_TRIGGER )->setValue( aData[ "columns" ][ COL_TRIGGER ][ "value" ] );
|
||||
|
||||
if( aData[ "columns" ][ COL_SHORTCUT ][ "value" ] != sDummyShortcut )
|
||||
pItem->getColumn( UI_COL_SHORTCUT )->setValue( aData[ "columns" ][ COL_SHORTCUT ][ "value" ] );
|
||||
|
||||
@@ -807,7 +865,15 @@ bool LLFloaterGesture::updateItem( LLUUID const &aItem, LLSD const &aData )
|
||||
|
||||
pItem->getColumn( UI_COL_NAME )->setValue( aData[ "columns" ][ COL_NAME ][ "value" ] );
|
||||
|
||||
LLFontGL::StyleFlags oStyle = LLGestureMgr::getInstance()->isGestureActive(aItem) ? LLFontGL::BOLD : LLFontGL::NORMAL;
|
||||
bool is_gesture_active = LLGestureMgr::getInstance()->isGestureActive(aItem);
|
||||
LLFontGL::StyleFlags oStyle = is_gesture_active ? LLFontGL::BOLD : LLFontGL::NORMAL;
|
||||
|
||||
pItem->getColumn(UI_COL_ACTIVE)->setValue(is_gesture_active ? "Activate_Checkmark" : "");
|
||||
LLScrollListIcon* icon = dynamic_cast<LLScrollListIcon*>(pItem->getColumn(UI_COL_ACTIVE));
|
||||
if (is_gesture_active && icon)
|
||||
{
|
||||
icon->setIconSize(10);
|
||||
}
|
||||
|
||||
LLScrollListText *pTextItem = dynamic_cast< LLScrollListText* >( pItem->getColumn( UI_COL_NAME ) );
|
||||
|
||||
|
||||
@@ -98,6 +98,11 @@ private:
|
||||
void onDeleteSelected();
|
||||
void onRenameSelected();
|
||||
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
void onGesturesEnabledChanged();
|
||||
void updateGesturesEnabledState();
|
||||
// </FS:PP>
|
||||
|
||||
static void onGestureRename(const LLSD& notification, const LLSD& response);
|
||||
|
||||
LLUUID mSelectedID;
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "llviewertexturelist.h"
|
||||
// PoundLife END
|
||||
#include "llvovolume.h"
|
||||
#include "llgltfmaterial.h"
|
||||
|
||||
// <FS:Ansariel> FIRE-22292: Configurable columns
|
||||
#include "llmenugl.h"
|
||||
@@ -648,32 +649,54 @@ void LLFloaterInspect::getObjectTextureMemory(LLViewerObject* object, U32& objec
|
||||
|
||||
for (U8 j = 0; j < te_count; j++)
|
||||
{
|
||||
LLViewerTexture* img = object->getTEImage(j);
|
||||
if (img)
|
||||
LLTextureEntry* te = object->getTE(j);
|
||||
if (!te)
|
||||
{
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
continue;
|
||||
}
|
||||
|
||||
// materials per face
|
||||
if (object->getTE(j)->getMaterialParams().notNull())
|
||||
// If a PBR (GLTF) render material is present, count this instead of legacy textures.
|
||||
if (LLGLTFMaterial* gltf_mat = te->getGLTFRenderMaterial())
|
||||
{
|
||||
uuid = object->getTE(j)->getMaterialParams()->getNormalID();
|
||||
if (uuid.notNull())
|
||||
for (U32 i = 0; i < LLGLTFMaterial::GLTF_TEXTURE_INFO_COUNT; ++i)
|
||||
{
|
||||
LLViewerTexture* img = gTextureList.getImage(uuid);
|
||||
if (img)
|
||||
const LLUUID& tex_id = gltf_mat->mTextureId[i];
|
||||
if (tex_id.notNull())
|
||||
{
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
if (LLViewerTexture* img = gTextureList.getImage(tex_id))
|
||||
{
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uuid = object->getTE(j)->getMaterialParams()->getSpecularID();
|
||||
if (uuid.notNull())
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy diffuse per face
|
||||
if (LLViewerTexture* img = object->getTEImage(j))
|
||||
{
|
||||
LLViewerTexture* img = gTextureList.getImage(uuid);
|
||||
if (img)
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
}
|
||||
|
||||
// Legacy materials per face (normal/specular)
|
||||
if (te->getMaterialParams().notNull())
|
||||
{
|
||||
uuid = te->getMaterialParams()->getNormalID();
|
||||
if (uuid.notNull())
|
||||
{
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
if (LLViewerTexture* img = gTextureList.getImage(uuid))
|
||||
{
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
}
|
||||
}
|
||||
|
||||
uuid = te->getMaterialParams()->getSpecularID();
|
||||
if (uuid.notNull())
|
||||
{
|
||||
if (LLViewerTexture* img = gTextureList.getImage(uuid))
|
||||
{
|
||||
calculateTextureMemory(img, object_texture_list, object_texture_memory, object_vram_memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,6 +101,22 @@ void LLFloaterMarketplace::openMarketplaceURL(const std::string& url)
|
||||
// static
|
||||
bool LLFloaterMarketplace::isMarketplaceURL(const std::string& url)
|
||||
{
|
||||
auto trimURL = [](std::string_view url) -> std::string_view
|
||||
{
|
||||
if (url.starts_with("https://"))
|
||||
url.remove_prefix(8);
|
||||
else if (url.starts_with("http://"))
|
||||
url.remove_prefix(7);
|
||||
|
||||
while (!url.empty() && url.back() == '/')
|
||||
url.remove_suffix(1);
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
static LLCachedControl<std::string> marketplace_url(gSavedSettings, "MarketplaceURL", "https://marketplace.secondlife.com/");
|
||||
return url.starts_with(marketplace_url());
|
||||
std::string_view marketplace_url_trimmed = trimURL(marketplace_url());
|
||||
std::string_view url_trimmed = trimURL(url);
|
||||
|
||||
return !marketplace_url_trimmed.empty() && url_trimmed.starts_with(marketplace_url_trimmed);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
* App-wide preferences. Note that these are not per-user,
|
||||
* because we need to load many preferences before we have
|
||||
* a login name.
|
||||
@@ -38,7 +37,6 @@
|
||||
|
||||
#include "message.h"
|
||||
#include "llfloaterautoreplacesettings.h"
|
||||
#include "llviewertexturelist.h"
|
||||
#include "llagent.h"
|
||||
#include "llagentcamera.h"
|
||||
#include "llcheckboxctrl.h"
|
||||
@@ -58,8 +56,6 @@
|
||||
#include "llfloatersidepanelcontainer.h"
|
||||
// <FS:Ansariel> [FS communication UI]
|
||||
//#include "llfloaterimsession.h"
|
||||
#include "fsfloaterim.h"
|
||||
#include "fsfloaternearbychat.h"
|
||||
// </FS:Ansariel> [FS communication UI]
|
||||
#include "llkeyboard.h"
|
||||
#include "llmodaldialog.h"
|
||||
@@ -136,17 +132,16 @@
|
||||
#include "fsavatarrenderpersistence.h"
|
||||
#include "fsdroptarget.h"
|
||||
#include "fsfloaterimcontainer.h"
|
||||
#include "fspanelpreferenceuisounds.h" // <FS:PP> UI Sounds
|
||||
#include "growlmanager.h"
|
||||
#include "lfsimfeaturehandler.h"
|
||||
#include "llaudioengine.h" // <FS:Ansariel> Output device selection
|
||||
#include "llavatarname.h" // <FS:CR> Deeper name cache stuffs
|
||||
#include "llclipboard.h" // <FS:Zi> Support preferences search SLURLs
|
||||
#include "lldiriterator.h" // <Kadah> for populating the fonts combo
|
||||
#include "llline.h"
|
||||
#include "llfiltereditor.h" // <FS:Zi> FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search.
|
||||
#include "lllocationhistory.h"
|
||||
#include "llpanelblockedlist.h"
|
||||
#include "llpanelmaininventory.h"
|
||||
#include "llspellcheck.h"
|
||||
#include "lltoolbarview.h"
|
||||
#include "lltoolpie.h"
|
||||
#include "llviewermenufile.h" // <FS:LO> FIRE-23606 Reveal path to external script editor in prefernces
|
||||
@@ -166,9 +161,6 @@
|
||||
#endif
|
||||
// </FS:LO>
|
||||
|
||||
// <FS:Zi> FIRE-19539 - Include the alert messages in Prefs>Notifications>Alerts in preference Search.
|
||||
#include "llfiltereditor.h"
|
||||
#include "llviewershadermgr.h"
|
||||
//<FS:HG> FIRE-6340, FIRE-6567 - Setting Bandwidth issues
|
||||
//const F32 BANDWIDTH_UPDATER_TIMEOUT = 0.5f;
|
||||
char const* const VISIBILITY_DEFAULT = "default";
|
||||
@@ -970,7 +962,7 @@ LLFloaterPreference::~LLFloaterPreference()
|
||||
// bool has_first_selected = (mDisabledPopups->getFirstSelected()!=NULL);
|
||||
// mEnablePopupBtn->setEnabled(has_first_selected);
|
||||
//
|
||||
// has_first_selected = (mEnabledPopups.getFirstSelected()!=NULL);
|
||||
// has_first_selected = (mEnabledPopups->getFirstSelected()!=NULL);
|
||||
// mDisablePopupBtn->setEnabled(has_first_selected);
|
||||
//
|
||||
// LLFloater::draw();
|
||||
@@ -3131,9 +3123,6 @@ void LLFloaterPreference::onLogChatHistorySaved()
|
||||
// <FS:PP> Load UI Sounds tabs settings
|
||||
void LLFloaterPreference::updateUISoundsControls()
|
||||
{
|
||||
getChild<LLComboBox>("PlayModeUISndNewIncomingIMSession")->setValue((int)gSavedSettings.getU32("PlayModeUISndNewIncomingIMSession")); // 0, 1, 2, 3. Shared with Chat > Notifications > "When receiving Instant Messages"
|
||||
getChild<LLComboBox>("PlayModeUISndNewIncomingGroupIMSession")->setValue((int)gSavedSettings.getU32("PlayModeUISndNewIncomingGroupIMSession")); // 0, 1, 2, 3. Shared with Chat > Notifications > "When receiving Group Instant Messages"
|
||||
getChild<LLComboBox>("PlayModeUISndNewIncomingConfIMSession")->setValue((int)gSavedSettings.getU32("PlayModeUISndNewIncomingConfIMSession")); // 0, 1, 2, 3. Shared with Chat > Notifications > "When receiving AdHoc Instant Messages"
|
||||
#ifdef OPENSIM
|
||||
// <FS:Beq> OpenSim has option to not attenuate nearby local voice by distance
|
||||
auto earPosGroup = findChild<LLRadioGroup>("ear_location");
|
||||
@@ -3145,14 +3134,14 @@ void LLFloaterPreference::updateUISoundsControls()
|
||||
earPosGroup->setIndexEnabled(hearNearbyVoiceFullVolume, LLGridManager::instance().isInOpenSim());
|
||||
}
|
||||
// <FS:Beq>
|
||||
getChild<LLTextBox>("textFSRestartOpenSim")->setVisible(true);
|
||||
getChild<LLLineEditor>("UISndRestartOpenSim")->setVisible(true);
|
||||
getChild<LLButton>("Prev_UISndRestartOpenSim")->setVisible(true);
|
||||
getChild<LLButton>("Def_UISndRestartOpenSim")->setVisible(true);
|
||||
getChild<LLCheckBoxCtrl>("PlayModeUISndRestartOpenSim")->setVisible(true);
|
||||
#endif
|
||||
getChild<LLComboBox>("UseLSLFlightAssist")->setValue((int)gSavedPerAccountSettings.getF32("UseLSLFlightAssist")); // Flight Assist combo box; Not sound-related, but better to place it here instead of creating whole new void
|
||||
|
||||
if (FSPanelPreferenceUISounds* ui_sounds_panel = findChild<FSPanelPreferenceUISounds>("UI Sounds tab"))
|
||||
{
|
||||
ui_sounds_panel->refreshList();
|
||||
}
|
||||
|
||||
// FIRE-9856: Mute sound effects disable plays sound from collisions and plays sound from gestures checkbox not disable after restart/relog
|
||||
bool mute_sound_effects = gSavedSettings.getBOOL("MuteSounds");
|
||||
bool mute_all_sounds = gSavedSettings.getBOOL("MuteAudio");
|
||||
@@ -4036,11 +4025,6 @@ void LLPanelPreferenceGraphics::onPresetsListChange()
|
||||
//{
|
||||
// instance->saveSettings(); //make cancel work correctly after changing the preset
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// std::string dummy;
|
||||
// instance->saveGraphicsPreset(dummy);
|
||||
//}
|
||||
}
|
||||
|
||||
void LLPanelPreferenceGraphics::setPresetText()
|
||||
@@ -4128,6 +4112,7 @@ bool LLPanelPreferenceGraphics::hasDirtyChilds()
|
||||
view_stack.push_back(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "lldelayedgestureerror.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llviewermessage.h"
|
||||
#include "llviewercontrol.h" // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
#include "llvoavatarself.h"
|
||||
#include "llviewerstats.h"
|
||||
// <FS:Ansariel> [FS Communication UI]
|
||||
@@ -71,6 +72,14 @@ const F32 MAX_WAIT_ANIM_SECS = 60.f;
|
||||
// This should be relatively long, but not too long. 10 minutes is enough
|
||||
const F32 MAX_WAIT_KEY_SECS = 60.f * 10.f;
|
||||
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
static bool are_gestures_enabled()
|
||||
{
|
||||
static LLCachedControl<bool> gestures_enabled(gSavedPerAccountSettings, "FSGesturesEnabled", true);
|
||||
return gestures_enabled;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// Lightweight constructor.
|
||||
// init() does the heavy lifting.
|
||||
LLGestureMgr::LLGestureMgr()
|
||||
@@ -543,6 +552,7 @@ void LLGestureMgr::replaceGesture(const LLUUID& item_id, const LLUUID& new_asset
|
||||
void LLGestureMgr::playGesture(LLMultiGesture* gesture, bool fromKeyPress)
|
||||
{
|
||||
if (!gesture) return;
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
// [RLVa:KB] - Checked: RLVa-2.0.0 | Handles: @sendgesture
|
||||
if (!RlvActions::canPlayGestures())
|
||||
@@ -627,6 +637,8 @@ void LLGestureMgr::playGesture(LLMultiGesture* gesture, bool fromKeyPress)
|
||||
// Convenience function that looks up the item_id for you.
|
||||
void LLGestureMgr::playGesture(const LLUUID& item_id)
|
||||
{
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
const LLUUID& base_item_id = gInventory.getLinkedItemID(item_id);
|
||||
|
||||
item_map_t::iterator it = mActive.find(base_item_id);
|
||||
@@ -644,6 +656,17 @@ void LLGestureMgr::playGesture(const LLUUID& item_id)
|
||||
// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
|
||||
bool LLGestureMgr::triggerAndReviseString(const std::string &utf8str, std::string* revised_string)
|
||||
{
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
if (!are_gestures_enabled())
|
||||
{
|
||||
if (revised_string)
|
||||
{
|
||||
*revised_string = utf8str;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
std::string tokenized = utf8str;
|
||||
|
||||
bool found_gestures = false;
|
||||
@@ -737,6 +760,8 @@ bool LLGestureMgr::triggerAndReviseString(const std::string &utf8str, std::strin
|
||||
|
||||
bool LLGestureMgr::triggerGesture(KEY key, MASK mask)
|
||||
{
|
||||
if (!are_gestures_enabled()) return false; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
std::vector <LLMultiGesture *> matching;
|
||||
item_map_t::iterator it;
|
||||
|
||||
@@ -772,6 +797,8 @@ bool LLGestureMgr::triggerGesture(KEY key, MASK mask)
|
||||
|
||||
bool LLGestureMgr::triggerGestureRelease(KEY key, MASK mask)
|
||||
{
|
||||
if (!are_gestures_enabled()) return false; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
std::vector <LLMultiGesture *> matching;
|
||||
item_map_t::iterator it;
|
||||
|
||||
@@ -811,6 +838,21 @@ struct IsGesturePlaying
|
||||
|
||||
void LLGestureMgr::update()
|
||||
{
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
if (!are_gestures_enabled())
|
||||
{
|
||||
if (!mPlaying.empty())
|
||||
{
|
||||
std::vector<LLMultiGesture*> playing_copy = mPlaying;
|
||||
for (LLMultiGesture* gesture : playing_copy)
|
||||
{
|
||||
stopGesture(gesture);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i < (S32)mPlaying.size(); ++i)
|
||||
{
|
||||
@@ -1487,6 +1529,13 @@ void LLGestureMgr::notifyObservers()
|
||||
|
||||
bool LLGestureMgr::matchPrefix(const std::string& in_str, std::string* out_str)
|
||||
{
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
if (!are_gestures_enabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
auto in_len = in_str.length();
|
||||
|
||||
//return whole trigger, if received text equals to it
|
||||
|
||||
@@ -325,7 +325,7 @@ void LLGroupList::refresh()
|
||||
sort();
|
||||
|
||||
// <FS:PP> Group favorites / pinning
|
||||
if (has_favorites && has_non_favorites && !have_filter)
|
||||
if (mShowFavoritesSeparator && has_favorites && has_non_favorites && !have_filter)
|
||||
{
|
||||
addFavoritesSeparator();
|
||||
}
|
||||
@@ -347,14 +347,50 @@ void LLGroupList::refresh()
|
||||
else
|
||||
{
|
||||
clear();
|
||||
S32 regular_group_count = 0; // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
|
||||
for (group_map_t::iterator it = mGroups.begin(); it != mGroups.end(); ++it)
|
||||
{
|
||||
addNewItem(it->second, it->first, LLUUID::null, ADD_BOTTOM);
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
if (it->second.notNull())
|
||||
{
|
||||
++regular_group_count;
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
// Sort the list.
|
||||
sort();
|
||||
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
if (!mSecondaryGroups.empty())
|
||||
{
|
||||
bool contact_set_separator_added = false;
|
||||
for (const std::string& name : mSecondaryGroups)
|
||||
{
|
||||
if (name.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!contact_set_separator_added && regular_group_count > 0)
|
||||
{
|
||||
LLGroupListSeparator* separator = new LLGroupListSeparator();
|
||||
static const LLUUID SECONDARY_SEPARATOR_UUID("00000000-0000-0000-0000-000000000002");
|
||||
addItem(separator, SECONDARY_SEPARATOR_UUID, ADD_BOTTOM, false);
|
||||
contact_set_separator_added = true;
|
||||
}
|
||||
LLGroupListItem* item = addNewItem(LLUUID::null, name, LLUUID::null, ADD_BOTTOM);
|
||||
if (item)
|
||||
{
|
||||
if (auto color_it = mSecondaryGroupColors.find(name); color_it != mSecondaryGroupColors.end())
|
||||
{
|
||||
item->setCustomTextColor(color_it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
setDirty(false);
|
||||
@@ -382,13 +418,31 @@ void LLGroupList::setGroups(const std::map< std::string,LLUUID> group_list)
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
void LLGroupList::setSecondaryGroups(const std::vector<std::string>& group_names, const std::map<std::string, LLColor4>& group_colors)
|
||||
{
|
||||
mSecondaryGroups = group_names;
|
||||
mSecondaryGroupColors = group_colors;
|
||||
setDirty(true);
|
||||
}
|
||||
|
||||
std::string LLGroupList::getSelectedGroupName()
|
||||
{
|
||||
if (const LLGroupListItem* selected_item = dynamic_cast<const LLGroupListItem*>(getSelectedItem()))
|
||||
{
|
||||
return selected_item->getGroupName();
|
||||
}
|
||||
return LLStringUtil::null;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PRIVATE Section
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// <FS:PP> Group favorites / pinning
|
||||
// <FS:PP> Group favorites / pinning; FIRE-32401: Contact Sets on groups list in profile
|
||||
// void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile)
|
||||
void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile, bool is_favorite)
|
||||
LLGroupListItem* LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos, bool visible_in_profile, bool is_favorite)
|
||||
// </FS:PP>
|
||||
{
|
||||
LLGroupListItem* item = new LLGroupListItem(mForAgent, mShowIcons);
|
||||
@@ -413,6 +467,7 @@ void LLGroupList::addNewItem(const LLUUID& id, const std::string& name, const LL
|
||||
addItem(item, id, pos);
|
||||
|
||||
// setCommentVisible(false);
|
||||
return item; // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
}
|
||||
|
||||
// virtual
|
||||
@@ -759,6 +814,13 @@ void LLGroupListItem::setVisibleInProfile(bool visible)
|
||||
mGroupNameBox->setColor(LLUIColorTable::instance().getColor((visible ? "GroupVisibleInProfile" : "GroupHiddenInProfile"), LLColor4::red).get());
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
void LLGroupListItem::setCustomTextColor(const LLColor4& color)
|
||||
{
|
||||
mGroupNameBox->setColor(color);
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Private Section
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -34,10 +34,16 @@
|
||||
#include "llpanel.h"
|
||||
#include "llstyle.h"
|
||||
#include "lltoggleablemenu.h"
|
||||
#include "v4color.h" // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
|
||||
#include "llgroupmgr.h"
|
||||
|
||||
#include <boost/signals2.hpp> // <FS:PP> Group favorites / pinning
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
#include <map>
|
||||
#include <vector>
|
||||
class LLGroupListItem;
|
||||
// </FS:PP>
|
||||
|
||||
/**
|
||||
* Auto-updating list of agent groups.
|
||||
@@ -70,7 +76,12 @@ public:
|
||||
bool getIconsVisible() const { return mShowIcons; }
|
||||
void setIconsVisible(bool show_icons) { mShowIcons = show_icons; }
|
||||
void setShowNone(bool show_none) { mShowNone = show_none; }
|
||||
void setShowFavoritesSeparator(bool show) { mShowFavoritesSeparator = show; setDirty(true); } // <FS:PP> Group favorites / pinning
|
||||
void setGroups(const std::map< std::string,LLUUID> group_list);
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
void setSecondaryGroups(const std::vector<std::string>& group_names, const std::map<std::string, LLColor4>& group_colors = {});
|
||||
std::string getSelectedGroupName();
|
||||
// </FS:PP>
|
||||
|
||||
LLToggleableMenu* getContextMenu() const { return mContextMenuHandle.get(); }
|
||||
|
||||
@@ -79,9 +90,9 @@ public:
|
||||
private:
|
||||
void setDirty(bool val = true) { mDirty = val; }
|
||||
void refresh();
|
||||
// <FS:PP> Group favorites / pinning
|
||||
// <FS:PP> Group favorites / pinning; FIRE-32401: Contact Sets on groups list in profile
|
||||
// void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true);
|
||||
void addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true, bool is_favorite = false);
|
||||
LLGroupListItem* addNewItem(const LLUUID& id, const std::string& name, const LLUUID& icon_id, EAddPosition pos = ADD_BOTTOM, bool visible_in_profile = true, bool is_favorite = false);
|
||||
void addFavoritesSeparator();
|
||||
void onFavoritesChanged();
|
||||
// </FS:PP>
|
||||
@@ -99,8 +110,13 @@ private:
|
||||
|
||||
bool mForAgent;
|
||||
bool mShowNone;
|
||||
bool mShowFavoritesSeparator{ true }; // <FS:PP> Group favorites / pinning
|
||||
typedef std::map< std::string,LLUUID> group_map_t;
|
||||
group_map_t mGroups;
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
std::vector<std::string> mSecondaryGroups;
|
||||
std::map<std::string, LLColor4> mSecondaryGroupColors;
|
||||
// </FS:PP>
|
||||
|
||||
boost::signals2::connection mFavoritesChangedConnection; // <FS:PP> Group favorites / pinning
|
||||
};
|
||||
@@ -144,6 +160,7 @@ public:
|
||||
void setGroupIconID(const LLUUID& group_icon_id);
|
||||
void setGroupIconVisible(bool visible);
|
||||
void setFavorite(bool favorite) { mIsFavorite = favorite; } // <FS:PP> Group favorites / pinning
|
||||
void setCustomTextColor(const LLColor4& color); // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
|
||||
virtual void changed(LLGroupChange gc);
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
#include "fscommon.h"
|
||||
#include "fsdata.h"
|
||||
#include "fskeywords.h"
|
||||
#include "lggcontactsets.h" // <FS:PP> FIRE-17006 Autoresponse based on Contact Set
|
||||
#include "llagentui.h"
|
||||
#include "llavataractions.h"
|
||||
#include "llgiveinventory.h"
|
||||
@@ -159,6 +160,58 @@ static std::string clean_name_from_task_im(const std::string& msg,
|
||||
return msg;
|
||||
}
|
||||
|
||||
// <FS> FS autoresponse feature
|
||||
std::string LLIMProcessing::getAutoresponseTextForAvatar(const LLUUID& from_id, bool is_do_not_disturb, bool is_autorespond, bool is_autorespond_nonfriends, bool is_afk, bool send_away_response, bool is_friend)
|
||||
{
|
||||
std::string response;
|
||||
if (is_do_not_disturb)
|
||||
{
|
||||
std::string override_response;
|
||||
if (LGGContactSets::getInstance()->getAutoresponseForFriend(from_id, ContactSetAutoresponseMode::BUSY, override_response))
|
||||
{
|
||||
response = override_response;
|
||||
}
|
||||
else
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("DoNotDisturbModeResponse");
|
||||
}
|
||||
}
|
||||
else if (is_autorespond_nonfriends && !is_friend)
|
||||
{
|
||||
std::string override_response;
|
||||
if (LGGContactSets::getInstance()->getAutoresponseForFriend(from_id, ContactSetAutoresponseMode::AUTORESPONSE_NONFRIENDS, override_response))
|
||||
{
|
||||
response = override_response;
|
||||
}
|
||||
else
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondNonFriendsResponse");
|
||||
}
|
||||
}
|
||||
else if (is_autorespond)
|
||||
{
|
||||
std::string override_response;
|
||||
if (LGGContactSets::getInstance()->getAutoresponseForFriend(from_id, ContactSetAutoresponseMode::AUTORESPONSE, override_response))
|
||||
{
|
||||
response = override_response;
|
||||
}
|
||||
else
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondModeResponse");
|
||||
}
|
||||
}
|
||||
else if (is_afk && send_away_response)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAwayAvatarResponse");
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Unknown auto-response mode" << LL_ENDL;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
// </FS>
|
||||
|
||||
const std::string NOT_ONLINE_MSG("User not online - message will be stored and delivered later.");
|
||||
const std::string NOT_ONLINE_INVENTORY("User not online - inventory has been saved.");
|
||||
void translate_if_needed(std::string& message)
|
||||
@@ -944,28 +997,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
|
||||
// <FS:Ansariel> FS autoresponse feature
|
||||
std::string my_name;
|
||||
LLAgentUI::buildFullname(my_name);
|
||||
if (is_do_not_disturb)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("DoNotDisturbModeResponse");
|
||||
}
|
||||
else if (is_autorespond_nonfriends && !is_friend)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondNonFriendsResponse");
|
||||
}
|
||||
else if (is_autorespond)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondModeResponse");
|
||||
}
|
||||
// <FS:PP> FIRE-10500: Autoresponse for (Away)
|
||||
else if (is_afk && FSSendAwayAvatarResponse)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAwayAvatarResponse");
|
||||
}
|
||||
// </FS:PP>
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Unknown auto-response mode" << LL_ENDL;
|
||||
}
|
||||
response = LLIMProcessing::getAutoresponseTextForAvatar(from_id, is_do_not_disturb, is_autorespond, is_autorespond_nonfriends, is_afk, FSSendAwayAvatarResponse, is_friend);
|
||||
pack_instant_message(
|
||||
gMessageSystem,
|
||||
gAgent.getID(),
|
||||
@@ -1973,22 +2005,7 @@ void LLIMProcessing::processNewMessage(LLUUID from_id,
|
||||
std::string my_name;
|
||||
std::string response;
|
||||
LLAgentUI::buildFullname(my_name);
|
||||
if (is_do_not_disturb)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("DoNotDisturbModeResponse");
|
||||
}
|
||||
else if (is_autorespond_nonfriends && !is_friend)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondNonFriendsResponse");
|
||||
}
|
||||
else if (is_autorespond)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondModeResponse");
|
||||
}
|
||||
else if (is_afk && FSSendAwayAvatarResponse)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAwayAvatarResponse");
|
||||
}
|
||||
response = LLIMProcessing::getAutoresponseTextForAvatar(from_id, is_do_not_disturb, is_autorespond, is_autorespond_nonfriends, is_afk, FSSendAwayAvatarResponse, is_friend);
|
||||
|
||||
if (!response.empty())
|
||||
{
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
class LLIMProcessing
|
||||
{
|
||||
public:
|
||||
// <FS> FS autoresponse feature
|
||||
static std::string getAutoresponseTextForAvatar(const LLUUID& from_id, bool is_do_not_disturb, bool is_autorespond, bool is_autorespond_nonfriends, bool is_afk, bool send_away_response, bool is_friend);
|
||||
|
||||
// Pre-process message for IM manager
|
||||
static void processNewMessage(LLUUID from_id,
|
||||
bool from_group,
|
||||
|
||||
+11
-23
@@ -60,6 +60,7 @@
|
||||
#include "llmd5.h"
|
||||
#include "llmutelist.h"
|
||||
#include "llrecentpeople.h"
|
||||
#include "llimprocessing.h" // <FS> FS autoresponse feature
|
||||
#include "llviewermessage.h"
|
||||
#include "llviewerwindow.h"
|
||||
#include "llnotifications.h"
|
||||
@@ -240,7 +241,10 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
|
||||
bool store_dnd_message = false; // flag storage of a dnd message
|
||||
bool is_session_focused = session_floater->isTornOff() && session_floater->hasFocus();
|
||||
bool contains_mention = LLUrlRegistry::getInstance()->containsAgentMention(msg["message"].asString());
|
||||
static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlaySoundChatMention", false);
|
||||
// <FS:PP> UI Sounds
|
||||
// static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlaySoundChatMention", false);
|
||||
static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlayModeUISndChatMention", false);
|
||||
// </FS:PP>
|
||||
bool play_snd_mention = contains_mention && play_snd_mention_pref && (msg["source_type"].asInteger() != CHAT_SOURCE_OBJECT);
|
||||
if (!LLFloater::isVisible(im_box) || im_box->isMinimized())
|
||||
{
|
||||
@@ -481,7 +485,10 @@ void notify_of_message(const LLSD& msg, bool is_dnd_msg)
|
||||
im_container->addFlashingSession(session_id);
|
||||
}
|
||||
|
||||
static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlaySoundChatMention", false);
|
||||
// <FS:PP> UI Sounds
|
||||
// static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlaySoundChatMention", false);
|
||||
static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlayModeUISndChatMention", false);
|
||||
// </FS:PP>
|
||||
if (!gAgent.isDoNotDisturb() && play_snd_mention_pref && (msg["source_type"].asInteger() != CHAT_SOURCE_OBJECT) && LLUrlRegistry::getInstance()->containsAgentMention(msg["message"].asString()))
|
||||
{
|
||||
make_ui_sound("UISndChatMention");
|
||||
@@ -3552,7 +3559,7 @@ void LLIMMgr::addMessage(
|
||||
static LLCachedControl<U32> PlayModeUISndNewIncomingIMSession(gSavedSettings, "PlayModeUISndNewIncomingIMSession");
|
||||
static LLCachedControl<U32> PlayModeUISndNewIncomingGroupIMSession(gSavedSettings, "PlayModeUISndNewIncomingGroupIMSession");
|
||||
static LLCachedControl<U32> PlayModeUISndNewIncomingConfIMSession(gSavedSettings, "PlayModeUISndNewIncomingConfIMSession");
|
||||
static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlaySoundChatMention", false);
|
||||
static LLCachedControl<bool> play_snd_mention_pref(gSavedSettings, "PlayModeUISndChatMention", false);
|
||||
bool play_snd_mention = play_snd_mention_pref && LLUrlRegistry::getInstance()->containsAgentMention(msg);
|
||||
bool do_not_disturb = gAgent.isDoNotDisturb();
|
||||
bool is_group_chat = false;
|
||||
@@ -4754,26 +4761,7 @@ void typingNameCallback(const LLUUID& av_id, const LLAvatarName& av_name, const
|
||||
std::string my_name;
|
||||
std::string response;
|
||||
LLAgentUI::buildFullname(my_name);
|
||||
if (is_busy)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("DoNotDisturbModeResponse");
|
||||
}
|
||||
else if (is_autorespond_nonfriends && !is_friend)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondNonFriendsResponse");
|
||||
}
|
||||
else if (is_autorespond)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAutorespondModeResponse");
|
||||
}
|
||||
else if (is_afk && FSSendAwayAvatarResponse)
|
||||
{
|
||||
response = gSavedPerAccountSettings.getString("FSAwayAvatarResponse");
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "Unknown auto-response mode" << LL_ENDL;
|
||||
}
|
||||
response = LLIMProcessing::getAutoresponseTextForAvatar(av_id, is_busy, is_autorespond, is_autorespond_nonfriends, is_afk, FSSendAwayAvatarResponse, is_friend);
|
||||
pack_instant_message(
|
||||
gMessageSystem,
|
||||
gAgent.getID(),
|
||||
|
||||
@@ -504,7 +504,10 @@ void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)
|
||||
item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
|
||||
if (item)
|
||||
{
|
||||
if(LLAssetType::AT_GESTURE == item->getType())
|
||||
// <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
// if(LLAssetType::AT_GESTURE == item->getType())
|
||||
if(LLAssetType::AT_GESTURE == item->getType() && !item->getIsLinkType())
|
||||
// </FS:PP>
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(item->getUUID());
|
||||
}
|
||||
@@ -520,7 +523,10 @@ void LLInvFVBridge::removeBatch(std::vector<LLFolderViewModelItem*>& batch)
|
||||
gInventory.collectDescendents( cat->getUUID(), descendent_categories, descendent_items, false );
|
||||
for (j=0; j<descendent_items.size(); j++)
|
||||
{
|
||||
if(LLAssetType::AT_GESTURE == descendent_items[j]->getType())
|
||||
// <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
// if(LLAssetType::AT_GESTURE == descendent_items[j]->getType())
|
||||
if(LLAssetType::AT_GESTURE == descendent_items[j]->getType() && !descendent_items[j]->getIsLinkType())
|
||||
// </FS:PP>
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(descendent_items[j]->getUUID());
|
||||
}
|
||||
@@ -3301,6 +3307,7 @@ bool LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
|
||||
{
|
||||
LLInventoryItem* item = descendent_items[i];
|
||||
if (item->getType() == LLAssetType::AT_GESTURE
|
||||
&& !item->getIsLinkType() // <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
&& LLGestureMgr::instance().isGestureActive(item->getUUID()))
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(item->getUUID());
|
||||
@@ -4894,6 +4901,8 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
|
||||
const LLUUID& marketplace_listings_id = model->getMarketplaceListingsUUID();
|
||||
const LLUUID &outfits_id = model->findCategoryUUIDForType(LLFolderType::FT_MY_OUTFITS);
|
||||
|
||||
const bool is_mp_listings_folder = isMarketplaceListingsFolder(); // <FS:PP> FIRE-36377 Do not show duplicated "new folder" in MP listings window context menu
|
||||
|
||||
// <FS:Ansariel> FIRE-11628: Option to delete broken links from AO folder
|
||||
if (mUUID == AOEngine::instance().getAOFolder())
|
||||
{
|
||||
@@ -4939,7 +4948,10 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
|
||||
{
|
||||
disabled_items.push_back(std::string("New Folder"));
|
||||
}
|
||||
if (isMarketplaceListingsFolder())
|
||||
// <FS:PP> FIRE-36377 Do not show duplicated "new folder" in MP listings window context menu
|
||||
// if (isMarketplaceListingsFolder())
|
||||
if (is_mp_listings_folder)
|
||||
// </FS:PP>
|
||||
{
|
||||
addMarketplaceContextMenuOptions(flags, items, disabled_items);
|
||||
if (LLMarketplaceData::instance().isUpdating(mUUID))
|
||||
@@ -5077,10 +5089,20 @@ void LLFolderBridge::buildContextMenuOptions(U32 flags, menuentry_vec_t& items
|
||||
// Do not allow to create 2-level subfolder in the Calling Card/Friends folder. EXT-694.
|
||||
if (!LLFriendCardsManager::instance().isCategoryInFriendFolder(cat))
|
||||
{
|
||||
items.push_back(std::string("New Folder"));
|
||||
menu_items_added = true;
|
||||
// <FS:PP> FIRE-36377 Do not show duplicated "new folder" in MP listings window context menu
|
||||
// items.push_back(std::string("New Folder"));
|
||||
// menu_items_added = true;
|
||||
if (!is_mp_listings_folder)
|
||||
{
|
||||
items.push_back(std::string("New Folder"));
|
||||
menu_items_added = true;
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
if (!isMarketplaceListingsFolder())
|
||||
// <FS:PP> FIRE-36377 Do not show duplicated "new folder" in MP listings window context menu
|
||||
// if (!isMarketplaceListingsFolder())
|
||||
if (!is_mp_listings_folder)
|
||||
// </FS:PP>
|
||||
{
|
||||
items.push_back(std::string("upload_options"));
|
||||
items.push_back(std::string("upload_def"));
|
||||
@@ -6435,6 +6457,7 @@ bool LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
|
||||
if (accept && drop)
|
||||
{
|
||||
if (inv_item->getType() == LLAssetType::AT_GESTURE
|
||||
&& !inv_item->getIsLinkType() // <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
&& LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(inv_item->getUUID());
|
||||
@@ -7671,7 +7694,15 @@ bool LLGestureBridge::removeItem()
|
||||
|
||||
// This will also force close the preview window, if it exists.
|
||||
// This may actually delete *this, if mUUID is in the COF.
|
||||
LLGestureMgr::instance().deactivateGesture(item_id);
|
||||
|
||||
// <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
// LLGestureMgr::instance().deactivateGesture(item_id);
|
||||
LLViewerInventoryItem* item = getItem();
|
||||
if (item && !item->getIsLinkType())
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(item_id);
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// If deactivateGesture deleted *this, then return out immediately.
|
||||
if (!model->getObject(item_id))
|
||||
|
||||
@@ -3444,6 +3444,7 @@ bool dragItemIntoFolder(LLUUID folder_id, LLInventoryItem* inv_item, bool drop,
|
||||
if (accept && drop)
|
||||
{
|
||||
if (inv_item->getType() == LLAssetType::AT_GESTURE
|
||||
&& !inv_item->getIsLinkType() // <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
&& LLGestureMgr::instance().isGestureActive(inv_item->getUUID()) && move_is_into_trash)
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(inv_item->getUUID());
|
||||
@@ -3980,6 +3981,7 @@ bool dragCategoryIntoFolder(LLUUID dest_id, LLInventoryCategory* inv_cat,
|
||||
{
|
||||
LLInventoryItem* item = descendent_items[i];
|
||||
if (item->getType() == LLAssetType::AT_GESTURE
|
||||
&& !item->getIsLinkType() // <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
&& LLGestureMgr::instance().isGestureActive(item->getUUID()))
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(item->getUUID());
|
||||
|
||||
@@ -4481,6 +4481,7 @@ void LLInventoryModel::removeCategory(const LLUUID& category_id)
|
||||
const LLViewerInventoryItem* item = (*iter);
|
||||
const LLUUID& item_id = item->getUUID();
|
||||
if (item->getType() == LLAssetType::AT_GESTURE
|
||||
&& !item->getIsLinkType() // <FS:PP> FIRE-36458 Prevent gesture from being deactivated when its link is moved to trash
|
||||
&& LLGestureMgr::instance().isGestureActive(item_id))
|
||||
{
|
||||
LLGestureMgr::instance().deactivateGesture(item_id);
|
||||
|
||||
@@ -553,6 +553,11 @@ void LLLandmarkActions::copySLURLtoClipboard(const LLUUID& landmarkInventoryItem
|
||||
|
||||
void copy_slurl_to_clipboard_callback(const std::string& slurl)
|
||||
{
|
||||
if (slurl.empty())
|
||||
{
|
||||
LLNotificationsUtil::add("LandmarkLocationUnknown");
|
||||
return;
|
||||
}
|
||||
gViewerWindow->getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl));
|
||||
LLSD args;
|
||||
args["SLURL"] = slurl;
|
||||
|
||||
@@ -876,7 +876,10 @@ void LLNavigationBar::invokeSearch(std::string search_text)
|
||||
collections.append("destinations");
|
||||
collections.append("places");
|
||||
key["collections"] = collections;
|
||||
LLFloaterReg::showInstance("search", key);
|
||||
// <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
//LLFloaterReg::showInstance("search", key);
|
||||
LLFloaterReg::showInstance(gSavedSettings.getBOOL("FSUseFSLegacySearch") ? "search" : "legacy_search", key);
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
void LLNavigationBar::clearHistoryCache()
|
||||
|
||||
@@ -95,6 +95,15 @@ bool LLPanelDirBrowser::postBuild()
|
||||
mNextPageBtn->setClickedCallback([this](LLUICtrl*, const LLSD&) { nextPage(); });
|
||||
mNextPageBtn->setVisible(false);
|
||||
|
||||
// <FS:Ansariel> Port over search term history
|
||||
mSearchComboBox = findChild<LLSearchComboBox>("name");
|
||||
if (mSearchComboBox)
|
||||
{
|
||||
mSearchComboBox->setCommitCallback(boost::bind(&LLPanelDirBrowser::onClickSearchCore, this));
|
||||
fillSearchComboBox();
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -176,11 +185,19 @@ void LLPanelDirBrowser::updateResultCount()
|
||||
{
|
||||
// Item count be off by a few if bogus items sent from database
|
||||
// Just use the number of results per page. JC
|
||||
result_text = llformat(">%d found", mResultsPerPage);
|
||||
// <FS:Ansariel> Make this localizable
|
||||
//result_text = llformat(">%d found", mResultsPerPage);
|
||||
LLStringUtil::format_map_t args;
|
||||
args["COUNT"] = llformat(">%d", mResultsPerPage);
|
||||
result_text = getString("found_text", args);
|
||||
}
|
||||
else
|
||||
{
|
||||
result_text = llformat("%d found", result_count);
|
||||
// <FS:Ansariel> Make this localizable
|
||||
//result_text = llformat("%d found", result_count);
|
||||
LLStringUtil::format_map_t args;
|
||||
args["COUNT"] = llformat("%d", mResultsPerPage);
|
||||
result_text = getString("found_text", args);
|
||||
}
|
||||
|
||||
childSetValue("result_text", result_text);
|
||||
@@ -190,7 +207,9 @@ void LLPanelDirBrowser::updateResultCount()
|
||||
// add none found response
|
||||
if (list->getItemCount() == 0)
|
||||
{
|
||||
list->setCommentText(std::string("None found.")); // *TODO: Translate
|
||||
// <FS:Ansariel> Make this localizable
|
||||
//list->setCommentText(std::string("None found.")); // *TODO: Translate
|
||||
list->setCommentText(getString("not_found_text"));
|
||||
list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
|
||||
}
|
||||
}
|
||||
@@ -372,6 +391,10 @@ void LLPanelDirBrowser::showDetailPanel(S32 type, LLSD id)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
mSelectedID = id;
|
||||
mFloaterDirectory->updateProfileButtonVisibility();
|
||||
}
|
||||
|
||||
|
||||
@@ -1064,7 +1087,9 @@ void LLPanelDirBrowser::setupNewSearch()
|
||||
|
||||
// ready the list for results
|
||||
list->operateOnAll(LLCtrlListInterface::OP_DELETE);
|
||||
list->setCommentText(LLTrans::getString("Searching"));
|
||||
// <FS:Ansariel> Make this localizable
|
||||
//list->setCommentText(LLTrans::getString("Searching"));
|
||||
list->setCommentText(getString("searching_text"));
|
||||
list->setEnabled(false);
|
||||
|
||||
mResultsReceived = 0;
|
||||
@@ -1084,6 +1109,13 @@ void LLPanelDirBrowser::onClickSearchCore(void* userdata)
|
||||
LLPanelDirBrowser* self = (LLPanelDirBrowser*)userdata;
|
||||
if (!self) return;
|
||||
|
||||
// <FS:Ansariel> Port over search term history
|
||||
if (self->mSearchComboBox && !self->mSearchComboBox->getValue().asString().empty())
|
||||
{
|
||||
LLSearchHistory::getInstance()->addEntry(self->mSearchComboBox->getValue().asString());
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
self->resetSearchStart();
|
||||
self->performQuery();
|
||||
}
|
||||
@@ -1157,3 +1189,19 @@ S32 LLPanelDirBrowser::showNextButton(S32 rows)
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
// <FS:Ansariel> Port over search term history
|
||||
void LLPanelDirBrowser::fillSearchComboBox()
|
||||
{
|
||||
if (!mSearchComboBox)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLSearchHistory::getInstance()->load();
|
||||
for (const auto& item : LLSearchHistory::getInstance()->getSearchHistoryList())
|
||||
{
|
||||
mSearchComboBox->add(item.search_query);
|
||||
}
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "llpanel.h"
|
||||
|
||||
#include "llframetimer.h"
|
||||
#include "llsearchcombobox.h"
|
||||
|
||||
class LLMessageSystem;
|
||||
class LLFloaterDirectory;
|
||||
@@ -101,6 +102,8 @@ public:
|
||||
|
||||
std::string filterShortWords( const std::string source_string, int shortest_word_length, bool& was_filtered );
|
||||
|
||||
virtual void openProfile() {}; // <FS:Ansariel> Add "open profile" button
|
||||
|
||||
protected:
|
||||
void updateResultCount();
|
||||
|
||||
@@ -114,6 +117,9 @@ protected:
|
||||
// rows to display.
|
||||
S32 showNextButton(S32 rows);
|
||||
|
||||
// <FS:Ansariel> Port over search term history
|
||||
void fillSearchComboBox();
|
||||
|
||||
protected:
|
||||
LLUUID mSearchID; // Unique ID for a pending search
|
||||
LLUUID mWantSelectID; // scroll item to select on arrival
|
||||
@@ -137,6 +143,9 @@ protected:
|
||||
LLFloaterDirectory* mFloaterDirectory;
|
||||
LLButton* mPrevPageBtn;
|
||||
LLButton* mNextPageBtn;
|
||||
|
||||
LLUUID mSelectedID; // <FS:Ansariel> Add "open profile" button
|
||||
LLSearchComboBox* mSearchComboBox{ nullptr }; // <FS:Ansariel> Port over search term history
|
||||
};
|
||||
|
||||
constexpr S32 RESULTS_PER_PAGE_DEFAULT = 100;
|
||||
|
||||
@@ -61,8 +61,10 @@ bool LLPanelDirClassified::postBuild()
|
||||
{
|
||||
LLPanelDirBrowser::postBuild();
|
||||
|
||||
childSetAction("Search", onClickSearchCore, this);
|
||||
setDefaultBtn("Search");
|
||||
// <FS:Ansariel> Port over search term history
|
||||
//childSetAction("Search", onClickSearchCore, this);
|
||||
//setDefaultBtn("Search");
|
||||
// </FS:Ansariel>
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,10 @@ bool LLPanelDirEvents::postBuild()
|
||||
|
||||
childSetCommitCallback("mature", onCommitMature, this);
|
||||
|
||||
childSetAction("Search", LLPanelDirBrowser::onClickSearchCore, this);
|
||||
setDefaultBtn("Search");
|
||||
// <FS:Ansariel> Port over search term history
|
||||
//childSetAction("Search", LLPanelDirBrowser::onClickSearchCore, this);
|
||||
//setDefaultBtn("Search");
|
||||
// </FS:Ansariel>
|
||||
|
||||
onDateModeCallback(NULL, this);
|
||||
|
||||
@@ -173,7 +175,10 @@ void LLPanelDirEvents::performQueryOrDelete(U32 event_id)
|
||||
U32 cat_id = childGetValue("category_combo").asInteger();
|
||||
|
||||
params << cat_id << "|";
|
||||
params << childGetValue("event_search_text").asString();
|
||||
// <FS:PP> FIRE-36501 Events don't have line_editor "event_search_text", but search_combo_box "name"
|
||||
// params << childGetValue("event_search_text").asString();
|
||||
params << childGetValue("name").asString();
|
||||
// </FS:PP>
|
||||
|
||||
// send the message
|
||||
if (0 == event_id)
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
#include "llviewercontrol.h"
|
||||
#include "llsearcheditor.h"
|
||||
|
||||
#include "llgroupactions.h"
|
||||
|
||||
static LLPanelInjector<LLPanelDirGroups> t_panel_dir_groups("panel_dir_groups");
|
||||
|
||||
LLPanelDirGroups::LLPanelDirGroups()
|
||||
@@ -48,8 +50,10 @@ bool LLPanelDirGroups::postBuild()
|
||||
|
||||
//getChild<LLLineEditor>("name")->setKeystrokeCallback(boost::bind(&LLPanelDirBrowser::onKeystrokeName, _1, _2), NULL);
|
||||
|
||||
childSetAction("Search", &LLPanelDirBrowser::onClickSearchCore, this);
|
||||
setDefaultBtn( "Search" );
|
||||
// <FS:Ansariel> Port over search term history
|
||||
//childSetAction("Search", &LLPanelDirBrowser::onClickSearchCore, this);
|
||||
//setDefaultBtn( "Search" );
|
||||
// </FS:Ansariel>
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -61,7 +65,12 @@ LLPanelDirGroups::~LLPanelDirGroups()
|
||||
// virtual
|
||||
void LLPanelDirGroups::performQuery()
|
||||
{
|
||||
if (childGetValue("name").asString().length() < mMinSearchChars)
|
||||
// <FS:PP> Improve query sanitization
|
||||
// if (childGetValue("name").asString().length() < mMinSearchChars)
|
||||
std::string search_text = childGetValue("name").asString();
|
||||
LLStringUtil::trim(search_text);
|
||||
if (search_text.length() < mMinSearchChars)
|
||||
// </FS:PP>
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +93,17 @@ void LLPanelDirGroups::performQuery()
|
||||
sendDirFindQuery(
|
||||
gMessageSystem,
|
||||
mSearchID,
|
||||
childGetValue("name").asString(),
|
||||
// <FS:PP> Search by UUID
|
||||
// childGetValue("name").asString(),
|
||||
search_text,
|
||||
// </FS:PP>
|
||||
scope,
|
||||
mSearchStart);
|
||||
}
|
||||
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
void LLPanelDirGroups::openProfile()
|
||||
{
|
||||
LLGroupActions::show(mSelectedID);
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
/*virtual*/ void performQuery();
|
||||
|
||||
static void onClickSearch(void *userdata);
|
||||
|
||||
void openProfile(); // <FS:Ansariel> Add "open profile" button
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
#include "llqueryflags.h"
|
||||
#include "llnotificationsutil.h"
|
||||
|
||||
// <FS:PP> Search by UUID
|
||||
#include "llavatarnamecache.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
// </FS:PP>
|
||||
|
||||
#include "llavataractions.h"
|
||||
|
||||
static LLPanelInjector<LLPanelDirPeople> t_panel_dir_people("panel_dir_people");
|
||||
|
||||
LLPanelDirPeople::LLPanelDirPeople()
|
||||
@@ -48,31 +55,73 @@ bool LLPanelDirPeople::postBuild()
|
||||
|
||||
//getChild<LLLineEditor>("name")->setKeystrokeCallback(boost::bind(&LLPanelDirBrowser::onKeystrokeName, _1, _2), NULL);
|
||||
|
||||
childSetAction("Search", &LLPanelDirBrowser::onClickSearchCore, this);
|
||||
setDefaultBtn( "Search" );
|
||||
// <FS:Ansariel> Port over search term history
|
||||
//childSetAction("Search", &LLPanelDirBrowser::onClickSearchCore, this);
|
||||
//setDefaultBtn( "Search" );
|
||||
// </FS:Ansariel>
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LLPanelDirPeople::~LLPanelDirPeople()
|
||||
{
|
||||
// <FS:PP> Search by UUID
|
||||
if (mAvatarNameCallbackConnection.connected())
|
||||
{
|
||||
mAvatarNameCallbackConnection.disconnect();
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
// virtual
|
||||
void LLPanelDirPeople::performQuery()
|
||||
{
|
||||
if (childGetValue("name").asString().length() < mMinSearchChars)
|
||||
// <FS:PP> Improve query sanitization
|
||||
// if (childGetValue("name").asString().length() < mMinSearchChars)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
std::string search_text = childGetValue("name").asString();
|
||||
LLStringUtil::replaceChar(search_text, '.', ' ');
|
||||
LLStringUtil::trim(search_text);
|
||||
if (search_text.length() < mMinSearchChars)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (LLUUID::validate(search_text))
|
||||
{
|
||||
setupNewSearch();
|
||||
|
||||
if (mAvatarNameCallbackConnection.connected())
|
||||
{
|
||||
mAvatarNameCallbackConnection.disconnect();
|
||||
}
|
||||
|
||||
const LLUUID avatar_id(search_text);
|
||||
LLAvatarName av_name;
|
||||
if (LLAvatarNameCache::get(avatar_id, &av_name))
|
||||
{
|
||||
addUUIDAvatarResult(avatar_id, av_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAvatarNameCallbackConnection = LLAvatarNameCache::get(avatar_id, boost::bind(&LLPanelDirPeople::onAvatarNameCallback, this, _1, _2));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// filter short words out of the query string
|
||||
// and indidate if we did have to filter it
|
||||
// The shortest username is 2 characters long.
|
||||
const S32 SHORTEST_WORD_LEN = 2;
|
||||
bool query_was_filtered = false;
|
||||
std::string query_string = LLPanelDirBrowser::filterShortWords(
|
||||
childGetValue("name").asString(),
|
||||
// <FS:PP> Improve query sanitization
|
||||
// childGetValue("name").asString(),
|
||||
search_text,
|
||||
// </FS:PP>
|
||||
SHORTEST_WORD_LEN,
|
||||
query_was_filtered );
|
||||
|
||||
@@ -103,3 +152,61 @@ void LLPanelDirPeople::performQuery()
|
||||
scope,
|
||||
mSearchStart);
|
||||
}
|
||||
|
||||
// <FS:PP> Search by UUID
|
||||
void LLPanelDirPeople::onAvatarNameCallback(const LLUUID& id, const LLAvatarName& av_name)
|
||||
{
|
||||
if (mAvatarNameCallbackConnection.connected())
|
||||
{
|
||||
mAvatarNameCallbackConnection.disconnect();
|
||||
}
|
||||
addUUIDAvatarResult(id, av_name);
|
||||
}
|
||||
|
||||
void LLPanelDirPeople::addUUIDAvatarResult(const LLUUID& id, const LLAvatarName& av_name)
|
||||
{
|
||||
LLScrollListCtrl* list = getChild<LLScrollListCtrl>("results");
|
||||
if (!list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string avatar_name = av_name.getUserName();
|
||||
if (avatar_name.empty())
|
||||
{
|
||||
avatar_name = av_name.getDisplayName();
|
||||
}
|
||||
if (avatar_name.empty())
|
||||
{
|
||||
avatar_name = id.asString();
|
||||
}
|
||||
|
||||
LLSD content;
|
||||
content["type"] = AVATAR_CODE;
|
||||
content["name"] = avatar_name;
|
||||
|
||||
LLSD row;
|
||||
row["id"] = id;
|
||||
row["columns"][0]["column"] = "icon";
|
||||
row["columns"][0]["type"] = "icon";
|
||||
row["columns"][0]["value"] = "icon_avatar_offline.tga";
|
||||
row["columns"][1]["column"] = "name";
|
||||
row["columns"][1]["value"] = avatar_name;
|
||||
row["columns"][1]["font"] = "SANSSERIF";
|
||||
|
||||
list->addElement(row);
|
||||
mResultsContents[id.asString()] = content;
|
||||
mHaveSearchResults = true;
|
||||
updateResultCount();
|
||||
list->setEnabled(true);
|
||||
list->selectFirstItem();
|
||||
onCommitList(nullptr, this);
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// <FS:Ansariel> Add "open profile" button
|
||||
void LLPanelDirPeople::openProfile()
|
||||
{
|
||||
LLAvatarActions::showProfile(mSelectedID);
|
||||
}
|
||||
// </FS:Ansariel>
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
#ifndef LL_LLPANELDIRPEOPLE_H
|
||||
#define LL_LLPANELDIRPEOPLE_H
|
||||
|
||||
#include <boost/signals2/connection.hpp> // <FS:PP> Search by UUID
|
||||
#include "llpaneldirbrowser.h"
|
||||
class LLLineEditor;
|
||||
class LLFloaterDirectory;
|
||||
class LLAvatarName; // <FS:PP> Search by UUID
|
||||
|
||||
class LLPanelDirPeople : public LLPanelDirBrowser
|
||||
{
|
||||
@@ -43,6 +45,16 @@ public:
|
||||
|
||||
static void onClickSearch(void *userdata);
|
||||
static void onKeystrokeName(LLLineEditor* line, void* data);
|
||||
|
||||
void openProfile(); // <FS:Ansariel> Add "open profile" button
|
||||
|
||||
// <FS:PP> Search by UUID
|
||||
private:
|
||||
void onAvatarNameCallback(const LLUUID& id, const LLAvatarName& av_name);
|
||||
void addUUIDAvatarResult(const LLUUID& id, const LLAvatarName& av_name);
|
||||
boost::signals2::connection mAvatarNameCallbackConnection;
|
||||
// </FS:PP>
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -59,8 +59,10 @@ bool LLPanelDirPlaces::postBuild()
|
||||
|
||||
//getChild<LLLineEditor>("name")->setKeystrokeCallback(boost::bind(&LLPanelDirBrowser::onKeystrokeName, _1, _2), NULL);
|
||||
|
||||
childSetAction("Search", &LLPanelDirBrowser::onClickSearchCore, this);
|
||||
setDefaultBtn("Search");
|
||||
// <FS:Ansariel> Port over search term history
|
||||
//childSetAction("Search", &LLPanelDirBrowser::onClickSearchCore, this);
|
||||
//setDefaultBtn("Search");
|
||||
// </FS:Ansariel>
|
||||
|
||||
mCurrentSortColumn = "dwell";
|
||||
mCurrentSortAscending = false;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "llbutton.h"
|
||||
#include "llfloaterdirectory.h"
|
||||
#include "lltextbox.h"
|
||||
#include "lluri.h" // <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
#include "llviewercontrol.h"
|
||||
#include "llweb.h"
|
||||
|
||||
@@ -89,11 +90,27 @@ void LLPanelDirWeb::onVisibilityChange(bool new_visibility)
|
||||
|
||||
void LLPanelDirWeb::navigateToDefaultPage()
|
||||
{
|
||||
// <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
navigateToSearchPage("standard", "");
|
||||
}
|
||||
|
||||
void LLPanelDirWeb::navigateToSearchPage(const std::string& category, const std::string& query, const std::string& collection)
|
||||
{
|
||||
// </FS:PP>
|
||||
std::string url = gSavedSettings.getString("SearchURL");
|
||||
|
||||
LLSD subs;
|
||||
subs["QUERY"] = "";
|
||||
subs["TYPE"] = "standard";
|
||||
// <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
// subs["QUERY"] = "";
|
||||
// subs["TYPE"] = "standard";
|
||||
subs["QUERY"] = LLURI::escape(query);
|
||||
subs["TYPE"] = category.empty() ? "standard" : category;
|
||||
subs["COLLECTION"] = "";
|
||||
if (subs["TYPE"].asString() == "standard" && !collection.empty())
|
||||
{
|
||||
subs["COLLECTION"] = "&collection_chosen=" + collection;
|
||||
}
|
||||
// </FS:PP>
|
||||
// Default to PG
|
||||
std::string maturity = "g";
|
||||
if (gAgent.prefersAdult())
|
||||
|
||||
@@ -48,6 +48,10 @@ public:
|
||||
|
||||
void navigateToDefaultPage();
|
||||
|
||||
// <FS:PP> FIRE-36483 Menu, navbar and toolbar button must open the same search window
|
||||
void navigateToSearchPage(const std::string& category, const std::string& query, const std::string& collection = "");
|
||||
// </FS:PP>
|
||||
|
||||
void setFloaterDirectory(LLFloaterDirectory* floater) { mFloaterDirectory = floater; }
|
||||
|
||||
protected:
|
||||
|
||||
+228
-24
@@ -140,12 +140,16 @@ protected:
|
||||
|
||||
if (online1 == online2)
|
||||
{
|
||||
std::string name1 = item1->getAvatarName();
|
||||
std::string name2 = item2->getAvatarName();
|
||||
// <FS:PP> FIRE-36478: Ignore alias quotation marks when comparing names from contact sets
|
||||
// std::string name1 = item1->getAvatarName();
|
||||
// std::string name2 = item2->getAvatarName();
|
||||
|
||||
LLStringUtil::toUpper(name1);
|
||||
LLStringUtil::toUpper(name2);
|
||||
// LLStringUtil::toUpper(name1);
|
||||
// LLStringUtil::toUpper(name2);
|
||||
|
||||
std::string name1 = getComparableName(item1);
|
||||
std::string name2 = getComparableName(item2);
|
||||
// </FS:PP>
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
@@ -153,6 +157,30 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
class LLAvatarItemOnlineSipStatusComparator : public LLAvatarItemComparator
|
||||
{
|
||||
public:
|
||||
LLAvatarItemOnlineSipStatusComparator() {};
|
||||
|
||||
protected:
|
||||
virtual bool doCompare(const LLAvatarListItem* item1, const LLAvatarListItem* item2) const
|
||||
{
|
||||
const bool online1 = LLAvatarTracker::instance().isBuddyOnline(item1->getAvatarId());
|
||||
const bool online2 = LLAvatarTracker::instance().isBuddyOnline(item2->getAvatarId());
|
||||
|
||||
if (online1 == online2)
|
||||
{
|
||||
std::string name1 = getComparableName(item1);
|
||||
std::string name2 = getComparableName(item2);
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
return online1 > online2;
|
||||
}
|
||||
};
|
||||
// </FS:PP>
|
||||
|
||||
/** Compares avatar items by distance between you and them */
|
||||
class LLAvatarItemDistanceComparator : public LLAvatarItemComparator
|
||||
{
|
||||
@@ -242,12 +270,16 @@ protected:
|
||||
|
||||
if (arr_time1 == arr_time2)
|
||||
{
|
||||
std::string name1 = item1->getAvatarName();
|
||||
std::string name2 = item2->getAvatarName();
|
||||
// <FS:PP> FIRE-36478: Ignore alias quotation marks when comparing names from contact sets
|
||||
// std::string name1 = item1->getAvatarName();
|
||||
// std::string name2 = item2->getAvatarName();
|
||||
|
||||
LLStringUtil::toUpper(name1);
|
||||
LLStringUtil::toUpper(name2);
|
||||
// LLStringUtil::toUpper(name1);
|
||||
// LLStringUtil::toUpper(name2);
|
||||
|
||||
std::string name1 = getComparableName(item1);
|
||||
std::string name2 = getComparableName(item2);
|
||||
// </FS:PP>
|
||||
return name1 < name2;
|
||||
}
|
||||
|
||||
@@ -257,6 +289,7 @@ protected:
|
||||
|
||||
static const LLAvatarItemRecentComparator RECENT_COMPARATOR;
|
||||
static const LLAvatarItemStatusComparator STATUS_COMPARATOR;
|
||||
static const LLAvatarItemOnlineSipStatusComparator ONLINE_SIP_STATUS_COMPARATOR; // <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
static LLAvatarItemDistanceComparator DISTANCE_COMPARATOR;
|
||||
static const LLAvatarItemRecentSpeakerComparator RECENT_SPEAKER_COMPARATOR;
|
||||
static LLAvatarItemRecentArrivalComparator RECENT_ARRIVAL_COMPARATOR;
|
||||
@@ -604,6 +637,7 @@ LLPanelPeople::LLPanelPeople()
|
||||
mCommitCallbackRegistrar.add("ContactSet.Action", boost::bind(&LLPanelPeople::onContactSetsMenuItemClicked, this, _2));
|
||||
mEnableCallbackRegistrar.add("ContactSet.Enable", boost::bind(&LLPanelPeople::onContactSetsEnable, this, _2));
|
||||
mContactSetChangedConnection = LGGContactSets::getInstance()->setContactSetChangeCallback(boost::bind(&LLPanelPeople::updateContactSets, this, _1));
|
||||
LLAvatarTracker::instance().addObserver(this);
|
||||
// [/FS:CR]
|
||||
|
||||
// <FS:Ansariel> FIRE-10839: Customizable radar columns (needed for Vintage skin)
|
||||
@@ -620,7 +654,10 @@ LLPanelPeople::~LLPanelPeople()
|
||||
delete mFriendListUpdater;
|
||||
delete mRecentListUpdater;
|
||||
|
||||
LLVoiceClient::removeObserver(this);
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
// LLVoiceClient::removeObserver(this);
|
||||
LLVoiceClient::removeObserver(static_cast<LLVoiceClientStatusObserver*>(this));
|
||||
// </FS:PP>
|
||||
|
||||
mNearbyFilterCommitConnection.disconnect();
|
||||
mFriedsFilterCommitConnection.disconnect();
|
||||
@@ -628,8 +665,10 @@ LLPanelPeople::~LLPanelPeople()
|
||||
mRecentFilterCommitConnection.disconnect();
|
||||
|
||||
// [FS:CR] Contact sets
|
||||
mContactSetsFilterCommitConnection.disconnect();
|
||||
if (mContactSetChangedConnection.connected())
|
||||
mContactSetChangedConnection.disconnect();
|
||||
LLAvatarTracker::instance().removeObserver(this);
|
||||
// [/FS:CR]
|
||||
}
|
||||
|
||||
@@ -754,6 +793,18 @@ bool LLPanelPeople::postBuild()
|
||||
mRecentFilterCommitConnection = recent_tab->getChild<LLFilterEditor>("recent_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
|
||||
mGroupsFilterCommitConnection = group_tab->getChild<LLFilterEditor>("groups_filter_input")->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
|
||||
|
||||
// <FS:PP> FIRE-17568: Search in Contact Sets
|
||||
if (LLFilterEditor* contact_sets_filter = getChild<LLFilterEditor>("contact_sets_filter_input", true))
|
||||
{
|
||||
mContactSetsFilterCommitConnection = contact_sets_filter->setCommitCallback(boost::bind(&LLPanelPeople::onFilterEdit, this, _2));
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
mOnlineFriendList->setUseContactSetColors(true);
|
||||
mAllFriendList->setUseContactSetColors(true);
|
||||
// </FS:PP>
|
||||
|
||||
// <FS:Ansariel> Use Firestorm radar menu handler
|
||||
//mNearbyList->setContextMenu(&LLPanelPeopleMenus::gNearbyPeopleContextMenu);
|
||||
// </FS:Ansariel>
|
||||
@@ -814,6 +865,9 @@ bool LLPanelPeople::postBuild()
|
||||
mContactSetList = getChild<LLAvatarList>("contact_list");
|
||||
if (mContactSetList)
|
||||
{
|
||||
mContactSetList->setUseContactSetColors(true);
|
||||
mContactSetList->setUseContactSetListStyle(true);
|
||||
mContactSetList->setAvatarDropCallback(boost::bind(&LLPanelPeople::handleAvatarDropToCurrentContactSet, this, _1, _2));
|
||||
mContactSetList->setCommitCallback(boost::bind(&LLPanelPeople::updateButtons, this));
|
||||
mContactSetList->setItemDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, _1));
|
||||
mContactSetList->setNoItemsCommentText(getString("empty_list"));
|
||||
@@ -837,7 +891,10 @@ bool LLPanelPeople::postBuild()
|
||||
// Must go after setting commit callback and initializing all pointers to children.
|
||||
mTabContainer->selectTabByName(NEARBY_TAB_NAME);
|
||||
|
||||
LLVoiceClient::addObserver(this);
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
// LLVoiceClient::addObserver(this);
|
||||
LLVoiceClient::addObserver(static_cast<LLVoiceClientStatusObserver*>(this));
|
||||
// </FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
|
||||
// call this method in case some list is empty and buttons can be in inconsistent state
|
||||
updateButtons();
|
||||
@@ -858,9 +915,26 @@ void LLPanelPeople::onChange(EStatusType status, const LLSD& channelInfo, bool p
|
||||
return;
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
if (mContactSetList && shouldSortByOnlineStatusForCurrentSet())
|
||||
{
|
||||
mContactSetList->sort();
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
void LLPanelPeople::changed(U32 mask)
|
||||
{
|
||||
if ((mask & LLFriendObserver::ONLINE) && mContactSetList && shouldSortByOnlineStatusForCurrentSet())
|
||||
{
|
||||
mContactSetList->sort();
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLPanelPeople::updateFriendListHelpText()
|
||||
{
|
||||
// show special help text for just created account to help finding friends. EXT-4836
|
||||
@@ -934,6 +1008,14 @@ void LLPanelPeople::updateFriendList()
|
||||
mOnlineFriendList->setDirty(true, !mOnlineFriendList->filterHasMatches());// do force update if list do NOT have items
|
||||
mAllFriendList->setDirty(true, !mAllFriendList->filterHasMatches());
|
||||
//update trash and other buttons according to a selected item
|
||||
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
if (mContactSetList && shouldSortByOnlineStatusForCurrentSet())
|
||||
{
|
||||
mContactSetList->sort();
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
updateButtons();
|
||||
showFriendsAccordionsIfNeeded();
|
||||
|
||||
@@ -1236,6 +1318,15 @@ void LLPanelPeople::onFilterEdit(const std::string& search_string)
|
||||
{
|
||||
mRecentList->setNameFilter(filter);
|
||||
}
|
||||
// <FS:PP> FIRE-17568: Search in Contact Sets
|
||||
else if (cur_tab == CONTACT_SETS_TAB_NAME)
|
||||
{
|
||||
if (mContactSetList)
|
||||
{
|
||||
mContactSetList->setNameFilter(filter);
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
void LLPanelPeople::onGroupLimitInfo()
|
||||
@@ -1266,6 +1357,13 @@ void LLPanelPeople::onTabSelected(const LLSD& param)
|
||||
|
||||
showFriendsAccordionsIfNeeded();
|
||||
|
||||
// <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
if (getActiveTabName() == CONTACT_SETS_TAB_NAME && mContactSetList && shouldSortByOnlineStatusForCurrentSet())
|
||||
{
|
||||
mContactSetList->sort();
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
// <FS:AO> Layout panels will not initialize at a constant size, force it here.
|
||||
if (mTabContainer->getCurrentPanel()->getName() == NEARBY_TAB_NAME)
|
||||
{
|
||||
@@ -1772,6 +1870,17 @@ bool LLPanelPeople::updateNearbyArrivalTime()
|
||||
// [FS:CR] Contact sets
|
||||
void LLPanelPeople::updateContactSets(LGGContactSets::EContactSetUpdate type)
|
||||
{
|
||||
// <FS:PP> FIRE-32748 Colorize Friends List with Contact Sets
|
||||
if (mOnlineFriendList)
|
||||
{
|
||||
mOnlineFriendList->refreshNames();
|
||||
}
|
||||
if (mAllFriendList)
|
||||
{
|
||||
mAllFriendList->refreshNames();
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case LGGContactSets::UPDATED_LISTS:
|
||||
@@ -1812,17 +1921,7 @@ void LLPanelPeople::generateContactList(const std::string& contact_set)
|
||||
|
||||
if (contact_set == CS_SET_ALL_SETS)
|
||||
{
|
||||
avatars = LGGContactSets::getInstance()->getListOfNonFriends();
|
||||
|
||||
// "All sets" includes buddies
|
||||
LLAvatarTracker::buddy_map_t all_buddies;
|
||||
LLAvatarTracker::instance().copyBuddyList(all_buddies);
|
||||
for (LLAvatarTracker::buddy_map_t::const_iterator buddy = all_buddies.begin();
|
||||
buddy != all_buddies.end();
|
||||
++buddy)
|
||||
{
|
||||
avatars.push_back(buddy->first);
|
||||
}
|
||||
avatars = LGGContactSets::getInstance()->getFriendsInAnySet();
|
||||
}
|
||||
else if (contact_set == CS_SET_NO_SETS)
|
||||
{
|
||||
@@ -1851,6 +1950,7 @@ void LLPanelPeople::generateContactList(const std::string& contact_set)
|
||||
}
|
||||
}
|
||||
}
|
||||
updateContactSetListSorting();
|
||||
mContactSetList->setDirty();
|
||||
}
|
||||
|
||||
@@ -1860,6 +1960,62 @@ void LLPanelPeople::generateCurrentContactList()
|
||||
generateContactList(mContactSetCombo->getValue().asString());
|
||||
}
|
||||
|
||||
void LLPanelPeople::updateContactSetListSorting()
|
||||
{
|
||||
if (!mContactSetList)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldSortByOnlineStatusForCurrentSet())
|
||||
{
|
||||
mContactSetList->setComparator(&ONLINE_SIP_STATUS_COMPARATOR);
|
||||
mContactSetList->sort();
|
||||
}
|
||||
else
|
||||
{
|
||||
mContactSetList->sortByName();
|
||||
}
|
||||
}
|
||||
|
||||
bool LLPanelPeople::shouldSortByOnlineStatusForCurrentSet() const
|
||||
{
|
||||
if (!mContactSetCombo)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string set_name = mContactSetCombo->getValue().asString();
|
||||
if (LGGContactSets::getInstance()->isInternalSetName(set_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return LGGContactSets::getInstance()->getSortByOnlineStatusForSet(set_name);
|
||||
}
|
||||
|
||||
bool LLPanelPeople::handleAvatarDropToCurrentContactSet(const LLUUID& avatar_id, bool drop)
|
||||
{
|
||||
if (!mContactSetCombo || avatar_id.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string set_name = mContactSetCombo->getValue().asString();
|
||||
if (LGGContactSets::getInstance()->isInternalSetName(set_name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (drop)
|
||||
{
|
||||
uuid_vec_t ids{ avatar_id };
|
||||
LGGContactSets::instance().addToSet(ids, set_name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLPanelPeople::onContactSetsEnable(const LLSD& userdata)
|
||||
{
|
||||
std::string item = userdata.asString();
|
||||
@@ -1945,6 +2101,10 @@ void LLPanelPeople::onContactSetsMenuItemClicked(const LLSD& userdata)
|
||||
}
|
||||
LLNotificationsUtil::add((selected_size > 1 ? "RemoveContactsFromSet" : "RemoveContactFromSet"), args, payload, &LGGContactSets::handleRemoveAvatarFromSetCallback);
|
||||
}
|
||||
else if (chosen_item == "move_contact")
|
||||
{
|
||||
moveSelectedContactsToSet();
|
||||
}
|
||||
else if (chosen_item == "set_config")
|
||||
{
|
||||
LLFloater* root_floater = gFloaterView->getParentFloater(this);
|
||||
@@ -1993,9 +2153,20 @@ void LLPanelPeople::onContactSetsMenuItemClicked(const LLSD& userdata)
|
||||
if (selected_uuids.empty()) return;
|
||||
|
||||
LLSD payload, args;
|
||||
args["AVATAR"] = LLSLURL("agent", selected_uuids.front(), "about").getSLURLString();
|
||||
payload["id"] = selected_uuids.front();
|
||||
LLNotificationsUtil::add("SetAvatarPseudonym", args, payload, &LGGContactSets::handleSetAvatarPseudonymCallback);
|
||||
if (selected_uuids.size() == 1)
|
||||
{
|
||||
args["AVATAR"] = LLSLURL("agent", selected_uuids.front(), "about").getSLURLString();
|
||||
payload["id"] = selected_uuids.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
args["COUNT"] = llformat("%d", static_cast<S32>(selected_uuids.size()));
|
||||
for (const LLUUID& id : selected_uuids)
|
||||
{
|
||||
payload["ids"].append(id);
|
||||
}
|
||||
}
|
||||
LLNotificationsUtil::add((selected_uuids.size() > 1 ? "SetAvatarPseudonymMultiple" : "SetAvatarPseudonym"), args, payload, &LGGContactSets::handleSetAvatarPseudonymCallback);
|
||||
}
|
||||
else if (chosen_item == "remove_pseudonym")
|
||||
{
|
||||
@@ -2036,6 +2207,29 @@ void LLPanelPeople::handlePickerCallback(const uuid_vec_t& ids, const std::strin
|
||||
|
||||
LGGContactSets::instance().addToSet(ids, set);
|
||||
}
|
||||
|
||||
void LLPanelPeople::moveSelectedContactsToSet()
|
||||
{
|
||||
if (!mContactSetCombo)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string source_set = mContactSetCombo->getValue().asString();
|
||||
if (LGGContactSets::getInstance()->isInternalSetName(source_set))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uuid_vec_t selected_uuids;
|
||||
getCurrentItemIDs(selected_uuids);
|
||||
if (selected_uuids.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLAvatarActions::moveToContactSet(selected_uuids, source_set);
|
||||
}
|
||||
// [/FS:CR]
|
||||
|
||||
// <FS:Ansariel> FIRE-10839: Customizable radar columns (needed for Vintage skin)
|
||||
@@ -2079,6 +2273,16 @@ bool LLPanelPeople::handleKeyHere(KEY key, MASK mask)
|
||||
getChild<LLFilterEditor>("recent_filter_input")->setFocus(true);
|
||||
return true;
|
||||
}
|
||||
// <FS:PP> FIRE-17568: Search in Contact Sets
|
||||
else if (cur_tab == CONTACT_SETS_TAB_NAME)
|
||||
{
|
||||
if (LLFilterEditor* filter_editor = getChild<LLFilterEditor>("contact_sets_filter_input", true))
|
||||
{
|
||||
filter_editor->setFocus(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
}
|
||||
|
||||
return LLPanel::handleKeyHere(key, mask);
|
||||
|
||||
@@ -55,6 +55,7 @@ class FSPanelRadar;
|
||||
|
||||
class LLPanelPeople
|
||||
: public LLPanel
|
||||
, public LLFriendObserver // <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
, public LLVoiceClientStatusObserver
|
||||
{
|
||||
LOG_CLASS(LLPanelPeople);
|
||||
@@ -68,6 +69,7 @@ public:
|
||||
// Implements LLVoiceClientStatusObserver::onChange() to enable call buttons
|
||||
// when voice is available
|
||||
void onChange(EStatusType status, const LLSD& channelInfo, bool proximal) override;
|
||||
void changed(U32 mask) override; // <FS:PP> FIRE-21531: Sort Contact Sets by Online Status
|
||||
|
||||
// <FS:Ansariel> CTRL-F focusses local search editor
|
||||
bool handleKeyHere(KEY key, MASK mask) override;
|
||||
@@ -199,14 +201,19 @@ private:
|
||||
boost::signals2::connection mFriedsFilterCommitConnection;
|
||||
boost::signals2::connection mGroupsFilterCommitConnection;
|
||||
boost::signals2::connection mRecentFilterCommitConnection;
|
||||
boost::signals2::connection mContactSetsFilterCommitConnection; // <FS:PP> FIRE-17568: Search in Contact Sets
|
||||
|
||||
// [FS:CR] Contact sets
|
||||
bool onContactSetsEnable(const LLSD& userdata);
|
||||
void onContactSetsMenuItemClicked(const LLSD& userdata);
|
||||
void handlePickerCallback(const uuid_vec_t& ids, const std::string& set);
|
||||
void moveSelectedContactsToSet();
|
||||
void refreshContactSets();
|
||||
void generateContactList(const std::string& contact_set);
|
||||
void generateCurrentContactList();
|
||||
void updateContactSetListSorting();
|
||||
bool shouldSortByOnlineStatusForCurrentSet() const;
|
||||
bool handleAvatarDropToCurrentContactSet(const LLUUID& avatar_id, bool drop);
|
||||
|
||||
void updateContactSets(LGGContactSets::EContactSetUpdate type);
|
||||
boost::signals2::connection mContactSetChangedConnection;
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#include "llvoiceclient.h"
|
||||
#include "llweb.h"
|
||||
#include "llviewernetwork.h" // <FS:Beq> For LLGridManager
|
||||
#include "lggcontactsets.h" // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
|
||||
#include "fsdata.h"
|
||||
#include "fsradar.h" // <FS:Zi> Update notes in radar when edited
|
||||
@@ -1043,7 +1044,10 @@ void LLPanelProfileSecondLife::resetData()
|
||||
|
||||
setDescriptionText(LLStringUtil::null);
|
||||
mGroups.clear();
|
||||
mGroupList->setGroups(mGroups);
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
// mGroupList->setGroups(mGroups);
|
||||
refreshGroupAndContactSetList();
|
||||
// </FS:PP>
|
||||
|
||||
bool own_profile = getSelfProfile();
|
||||
mCanSeeOnlineIcon->setVisible(false);
|
||||
@@ -1169,7 +1173,11 @@ void LLPanelProfileSecondLife::processProfileProperties(const LLAvatarData* avat
|
||||
mGroups[group_data.group_name] = group_data.group_id;
|
||||
}
|
||||
|
||||
mGroupList->setGroups(mGroups);
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
// mGroupList->setGroups(mGroups);
|
||||
refreshGroupAndContactSetList();
|
||||
// </FS:PP>
|
||||
|
||||
} // </FS>
|
||||
|
||||
// <FS:Beq> Restore UDP profiles
|
||||
@@ -1213,15 +1221,73 @@ void LLPanelProfileSecondLife::processGroupProperties(const LLAvatarGroups* avat
|
||||
mGroups[group_data.group_name] = group_data.group_id;
|
||||
}
|
||||
|
||||
mGroupList->setGroups(mGroups);
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
// mGroupList->setGroups(mGroups);
|
||||
refreshGroupAndContactSetList();
|
||||
// </FS:PP>
|
||||
|
||||
}
|
||||
// </FS>
|
||||
|
||||
// <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
void LLPanelProfileSecondLife::refreshGroupAndContactSetList()
|
||||
{
|
||||
std::vector<std::string> profile_contact_sets = LGGContactSets::instance().getFriendSets(getAvatarId());
|
||||
std::vector<std::string> filtered_contact_sets;
|
||||
std::map<std::string, LLColor4> profile_contact_set_colors;
|
||||
|
||||
mProfileContactSets.clear();
|
||||
for (const std::string& set_name : profile_contact_sets)
|
||||
{
|
||||
if (set_name.empty() || LGGContactSets::instance().isInternalSetName(set_name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
filtered_contact_sets.push_back(set_name);
|
||||
mProfileContactSets.insert(set_name);
|
||||
}
|
||||
|
||||
static LLCachedControl<bool> fsContactSetsColorizeFriends(gSavedSettings, "FSContactSetsColorizeFriends", false);
|
||||
if (fsContactSetsColorizeFriends)
|
||||
{
|
||||
for (const std::string& set_name : filtered_contact_sets)
|
||||
{
|
||||
profile_contact_set_colors[set_name] = LGGContactSets::instance().getSetColor(set_name);
|
||||
}
|
||||
}
|
||||
|
||||
mGroupList->setShowFavoritesSeparator(!getSelfProfile());
|
||||
mGroupList->setGroups(mGroups);
|
||||
mGroupList->setSecondaryGroups(filtered_contact_sets, profile_contact_set_colors);
|
||||
}
|
||||
|
||||
// void LLPanelProfileSecondLife::openGroupProfile()
|
||||
// {
|
||||
// LLUUID group_id = mGroupList->getSelectedUUID();
|
||||
// LLGroupActions::show(group_id);
|
||||
// }
|
||||
|
||||
void LLPanelProfileSecondLife::openGroupProfile()
|
||||
{
|
||||
const std::string selected_name = mGroupList->getSelectedGroupName();
|
||||
if (selected_name.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LLUUID group_id = mGroupList->getSelectedUUID();
|
||||
LLGroupActions::show(group_id);
|
||||
if (group_id.notNull())
|
||||
{
|
||||
LLGroupActions::show(group_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selected_name.empty() && mProfileContactSets.find(selected_name) != mProfileContactSets.end())
|
||||
{
|
||||
LLFloaterReg::showInstance("imcontacts", LLSD("contact_sets"));
|
||||
}
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void LLPanelProfileSecondLife::onAvatarNameCache(const LLUUID& agent_id, const LLAvatarName& av_name)
|
||||
{
|
||||
@@ -2307,6 +2373,13 @@ void LLPanelProfileSecondLife::onHideAgeCallback()
|
||||
|
||||
void LLPanelProfileSecondLife::onSaveDescriptionChanges()
|
||||
{
|
||||
// <FS:Trish> Fix applying changes when exiting profile causing the preview text to apply if preview mode is active
|
||||
if (mPreview)
|
||||
{
|
||||
onCommitMenu(LLSD("preview"));
|
||||
}
|
||||
// </FS:Trish>
|
||||
|
||||
mDescriptionText = mDescriptionEdit->getValue().asString();
|
||||
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
|
||||
{
|
||||
@@ -2972,6 +3045,13 @@ void LLPanelProfileFirstLife::onSetDescriptionDirty()
|
||||
|
||||
void LLPanelProfileFirstLife::onSaveDescriptionChanges()
|
||||
{
|
||||
// <FS:Trish> Fix applying changes when exiting profile causing the preview text to apply if preview mode is active
|
||||
if (mPreview)
|
||||
{
|
||||
onClickPreview();
|
||||
}
|
||||
// </FS:Trish>
|
||||
|
||||
mCurrentDescription = mDescriptionEdit->getValue().asString();
|
||||
if (!gAgent.getRegionCapability(PROFILE_PROPERTIES_CAP).empty())
|
||||
{
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
#include "llvoiceclient.h"
|
||||
#include "rlvhandler.h"
|
||||
|
||||
#include <set> // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
|
||||
// class LLPanelProfileClassifieds;
|
||||
// class LLTabContainer;
|
||||
|
||||
@@ -147,6 +149,7 @@ protected:
|
||||
*/
|
||||
// <FS> OpenSim
|
||||
void processGroupProperties(const LLAvatarGroups* avatar_groups);
|
||||
void refreshGroupAndContactSetList(); // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
|
||||
/**
|
||||
* Fills common for Avatar profile and My Profile fields.
|
||||
@@ -231,6 +234,7 @@ private:
|
||||
private:
|
||||
typedef std::map<std::string, LLUUID> group_map_t;
|
||||
group_map_t mGroups;
|
||||
std::set<std::string> mProfileContactSets; // <FS:PP> FIRE-32401: Contact Sets on groups list in profile
|
||||
void openGroupProfile();
|
||||
|
||||
LLTextBox* mStatusText; // <FS:Ansariel> Fix LL UI/UX design accident
|
||||
|
||||
@@ -784,6 +784,12 @@ void LLPanelProfilePick::apply()
|
||||
{
|
||||
if ((mNewPick || getIsLoaded()) && isDirty())
|
||||
{
|
||||
// <FS:Trish> Fix applying changes when exiting profile causing the preview text to apply if preview mode is active
|
||||
if (mPreview)
|
||||
{
|
||||
onClickPreview();
|
||||
}
|
||||
// </FS:Trish>
|
||||
sendUpdate();
|
||||
}
|
||||
}
|
||||
@@ -847,6 +853,13 @@ void LLPanelProfilePick::onClickTeleport()
|
||||
|
||||
void LLPanelProfilePick::enableSaveButton(bool enable)
|
||||
{
|
||||
//<FS:Trish> Fix Save Buttons being enabled in various cases while preview mode is on
|
||||
if (mPreview)
|
||||
{
|
||||
enable = false;
|
||||
}
|
||||
//</FS:Trish>
|
||||
|
||||
childSetVisible("save_changes_lp", enable);
|
||||
|
||||
childSetVisible("save_btn_lp", enable && !mNewPick);
|
||||
|
||||
@@ -52,11 +52,20 @@
|
||||
#include "llviewerregion.h"
|
||||
#include "llviewerstats.h"
|
||||
#include "llviewerassetupload.h"
|
||||
#include "llviewercontrol.h" // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
std::string NONE_LABEL;
|
||||
std::string SHIFT_LABEL;
|
||||
std::string CTRL_LABEL;
|
||||
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
static bool are_gestures_enabled()
|
||||
{
|
||||
static LLCachedControl<bool> gestures_enabled(gSavedPerAccountSettings, "FSGesturesEnabled", true);
|
||||
return gestures_enabled;
|
||||
}
|
||||
// </FS:PP>
|
||||
|
||||
void dialog_refresh_all();
|
||||
|
||||
// used for getting
|
||||
@@ -656,7 +665,11 @@ void LLPreviewGesture::refresh()
|
||||
mSaveBtn->setEnabled(false);
|
||||
|
||||
// Make sure preview button is enabled, so we can stop it
|
||||
mPreviewBtn->setEnabled(true);
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
// mPreviewBtn->setEnabled(true);
|
||||
mPreviewBtn->setEnabled(are_gestures_enabled());
|
||||
// </FS:PP>
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -774,7 +787,10 @@ void LLPreviewGesture::refresh()
|
||||
mActiveCheck->set(active);
|
||||
|
||||
// Can only preview if there are steps
|
||||
mPreviewBtn->setEnabled(step_count > 0);
|
||||
// <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
// mPreviewBtn->setEnabled(step_count > 0);
|
||||
mPreviewBtn->setEnabled(step_count > 0 && are_gestures_enabled());
|
||||
// </FS:PP>
|
||||
|
||||
// And can only save if changes have been made
|
||||
mSaveBtn->setEnabled(mDirty);
|
||||
@@ -1783,6 +1799,7 @@ void LLPreviewGesture::onClickSave(void* data)
|
||||
void LLPreviewGesture::onClickPreview(void* data)
|
||||
{
|
||||
LLPreviewGesture* self = (LLPreviewGesture*)data;
|
||||
if (!are_gestures_enabled()) return; // <FS:PP> FIRE-36169 Gestures enable/disable switch
|
||||
|
||||
if (!self->mPreviewGesture)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user