diff --git a/indra/newview/omnifilter.cpp b/indra/newview/omnifilter.cpp index 260111015d..e9874372bf 100644 --- a/indra/newview/omnifilter.cpp +++ b/indra/newview/omnifilter.cpp @@ -217,6 +217,66 @@ void Omnifilter::onRemoveNeedleClicked() onSelectNeedle(); } +// [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); + } +} +// [FIRE-36649] + void Omnifilter::onNeedleNameChanged() { const std::string& old_name = mNeedleListCtrl->getSelectedItemLabel(NEEDLE_NAME_COLUMN); @@ -309,6 +369,11 @@ bool Omnifilter::postBuild() mNeedleListCtrl = getChild("needle_list"); mAddNeedleBtn = getChild("add_needle"); mRemoveNeedleBtn = getChild("remove_needle"); + // [FIRE-36649] - Add reordering to OmniFilter + // Add the up and down buttons for re-aranging the order of the needles + mUpNeedleBtn = getChild("up_needle"); + mDownNeedleBtn = getChild("down_needle"); + // [FIRE-36649] mFilterLogCtrl = getChild("filter_log"); mPanelDetails = getChild("panel_details"); mNeedleNameCtrl = getChild("needle_name"); @@ -347,8 +412,13 @@ bool Omnifilter::postBuild() mFilterLogCtrl->deleteAllItems(); auto& instance = OmnifilterEngine::instance(); - for (const auto& [needle_name, needle] : instance.getNeedleList()) + // [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]; + // [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)); + // [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)); + // [FIRE-36649] 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 e36f35f8e2..7aafa3712b 100644 --- a/indra/newview/omnifilter.h +++ b/indra/newview/omnifilter.h @@ -55,6 +55,11 @@ protected: void onNeedleChanged(); void onAddNeedleClicked(); void onRemoveNeedleClicked(); + // [FIRE-36649] - Add reordering to OmniFilter + void onSortChanged(); + void onUpNeedleClicked(); + void onDownNeedleClicked(); + // [FIRE-36649] void onNeedleNameChanged(); void onNeedleCheckboxChanged(LLUICtrl* ctrl); void onOwnerChanged(); @@ -64,6 +69,10 @@ protected: FSScrollListCtrl* mNeedleListCtrl{ nullptr }; LLButton* mAddNeedleBtn{ nullptr }; LLButton* mRemoveNeedleBtn{ nullptr }; + // [FIRE-36649] - Add reordering to OmniFilter + LLButton* mUpNeedleBtn{ nullptr }; + LLButton* mDownNeedleBtn{ nullptr }; + // [FIRE-36649] FSScrollListCtrl* mFilterLogCtrl{ nullptr }; LLPanel* mPanelDetails{ nullptr }; LLLineEditor* mNeedleNameCtrl{ nullptr }; diff --git a/indra/newview/omnifilterengine.cpp b/indra/newview/omnifilterengine.cpp index ae7b882d99..3c8b4146db 100644 --- a/indra/newview/omnifilterengine.cpp +++ b/indra/newview/omnifilterengine.cpp @@ -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) + // [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]; + // [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); + // [FIRE-36649] - Add reordering to OmniFilter + // Add to the ordered needle vector the name of the new needle + mOrderedNeedles.push_back(needle_name); + // [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)); + // [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; + // [FIRE-36649] setDirty(true); } void OmnifilterEngine::deleteNeedle(const std::string& needle_name) { + // [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); + // [FIRE-36649] mNeedles.erase(needle_name); setDirty(true); } @@ -217,6 +240,77 @@ OmnifilterEngine::needle_list_t& OmnifilterEngine::getNeedleList() return mNeedles; } +// [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; +} +// [FIRE-36649] + void OmnifilterEngine::setDirty(bool dirty) { mDirty = dirty; @@ -297,6 +391,12 @@ void OmnifilterEngine::loadNeedles() return; } + // [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()); + // [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; + // [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; + // [/FIRE-36649] } } @@ -353,8 +460,16 @@ void OmnifilterEngine::saveNeedles() LLSD needles_llsd; - for (const auto& [needle_name, needle] : mNeedles) + // [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++; + // [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; diff --git a/indra/newview/omnifilterengine.h b/indra/newview/omnifilterengine.h index 1830058989..5fb2020884 100644 --- a/indra/newview/omnifilterengine.h +++ b/indra/newview/omnifilterengine.h @@ -103,6 +103,18 @@ class OmnifilterEngine typedef std::map> needle_list_t; needle_list_t& getNeedleList(); + // [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 needle_ordered_list_t; + needle_ordered_list_t& getOrderedNeedleList() { return mOrderedNeedles; }; + S32 getOrderedNeedleListSize() const { return static_cast(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); + // [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; + // [FIRE-36649] - Add reordering to OmniFilter + needle_ordered_list_t mOrderedNeedles; + // [FIRE-36649] std::string mNeedlesXMLPath; diff --git a/indra/newview/skins/default/xui/en/floater_omnifilter.xml b/indra/newview/skins/default/xui/en/floater_omnifilter.xml index 768af06c65..d096a175b1 100644 --- a/indra/newview/skins/default/xui/en/floater_omnifilter.xml +++ b/indra/newview/skins/default/xui/en/floater_omnifilter.xml @@ -33,7 +33,7 @@ + +