From 0ed0cf8c4982760b43bc011c8adc86cc1d016096 Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Thu, 13 Oct 2016 11:13:46 +0200 Subject: [PATCH] Support for searchable character tags added --- ZRCola/ZRCola.fbp | 91 +++++++++++++++++++++++++++++ ZRCola/zrcolaapp.cpp | 12 ++++ ZRCola/zrcolaapp.h | 3 + ZRCola/zrcolachrslct.cpp | 57 +++++++++++++++++- ZRCola/zrcolachrslct.h | 1 + ZRCola/zrcolagui.cpp | 5 ++ ZRCola/zrcolagui.h | 1 + ZRColaCompile/main.cpp | 3 + lib/libZRCola/include/zrcola/tag.h | 62 +++++++++++++++++++- lib/libZRCola/src/tag.cpp | 34 ++++++++++- output/data/ZRCola.zrcdb | Bin 2378846 -> 2379378 bytes 11 files changed, 261 insertions(+), 8 deletions(-) diff --git a/ZRCola/ZRCola.fbp b/ZRCola/ZRCola.fbp index 70e9992..731893b 100644 --- a/ZRCola/ZRCola.fbp +++ b/ZRCola/ZRCola.fbp @@ -3887,6 +3887,97 @@ + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_tags + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_CENTRE|wxTE_MULTILINE|wxTE_READONLY + + 0 + Character tags + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 wxALL|wxEXPAND diff --git a/ZRCola/zrcolaapp.cpp b/ZRCola/zrcolaapp.cpp index 5926849..f708ae4 100644 --- a/ZRCola/zrcolaapp.cpp +++ b/ZRCola/zrcolaapp.cpp @@ -131,6 +131,18 @@ bool ZRColaApp::OnInit() wxFAIL_MSG(wxT("Error reading character category data from ZRCola.zrcdb.")); m_cc_db.clear(); } + } else if (id == ZRCola::chrtag_rec::id) { + dat >> ZRCola::chrtag_rec(m_ct_db); + if (!dat.good()) { + wxFAIL_MSG(wxT("Error reading character tag data from ZRCola.zrcdb.")); + m_ct_db.clear(); + } + } else if (id == ZRCola::tagname_rec::id) { + dat >> ZRCola::tagname_rec(m_tn_db); + if (!dat.good()) { + wxFAIL_MSG(wxT("Error reading tag name data from ZRCola.zrcdb.")); + m_tn_db.clear(); + } } else stdex::idrec::ignore(dat); } diff --git a/ZRCola/zrcolaapp.h b/ZRCola/zrcolaapp.h index 294cc40..6c1f374 100644 --- a/ZRCola/zrcolaapp.h +++ b/ZRCola/zrcolaapp.h @@ -33,6 +33,7 @@ class ZRColaApp; #include #include #include +#include #include @@ -77,6 +78,8 @@ public: ZRCola::keyseq_db m_ks_db; ///< Key sequence database ZRCola::character_db m_chr_db; ///< Character database ZRCola::chrcat_db m_cc_db; ///< Characted category database + ZRCola::chrtag_db m_ct_db; ///< Character tag database + ZRCola::tagname_db m_tn_db; ///< Tag name database wxZRColaFrame *m_mainWnd; ///< Main window diff --git a/ZRCola/zrcolachrslct.cpp b/ZRCola/zrcolachrslct.cpp index 5118f2b..4d5baa9 100644 --- a/ZRCola/zrcolachrslct.cpp +++ b/ZRCola/zrcolachrslct.cpp @@ -34,6 +34,13 @@ wxZRColaCharSelect::wxZRColaCharSelect(wxWindow* parent) : m_searchThread(NULL), wxZRColaCharSelectBase(parent) { + // Set tag lookup locale. + auto language = static_cast(dynamic_cast(wxTheApp)->m_locale.GetLanguage()); + if (wxLANGUAGE_ENGLISH <= language && language <= wxLANGUAGE_ENGLISH_ZIMBABWE) m_locale = MAKELCID(MAKELANGID(LANG_ENGLISH , SUBLANG_DEFAULT), SORT_DEFAULT); + else if (wxLANGUAGE_RUSSIAN <= language && language <= wxLANGUAGE_RUSSIAN_UKRAINE ) m_locale = MAKELCID(MAKELANGID(LANG_RUSSIAN , SUBLANG_DEFAULT), SORT_DEFAULT); + else if (wxLANGUAGE_SLOVENIAN == language ) m_locale = MAKELCID(MAKELANGID(LANG_SLOVENIAN, SUBLANG_DEFAULT), SORT_DEFAULT); + else m_locale = MAKELCID(MAKELANGID(LANG_ENGLISH , SUBLANG_DEFAULT), SORT_DEFAULT); + Connect(wxID_ANY, wxEVT_SEARCH_COMPLETE, wxThreadEventHandler(wxZRColaCharSelect::OnSearchComplete), NULL, this); m_search_more->SetLabel(_(L"▸ Search Options")); @@ -83,7 +90,7 @@ void wxZRColaCharSelect::OnIdle(wxIdleEvent& event) size_t start; if (app->m_chr_db.idxChr.find(*(ZRCola::character_db::character*)chr, start)) { const auto &chr = app->m_chr_db.idxChr[start]; - // Update characted description. + // Update character description. m_description->SetValue(wxString(chr.data, chr.desc_len)); { // See if this character has a key sequence registered. @@ -120,6 +127,45 @@ void wxZRColaCharSelect::OnIdle(wxIdleEvent& event) m_gridRelated->ClearGrid(); } + // Find character tags. + std::list tag_names; + ZRCola::chrtag_db::chrtag ct = { m_char }; + size_t end; + if (app->m_ct_db.idxChr.find(ct, start, end)) { + for (size_t i = start; i < end; i++) { + const ZRCola::chrtag_db::chrtag &ct = app->m_ct_db.idxChr[i]; + + // Find tag names. + char tn[sizeof(ZRCola::tagname_db::tagname)] = {}; + ((ZRCola::tagname_db::tagname*)tn)->locale = m_locale; + ((ZRCola::tagname_db::tagname*)tn)->tag = ct.tag; + size_t start, end; + if (app->m_tn_db.idxTag.find(*((ZRCola::tagname_db::tagname*)tn), start, end)) { + for (size_t i = start; i < end; i++) { + const ZRCola::tagname_db::tagname &tn = app->m_tn_db.idxTag[i]; + + // Add tag name to the list (prevent duplicates). + for (auto name = tag_names.cbegin(), name_end = tag_names.cend();; ++name) { + if (name == name_end) { + // Add name to the list. + tag_names.push_back(std::wstring(tn.name, tn.name + tn.name_len)); + break; + } else if (ZRCola::tagname_db::tagname::CompareName(m_locale, name->data(), (unsigned __int16)name->length(), tn.name, tn.name_len) == 0) + // Name is already on the list. + break; + } + } + } + } + } + wxString tags; + for (auto name = tag_names.cbegin(), name_end = tag_names.cend(); name != name_end; ++name) { + if (!tags.empty()) + tags += _(", "); + tags += *name; + } + m_tags->SetValue(tags); + m_gridRelated->GoToCell(m_historyCursor->m_related.m_selected); wxGridCellCoords coord(m_gridResults->GetCharacterCoords(m_char)); @@ -506,7 +552,14 @@ wxThread::ExitCode wxZRColaCharSelect::SearchThread::Entry() if (TestDestroy()) return (wxThread::ExitCode)1; { - // Search by indexes and merge results. + // Search by tags: Get tags with given names. Then, get characters of found tags. + std::map hits_tag; + if (!app->m_tn_db.Search(m_search.c_str(), m_parent->m_locale, hits_tag, TestDestroyS, this)) return (wxThread::ExitCode)1; + if (!app->m_ct_db.Search(hits_tag, hits, TestDestroyS, this)) return (wxThread::ExitCode)1; + } + + { + // Search by description and merge results. std::map hits_sub; if (!app->m_chr_db.Search(m_search.c_str(), m_cats, hits, hits_sub, TestDestroyS, this)) return (wxThread::ExitCode)1; for (auto i = hits_sub.cbegin(), i_end = hits_sub.cend(); i != i_end; ++i) { diff --git a/ZRCola/zrcolachrslct.h b/ZRCola/zrcolachrslct.h index ac452ec..25019f8 100644 --- a/ZRCola/zrcolachrslct.h +++ b/ZRCola/zrcolachrslct.h @@ -82,6 +82,7 @@ public: wchar_t m_char; ///< Currently selected character (0 when none) protected: + LCID m_locale; ///< Locale for tag lookup bool m_searchChanged; ///< Did Search field or category selection change? std::map m_ccOrder; ///< Character category order bool m_unicodeChanged; ///< Did Unicode field change? diff --git a/ZRCola/zrcolagui.cpp b/ZRCola/zrcolagui.cpp index dae75b6..d37fe80 100644 --- a/ZRCola/zrcolagui.cpp +++ b/ZRCola/zrcolagui.cpp @@ -619,6 +619,11 @@ wxZRColaCharSelectBase::wxZRColaCharSelectBase( wxWindow* parent, wxWindowID id, sbSizerPreview->Add( m_description, 1, wxALL|wxEXPAND, 5 ); + m_tags = new wxTextCtrl( sbSizerPreview->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE|wxTE_MULTILINE|wxTE_READONLY ); + m_tags->SetToolTip( _("Character tags") ); + + sbSizerPreview->Add( m_tags, 1, wxALL|wxEXPAND, 5 ); + m_category = new wxTextCtrl( sbSizerPreview->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE|wxTE_READONLY ); m_category->SetToolTip( _("Unicode character category") ); diff --git a/ZRCola/zrcolagui.h b/ZRCola/zrcolagui.h index 7ca7079..9805871 100644 --- a/ZRCola/zrcolagui.h +++ b/ZRCola/zrcolagui.h @@ -210,6 +210,7 @@ class wxZRColaCharSelectBase : public wxDialog wxTextCtrl* m_shortcut; wxGrid* m_gridPreview; wxTextCtrl* m_description; + wxTextCtrl* m_tags; wxTextCtrl* m_category; wxHyperlinkCtrl* m_navigateBack; wxHyperlinkCtrl* m_navigateForward; diff --git a/ZRColaCompile/main.cpp b/ZRColaCompile/main.cpp index 4050960..44b6aa2 100644 --- a/ZRColaCompile/main.cpp +++ b/ZRColaCompile/main.cpp @@ -666,6 +666,7 @@ int _tmain(int argc, _TCHAR *argv[]) // Preallocate memory. db.idxName.reserve(count*3); + db.idxTag .reserve(count*3); db.data .reserve(count*3*4); // Parse tags and build index and data. @@ -686,6 +687,7 @@ int _tmain(int argc, _TCHAR *argv[]) for (wstring::size_type i = 0; i < n; i++) db.data.push_back(nm->at(i)); db.idxName.push_back(idx); + db.idxTag .push_back(idx); } } } else @@ -694,6 +696,7 @@ int _tmain(int argc, _TCHAR *argv[]) // Sort indices. db.idxName.sort(); + db.idxTag .sort(); // Write tags to file. dst << ZRCola::tagname_rec(db); diff --git a/lib/libZRCola/include/zrcola/tag.h b/lib/libZRCola/include/zrcola/tag.h index b9d556d..be581f3 100644 --- a/lib/libZRCola/include/zrcola/tag.h +++ b/lib/libZRCola/include/zrcola/tag.h @@ -180,6 +180,16 @@ namespace ZRCola { idxTag.clear(); data .clear(); } + + /// + /// Search for characters by tags + /// + /// \param[in ] tags Search tags + /// \param[inout] hits (character, count) map to append hits to + /// \param[in ] fn_abort Pointer to function to periodically test for search cancellation + /// \param[in ] cookie Cookie for \p fn_abort call + /// + bool Search(_In_ const std::map &tags, _Inout_ std::map &hits, _In_opt_ bool (__cdecl *fn_abort)(void *cookie) = NULL, _In_opt_ void *cookie = NULL) const; }; @@ -294,13 +304,50 @@ namespace ZRCola { } } idxName; ///< Name index + /// + /// Tag index + /// + class indexTag : public index + { + public: + /// + /// Constructs the index + /// + /// \param[in] h Reference to vector holding the data + /// \param[in] locale Locale used to perform tag name comparison + /// + indexTag(_In_ std::vector &h) : index(h) {} + + /// + /// Compares two tag names by tag (for searching) + /// + /// \param[in] a Pointer to first element + /// \param[in] b Pointer to second element + /// + /// \returns + /// - <0 when a < b + /// - =0 when a == b + /// - >0 when a > b + /// + virtual int compare(_In_ const tagname &a, _In_ const tagname &b) const + { + if (a.locale < b.locale) return -1; + else if (a.locale > b.locale) return 1; + + if (a.tag < b.tag) return -1; + else if (a.tag > b.tag) return 1; + + return 0; + } + } idxTag; ///< Tag index + std::vector data; ///< Tag data public: /// /// Constructs the database /// - inline tagname_db() : idxName(data) {} + inline tagname_db() : idxName(data), idxTag(data) {} /// /// Clears the database @@ -308,6 +355,7 @@ namespace ZRCola { inline void clear() { idxName.clear(); + idxTag .clear(); data .clear(); } @@ -415,10 +463,14 @@ inline std::istream& operator >>(_In_ std::istream& stream, _Out_ ZRCola::chrtag /// inline std::ostream& operator <<(_In_ std::ostream& stream, _In_ const ZRCola::tagname_db &db) { - // Write tag index. + // Write name index. if (stream.fail()) return stream; stream << db.idxName; + // Write tag index. + if (stream.fail()) return stream; + stream << db.idxTag; + // Write data count. auto data_count = db.data.size(); #if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) @@ -450,10 +502,14 @@ inline std::ostream& operator <<(_In_ std::ostream& stream, _In_ const ZRCola::t /// inline std::istream& operator >>(_In_ std::istream& stream, _Out_ ZRCola::tagname_db &db) { - // Read tag index. + // Read name index. stream >> db.idxName; if (!stream.good()) return stream; + // Read tag index. + stream >> db.idxTag; + if (!stream.good()) return stream; + // Read data count. unsigned __int32 count; stream.read((char*)&count, sizeof(count)); diff --git a/lib/libZRCola/src/tag.cpp b/lib/libZRCola/src/tag.cpp index 4ff965b..222efd1 100644 --- a/lib/libZRCola/src/tag.cpp +++ b/lib/libZRCola/src/tag.cpp @@ -20,6 +20,34 @@ #include "stdafx.h" +bool ZRCola::chrtag_db::Search(_In_ const std::map &tags, _Inout_ std::map &hits, _In_opt_ bool (__cdecl *fn_abort)(void *cookie), _In_opt_ void *cookie) const +{ + for (auto tag = tags.cbegin(), tag_end = tags.cend(); tag != tag_end; ++tag) { + if (fn_abort && fn_abort(cookie)) return false; + + // Search for tagged characters. + chrtag el = { 0, tag->first }; + size_t start, end; + if (idxTag.find(el, start, end)) { + for (size_t i = start; i < end; i++) { + if (fn_abort && fn_abort(cookie)) return false; + const chrtag &ct = idxTag[i]; + auto idx = hits.find(ct.chr); + if (idx == hits.end()) { + // New character. + hits.insert(std::make_pair(ct.chr, tag->second)); + } else { + // Increase count for existing character. + idx->second += tag->second; + } + } + } + } + + return true; +} + + bool ZRCola::tagname_db::Search(_In_z_ const wchar_t *str, _In_ LCID locale, _Inout_ std::map &hits, _In_opt_ bool (__cdecl *fn_abort)(void *cookie), _In_opt_ void *cookie) const { assert(str); @@ -64,15 +92,15 @@ bool ZRCola::tagname_db::Search(_In_z_ const wchar_t *str, _In_ LCID locale, _In if (fn_abort && fn_abort(cookie)) return false; // Find the name. - std::unique_ptr tn(reinterpret_cast(new char[sizeof(tagname) + name.length()])); + std::unique_ptr tn(reinterpret_cast(new char[sizeof(tagname) + sizeof(wchar_t)*name.length()])); tn->locale = locale; - wmemcpy(tn->name, name.data(), tn->name_len = (unsigned __int16)name.length()); + memcpy(tn->name, name.data(), sizeof(wchar_t)*(tn->name_len = (unsigned __int16)name.length())); size_t start, end; if (idxName.find(*tn, start, end)) { // The name was found. for (size_t i = start; i < end; i++) { - const tagname &name = idxName[i]; if (fn_abort && fn_abort(cookie)) return false; + const tagname &name = idxName[i]; auto idx = hits.find(name.tag); if (idx == hits.end()) { // New tag. diff --git a/output/data/ZRCola.zrcdb b/output/data/ZRCola.zrcdb index 6398ee5b077f9bbc77cd0d3aa2b9d2b5b07ebc26..3a9d7b26de95cd7b29ee56c461065d6dc1189d78 100644 GIT binary patch delta 676 zcmXBPF-VkQ9LMqJ-n;i#n%-remw8!vUbTopgSLdB1`QP=L15D6a4k8hOI(V-aAS;$gWvX;$* T+VhhparlgP$vOG~q-!Df