FIRE-36763 - Add rule sets to Omnifilter

Added new UI elements to the Omnifilter to allow adding multiple rule sets to the Omnifilter and switching between each rule set with an drop down. There is an option to create a new empty rule set with a specified name, clone the current active rule set with a specified name, and remove the current rule set.

This allows the user to have different rule sets configured and to be able to switch to the rule sets.

Added new setting to keep track of the rule set selected so it can be restored after logging out. Also added a new notifications that either show errors to the user or prompt for new rule set names or ask the user if they really want to remove a rule set.
This commit is contained in:
minerjr
2026-06-19 07:27:18 -03:00
parent 5065e78af7
commit 9d42fa317c
7 changed files with 935 additions and 4 deletions
+13
View File
@@ -27530,6 +27530,19 @@ Change of this parameter will affect the layout of buttons in notification toast
<key>Value</key>
<integer>1</integer>
</map>
<!-- <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter -->
<key>OmnifilterRuleSetID</key>
<map>
<key>Comment</key>
<string>The id of the current rule set for the Omnifilter.</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>S32</string>
<key>Value</key>
<integer>-1</integer>
</map>
<!-- </FS:minerjr> [FIRE-36763] -->
<key>FSAutoOrderIMTabs</key>
<map>
<key>Comment</key>
+260 -1
View File
@@ -29,6 +29,10 @@
#include "llcombobox.h"
#include "lllineeditor.h"
#include "lltexteditor.h"
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
#include "llviewercontrol.h" // Needed for gSavedSettings
#include "llnotificationsutil.h" // Needed for Notifications
// </FS:minerjr> [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())
// <FS:minerjr> [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))
// </FS:minerjr> [FIRE-36763]
{
return &OmnifilterEngine::getInstance()->getNeedleList().at(needle_name);
}
@@ -277,6 +285,237 @@ void Omnifilter::onDownNeedleClicked()
}
// </FS:minerjr> [FIRE-36649]
// <FS:minerjr> [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();
}
// </FS:minerjr> [FIRE-36763]
void Omnifilter::onNeedleNameChanged()
{
const std::string& old_name = mNeedleListCtrl->getSelectedItemLabel(NEEDLE_NAME_COLUMN);
@@ -381,6 +620,13 @@ bool Omnifilter::postBuild()
mSenderCaseSensitiveCheck = getChild<LLCheckBoxCtrl>("sender_case");
mSenderMatchTypeCombo = getChild<LLComboBox>("sender_match_type");
mContentCtrl = getChild<LLTextEditor>("content");
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
// Add the preset controls
mRuleSetsCmb = getChild<LLComboBox>("cmb_rule_sets"); // Rule Set Drop Down
mNewRuleSetBtn = getChild<LLButton>("btn_rule_set_new"); // New Rule Set
mCloneRuleSetBtn = getChild<LLButton>("btn_rule_set_clone"); // Clone Rule Set
mRemoveRuleSetBtn = getChild<LLButton>("btn_rule_set_remove"); // Remove Rule Set
// <//FS:minerjr> [FIRE-36763]
mContentCaseSensitiveCheck = getChild<LLCheckBoxCtrl>("content_case");
mContentMatchTypeCombo = getChild<LLComboBox>("content_match_type");
mRegionNameCtrl = getChild<LLLineEditor>("region_name");
@@ -412,6 +658,12 @@ bool Omnifilter::postBuild()
mFilterLogCtrl->deleteAllItems();
auto& instance = OmnifilterEngine::instance();
// <FS:minerjr> [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);
// </FS:minerjr> [FIRE-36763]
// <FS:minerjr> [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));
// </FS:minerjr> [FIRE-36649]
// <FS:minerjr> [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));
+17
View File
@@ -60,6 +60,17 @@ protected:
void onUpNeedleClicked();
void onDownNeedleClicked();
// </FS:minerjr> [FIRE-36649]
// <FS:minerjr> [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();
// </FS:minerjr> [FIRE-36763]
void onNeedleNameChanged();
void onNeedleCheckboxChanged(LLUICtrl* ctrl);
void onOwnerChanged();
@@ -73,6 +84,12 @@ protected:
LLButton* mUpNeedleBtn{ nullptr };
LLButton* mDownNeedleBtn{ nullptr };
// </FS:minerjr> [FIRE-36649]
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
LLComboBox* mRuleSetsCmb{ nullptr };
LLButton* mNewRuleSetBtn{ nullptr };
LLButton* mCloneRuleSetBtn{ nullptr };
LLButton* mRemoveRuleSetBtn{ nullptr };
// </FS:minerjr> [FIRE-36763]
FSScrollListCtrl* mFilterLogCtrl{ nullptr };
LLPanel* mPanelDetails{ nullptr };
LLLineEditor* mNeedleNameCtrl{ nullptr };
+406 -1
View File
@@ -38,13 +38,18 @@ OmnifilterEngine::OmnifilterEngine()
: LLSingleton<OmnifilterEngine>()
, LLEventTimer(5.0f)
, mDirty(false)
, mCurrentSelectedRuleSet("Default") // <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
{
mEventTimer.stop();
}
OmnifilterEngine::~OmnifilterEngine()
{
// delete Xxx;
// <FS:minerjr> [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();
// </FS:minerjr> [FIRE-36763]
}
void OmnifilterEngine::init()
@@ -325,6 +330,363 @@ void OmnifilterEngine::setDirty(bool dirty)
}
}
// <FS:minerjr> [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<OmnifilterEngine::eMatchType>(needle_data["sender_name_match_type"].asInteger());
if (needle_data.has("content_match_type"))
new_needle.mContentMatchType = static_cast<OmnifilterEngine::eMatchType>(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<OmnifilterEngine::eType>(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];
// <FS:minerjr> [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++;
// </FS:minerjr> [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<S32>(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<S32>(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<S32>(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;
}
// </FS:minerjr> [FIRE-36763]
void OmnifilterEngine::loadNeedles()
{
if (mNeedlesXMLPath.empty())
@@ -391,6 +753,28 @@ void OmnifilterEngine::loadNeedles()
return;
}
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
static LLCachedControl<S32> 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
// <F/S:minerjr> [FIRE-36763]
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Clear the vector of filters
mOrderedNeedles.clear();
@@ -444,6 +828,19 @@ void OmnifilterEngine::loadNeedles()
}
// <FS:minerjr> [/FIRE-36649]
}
// <FS:minerjr> [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();
// </FS:minerjr> [FIRE-36649]
}
void OmnifilterEngine::saveNeedles()
@@ -469,6 +866,12 @@ void OmnifilterEngine::saveNeedles()
LLSD needles_llsd;
// <FS:minerjr> [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();
/*
// <FS:minerjr> [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;
}
*/
// </FS:minerjr> [FIRE-36763]
LLSDSerialize::toXML(needles_llsd, file);
+27
View File
@@ -125,21 +125,48 @@ class OmnifilterEngine
void init();
// <FS:minerjr> [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<S32>(mOrderedRuleSets.size()); }
// </FS:minerjr> [FIRE-36763]
typedef boost::signals2::signal<void(time_t, const std::string&)> log_signal_t;
log_signal_t mLogSignal;
std::vector<std::pair<time_t, std::string>> mLog;
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
typedef std::pair<needle_ordered_list_t, needle_list_t> rule_set_t;
typedef std::map<std::string, rule_set_t> rule_sets_t;
rule_sets_t& getRuleSets() { return mNeedleRuleSets; }
// </FS:minerjr> [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);
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
bool importFromLLSD(const LLSD& data);
LLSD exportToLLSD();
// </FS:minerjr> [FIRE-36763]
void loadNeedles();
void saveNeedles();
bool tick() override;
protected:
// <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter
rule_sets_t mNeedleRuleSets;
std::string mCurrentSelectedRuleSet;
needle_ordered_list_t mOrderedRuleSets;
// </FS:minerjr> [FIRE-36763]
needle_list_t mNeedles;
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
needle_ordered_list_t mOrderedNeedles;
@@ -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
</floater.string>
<!-- <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter --> <!-- <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter -->
<panel
name="needle_rule_set_controls"
layout="topleft"
follows="left|top|right"
height="25"
left="0"
min_height="25"
top="20"
user_resize="false">
<text
name="lbl_ruleset"
layout="topleft"
follows="left|top"
height="20"
width="70"
left="8"
top="0"
valign="center"
value="Rule Sets:"/>
<combo_box
height="20"
layout="topleft"
left_pad="4"
follows="left|top"
name="cmb_rule_sets"
tool_tip="Choose the Omnifilter Rule Set to use."
width="150">
</combo_box>
<button
name="btn_rule_set_new"
layout="topleft"
follows="left|top"
width="150"
height="20"
left_pad="4"
label="New Rule Set"
tool_tip="Create a new blank Omnifilter Rule Set."/>
<button
name="btn_rule_set_clone"
layout="topleft"
follows="left|top"
width= "150"
height="20"
left_pad="4"
label="Clone Rule Set"
tool_tip="Create a clone of the current Omnifilter Rule Set."/>
<button
name="btn_rule_set_remove"
layout="topleft"
follows="left|top"
width= "150"
height="20"
left_pad="4"
label="Remove Rule Set"
tool_tip="Remove
the current Omnifilter Rule Set."/>
</panel>
<!-- </FS:minerjr> [FIRE-36763] -->
<layout_stack
name="needle_list_stack"
layout="topleft"
follows="left|top|bottom"
height="374"
top="20"
top="45"
left="8"
width="190"
orientation="vertical"
@@ -15150,4 +15150,155 @@ You can enable saving transcripts under Preferences &gt; Privacy &gt; Logs &amp;
yestext="OK"/>
</notification>
<!-- </FS:TJ>-->
<!-- <FS:minerjr> [FIRE-36763] - Add rule sets to Omnifilter -->
<notification
icon="alertmodal.tga"
label="New Rule Set"
name="OmniFilterNewRuleSet"
type="alertmodal">
Enter a name for the New Rule Set:
<tag>confirm</tag>
<form name="form">
<input name="new_name" type="text" width="300" default="true">
[RULESETNAME]
</input>
<button
default="true"
index="0"
name="OK"
text="OK"/>
<button
index="1"
name="Cancel"
text="Cancel"/>
</form>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterrNewRuleSetBlank"
sound="UISndAlert"
persist="true"
type="alertmodal">
Blank names are not allowed. Please try again with a name.
<usetemplate
ignoretext="Warn me again when trying to add a New Rule Set with no name."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterNewRuleSetDuplicate"
sound="UISndAlert"
persist="true"
type="alertmodal">
Duplicate names are not allowed. Please try again with a different name.
<usetemplate
ignoretext="Warn me again when trying to add a New Rule Set with a duplicate name."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<notification
icon="alertmodal.tga"
label="Clone Rule Set"
name="OmniFilterCloneRuleSet"
type="alertmodal">
Enter a name to the Cloned Rule Set:
<tag>confirm</tag>
<form name="form">
<input name="new_name" type="text" width="300" default="true">
[RULESETNAME]
</input>
<button
default="true"
index="0"
name="OK"
text="OK"/>
<button
index="1"
name="Cancel"
text="Cancel"/>
</form>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterCloneRuleSetBlank"
sound="UISndAlert"
persist="true"
type="alertmodal">
Blank names are not allowed. Please try again with a name.
<usetemplate
ignoretext="Warn me again when trying to add a Cloned Rule Set with no name."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterCloneRuleSetDuplicate"
sound="UISndAlert"
persist="true"
type="alertmodal">
Duplicate names are not allowed. Please try again with a different name.
<usetemplate
ignoretext="Warn me again when trying to add a Cloned Rule Set with a duplicate name."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterRemoveRuleSet"
type="alertmodal">
Are you sure you want to remove [RULESETNAME]?
<tag>confirm</tag>
<usetemplate
ignoretext="Confirm before I remove a Rule Set."
name="okcancelignore"
notext="Cancel"
yestext="OK"/>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterRemoveRuleSetDefault"
sound="UISndAlert"
persist="true"
type="alertmodal">
Unable to remove the Default Rule Set.
<usetemplate
ignoretext="Warn me again when trying to remove the Default Rule Set."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterRemoveRuleSetOutofBounds"
sound="UISndAlert"
persist="true"
type="alertmodal">
Unable to remove the Rule Set due to index out of bounds.
<usetemplate
ignoretext="Warn me again when trying to remove a Rule Set with an index [OUTOFBOUNDS] out of bounds."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<notification
icon="alertmodal.tga"
name="OmniFilterRemoveRuleSetInvalidName"
sound="UISndAlert"
persist="true"
type="alertmodal">
Unable to remove the Rule Set due to name not found.
<usetemplate
ignoretext="Warn me again when trying to remove a Rule Set with an invalid name '[INVALIDNAME]'."
name="okignore"
yestext="OK"/>
<tag>voice</tag>
</notification>
<!-- </FS:minerjr> [FIRE-36763] -->
</notifications>