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
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 6398ee5..3a9d7b2 100644
Binary files a/output/data/ZRCola.zrcdb and b/output/data/ZRCola.zrcdb differ