diff --git a/Source/UI/ProbabilityTable/UIProbabilityTable.cpp b/Source/UI/ProbabilityTable/UIProbabilityTable.cpp index e3b2291e..7b92f17d 100644 --- a/Source/UI/ProbabilityTable/UIProbabilityTable.cpp +++ b/Source/UI/ProbabilityTable/UIProbabilityTable.cpp @@ -1,5 +1,7 @@ #include "UI/ProbabilityTable/UIProbabilityTable.h" +#include "Network/TextEncoding.h" +#include "UI/Edit/UIEditBoxEx.h" #include "UI/Lists/UIVScrollBar.h" #include "UI/Window/UIControl.h" #include "UI/Window/WindowBorder.h" @@ -7,13 +9,23 @@ #include "UI/Text/UIStaticTextControl.h" #include "Contents/MessageTable.h" #include "Render/Renderer.h" +#include "UI/Window/WindowDialogs.h" #include "Utility/Cipher/XorBytes.h" #include "Utility/Text/FormatString.h" +#include "Utility/Text/StringUtil.h" #include #include +#include #include +#include #include +#include + +// GLOBAL: gl_25157 +static const std::string g_probabilityHighlightBegin = "^FF0000"; +// GLOBAL: gl_25158 +static const std::string g_probabilityHighlightEnd = "^000000"; namespace { @@ -106,6 +118,9 @@ void ProbabilityTableSelectCallback::operator()(CUIListItem* const& item) { template <> void CUIListBoxEx::OnScroll(int position); +template <> +bool CUIListBoxEx::RebindIndex(int index); + // OFFSET: 0x00A47EF0 // FUNCTION: fn_16104 bool UnusedTrueA47EF0() { @@ -328,6 +343,66 @@ void CUIListBoxEx::Selec void CUIProbabilityTable::OnButtonClick() { } +// OFFSET: 0x00A4DCA0 +// FUNCTION: fn_16196 +void CUIProbabilityTable::Search() { + CUIListBoxEx* list = this->m_list; + if (list->data.size() == 0) { + return; + } + + if (static_cast(list->selected) >= 0 && list->selected < list->data.size()) { + auto node = list->data.begin(); + for (int index = static_cast(list->selected); index > 0; --index) { + ++node; + } + RemoveAll(node->name, g_probabilityHighlightBegin); + RemoveAll(node->name, g_probabilityHighlightEnd); + this->m_list->RebindIndex(this->m_list->selected); + } + + const std::string_view keyword(this->m_searchEdit->edit->Text()); + if (keyword.size() == 0) { + this->m_lastSearch = keyword; + return; + } + + list = this->m_list; + auto matches = [keyword](const ProbabilityTable::CItemData& data) { + return FindSubstring(data.name.c_str(), static_cast(data.name.size()), keyword.data()) != -1; + }; + + auto start = list->data.begin(); + if (keyword == this->m_lastSearch) { + const int next = list->data.size() - 1 != list->selected ? static_cast(list->selected) + 1 : 0; + start = std::next(list->data.begin(), next); + } + + auto found = std::find_if(start, list->data.end(), matches); + if (found == list->data.end() && keyword == this->m_lastSearch) { + found = std::find_if(list->data.begin(), list->data.end(), matches); + } + + if (found == list->data.end()) { + reinterpret_cast(&g_windowMgr)->Confirm(LookupMessage(0x1012), 0, true, false, 0, "Message", 280, 120, 0); + } else { + HighlightKeyword(found->name, keyword, g_probabilityHighlightBegin, g_probabilityHighlightEnd); + + CUIListBoxEx* current = this->m_list; + const int index = static_cast(std::distance(list->data.begin(), found)); + current->scrollBar->SetPosition(current->rowHeight * index); + for (ProbabilityTable::CUIItem* item : current->items) { + if (index == item->index) { + current->SelectItem(item); + break; + } + } + this->m_list->RebindIndex(this->m_list->selected); + } + + this->m_lastSearch = keyword; +} + // OFFSET: 0x00A4E280 // FUNCTION: fn_16203 void CUIProbabilityTable::Slot16() { diff --git a/Source/UI/ProbabilityTable/UIProbabilityTable.h b/Source/UI/ProbabilityTable/UIProbabilityTable.h index c7cfa773..db273595 100644 --- a/Source/UI/ProbabilityTable/UIProbabilityTable.h +++ b/Source/UI/ProbabilityTable/UIProbabilityTable.h @@ -7,15 +7,16 @@ #include #include +#include namespace ProbabilityTable { class CItemData { public: - /* 0000 */ std::uint32_t color; + /* 0000 */ std::uint32_t color = 0xFFFFFFFF; /* 0004 */ std::string name; /* 001C */ int unknown_001C; - /* 0020 */ double probability; + /* 0020 */ double probability = 0.0; }; class CUIItem : public CUIListItem { @@ -43,14 +44,22 @@ public: int SendMsg(UIWindow* sender, int message, std::intptr_t argument1, std::intptr_t argument2, std::intptr_t argument3, std::intptr_t argument4) override; void OnButtonClick(); void OnPrepareItem(ProbabilityTable::CUIItem* item, const ProbabilityTable::CItemData& data); + void Search(); + void SetItem(int itemId); /* 00D8 */ CUITitleBar* m_titleBar; /* 00DC */ CUIStaticText* m_nameHeader; /* 00E0 */ CUIEditBoxEx* m_searchEdit; - /* 00E4 */ unsigned char unknown_00E4[0x08]; + /* 00E4 */ unsigned char unknown_00E4[0x04]; + /* 00E8 */ CUIStaticText* m_itemName; /* 00EC */ CUIStaticText* m_probabilityHeader; + /* 00F0 */ CUIListBoxEx* m_list; + /* 00F4 */ std::string m_lastSearch; + /* 010C */ int m_itemId; }; +int HighlightKeyword(std::string& text, std::string_view keyword, std::string_view prefix, std::string_view suffix); + #if defined(_WIN32) && !defined(_WIN64) static_assert(sizeof(ProbabilityTable::CItemData) == 0x28); static_assert(sizeof(ProbabilityTable::CUIItem) == 0xF8); diff --git a/Source/UI/ProbabilityTable/UIProbabilityTableEvents.cpp b/Source/UI/ProbabilityTable/UIProbabilityTableEvents.cpp index 21e33cc2..eacc7dba 100644 --- a/Source/UI/ProbabilityTable/UIProbabilityTableEvents.cpp +++ b/Source/UI/ProbabilityTable/UIProbabilityTableEvents.cpp @@ -1,8 +1,26 @@ #include "UI/ProbabilityTable/UIProbabilityTable.h" +#include "Client/Input/InputCharset.h" +#include "Contents/Configuration/ServiceConfiguration.h" +#include "Contents/Items/ItemDescription.h" +#include "Contents/Items/ItemInfo.h" +#include "Contents/Items/ItemNaming.h" +#include "Contents/Items/NeoPackageItemMgr.h" +#include "Contents/PlayerNameDictionary.h" +#include "Network/TextEncoding.h" #include "UI/Edit/UIEditBoxEx.h" +#include "UI/Text/UIStaticTextControl.h" +#include +#include #include +#include +#include +#include +#include +#include +#include +#include // OFFSET: 0x00A4E3B0 // FUNCTION: fn_16206 @@ -30,3 +48,34 @@ int CUIProbabilityTable::SendMsg(UIWindow* sender, int message, std::intptr_t ar } return UIFrameWnd::SendMsg(sender, message, argument1, argument2, argument3, argument4); } + +// OFFSET: 0x00A4E4D0 + +// OFFSET: 0x00A4EBD0 +// FUNCTION: fn_16210 +int HighlightKeyword(std::string& text, std::string_view keyword, std::string_view prefix, std::string_view suffix) { + int count = 0; + if (FindSubstring(text.c_str(), static_cast(text.size()), keyword.data()) == -1) { + return 0; + } + + std::wstring wideText(ATL::CA2WEX<128>(text.c_str(), CP_THREAD_ACP)); + std::wstring lowerText = LowercaseWide(text); + std::wstring lowerKeyword = LowercaseWide(keyword.data()); + std::wstring widePrefix(ATL::CA2WEX<128>(prefix.data(), CP_THREAD_ACP)); + std::wstring wideSuffix(ATL::CA2WEX<128>(suffix.data(), CP_THREAD_ACP)); + + std::size_t position = 0; + while ((position = lowerText.find(lowerKeyword, position)) != std::wstring::npos) { + lowerText.insert(position, widePrefix.c_str(), widePrefix.size()); + wideText.insert(position, widePrefix.c_str(), widePrefix.size()); + position += lowerKeyword.size() + widePrefix.size(); + lowerText.insert(position, wideSuffix.c_str(), wideSuffix.size()); + wideText.insert(position, wideSuffix.c_str(), wideSuffix.size()); + position += suffix.size(); + ++count; + } + + text = std::string(ATL::CW2AEX<128>(wideText.c_str(), CP_THREAD_ACP)); + return count; +}