Support for searchable character tags added

This commit is contained in:
Simon Rozman 2016-10-13 11:13:46 +02:00
parent 38a77ca51b
commit 0ed0cf8c49
11 changed files with 261 additions and 8 deletions

View File

@ -3887,6 +3887,97 @@
<event name="OnUpdateUI"></event> <event name="OnUpdateUI"></event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="0">
<property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxTextCtrl" expanded="0">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_tags</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style">wxTE_CENTRE|wxTE_MULTILINE|wxTE_READONLY</property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip">Character tags</property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnText"></event>
<event name="OnTextEnter"></event>
<event name="OnTextMaxLen"></event>
<event name="OnTextURL"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="0"> <object class="sizeritem" expanded="0">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>

View File

@ -131,6 +131,18 @@ bool ZRColaApp::OnInit()
wxFAIL_MSG(wxT("Error reading character category data from ZRCola.zrcdb.")); wxFAIL_MSG(wxT("Error reading character category data from ZRCola.zrcdb."));
m_cc_db.clear(); 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 } else
stdex::idrec::ignore<ZRCola::recordsize_t, ZRCOLA_RECORD_ALIGN>(dat); stdex::idrec::ignore<ZRCola::recordsize_t, ZRCOLA_RECORD_ALIGN>(dat);
} }

View File

@ -33,6 +33,7 @@ class ZRColaApp;
#include <zrcola/character.h> #include <zrcola/character.h>
#include <zrcola/language.h> #include <zrcola/language.h>
#include <zrcola/translate.h> #include <zrcola/translate.h>
#include <zrcola/tag.h>
#include <zrcolaui/keyboard.h> #include <zrcolaui/keyboard.h>
@ -77,6 +78,8 @@ public:
ZRCola::keyseq_db m_ks_db; ///< Key sequence database ZRCola::keyseq_db m_ks_db; ///< Key sequence database
ZRCola::character_db m_chr_db; ///< Character database ZRCola::character_db m_chr_db; ///< Character database
ZRCola::chrcat_db m_cc_db; ///< Characted category 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 wxZRColaFrame *m_mainWnd; ///< Main window

View File

@ -34,6 +34,13 @@ wxZRColaCharSelect::wxZRColaCharSelect(wxWindow* parent) :
m_searchThread(NULL), m_searchThread(NULL),
wxZRColaCharSelectBase(parent) wxZRColaCharSelectBase(parent)
{ {
// Set tag lookup locale.
auto language = static_cast<wxLanguage>(dynamic_cast<ZRColaApp*>(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); Connect(wxID_ANY, wxEVT_SEARCH_COMPLETE, wxThreadEventHandler(wxZRColaCharSelect::OnSearchComplete), NULL, this);
m_search_more->SetLabel(_(L"▸ Search Options")); m_search_more->SetLabel(_(L"▸ Search Options"));
@ -83,7 +90,7 @@ void wxZRColaCharSelect::OnIdle(wxIdleEvent& event)
size_t start; size_t start;
if (app->m_chr_db.idxChr.find(*(ZRCola::character_db::character*)chr, start)) { if (app->m_chr_db.idxChr.find(*(ZRCola::character_db::character*)chr, start)) {
const auto &chr = app->m_chr_db.idxChr[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)); m_description->SetValue(wxString(chr.data, chr.desc_len));
{ {
// See if this character has a key sequence registered. // See if this character has a key sequence registered.
@ -120,6 +127,45 @@ void wxZRColaCharSelect::OnIdle(wxIdleEvent& event)
m_gridRelated->ClearGrid(); m_gridRelated->ClearGrid();
} }
// Find character tags.
std::list<std::wstring> 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); m_gridRelated->GoToCell(m_historyCursor->m_related.m_selected);
wxGridCellCoords coord(m_gridResults->GetCharacterCoords(m_char)); wxGridCellCoords coord(m_gridResults->GetCharacterCoords(m_char));
@ -506,7 +552,14 @@ wxThread::ExitCode wxZRColaCharSelect::SearchThread::Entry()
if (TestDestroy()) return (wxThread::ExitCode)1; 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<ZRCola::tagid_t, unsigned __int16> 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<wchar_t, ZRCola::charrank_t> hits_sub; std::map<wchar_t, ZRCola::charrank_t> hits_sub;
if (!app->m_chr_db.Search(m_search.c_str(), m_cats, hits, hits_sub, TestDestroyS, this)) return (wxThread::ExitCode)1; 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) { for (auto i = hits_sub.cbegin(), i_end = hits_sub.cend(); i != i_end; ++i) {

View File

@ -82,6 +82,7 @@ public:
wchar_t m_char; ///< Currently selected character (0 when none) wchar_t m_char; ///< Currently selected character (0 when none)
protected: protected:
LCID m_locale; ///< Locale for tag lookup
bool m_searchChanged; ///< Did Search field or category selection change? bool m_searchChanged; ///< Did Search field or category selection change?
std::map<ZRCola::chrcatid_t, int> m_ccOrder; ///< Character category order std::map<ZRCola::chrcatid_t, int> m_ccOrder; ///< Character category order
bool m_unicodeChanged; ///< Did Unicode field change? bool m_unicodeChanged; ///< Did Unicode field change?

View File

@ -619,6 +619,11 @@ wxZRColaCharSelectBase::wxZRColaCharSelectBase( wxWindow* parent, wxWindowID id,
sbSizerPreview->Add( m_description, 1, wxALL|wxEXPAND, 5 ); 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 = new wxTextCtrl( sbSizerPreview->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_CENTRE|wxTE_READONLY );
m_category->SetToolTip( _("Unicode character category") ); m_category->SetToolTip( _("Unicode character category") );

View File

@ -210,6 +210,7 @@ class wxZRColaCharSelectBase : public wxDialog
wxTextCtrl* m_shortcut; wxTextCtrl* m_shortcut;
wxGrid* m_gridPreview; wxGrid* m_gridPreview;
wxTextCtrl* m_description; wxTextCtrl* m_description;
wxTextCtrl* m_tags;
wxTextCtrl* m_category; wxTextCtrl* m_category;
wxHyperlinkCtrl* m_navigateBack; wxHyperlinkCtrl* m_navigateBack;
wxHyperlinkCtrl* m_navigateForward; wxHyperlinkCtrl* m_navigateForward;

View File

@ -666,6 +666,7 @@ int _tmain(int argc, _TCHAR *argv[])
// Preallocate memory. // Preallocate memory.
db.idxName.reserve(count*3); db.idxName.reserve(count*3);
db.idxTag .reserve(count*3);
db.data .reserve(count*3*4); db.data .reserve(count*3*4);
// Parse tags and build index and data. // 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++) for (wstring::size_type i = 0; i < n; i++)
db.data.push_back(nm->at(i)); db.data.push_back(nm->at(i));
db.idxName.push_back(idx); db.idxName.push_back(idx);
db.idxTag .push_back(idx);
} }
} }
} else } else
@ -694,6 +696,7 @@ int _tmain(int argc, _TCHAR *argv[])
// Sort indices. // Sort indices.
db.idxName.sort(); db.idxName.sort();
db.idxTag .sort();
// Write tags to file. // Write tags to file.
dst << ZRCola::tagname_rec(db); dst << ZRCola::tagname_rec(db);

View File

@ -180,6 +180,16 @@ namespace ZRCola {
idxTag.clear(); idxTag.clear();
data .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<tagid_t, unsigned __int16> &tags, _Inout_ std::map<wchar_t, charrank_t> &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 } idxName; ///< Name index
///
/// Tag index
///
class indexTag : public index<unsigned __int16, unsigned __int32, tagname>
{
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<unsigned __int16> &h) : index<unsigned __int16, unsigned __int32, tagname>(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<unsigned __int16> data; ///< Tag data std::vector<unsigned __int16> data; ///< Tag data
public: public:
/// ///
/// Constructs the database /// Constructs the database
/// ///
inline tagname_db() : idxName(data) {} inline tagname_db() : idxName(data), idxTag(data) {}
/// ///
/// Clears the database /// Clears the database
@ -308,6 +355,7 @@ namespace ZRCola {
inline void clear() inline void clear()
{ {
idxName.clear(); idxName.clear();
idxTag .clear();
data .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) 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; if (stream.fail()) return stream;
stream << db.idxName; stream << db.idxName;
// Write tag index.
if (stream.fail()) return stream;
stream << db.idxTag;
// Write data count. // Write data count.
auto data_count = db.data.size(); auto data_count = db.data.size();
#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) #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) inline std::istream& operator >>(_In_ std::istream& stream, _Out_ ZRCola::tagname_db &db)
{ {
// Read tag index. // Read name index.
stream >> db.idxName; stream >> db.idxName;
if (!stream.good()) return stream; if (!stream.good()) return stream;
// Read tag index.
stream >> db.idxTag;
if (!stream.good()) return stream;
// Read data count. // Read data count.
unsigned __int32 count; unsigned __int32 count;
stream.read((char*)&count, sizeof(count)); stream.read((char*)&count, sizeof(count));

View File

@ -20,6 +20,34 @@
#include "stdafx.h" #include "stdafx.h"
bool ZRCola::chrtag_db::Search(_In_ const std::map<tagid_t, unsigned __int16> &tags, _Inout_ std::map<wchar_t, charrank_t> &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<tagid_t, unsigned __int16> &hits, _In_opt_ bool (__cdecl *fn_abort)(void *cookie), _In_opt_ void *cookie) const bool ZRCola::tagname_db::Search(_In_z_ const wchar_t *str, _In_ LCID locale, _Inout_ std::map<tagid_t, unsigned __int16> &hits, _In_opt_ bool (__cdecl *fn_abort)(void *cookie), _In_opt_ void *cookie) const
{ {
assert(str); 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; if (fn_abort && fn_abort(cookie)) return false;
// Find the name. // Find the name.
std::unique_ptr<tagname> tn(reinterpret_cast<tagname*>(new char[sizeof(tagname) + name.length()])); std::unique_ptr<tagname> tn(reinterpret_cast<tagname*>(new char[sizeof(tagname) + sizeof(wchar_t)*name.length()]));
tn->locale = locale; 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; size_t start, end;
if (idxName.find(*tn, start, end)) { if (idxName.find(*tn, start, end)) {
// The name was found. // The name was found.
for (size_t i = start; i < end; i++) { for (size_t i = start; i < end; i++) {
const tagname &name = idxName[i];
if (fn_abort && fn_abort(cookie)) return false; if (fn_abort && fn_abort(cookie)) return false;
const tagname &name = idxName[i];
auto idx = hits.find(name.tag); auto idx = hits.find(name.tag);
if (idx == hits.end()) { if (idx == hits.end()) {
// New tag. // New tag.

Binary file not shown.