/*
Copyright 2015-2016 Amebis
This file is part of ZRCola.
ZRCola is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ZRCola is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with ZRCola. If not, see .
*/
#include "stdafx.h"
static int __cdecl compare_hits(const void *a, const void *b)
{
const std::pair *_a = (const std::pair*)a;
const std::pair *_b = (const std::pair*)b;
if (_a->first > _b->first) return -1;
else if (_a->first < _b->first) return 1;
if (_a->second < _b->second) return -1;
else if (_a->second > _b->second) return 1;
return 0;
}
//////////////////////////////////////////////////////////////////////////
// wxZRColaCharSelect
//////////////////////////////////////////////////////////////////////////
wxZRColaCharSelect::wxZRColaCharSelect(wxWindow* parent) :
m_searchChanged(false),
m_unicodeChanged(false),
m_char(0),
wxZRColaCharSelectBase(parent)
{
m_unicode->SetValidator(wxHexValidator(&m_char));
// Fill categories.
ZRColaApp *app = (ZRColaApp*)wxTheApp;
size_t i, n;
for (i = 0, n = app->m_cc_db.idxRnk.size(); i < n; i++) {
const ZRCola::chrcat_db::chrcat &cc = app->m_cc_db.idxRnk[i];
int idx = m_categories->Insert(wxGetTranslation(wxString(cc.name, cc.name_len), wxT("ZRCola-zrcdb")), i);
m_categories->Check(idx);
m_ccOrder.insert(std::make_pair(cc.id, idx));
}
ResetResults();
}
void wxZRColaCharSelect::OnIdle(wxIdleEvent& event)
{
event.Skip();
if (m_unicodeChanged) {
if (m_unicode->GetValidator()->TransferFromWindow()) {
ZRColaApp *app = (ZRColaApp*)wxTheApp;
m_gridPreview->SetCellValue(wxString(1, m_char), 0, 0);
{
ZRCola::character_db::character *chr = (ZRCola::character_db::character*)new char[sizeof(ZRCola::character_db::character)];
chr->chr = m_char;
size_t start, end;
if (app->m_chr_db.idxChr.find(*chr, start, end)) {
const ZRCola::character_db::character &chr = app->m_chr_db.idxChr[start];
m_description->SetValue(wxString(chr.data, chr.desc_len));
m_gridRelated->SetCharacters(wxString(chr.data + chr.desc_len, chr.rel_len));
} else {
m_description->SetValue(wxEmptyString);
m_gridRelated->ClearGrid();
}
m_gridRelated->Scroll(0, 0);
delete chr;
}
}
m_unicodeChanged = false;
} else if (m_searchChanged) {
m_timerSearch.Start(1000, true);
m_searchChanged = false;
}
}
void wxZRColaCharSelect::OnSearchText(wxCommandEvent& event)
{
event.Skip();
m_timerSearch.Stop();
m_searchChanged = true;
}
void wxZRColaCharSelect::OnSearchEnter(wxCommandEvent& event)
{
event.Skip();
m_timerSearch.Stop();
wxTimerEvent e(m_timerSearch);
GetEventHandler()->ProcessEvent(e);
m_searchChanged = false;
}
void wxZRColaCharSelect::OnSearchTimer(wxTimerEvent& event)
{
wxString val(m_search->GetValue());
if (!val.IsEmpty()) {
ZRColaApp *app = (ZRColaApp*)wxTheApp;
std::map hits;
{
// Search by indexes and merge results.
std::map hits_sub;
app->m_chr_db.search_by_desc(val.c_str(), hits, hits_sub);
for (std::map::const_iterator i = hits_sub.cbegin(), i_end = hits_sub.cend(); i != i_end; ++i) {
std::map::iterator idx = hits.find(i->first);
if (idx == hits.end())
hits.insert(std::make_pair(i->first, i->second / 4));
else
idx->second += i->second / 4;
}
}
// Filter by categories.
ZRCola::character_db::character *chr = (ZRCola::character_db::character*)new char[sizeof(ZRCola::character_db::character)];
for (std::map::const_iterator i = hits.cbegin(), i_end = hits.cend(); i != i_end;) {
chr->chr = i->first;
size_t start, end;
std::map::const_iterator idx;
if (app->m_chr_db.idxChr.find(*chr, start, end) &&
((idx = m_ccOrder.find(app->m_chr_db.idxChr[start].cat)) == m_ccOrder.end() || m_categories->IsChecked(idx->second)))
{
// Character category approved.
++i;
} else {
// Character category not approved.
std::map::const_iterator i_remove = i;
++i;
hits.erase(i_remove);
}
}
delete chr;
// Now sort the characters by rank.
std::vector< std::pair > hits2;
hits2.reserve(hits.size());
for (std::map::const_iterator i = hits.cbegin(), i_end = hits.cend(); i != i_end; ++i)
hits2.push_back(std::make_pair(i->second, i->first));
std::qsort(hits2.data(), hits2.size(), sizeof(std::pair), compare_hits);
// Display results.
wxString chars;
chars.reserve(hits2.size());
for (std::vector< std::pair >::const_iterator i = hits2.cbegin(), i_end = hits2.cend(); i != i_end; ++i)
chars += i->second;
m_gridResults->SetCharacters(chars);
} else
ResetResults();
m_gridResults->Scroll(0, 0);
}
void wxZRColaCharSelect::OnCategoriesToggle(wxCommandEvent& event)
{
m_timerSearch.Stop();
m_timerSearch.Start(500, true);
}
void wxZRColaCharSelect::OnResultSelectCell(wxGridEvent& event)
{
wxString val(m_gridResults->GetCellValue(event.GetRow(), event.GetCol()));
m_char = val.IsEmpty() ? 0 : val[0];
m_unicode->GetValidator()->TransferToWindow();
}
void wxZRColaCharSelect::OnResultCellDClick(wxGridEvent& event)
{
event.Skip();
wxString val(m_gridResults->GetCellValue(event.GetRow(), event.GetCol()));
if (!val.IsEmpty()) {
m_char = val[0];
wxCommandEvent e(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
m_sdbSizerButtonsOK->GetEventHandler()->ProcessEvent(e);
}
}
void wxZRColaCharSelect::OnResultsKeyDown(wxKeyEvent& event)
{
switch (event.GetKeyCode()) {
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
wxString val(m_gridResults->GetCellValue(m_gridResults->GetCursorRow(), m_gridResults->GetCursorColumn()));
if (!val.IsEmpty()) {
m_char = val[0];
wxCommandEvent e(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
m_sdbSizerButtonsOK->GetEventHandler()->ProcessEvent(e);
event.StopPropagation();
return;
}
}
event.Skip();
}
void wxZRColaCharSelect::OnRecentSelectCell(wxGridEvent& event)
{
wxString val(m_gridRecent->GetCellValue(event.GetRow(), event.GetCol()));
m_char = val.IsEmpty() ? 0 : val[0];
m_unicode->GetValidator()->TransferToWindow();
}
void wxZRColaCharSelect::OnRecentCellDClick(wxGridEvent& event)
{
event.Skip();
wxString val(m_gridRecent->GetCellValue(event.GetRow(), event.GetCol()));
if (!val.IsEmpty()) {
m_char = val[0];
wxCommandEvent e(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
m_sdbSizerButtonsOK->GetEventHandler()->ProcessEvent(e);
}
}
void wxZRColaCharSelect::OnRecentKeyDown(wxKeyEvent& event)
{
switch (event.GetKeyCode()) {
case WXK_RETURN:
case WXK_NUMPAD_ENTER:
wxString val(m_gridRecent->GetCellValue(m_gridRecent->GetCursorRow(), m_gridRecent->GetCursorColumn()));
if (!val.IsEmpty()) {
m_char = val[0];
wxCommandEvent e(wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK);
m_sdbSizerButtonsOK->GetEventHandler()->ProcessEvent(e);
event.StopPropagation();
return;
}
}
event.Skip();
}
void wxZRColaCharSelect::OnRelatedSelectCell(wxGridEvent& event)
{
wxString val(m_gridRelated->GetCellValue(event.GetRow(), event.GetCol()));
m_char = val.IsEmpty() ? 0 : val[0];
m_unicode->GetValidator()->TransferToWindow();
}
void wxZRColaCharSelect::OnUnicodeText(wxCommandEvent& event)
{
event.Skip();
m_unicodeChanged = true;
}
void wxZRColaCharSelect::OnOKButtonClick(wxCommandEvent& event)
{
event.Skip();
wxString
recent(m_gridRecent->GetCharacters()),
val(1, m_char);
for (size_t i = 0, n = recent.Length(); i < n; i++) {
const wxStringCharType c = recent[i];
if (c != m_char)
val += c;
}
m_gridRecent->SetCharacters(val);
}
void wxZRColaCharSelect::ResetResults()
{
// Fill the results.
ZRColaApp *app = (ZRColaApp*)wxTheApp;
size_t i, n = app->m_chr_db.idxChr.size();
wxString val;
val.reserve(n);
for (i = 0; i < n; i++) {
const ZRCola::character_db::character &chr = app->m_chr_db.idxChr[i];
std::map::const_iterator idx = m_ccOrder.find(chr.cat);
if (idx == m_ccOrder.end() || m_categories->IsChecked(idx->second))
val += chr.chr;
}
m_gridResults->SetCharacters(val);
}
//////////////////////////////////////////////////////////////////////////
// wxPersistentZRColaCharSelect
//////////////////////////////////////////////////////////////////////////
wxPersistentZRColaCharSelect::wxPersistentZRColaCharSelect(wxZRColaCharSelect *wnd) : wxPersistentDialog(wnd)
{
}
void wxPersistentZRColaCharSelect::Save() const
{
wxPersistentDialog::Save();
const wxZRColaCharSelect * const wnd = static_cast(GetWindow());
SaveValue(wxT("recentChars"), wnd->m_gridRecent->GetCharacters());
ZRColaApp *app = (ZRColaApp*)wxTheApp;
for (size_t i = 0, n = app->m_cc_db.idxRnk.size(); i < n; i++) {
const ZRCola::chrcat_db::chrcat &cc = app->m_cc_db.idxRnk[i];
wxString name(wxT("category"));
name.Append(cc.id.data, _countof(cc.id.data));
SaveValue(name, wnd->m_categories->IsChecked(i));
}
}
bool wxPersistentZRColaCharSelect::Restore()
{
wxZRColaCharSelect * const wnd = static_cast(GetWindow());
wxString recent;
if (RestoreValue(wxT("recentChars"), &recent))
wnd->m_gridRecent->SetCharacters(recent);
ZRColaApp *app = (ZRColaApp*)wxTheApp;
for (size_t i = 0, n = app->m_cc_db.idxRnk.size(); i < n; i++) {
const ZRCola::chrcat_db::chrcat &cc = app->m_cc_db.idxRnk[i];
wxString name(wxT("category"));
name.Append(cc.id.data, _countof(cc.id.data));
bool val;
if (RestoreValue(name, &val))
wnd->m_categories->Check(i, val);
}
wnd->ResetResults();
return wxPersistentDialog::Restore();
}