FIRE-36649 - Add reordering to OmniFilter

Added two buttons to the OmniFilter floater which when an filter is selected on the Rule list, can change the order of the filter by moving it up or down the list.

This change also includes saving and loading and allowing the displayed order to be used for all filter aspects and not the original order in which the filters are added.

Also allows for sorting of the list to actually affect the order of the filters are applied and are also saved.

Added new vector lookup for the name of the buttons to allow it to be sorted and re-arranged so that the map is maintained for storage of the needles and accessing.

Had to slightly change the size of the UI for the Rules to make room for the two new buttons.

Added various accessor methods to allow using the ordered list.
This commit is contained in:
minerjr
2026-06-13 10:52:02 -03:00
parent 92c818febb
commit e00eb94ffa
5 changed files with 240 additions and 4 deletions
+77 -1
View File
@@ -217,6 +217,66 @@ void Omnifilter::onRemoveNeedleClicked()
onSelectNeedle();
}
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Handles re-ordering the list of needle names when the UI is sorted.
void Omnifilter::onSortChanged()
{
static OmnifilterEngine* omni_filter_engine = OmnifilterEngine::getInstance();
if (!omni_filter_engine)
{
return;
}
// Loop over the list of thems
for (S32 index = 0; index < mNeedleListCtrl->getItemCount(); index++)
{
const LLScrollListItem* needle_item = mNeedleListCtrl->getAllData()[index];
if (needle_item)
{
const LLScrollListCell* needle_name_cell = needle_item->getColumn(NEEDLE_NAME_COLUMN);
if (needle_name_cell)
{
const std::string& needle_name = needle_name_cell->getValue().asString();
if (!needle_name.empty())
{
omni_filter_engine->setOrderedNeedleName(index, needle_name);
}
}
}
}
// Flag as dirty the system will save the changes to the order.
omni_filter_engine->setDirty(true);
}
// Moves an selected filter up on place
void Omnifilter::onUpNeedleClicked()
{
static OmnifilterEngine* omni_filter_engine = OmnifilterEngine::getInstance();
S32 current_index = mNeedleListCtrl->getFirstSelectedIndex();
// Don't try to move the first item up.
if (current_index > 0)
{
// Order is based upon 0 is the top of the screen in the list, so have to subtract 1 to get up visually.
omni_filter_engine->swapNeedles(current_index, current_index - 1);
mNeedleListCtrl->swapWithPrevious(current_index);
}
}
void Omnifilter::onDownNeedleClicked()
{
static OmnifilterEngine* omni_filter_engine = OmnifilterEngine::getInstance();
S32 current_index = mNeedleListCtrl->getFirstSelectedIndex();
// If the value is within range of the filter list, prevents from going past the end of the list
if (omni_filter_engine->getOrderedNeedleListSize() > 1 && current_index < omni_filter_engine->getOrderedNeedleListSize() - 1)
{
// Order is based upon 0 is the top of the screen in the list, so have to add 1 to get down visually.
omni_filter_engine->swapNeedles(current_index, current_index + 1);
mNeedleListCtrl->swapWithNext(current_index);
}
}
// </FS:minerjr> [FIRE-36649]
void Omnifilter::onNeedleNameChanged()
{
const std::string& old_name = mNeedleListCtrl->getSelectedItemLabel(NEEDLE_NAME_COLUMN);
@@ -309,6 +369,11 @@ bool Omnifilter::postBuild()
mNeedleListCtrl = getChild<FSScrollListCtrl>("needle_list");
mAddNeedleBtn = getChild<LLButton>("add_needle");
mRemoveNeedleBtn = getChild<LLButton>("remove_needle");
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Add the up and down buttons for re-aranging the order of the needles
mUpNeedleBtn = getChild<LLButton>("up_needle");
mDownNeedleBtn = getChild<LLButton>("down_needle");
// </FS:minerjr> [FIRE-36649]
mFilterLogCtrl = getChild<FSScrollListCtrl>("filter_log");
mPanelDetails = getChild<LLPanel>("panel_details");
mNeedleNameCtrl = getChild<LLLineEditor>("needle_name");
@@ -347,8 +412,13 @@ bool Omnifilter::postBuild()
mFilterLogCtrl->deleteAllItems();
auto& instance = OmnifilterEngine::instance();
for (const auto& [needle_name, needle] : instance.getNeedleList())
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
//for (const auto& [needle_name, needle] : instance.getNeedleList())
// Loop over the ordered list
for (const auto& needle_name : instance.getOrderedNeedleList())
{
const auto& needle = instance.getNeedleList()[needle_name];
// </FS:minerjr> [FIRE-36649]
addNeedle(needle_name, needle);
}
@@ -370,6 +440,12 @@ bool Omnifilter::postBuild()
mNeedleListCtrl->setCommitCallback(boost::bind(&Omnifilter::onSelectNeedle, this));
mAddNeedleBtn->setCommitCallback(boost::bind(&Omnifilter::onAddNeedleClicked, this));
mRemoveNeedleBtn->setCommitCallback(boost::bind(&Omnifilter::onRemoveNeedleClicked, this));
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Add the callbacks for the up and down buttons to re-order the filter list
mNeedleListCtrl->setSortChangedCallback(boost::bind(&Omnifilter::onSortChanged, this));
mUpNeedleBtn->setCommitCallback(boost::bind(&Omnifilter::onUpNeedleClicked, this));
mDownNeedleBtn->setCommitCallback(boost::bind(&Omnifilter::onDownNeedleClicked, this));
// </FS:minerjr> [FIRE-36649]
mNeedleNameCtrl->setCommitCallback(boost::bind(&Omnifilter::onNeedleNameChanged, this));
mSenderNameCtrl->setCommitCallback(boost::bind(&Omnifilter::onNeedleChanged, this));
mSenderCaseSensitiveCheck->setCommitCallback(boost::bind(&Omnifilter::onNeedleChanged, this));
+9
View File
@@ -55,6 +55,11 @@ protected:
void onNeedleChanged();
void onAddNeedleClicked();
void onRemoveNeedleClicked();
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
void onSortChanged();
void onUpNeedleClicked();
void onDownNeedleClicked();
// </FS:minerjr> [FIRE-36649]
void onNeedleNameChanged();
void onNeedleCheckboxChanged(LLUICtrl* ctrl);
void onOwnerChanged();
@@ -64,6 +69,10 @@ protected:
FSScrollListCtrl* mNeedleListCtrl{ nullptr };
LLButton* mAddNeedleBtn{ nullptr };
LLButton* mRemoveNeedleBtn{ nullptr };
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
LLButton* mUpNeedleBtn{ nullptr };
LLButton* mDownNeedleBtn{ nullptr };
// </FS:minerjr> [FIRE-36649]
FSScrollListCtrl* mFilterLogCtrl{ nullptr };
LLPanel* mPanelDetails{ nullptr };
LLLineEditor* mNeedleNameCtrl{ nullptr };
+117 -2
View File
@@ -134,8 +134,13 @@ bool OmnifilterEngine::matchStrings(std::string_view needle_string, std::string_
const OmnifilterEngine::Needle* OmnifilterEngine::match(const Haystack& haystack)
{
for (const auto& [needle_name, needle]: mNeedles)
// <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 : mOrderedNeedles)
//for (const auto& [needle_name, needle]: mNeedles)
{
const auto& needle = mNeedles[needle_name];
// </FS:minerjr> [FIRE-36649]
if (!needle.mEnabled)
{
continue;
@@ -193,6 +198,10 @@ OmnifilterEngine::Needle& OmnifilterEngine::newNeedle(const std::string& needle_
mNeedles[needle_name] = new_needle;
}
setDirty(true);
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Add to the ordered needle vector the name of the new needle
mOrderedNeedles.push_back(needle_name);
// <F/S:minerjr> [FIRE-36649]
return mNeedles[needle_name];
}
@@ -202,12 +211,26 @@ void OmnifilterEngine::renameNeedle(const std::string& old_name, const std::stri
auto node_handler = mNeedles.extract(old_name);
node_handler.key() = new_name;
mNeedles.insert(std::move(node_handler));
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Find the index of the given old name
S32 found_index = getOrderedNeedleIndex(old_name);
// If the name was found (-1 when not found), set the ordered needle vector at the found index to the new value
if (found_index >= 0)
mOrderedNeedles[found_index] = new_name;
// </FS:minerjr> [FIRE-36649]
setDirty(true);
}
void OmnifilterEngine::deleteNeedle(const std::string& needle_name)
{
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Find the index of the given needle name
S32 found_index = getOrderedNeedleIndex(needle_name);
// If the name was found (-1 when not found), erase the need based upon the offset
if (found_index >= 0)
mOrderedNeedles.erase(mOrderedNeedles.begin() + found_index);
// </FS:minerjr> [FIRE-36649]
mNeedles.erase(needle_name);
setDirty(true);
}
@@ -217,6 +240,77 @@ OmnifilterEngine::needle_list_t& OmnifilterEngine::getNeedleList()
return mNeedles;
}
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Get the name from the vector of ordered needles at the specified index
std::string_view OmnifilterEngine::getOrderedNeedleName(const S32 index) const
{
// If the index is within the range of the vector, return the stored value
if (index >= 0 && index < mOrderedNeedles.size() && mOrderedNeedles.size() > 0)
{
return mOrderedNeedles[index];
}
// Return an empty string if not found.
return "";
}
// Get a needle at a specified index, returns nullptr if none is found ("")
const OmnifilterEngine::Needle* OmnifilterEngine::getOrderedNeedle(const S32 index)
{
std::string_view found_needle = getOrderedNeedleName(index);
if (found_needle.empty())
{
return nullptr;
}
return &mNeedles[std::string(found_needle)];
}
// Re-assigns a name to a specified needle location in the Ordered list
bool OmnifilterEngine::setOrderedNeedleName(const S32 needle_index, std::string_view new_name)
{
// If the index is invalid, return false
if (needle_index < 0 || needle_index > mOrderedNeedles.size())
{
return false;
}
// Otherwise assign the new name to the ordered needle at the specified index.
mOrderedNeedles[needle_index] = new_name;
return true;
}
S32 OmnifilterEngine::getOrderedNeedleIndex(std::string_view lookup_name)
{
for (S32 index = 0; index < mOrderedNeedles.size(); index++)
{
if (mOrderedNeedles[index] == lookup_name)
{
return index;
}
}
return -1;
}
// Swaps 2 ordered needles
bool OmnifilterEngine::swapNeedles(S32 index1, S32 index2)
{
// Validation check to make sure the indexs are valid and if not to return false
if (index1 < 0 || index1 > mOrderedNeedles.size() || index2 < 0 || index2 > mOrderedNeedles.size() || index1 == index2)
{
return false;
}
// Perform the actual swap
std::swap(mOrderedNeedles[index1], mOrderedNeedles[index2]);
// Force a view update
setDirty(true);
return true;
}
// </FS:minerjr> [FIRE-36649]
void OmnifilterEngine::setDirty(bool dirty)
{
mDirty = dirty;
@@ -297,6 +391,12 @@ void OmnifilterEngine::loadNeedles()
return;
}
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Clear the vector of filters
mOrderedNeedles.clear();
// Pre-allocate space for the list of needle names, so we can use an index into it for assignments down below
mOrderedNeedles.resize(needles_llsd.size());
// </FS:minerjr> [FIRE-36649]
for (const auto& [new_needle_name, needle_data] : llsd::inMap(needles_llsd))
{
Needle new_needle;
@@ -327,6 +427,13 @@ void OmnifilterEngine::loadNeedles()
}
mNeedles[new_needle_name] = new_needle;
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// 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.
mOrderedNeedles[needle_data["order"].asInteger()] = new_needle_name;
// <FS:minerjr> [/FIRE-36649]
}
}
@@ -353,8 +460,16 @@ void OmnifilterEngine::saveNeedles()
LLSD needles_llsd;
for (const auto& [needle_name, needle] : mNeedles)
// <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 : mOrderedNeedles)
{
const auto& needle = mNeedles[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;
+15
View File
@@ -103,6 +103,18 @@ class OmnifilterEngine
typedef std::map<std::string, OmnifilterEngine::OmnifilterEngine::Needle, std::less<>> needle_list_t;
needle_list_t& getNeedleList();
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
// Typedef for the ordered list which is a vector of strings, used to keep track of the map order, which uses strings to lookup the
// needles.
typedef std::vector<std::string> needle_ordered_list_t;
needle_ordered_list_t& getOrderedNeedleList() { return mOrderedNeedles; };
S32 getOrderedNeedleListSize() const { return static_cast<S32>(mOrderedNeedles.size()); };
std::string_view getOrderedNeedleName(const S32 index) const;
S32 getOrderedNeedleIndex(std::string_view);
bool setOrderedNeedleName(const S32 needle_index, std::string_view new_name);
const Needle* getOrderedNeedle(const S32 index);
bool swapNeedles(const S32 index1, const S32 index2);
// </FS:minerjr> [FIRE-36649]
Needle& newNeedle(const std::string& needle_name);
void renameNeedle(const std::string& old_name, const std::string& new_name);
@@ -129,6 +141,9 @@ class OmnifilterEngine
protected:
needle_list_t mNeedles;
// <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter
needle_ordered_list_t mOrderedNeedles;
// </FS:minerjr> [FIRE-36649]
std::string mNeedlesXMLPath;
@@ -33,7 +33,7 @@
<layout_panel
name="needle_list_layout"
layout="topleft"
height="150"
height="175"
min_height="94"
user_resize="true">
<check_box
@@ -104,6 +104,27 @@
left_pad="0"
width="96"
label="Remove"/>
<!-- <FS:minerjr> [FIRE-36649] - Add reordering to OmniFilter -->
<button
name="up_needle"
layout="topleft"
follows="left|bottom"
height="20"
left="0"
top_pad="4"
width="96"
tooltip="Selected needle is moved Up 1 position."
label="Up"/>
<button
name="down_needle"
layout="topleft"
follows="right|bottom"
height="20"
left_pad="0"
width="96"
tooltip="Selected needle is moved Down 1 position."
label="Down"/>
<!-- </FS:minerjr> [FIRE-36649] -->
</layout_panel>
<layout_panel
name="filter_log_layout"