diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 9feaeb96ff..9167a771da 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -27530,6 +27530,19 @@ Change of this parameter will affect the layout of buttons in notification toast
Value
1
+
+ OmnifilterRuleSetID
+
+ Comment
+ The id of the current rule set for the Omnifilter.
+ Persist
+ 1
+ Type
+ S32
+ Value
+ -1
+
+
FSAutoOrderIMTabs
Comment
diff --git a/indra/newview/omnifilter.cpp b/indra/newview/omnifilter.cpp
index e9874372bf..02bb4abc03 100644
--- a/indra/newview/omnifilter.cpp
+++ b/indra/newview/omnifilter.cpp
@@ -29,6 +29,10 @@
#include "llcombobox.h"
#include "lllineeditor.h"
#include "lltexteditor.h"
+// [FIRE-36763] - Add rule sets to Omnifilter
+#include "llviewercontrol.h" // Needed for gSavedSettings
+#include "llnotificationsutil.h" // Needed for Notifications
+// [FIRE-36763]
#include "fsscrolllistctrl.h"
@@ -78,7 +82,11 @@ OmnifilterEngine::Needle* Omnifilter::getSelectedNeedle()
if (needle_name_cell)
{
const std::string& needle_name = needle_name_cell->getValue().asString();
- if (!needle_name.empty())
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ //if (!needle_name.empty())
+ // Only try to access the needle if the name is not empty and it is on the current needle list, otherwise it will cause an error
+ if (!needle_name.empty() && OmnifilterEngine::getInstance()->getNeedleList().contains(needle_name))
+ // [FIRE-36763]
{
return &OmnifilterEngine::getInstance()->getNeedleList().at(needle_name);
}
@@ -277,6 +285,237 @@ void Omnifilter::onDownNeedleClicked()
}
// [FIRE-36649]
+// [FIRE-36763] - Add rule sets to Omnifilter
+// Callback method for the New Rule Set button when clicked.
+// Creates an notification to the user to ask for the name of the new rule set.
+void Omnifilter::onNewRuleSetClicked()
+{
+ LLSD args;
+ // Default name of the new rule set
+ args["RULESETNAME"] = "New Rule Set";
+ // Display the new rule set notification and if the user presses the OK button, call the new rule set name selected callback method
+ LLNotificationsUtil::add("OmniFilterNewRuleSet", args, LLSD(),
+ boost::bind(&Omnifilter::onNewRuleSetNameSelectedCallback, this, _1, _2));
+}
+
+// Callback method for the Clone Rule Set button when clicked.
+// Creates an notification to the user to ask for the name of the clone rule set.
+void Omnifilter::onCloneRuleSetClicked()
+{
+ LLSD args;
+ // Default name of the cloned rule set
+ args["RULESETNAME"] = "Cloned Rule Set";
+ // Display the new rule set notification and if the user presses the OK button, call the new rule set name selected callback method
+ LLNotificationsUtil::add("OmniFilterCloneRuleSet", args, LLSD(),
+ boost::bind(&Omnifilter::onCloneRuleSetNameSelectedCallback, this, _1, _2));
+}
+
+// Callback method for the Remove Rule Set button when clicked.
+// Creates a notification to the user to ask if they are sure they want to remove the specified Rule Set.
+void Omnifilter::onRemoveRuleSetClicked()
+{
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+ // IF the user tries to remove the Default rule set, send an error as that is not allowed.
+ if (mRuleSetsCmb->getCurrentIndex() == 0)
+ {
+ LLNotificationsUtil::add("OmniFilterRemoveRuleSetDefault", LLSD(), LLSD());
+ }
+ // Else prompt the user with a confirmation notification if they really want to remove the
+ else
+ {
+ LLSD args;
+ args["RULESETNAME"] = instance->getCurrentSelectedRuleSet();
+ // Display the remove rule set notification and if the user presses the OK button, call the remove rule set confirm callback method
+ LLNotificationsUtil::add("OmniFilterRemoveRuleSet", args, LLSD(),
+ boost::bind(&Omnifilter::onRemoveRuleSetConfirmedCallback, this, _1, _2));
+ }
+}
+
+// New Rule Set callback, which does the actual cloning.
+void Omnifilter::onNewRuleSetNameSelectedCallback(const LLSD& notification, const LLSD& response)
+{
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0) // YES
+ {
+ std::string new_name = response["new_name"].asString();
+ // Try to perform the actual creating a new rule set.
+ S32 return_value = instance->addNewRuleSet(new_name);
+
+ if (return_value == 1)
+ {
+ S32 new_rule_index = gSavedSettings.getS32("OmnifilterRuleSetID");
+ // Add the new rule set to the Rule Set Drop Down
+ mRuleSetsCmb->add(new_name, LLSD(new_rule_index));
+ // Select the new rule set
+ mRuleSetsCmb->setCurrentByIndex(new_rule_index);
+
+ // Clear the Rule list
+ mNeedleListCtrl->clear();
+ mNeedleListCtrl->clearRows();
+
+ // Add the new item.
+ onAddNeedleClicked();
+
+ // Save the state of the OmniFilter Window
+ instance->setDirty(true);
+ }
+ // Else the user tried to create a new rule set with a blank name, so show an error.
+ else if (return_value == -1)
+ {
+ LLNotificationsUtil::add("OmniFilterrNewRuleSetBlank", LLSD(), LLSD(), boost::bind(&Omnifilter::onNewRuleSetClicked, this));
+ }
+ // Else the user tried to create a new rule set with a duplicate name, so show an error.
+ else if (return_value == 0)
+ {
+ LLNotificationsUtil::add("OmniFilterNewRuleSetDuplicate", LLSD(), LLSD(), boost::bind(&Omnifilter::onNewRuleSetClicked, this));
+ }
+ }
+}
+
+// Clone Rule Set callback, which does the actual cloning.
+void Omnifilter::onCloneRuleSetNameSelectedCallback(const LLSD& notification, const LLSD& response)
+{
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0) // YES
+ {
+ std::string new_name = response["new_name"].asString();
+ // Try to perform the actual cloning of the rule set.
+ S32 return_value = instance->addClonedRuleSet(new_name);
+ // If the cloning succeeded
+ if (return_value == 1)
+ {
+ // Get the index of the added rule set.
+ S32 new_rule_index = gSavedSettings.getS32("OmnifilterRuleSetID");
+ // Add the new rule set to the Rule Set Drop Down
+ mRuleSetsCmb->add(new_name, LLSD(new_rule_index));
+
+ // Select the new rule set
+ mRuleSetsCmb->setCurrentByIndex(new_rule_index);
+
+ // Save the state of the OmniFilter Window
+ instance->setDirty(true);
+ }
+ // Else the user tried to create a cloned rule set with a blank name, so show an error.
+ else if (return_value == -1)
+ {
+ LLNotificationsUtil::add("OmniFilterCloneRuleSetBlank", LLSD(), LLSD(), boost::bind(&Omnifilter::onCloneRuleSetClicked, this));
+ }
+ // Else the user tried to create a cloned rule set with a duplicate name, so show an error.
+ else if (return_value == 0)
+ {
+ LLNotificationsUtil::add("OmniFilterCloneRuleSetDuplicate", LLSD(), LLSD(), boost::bind(&Omnifilter::onCloneRuleSetClicked, this));
+ }
+ }
+}
+
+// Remove Rule Set callback, which does the actual removal.
+void Omnifilter::onRemoveRuleSetConfirmedCallback(const LLSD& notification, const LLSD& response)
+{
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+ // Get the user response
+ S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
+ if (option == 0) // YES
+ {
+ std::string tooltip_msg;
+ // Get the index of the current rule set being removed.
+ S32 current_index = gSavedSettings.getS32("OmnifilterRuleSetID");
+ // Try to perform the actual removal.
+ S32 return_value = instance->removeCurrentRuleSet();
+ // If the removal succeeded
+ if (return_value == 1)
+ {
+ // Remove the rule set from the Rule Set Drop Down
+ mRuleSetsCmb->remove(current_index);
+ // Reload the current rule set and switch the UI over to the Default rule set.
+ reloadRule();
+ mRuleSetsCmb->setCurrentByIndex(0);
+
+ // Save the state of the OmniFilter Window
+ instance->setDirty(true);
+ }
+ // Else the user tried to remove the default rule set, so show an error.
+ else if (return_value == 0)
+ {
+ LLNotificationsUtil::add("OmniFilterRemoveRuleSetDefault", LLSD(), LLSD());
+ }
+ // Else the user tried to remove a rule set that index was out of bounds, so show an error.
+ else if (return_value == -1)
+ {
+ LLSD args;
+ args["OUTOFBOUNDS"] = current_index;
+ LLNotificationsUtil::add("OmniFilterRemoveRuleSetOutofBounds", args, LLSD());
+ }
+ // Else the user tried to remove a rule set that had an name that does not exist in the map of rule sets, so show an error.
+ else if (return_value == -2)
+ {
+ LLSD args;
+ args["INVALIDNAME"] = instance->getCurrentSelectedRuleSet();
+ LLNotificationsUtil::add("OmniFilterRemoveRuleSetInvalidName", args, LLSD());
+ }
+ }
+}
+
+// Rule Set Drop Down on commit callback method
+void Omnifilter::onRuleSetChanged()
+{
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+
+ // Save the current
+ // Write the current rule set back to the storeage
+ instance->assignRuleSet(false);
+ // Store the current combo box index as the Needle Preset Index
+ gSavedSettings.setS32("OmnifilterRuleSetID", mRuleSetsCmb->getCurrentIndex());
+ // Assign and reload the current rule
+ instance->assignRuleSetNameFromSettings();
+
+ instance->assignRuleSet(true);
+ reloadRule();
+}
+
+// Reloads the full rule sets drop down widget (combobox)
+void Omnifilter::reloadRules()
+{
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+ // Clear the current needles
+ mRuleSetsCmb->clear();
+ S32 current_rule_set_id = gSavedSettings.getS32("OmnifilterRuleSetID");
+ for (S32 index = 0; index < instance->getOrderedRuleSetSize(); index++)
+ {
+ // Get the name from the ordered list of rule sets.
+ const std::string needle_rule_set_name(instance->getOrderedRuleSetName(index));
+ LLSD value(index);
+ // And add to the UI element. The value is the value that is passed along with the commit callback method.
+ mRuleSetsCmb->add(needle_rule_set_name, value);
+ }
+ // Finally, select the stored rule set
+ mRuleSetsCmb->selectByValue(LLSD(current_rule_set_id));
+}
+
+// Reloads the actual rule set UI elements, including the Rule scroll list and editor panel
+void Omnifilter::reloadRule()
+{
+ // Use static pointer for the instance so that don't have to keep requesting every time this code is touched.
+ static OmnifilterEngine* instance = OmnifilterEngine::getInstance();
+ // Clear the current needles
+ mNeedleListCtrl->clearRows();
+ mNeedleListCtrl->clear();
+
+ // Loop over the ordered list
+ for (const auto& needle_name : instance->getOrderedNeedleList())
+ {
+ // Get the current needle and use the addNeedle method to add the the UI.
+ const auto& needle = instance->getNeedleList()[needle_name];
+ addNeedle(needle_name, needle);
+ }
+
+ mNeedleListCtrl->selectFirstItem();
+ onSelectNeedle();
+}
+
+// [FIRE-36763]
+
void Omnifilter::onNeedleNameChanged()
{
const std::string& old_name = mNeedleListCtrl->getSelectedItemLabel(NEEDLE_NAME_COLUMN);
@@ -381,6 +620,13 @@ bool Omnifilter::postBuild()
mSenderCaseSensitiveCheck = getChild("sender_case");
mSenderMatchTypeCombo = getChild("sender_match_type");
mContentCtrl = getChild("content");
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ // Add the preset controls
+ mRuleSetsCmb = getChild("cmb_rule_sets"); // Rule Set Drop Down
+ mNewRuleSetBtn = getChild("btn_rule_set_new"); // New Rule Set
+ mCloneRuleSetBtn = getChild("btn_rule_set_clone"); // Clone Rule Set
+ mRemoveRuleSetBtn = getChild("btn_rule_set_remove"); // Remove Rule Set
+ // /FS:minerjr> [FIRE-36763]
mContentCaseSensitiveCheck = getChild("content_case");
mContentMatchTypeCombo = getChild("content_match_type");
mRegionNameCtrl = getChild("region_name");
@@ -412,6 +658,12 @@ bool Omnifilter::postBuild()
mFilterLogCtrl->deleteAllItems();
auto& instance = OmnifilterEngine::instance();
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ // Reload the rules set UI elements, uses the settings stored rule set ID
+ reloadRules();
+ instance.assignRuleSetNameFromSettings();
+ instance.assignRuleSet(true);
+ // [FIRE-36763]
// [FIRE-36649] - Add reordering to OmniFilter
//for (const auto& [needle_name, needle] : instance.getNeedleList())
// Loop over the ordered list
@@ -446,6 +698,13 @@ bool Omnifilter::postBuild()
mUpNeedleBtn->setCommitCallback(boost::bind(&Omnifilter::onUpNeedleClicked, this));
mDownNeedleBtn->setCommitCallback(boost::bind(&Omnifilter::onDownNeedleClicked, this));
// [FIRE-36649]
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ // Add the Rule Set controls
+ mRuleSetsCmb->setCommitCallback(boost::bind(&Omnifilter::onRuleSetChanged, this)); // Rule Set Drop Down
+ mNewRuleSetBtn->setCommitCallback(boost::bind(&Omnifilter::onNewRuleSetClicked, this)); // New Rule Set
+ mCloneRuleSetBtn->setCommitCallback(boost::bind(&Omnifilter::onCloneRuleSetClicked, this)); // Clone Rule Set
+ mRemoveRuleSetBtn->setCommitCallback(boost::bind(&Omnifilter::onRemoveRuleSetClicked, this)); // Remove Rule Set
+ // /FS:minerjr> [FIRE-36763]
mNeedleNameCtrl->setCommitCallback(boost::bind(&Omnifilter::onNeedleNameChanged, this));
mSenderNameCtrl->setCommitCallback(boost::bind(&Omnifilter::onNeedleChanged, this));
mSenderCaseSensitiveCheck->setCommitCallback(boost::bind(&Omnifilter::onNeedleChanged, this));
diff --git a/indra/newview/omnifilter.h b/indra/newview/omnifilter.h
index 7aafa3712b..db9299752b 100644
--- a/indra/newview/omnifilter.h
+++ b/indra/newview/omnifilter.h
@@ -60,6 +60,17 @@ protected:
void onUpNeedleClicked();
void onDownNeedleClicked();
// [FIRE-36649]
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ void onNewRuleSetClicked();
+ void onCloneRuleSetClicked();
+ void onRemoveRuleSetClicked();
+ void onNewRuleSetNameSelectedCallback(const LLSD& notification, const LLSD& response);
+ void onCloneRuleSetNameSelectedCallback(const LLSD& notification, const LLSD& response);
+ void onRemoveRuleSetConfirmedCallback(const LLSD& notification, const LLSD& response);
+ void onRuleSetChanged();
+ void reloadRules();
+ void reloadRule();
+ // [FIRE-36763]
void onNeedleNameChanged();
void onNeedleCheckboxChanged(LLUICtrl* ctrl);
void onOwnerChanged();
@@ -73,6 +84,12 @@ protected:
LLButton* mUpNeedleBtn{ nullptr };
LLButton* mDownNeedleBtn{ nullptr };
// [FIRE-36649]
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ LLComboBox* mRuleSetsCmb{ nullptr };
+ LLButton* mNewRuleSetBtn{ nullptr };
+ LLButton* mCloneRuleSetBtn{ nullptr };
+ LLButton* mRemoveRuleSetBtn{ nullptr };
+ // [FIRE-36763]
FSScrollListCtrl* mFilterLogCtrl{ nullptr };
LLPanel* mPanelDetails{ nullptr };
LLLineEditor* mNeedleNameCtrl{ nullptr };
diff --git a/indra/newview/omnifilterengine.cpp b/indra/newview/omnifilterengine.cpp
index bdc524049d..87ffb5978e 100644
--- a/indra/newview/omnifilterengine.cpp
+++ b/indra/newview/omnifilterengine.cpp
@@ -38,13 +38,18 @@ OmnifilterEngine::OmnifilterEngine()
: LLSingleton()
, LLEventTimer(5.0f)
, mDirty(false)
+ , mCurrentSelectedRuleSet("Default") // [FIRE-36763] - Add rule sets to Omnifilter
{
mEventTimer.stop();
}
OmnifilterEngine::~OmnifilterEngine()
{
- // delete Xxx;
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ // If the user changed a setting and quickly closed the viewer, the changes would not be saved, but
+ // the changes to the settings.xml would, which would cause issues. So save upon close just in case.
+ saveNeedles();
+ // [FIRE-36763]
}
void OmnifilterEngine::init()
@@ -325,6 +330,363 @@ void OmnifilterEngine::setDirty(bool dirty)
}
}
+// [FIRE-36763] - Add rule sets to Omnifilter
+// Get the name from the vector of ordered rule sets at the specified index
+std::string_view OmnifilterEngine::getOrderedRuleSetName(const S32 index) const
+{
+ // If the index is within the range of the vector, return the stored value
+ if (index >= 0 && index < mOrderedRuleSets.size() && mOrderedRuleSets.size() > 0)
+ {
+ return mOrderedRuleSets[index];
+ }
+
+ // Return an empty string if not found.
+ return "";
+}
+
+// Gets the ordered rule set index by looking up the name of a rule set.
+S32 OmnifilterEngine::getOrderedRuleSetIndex(std::string_view lookup_name)
+{
+ for (S32 index = 0; index < mOrderedNeedles.size(); index++)
+ {
+ if (mOrderedNeedles[index] == lookup_name)
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+// Parses a passed in LLSD as a Map of rule sets, which contain another map, which contains the actual rule set data and an order value
+// return false if the current LLSD is an older format, return true when finish importing the data.
+bool OmnifilterEngine::importFromLLSD(const LLSD& needle_rule_sets_llsd)
+{
+ std::string preset_name = "Default";
+ // If there is no default Rule Set return false
+ if (!needle_rule_sets_llsd.has("Default"))
+ {
+ return false;
+ }
+ // Else there is a map named default, does it contain a variable of "order", if not, return false
+ else if (!needle_rule_sets_llsd["Default"].has("order"))
+ {
+ return false;
+ }
+ mOrderedRuleSets.clear();
+ // Use the number of rule sets to pre-allocate the ordered rule sets. So can use assignment operators without re-allocating.
+ mOrderedRuleSets.resize(needle_rule_sets_llsd.size());
+ // Loop over the outer Rule Set map
+ S32 rule_set_index = 0;
+ for (const auto&[new_rule_set_name, new_rule_set_llsd] : llsd::inMap(needle_rule_sets_llsd))
+ {
+
+ // If there is an order tag, then
+ if (new_rule_set_llsd.has("order"))
+ {
+ // Add the rule set name to the ordered list at the order position.
+ mOrderedRuleSets[new_rule_set_llsd["order"].asInteger()] = new_rule_set_name;
+ }
+ else
+ {
+ // Else if there is no order tag, then add add the rule set name to the ordered list in the order read in.
+ // NOTE maps don't store data order added, but optimized order for storage.
+ mOrderedRuleSets[rule_set_index++] = new_rule_set_name;
+ }
+ // Generate a new rule set
+ rule_set_t new_rule_set = rule_set_t(needle_ordered_list_t(), needle_list_t());
+ // Apply the size of the rule set (number of rules) to the ordered rule set set.
+ // First = Ordered Rule Set Vector, Second = Actual Rule Set Map
+ new_rule_set.first.resize(new_rule_set_llsd["rule_set"].size());
+ // Set the current needles_llsd to the actual rule set stored.
+ // keeps old code valid.
+ LLSD needles_llsd = new_rule_set_llsd["rule_set"];
+ S32 index = 0;
+ // Now loop over the rule map stored in the rule set.
+ for (const auto& [new_needle_name, needle_data] : llsd::inMap(needles_llsd))
+ {
+ Needle new_needle;
+ // Perform checks on all input values from the data format and skip any that don't exist.
+ if (needle_data.has("sender_name"))
+ new_needle.mSenderName = needle_data["sender_name"].asString();
+ if (needle_data.has("content"))
+ new_needle.mContent = needle_data["content"].asString();
+ if (needle_data.has("region_name"))
+ new_needle.mRegionName = needle_data["region_name"].asString();
+ if (needle_data.has("chat_replace"))
+ new_needle.mChatReplace = needle_data["chat_replace"].asString();
+ if (needle_data.has("button_reply"))
+ new_needle.mButtonReply = needle_data["button_reply"].asString();
+ if (needle_data.has("textbox_reply"))
+ new_needle.mTextBoxReply = needle_data["textbox_reply"].asString();
+ if (needle_data.has("sender_name_match_type"))
+ new_needle.mSenderNameMatchType = static_cast(needle_data["sender_name_match_type"].asInteger());
+ if (needle_data.has("content_match_type"))
+ new_needle.mContentMatchType = static_cast(needle_data["content_match_type"].asInteger());
+
+ if (needle_data.has("types"))
+ {
+ LLSD types_llsd = needle_data["types"];
+
+ for (const auto& needle_type : llsd::inArray(types_llsd))
+ {
+ new_needle.mTypes.insert(static_cast(needle_type.asInteger()));
+ }
+ }
+ if (needle_data.has("enabled"))
+ new_needle.mEnabled = needle_data["enabled"].asBoolean();
+ if (needle_data.has("sender_name_case_insensitive"))
+ new_needle.mSenderNameCaseInsensitive = needle_data["sender_name_case_insensitive"].asBoolean();
+ if (needle_data.has("content_case_insensitive"))
+ new_needle.mContentCaseInsensitive = needle_data["content_case_insensitive"].asBoolean();
+
+ if (needle_data.has("enabled"))
+ {
+ const std::string owner_id_str = needle_data["owner_id"].asString();
+ if (!owner_id_str.empty())
+ {
+ new_needle.mOwnerID.set(owner_id_str);
+ }
+ }
+ // Assign the actuall new rule set to the parsed needle object
+ new_rule_set.second[new_needle_name] = new_needle;
+ // Add the loaded needle name to the ordered needle list
+ // Needles are stored in order added to the map originally so use the
+ // order value stored to restore the order back to the user
+ // defined order.
+ if (needle_data.has("order"))
+ {
+ new_rule_set.first[needle_data["order"].asInteger()] = new_needle_name;
+ }
+ else
+ {
+ new_rule_set.first[index++] = new_needle_name;
+ }
+ }
+ mNeedleRuleSets[new_rule_set_name] = new_rule_set;
+ }
+
+ return true;
+}
+
+// Exports the current stored orered rules sets to an LLSD object and returns the object created.
+LLSD OmnifilterEngine::exportToLLSD()
+{
+ LLSD output;
+ // Loop over all the rulesets
+ S32 rule_set_order = 0;
+ // Loop over the ordered rule set names
+ for (const auto& export_rule_set_name : mOrderedRuleSets)
+ {
+ LLSD needles_llsd;
+ // Get the map of needles that have the name of the current ordered rule set
+ const auto& export_rule_set = mNeedleRuleSets[export_rule_set_name];
+
+ // [FIRE-36649] - Add reordering to OmniFilter
+ // Use ordered needle list to get the names of the needles in specified order and not the order added to the map.
+ // for (const auto& [needle_name, needle] : mNeedles)
+ S32 order = 0;
+ for (const auto& needle_name : export_rule_set.first)
+ {
+ const Needle &needle = export_rule_set.second.at(needle_name);
+ // Store the order of the needle
+ needles_llsd[needle_name]["order"] = order++;
+ // [FIRE-36649]
+ needles_llsd[needle_name]["sender_name"] = needle.mSenderName;
+ needles_llsd[needle_name]["content"] = needle.mContent;
+ needles_llsd[needle_name]["region_name"] = needle.mRegionName;
+ needles_llsd[needle_name]["chat_replace"] = needle.mChatReplace;
+ needles_llsd[needle_name]["button_reply"] = needle.mButtonReply;
+ needles_llsd[needle_name]["textbox_reply"] = needle.mTextBoxReply;
+ needles_llsd[needle_name]["sender_name_match_type"] = needle.mSenderNameMatchType;
+ needles_llsd[needle_name]["content_match_type"] = needle.mContentMatchType;
+ needles_llsd[needle_name]["owner_id"] = needle.mOwnerID;
+
+ LLSD types_llsd;
+ for (auto type : needle.mTypes)
+ {
+ types_llsd.append(static_cast(type));
+ }
+
+ needles_llsd[needle_name]["types"] = types_llsd;
+ needles_llsd[needle_name]["enabled"] = needle.mEnabled;
+ needles_llsd[needle_name]["sender_name_case_insensitive"] = needle.mSenderNameCaseInsensitive;
+ needles_llsd[needle_name]["content_case_insensitive"] = needle.mContentCaseInsensitive;
+ }
+ // Store the rule set into the otuput with the rule set store in the LLSD as well as the rule set order value
+ output[export_rule_set_name]["rule_set"] = needles_llsd;
+ output[export_rule_set_name]["order"] = rule_set_order++;
+ }
+
+ // Return the final LLSD which contains all the Rule Sets
+ return output;
+}
+
+// Assignes a rule set to either fron the internal variable to the stored map of rule sets, or
+// copies the current selected rule set from storage to the internal variables.
+// This lets us keep all the original design and not require refactoring of code
+// in other classes that interact with the Omnifilter.
+bool OmnifilterEngine::assignRuleSet(const bool rule_set_to_internal)
+{
+ // If there is no current selected rule set or the rule set name is not in the list of rule sets, return false
+ if (mCurrentSelectedRuleSet.empty() || !mNeedleRuleSets.contains(mCurrentSelectedRuleSet))
+ {
+ return false;
+ }
+
+ // If transfering data from the currently selected rule set to the internal variables
+ if (rule_set_to_internal)
+ {
+ // Assign the needle map and vector of ordered neele names from the currently selected rule set.
+ mNeedles = mNeedleRuleSets[mCurrentSelectedRuleSet].second;
+ mOrderedNeedles = mNeedleRuleSets[mCurrentSelectedRuleSet].first;
+ }
+ // Else, want to move the data back the from the internal variables to selected data values
+ else
+ {
+ mNeedleRuleSets[mCurrentSelectedRuleSet].second = mNeedles;
+ mNeedleRuleSets[mCurrentSelectedRuleSet].first = mOrderedNeedles;
+ }
+ return true;
+}
+
+// Assigns the current selected rule set string to the value passed in
+bool OmnifilterEngine::setCurrentRuleSet(std::string_view rule_set_name)
+{
+ // If blank, return false as we should not have a blank name.
+ if (rule_set_name.empty())
+ {
+ return false;
+ }
+ // Store the pass in rule set name.
+ mCurrentSelectedRuleSet = rule_set_name;
+
+ // Apply the rule set to the current variables
+ assignRuleSet();
+
+ return true;
+}
+
+// Performs the actual removal of the current rule set.
+S32 OmnifilterEngine::removeCurrentRuleSet()
+{
+ // If the current rule set list contains the currently selceted rule set, then erase it.
+ if (mNeedleRuleSets.contains(mCurrentSelectedRuleSet))
+ {
+ S32 current_index = gSavedSettings.getS32("OmnifilterRuleSetID");
+ // If the current index is greater then the
+ if (current_index == 0)
+ {
+ // Return 0 for unable to remove default
+ return 0;
+ }
+
+ // If the index is within range of the needle rule sets, then
+ if (current_index < mNeedleRuleSets.size() && current_index > 0)
+ {
+ // Erase both the the current rule set and the ordered rule set name
+ mNeedleRuleSets.erase(mCurrentSelectedRuleSet);
+ mOrderedRuleSets.erase(mOrderedRuleSets.begin() + current_index);
+ // Reset the selected rule set ID back to 0, let the user pick the one to use next. 0 is also safe as it should never be removed.
+ gSavedSettings.setS32("OmnifilterRuleSetID", 0);
+ // Re-assign the current rule set name and reload the rule set from storage to the rule set
+ assignRuleSetNameFromSettings();
+ assignRuleSet();
+ // Return 1 indicating that the removal worked correctly.
+ return 1;
+ }
+ // Return -1 as the rule set index out of bounds
+ return 0;
+ }
+ // Return -2 for rule set name does not exist
+ return -2;
+}
+
+// Add a new rule set based upon the name passed in
+S32 OmnifilterEngine::addNewRuleSet(std::string_view new_name)
+{
+ // If the name is blank, return an error code, to then let the user know with a notification
+ if (new_name.empty())
+ {
+ return -1;
+ }
+
+ std::string str_new_name(new_name);
+ // If the name already exists, return an error to let the user know
+ if (mNeedleRuleSets.contains(str_new_name))
+ {
+ return 0;
+ }
+
+ // We want to clear the current rule set and ordered rules as a new rule set has only a single template rule that is disabled.
+ mNeedles.clear();
+ mOrderedNeedles.clear();
+ // Set the rule set in the map to a new rule set pair.
+ mNeedleRuleSets[str_new_name] = rule_set_t(needle_ordered_list_t(), needle_list_t());
+ // Update the OmnifilterRuleSetID saved setting to store the location of the new rule set.
+ // We want to do this to support auto-selecting the new rule set.
+ gSavedSettings.setS32("OmnifilterRuleSetID", static_cast(mNeedleRuleSets.size() - 1));
+ // Set the current selcted rule set to the new rule set
+ mCurrentSelectedRuleSet = str_new_name;
+ // Add the rule set name to the list of ordered rule sets
+ mOrderedRuleSets.push_back(mCurrentSelectedRuleSet);
+
+ return 1;
+}
+
+// Clone the current rule set based upon the name passed in
+S32 OmnifilterEngine::addClonedRuleSet(std::string_view new_name)
+{
+ // If the name is blank, return an error code, to then let the user know with a notification
+ if (new_name.empty())
+ {
+ return -1;
+ }
+
+ std::string str_new_name(new_name);
+
+ // If the name already exists, return an error to let the user know
+ if (mNeedleRuleSets.contains(str_new_name))
+ {
+ return 0;
+ }
+
+ // This method is different in the new rule set method by not clearing the current rules and ordered rules.
+
+ // Set the rule set in the map to a new rule set pair.
+ mNeedleRuleSets[str_new_name] = rule_set_t(needle_ordered_list_t(), needle_list_t());
+ // Update the OmnifilterRuleSetID saved setting to store the location of the new rule set.
+ // We want to do this to support auto-selecting the new rule set.
+ gSavedSettings.setS32("OmnifilterRuleSetID", static_cast(mNeedleRuleSets.size() - 1));
+ // Set the current selcted rule set to the new rule set
+ mCurrentSelectedRuleSet = str_new_name;
+ // Add the rule set name to the list of ordered rule sets
+ mOrderedRuleSets.push_back(mCurrentSelectedRuleSet);
+
+ // Copy the internal rule set to the stored rulset
+ assignRuleSet(false);
+
+ return 1;
+}
+
+// Takes the stored setting OmnifilterRuleSetID and assigns the current
+// selected rule set to the ordered rule set at that location.
+bool OmnifilterEngine::assignRuleSetNameFromSettings()
+{
+ // Get hte number of items to loop over
+ S32 rule_set_id = gSavedSettings.getS32("OmnifilterRuleSetID");
+ std::string found_rule_set(getOrderedRuleSetName(rule_set_id));
+
+ if (found_rule_set.empty())
+ {
+ return false;
+ }
+
+ mCurrentSelectedRuleSet = found_rule_set;
+
+ return true;
+}
+// [FIRE-36763]
+
void OmnifilterEngine::loadNeedles()
{
if (mNeedlesXMLPath.empty())
@@ -391,6 +753,28 @@ void OmnifilterEngine::loadNeedles()
return;
}
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ static LLCachedControl NeedlePresetIndex(gSavedSettings, "OmnifilterRuleSetID");
+
+ // If this is not the first time loading, based upon the preset index being set
+ // to an invalid valid, after this there should always be atleast 1 rule set (default)
+ if (NeedlePresetIndex > -1)
+ {
+ mNeedleRuleSets.clear();
+ // If the importing works out correctly, then use the reloaded rulset set name from the settings and assign that rule set.
+ if (importFromLLSD(needles_llsd))
+ {
+ assignRuleSetNameFromSettings();
+ // Assign the rule set
+ assignRuleSet();
+ // Return as we can skip the rest of the old loading.
+ return;
+ }
+ // Otherwise, the import failed as it may be an existing older rule set that has a vector of ordered rules.
+ // If so, we still want to use the older load code to parse the older form and convert to the newer format.
+ }
+ // Load the file with the old code to get the old .xml file data loaded, to then be outputted in the new format
+ // [FIRE-36763]
// [FIRE-36649] - Add reordering to OmniFilter
// Clear the vector of filters
mOrderedNeedles.clear();
@@ -444,6 +828,19 @@ void OmnifilterEngine::loadNeedles()
}
// [/FIRE-36649]
}
+ // [FIRE-36649] - Add reordering to OmniFilter
+ // Create a default from the current version
+ gSavedSettings.setS32("OmnifilterRuleSetID", 0);
+ // The first rule set is always called Default.
+ mCurrentSelectedRuleSet = "Default";
+ // Create a new rule set with the already parsed ordered ruules and map of rules.
+ mNeedleRuleSets["Default"] = rule_set_t(mOrderedNeedles, mNeedles);
+ mOrderedRuleSets.push_back(mCurrentSelectedRuleSet);
+ // Save the ruleset to the storage rule set.
+ assignRuleSet(false);
+ // Save the changes back to the Omnifilter.xml file.
+ saveNeedles();
+ // [FIRE-36649]
}
void OmnifilterEngine::saveNeedles()
@@ -469,6 +866,12 @@ void OmnifilterEngine::saveNeedles()
LLSD needles_llsd;
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ // First assign the current rule set back to the currently selected rule set.
+ assignRuleSet(false);
+ // Export the needles to LLSD storage
+ needles_llsd = exportToLLSD();
+ /*
// [FIRE-36649] - Add reordering to OmniFilter
// Use ordered needle list to get the names of the needles in specified order and not the order added to the map.
//for (const auto& [needle_name, needle] : mNeedles)
@@ -500,6 +903,8 @@ void OmnifilterEngine::saveNeedles()
needles_llsd[needle_name]["sender_name_case_insensitive"] = needle.mSenderNameCaseInsensitive;
needles_llsd[needle_name]["content_case_insensitive"] = needle.mContentCaseInsensitive;
}
+ */
+ // [FIRE-36763]
LLSDSerialize::toXML(needles_llsd, file);
diff --git a/indra/newview/omnifilterengine.h b/indra/newview/omnifilterengine.h
index 5fb2020884..0c2d05f1b3 100644
--- a/indra/newview/omnifilterengine.h
+++ b/indra/newview/omnifilterengine.h
@@ -125,21 +125,48 @@ class OmnifilterEngine
void init();
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ bool setCurrentRuleSet(std::string_view rule_set_name);
+ S32 removeCurrentRuleSet();
+ S32 addNewRuleSet(std::string_view new_name);
+ S32 addClonedRuleSet(std::string_view new_name);
+ bool assignRuleSet(const bool rule_set_to_internal = true);
+ bool assignRuleSetNameFromSettings();
+ std::string getCurrentSelectedRuleSet() { return mCurrentSelectedRuleSet; }
+ needle_ordered_list_t& getOrderedRuleSets() { return mOrderedRuleSets; }
+ std::string_view getOrderedRuleSetName(const S32 index) const;
+ S32 getOrderedRuleSetIndex(std::string_view lookup_name);
+ S32 getOrderedRuleSetSize() { return static_cast(mOrderedRuleSets.size()); }
+ // [FIRE-36763]
typedef boost::signals2::signal log_signal_t;
log_signal_t mLogSignal;
std::vector> mLog;
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ typedef std::pair rule_set_t;
+ typedef std::map rule_sets_t;
+ rule_sets_t& getRuleSets() { return mNeedleRuleSets; }
+ // [FIRE-36763]
protected:
const Needle* logMatch(const std::string& needle_name, const Needle& needle);
bool matchStrings(std::string_view needle_string, std::string_view haystack_string, eMatchType match_type, bool case_insensitive);
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ bool importFromLLSD(const LLSD& data);
+ LLSD exportToLLSD();
+ // [FIRE-36763]
void loadNeedles();
void saveNeedles();
bool tick() override;
protected:
+ // [FIRE-36763] - Add rule sets to Omnifilter
+ rule_sets_t mNeedleRuleSets;
+ std::string mCurrentSelectedRuleSet;
+ needle_ordered_list_t mOrderedRuleSets;
+ // [FIRE-36763]
needle_list_t mNeedles;
// [FIRE-36649] - Add reordering to OmniFilter
needle_ordered_list_t mOrderedNeedles;
diff --git a/indra/newview/skins/default/xui/en/floater_omnifilter.xml b/indra/newview/skins/default/xui/en/floater_omnifilter.xml
index 8d2b013c43..2e96b8befb 100644
--- a/indra/newview/skins/default/xui/en/floater_omnifilter.xml
+++ b/indra/newview/skins/default/xui/en/floater_omnifilter.xml
@@ -4,7 +4,7 @@
can_resize="true"
can_minimize="true"
can_close="true"
- height="400"
+ height="425"
min_height="348"
min_width="668"
layout="topleft"
@@ -20,12 +20,71 @@
NEW RULE
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter a name for the New Rule Set:
+ confirm
+
+
+
+ Blank names are not allowed. Please try again with a name.
+
+ voice
+
+
+ Duplicate names are not allowed. Please try again with a different name.
+
+ voice
+
+
+
+ Enter a name to the Cloned Rule Set:
+ confirm
+
+
+
+ Blank names are not allowed. Please try again with a name.
+
+ voice
+
+
+ Duplicate names are not allowed. Please try again with a different name.
+
+ voice
+
+
+
+ Are you sure you want to remove [RULESETNAME]?
+ confirm
+
+
+
+ Unable to remove the Default Rule Set.
+
+ voice
+
+
+ Unable to remove the Rule Set due to index out of bounds.
+
+ voice
+
+
+ Unable to remove the Rule Set due to name not found.
+
+ voice
+
+