diff --git a/ZRColaCompile/ZRColaCompile.props b/ZRColaCompile/ZRColaCompile.props index ceb8841..b15055c 100644 --- a/ZRColaCompile/ZRColaCompile.props +++ b/ZRColaCompile/ZRColaCompile.props @@ -7,7 +7,7 @@ - ..\lib\wxExtend\include;..\lib\stdex\include;..\lib\libZRCola\include + ..\lib\wxExtend\include;..\lib\stdex\include;..\lib\libZRCola\include;..\lib\libZRColaUI\include _CONSOLE;%(PreprocessorDefinitions) diff --git a/ZRColaCompile/ZRColaCompile.vcxproj b/ZRColaCompile/ZRColaCompile.vcxproj index 5b5d1e3..8db53c7 100644 --- a/ZRColaCompile/ZRColaCompile.vcxproj +++ b/ZRColaCompile/ZRColaCompile.vcxproj @@ -57,6 +57,9 @@ + + {c0a84bd2-3870-4cd6-b281-0ab322e3c579} + {3c61929e-7289-4101-8d0a-da22d6e1aea8} diff --git a/ZRColaCompile/dbsource.cpp b/ZRColaCompile/dbsource.cpp index 473f3df..a850611 100644 --- a/ZRColaCompile/dbsource.cpp +++ b/ZRColaCompile/dbsource.cpp @@ -29,10 +29,13 @@ ZRCola::DBSource::~DBSource() { if (m_db) m_db->Close(); + + if (m_locale) + _free_locale(m_locale); } -bool ZRCola::DBSource::Open(LPCTSTR _filename) +bool ZRCola::DBSource::Open(LPCTSTR filename) { wxASSERT_MSG(!m_db, wxT("database already open")); @@ -43,20 +46,21 @@ bool ZRCola::DBSource::Open(LPCTSTR _filename) std::wstring cn; cn = L"Driver={Microsoft Access Driver (*.mdb)};"; cn += L"Dbq="; - cn += _filename; + cn += filename; cn += L";Uid=;Pwd=;"; hr = m_db->Open(ATL::CComBSTR(cn.c_str())); if (SUCCEEDED(hr)) { // Database open and ready. - filename = _filename; + m_filename = filename; + m_locale = _create_locale(LC_ALL, "Slovenian_Slovenia.1250"); return true; } else { - _ftprintf(stderr, wxT("%s: error ZCC0011: Could not open database (0x%x).\n"), (LPCTSTR)_filename, hr); + _ftprintf(stderr, wxT("%s: error ZCC0011: Could not open database (0x%x).\n"), (LPCTSTR)filename, hr); LogErrors(); } m_db.Release(); } else - _ftprintf(stderr, wxT("%s: error ZCC0012: Creating ADOConnection object failed (0x%x).\n"), (LPCTSTR)_filename, hr); + _ftprintf(stderr, wxT("%s: error ZCC0012: Creating ADOConnection object failed (0x%x).\n"), (LPCTSTR)filename, hr); return false; } @@ -95,7 +99,38 @@ void ZRCola::DBSource::LogErrors() const } -bool ZRCola::DBSource::GetUnicodeString(const CComPtr& f, std::wstring& str) const +bool ZRCola::DBSource::GetUnicodeCharacter(const ATL::CComPtr& f, wchar_t& chr) const +{ + wxASSERT_MSG(f, wxT("field is empty")); + + CComVariant v; + wxVERIFY(SUCCEEDED(f->get_Value(&v))); + + // Parse the field. Must be exactly one Unicode code. + wxVERIFY(SUCCEEDED(v.ChangeType(VT_BSTR))); + UINT i = 0, n = ::SysStringLen(V_BSTR(&v)); + chr = 0; + for (; i < n && V_BSTR(&v)[i]; i++) { + if (L'0' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'9') chr = chr*0x10 + (V_BSTR(&v)[i] - L'0'); + else if (L'A' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'F') chr = chr*0x10 + (V_BSTR(&v)[i] - L'A' + 10); + else if (L'a' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'f') chr = chr*0x10 + (V_BSTR(&v)[i] - L'a' + 10); + else break; + } + if (i <= 0 && 4 < i) { + CComBSTR fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); + _ftprintf(stderr, wxT("%s: error ZCC0030: Syntax error in \"%.*ls\" field (\"%.*ls\"). Unicode code must be one to four hexadecimal characters long.\n"), m_filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); + return false; + } else if (i != n) { + CComBSTR fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); + _ftprintf(stderr, wxT("%s: error ZCC0031: Syntax error in \"%.*ls\" field (\"%.*ls\"). Extra trailing characters.\n"), m_filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); + return false; + } + + return true; +} + + +bool ZRCola::DBSource::GetUnicodeString(const ATL::CComPtr& f, std::wstring& str) const { wxASSERT_MSG(f, wxT("field is empty")); @@ -117,44 +152,63 @@ bool ZRCola::DBSource::GetUnicodeString(const CComPtr& f, std::wstring } if (j <= 0 || 4 < j) { CComBSTR fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); - _ftprintf(stderr, wxT("%s: error ZCC0020: Syntax error in \"%.*ls\" field (\"%.*ls\"). Unicode code must be one to four hexadecimal characters long.\n"), filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); + _ftprintf(stderr, wxT("%s: error ZCC0020: Syntax error in \"%.*ls\" field (\"%.*ls\"). Unicode code must be one to four hexadecimal characters long.\n"), m_filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); return false; } str += c; // Skip delimiter(s) and whitespace. - for (; i < n && V_BSTR(&v)[i] && (V_BSTR(&v)[i] == L'+' || iswspace(V_BSTR(&v)[i])); i++); + for (; i < n && V_BSTR(&v)[i] && (V_BSTR(&v)[i] == L'+' || _iswspace_l(V_BSTR(&v)[i], m_locale)); i++); } return true; } -bool ZRCola::DBSource::GetUnicodeCharacter(const CComPtr& f, wchar_t& chr) const +bool ZRCola::DBSource::GetKeySequence(const ATL::CComPtr& f, std::vector& seq) const { wxASSERT_MSG(f, wxT("field is empty")); CComVariant v; wxVERIFY(SUCCEEDED(f->get_Value(&v))); - // Parse the field. Must be exactly one Unicode code. wxVERIFY(SUCCEEDED(v.ChangeType(VT_BSTR))); - UINT i = 0, n = ::SysStringLen(V_BSTR(&v)); - chr = 0; - for (; i < n && V_BSTR(&v)[i]; i++) { - if (L'0' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'9') chr = chr*0x10 + (V_BSTR(&v)[i] - L'0'); - else if (L'A' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'F') chr = chr*0x10 + (V_BSTR(&v)[i] - L'A' + 10); - else if (L'a' <= V_BSTR(&v)[i] && V_BSTR(&v)[i] <= L'f') chr = chr*0x10 + (V_BSTR(&v)[i] - L'a' + 10); - else break; - } - if (i <= 0 && 4 < i) { - CComBSTR fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); - _ftprintf(stderr, wxT("%s: error ZCC0030: Syntax error in \"%.*ls\" field (\"%.*ls\"). Unicode code must be one to four hexadecimal characters long.\n"), filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); - return false; - } else if (i != n) { - CComBSTR fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); - _ftprintf(stderr, wxT("%s: error ZCC0031: Syntax error in \"%.*ls\" field (\"%.*ls\"). Extra trailing characters.\n"), filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); - return false; + + // Convert to uppercase. + _wcsupr_l(V_BSTR(&v), m_locale); + + // Parse the field. Must be comma delimited sequence of key codes. + seq.clear(); + for (UINT i = 0, n = ::SysStringLen(V_BSTR(&v)); i < n && V_BSTR(&v)[i];) { + keyseq::keycode kc = {}; + + while (i < n && V_BSTR(&v)[i]) { + // Parse key code. + static const wchar_t str_shift[] = L"SHIFT+", str_ctrl[] = L"CTRL+", str_alt[] = L"ALT+"; + if (i + _countof(str_shift) <= n && wmemcmp(V_BSTR(&v) + i, str_shift, _countof(str_shift) - 1) == 0) { + kc.shift = true; + i += _countof(str_shift) - 1; + } else if (i + _countof(str_ctrl) <= n && wmemcmp(V_BSTR(&v) + i, str_ctrl, _countof(str_ctrl) - 1) == 0) { + kc.ctrl = true; + i += _countof(str_ctrl) - 1; + } else if (i + _countof(str_alt) <= n && wmemcmp(V_BSTR(&v) + i, str_alt, _countof(str_alt) - 1) == 0) { + kc.alt = true; + i += _countof(str_alt) - 1; + } else { + kc.key = V_BSTR(&v)[i]; + i++; + break; + } + } + if (i < n && V_BSTR(&v)[i] && V_BSTR(&v)[i] != L',' && !_iswspace_l(V_BSTR(&v)[i], m_locale)) { + CComBSTR fieldname; wxVERIFY(SUCCEEDED(f->get_Name(&fieldname))); + _ftprintf(stderr, wxT("%s: error ZCC0060: Syntax error in \"%.*ls\" field (\"%.*ls\"). Key sequences must be \"Ctrl+Alt+\" formatted, delimited by commas and/or space.\n"), m_filename.c_str(), fieldname.Length(), (BSTR)fieldname, n, V_BSTR(&v)); + return false; + } + seq.push_back(kc); + + // Skip delimiter(s) and whitespace. + for (; i < n && V_BSTR(&v)[i] && (V_BSTR(&v)[i] == L',' || _iswspace_l(V_BSTR(&v)[i], m_locale)); i++); } return true; @@ -169,7 +223,7 @@ bool ZRCola::DBSource::SelectTranslations(ATL::CComPtr &rs) const // Open it. if (FAILED(rs->Open(ATL::CComVariant(L"SELECT [komb], [znak] FROM [VRS_ReplChar] WHERE [rang_komb]=1"), ATL::CComVariant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { - _ftprintf(stderr, wxT("%s: error ZCC0040: Error loading compositions from database. Please make sure the file is ZRCola.zrc compatible.\n"), filename.c_str()); + _ftprintf(stderr, wxT("%s: error ZCC0040: Error loading compositions from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); LogErrors(); return false; } @@ -182,20 +236,60 @@ bool ZRCola::DBSource::GetTranslation(const ATL::CComPtr& rs, ZRCo { wxASSERT_MSG(rs, wxT("recordset is empty")); - CComPtr flds; + ATL::CComPtr flds; wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); { - CComPtr f; + ATL::CComPtr f; wxVERIFY(SUCCEEDED(flds->get_Item(CComVariant(L"komb"), &f))); wxCHECK(GetUnicodeString(f, t.str), false); } { - CComPtr f; + ATL::CComPtr f; wxVERIFY(SUCCEEDED(flds->get_Item(CComVariant(L"znak"), &f))); wxCHECK(GetUnicodeCharacter(f, t.chr), false); } return true; } + + +bool ZRCola::DBSource::SelectKeySequences(ATL::CComPtr &rs) const +{ + // Create a new recordset. + if (rs) rs.Release(); + wxCHECK(SUCCEEDED(::CoCreateInstance(CLSID_CADORecordset, NULL, CLSCTX_ALL, IID_IADORecordset, (LPVOID*)&rs)), false); + + // Open it. + if (FAILED(rs->Open(ATL::CComVariant(L"SELECT [Znak], [tipka] FROM [wrd_KeyCodes]"), ATL::CComVariant(m_db), adOpenStatic, adLockReadOnly, adCmdText))) { + _ftprintf(stderr, wxT("%s: error ZCC0050: Error loading key sequences from database. Please make sure the file is ZRCola.zrc compatible.\n"), m_filename.c_str()); + LogErrors(); + return false; + } + + return true; +} + + +bool ZRCola::DBSource::GetKeySequence(const ATL::CComPtr& rs, ZRCola::DBSource::keyseq& ks) const +{ + wxASSERT_MSG(rs, wxT("recordset is empty")); + + ATL::CComPtr flds; + wxVERIFY(SUCCEEDED(rs->get_Fields(&flds))); + + { + ATL::CComPtr f; + wxVERIFY(SUCCEEDED(flds->get_Item(CComVariant(L"Znak"), &f))); + wxCHECK(GetUnicodeCharacter(f, ks.chr), false); + } + + { + ATL::CComPtr f; + wxVERIFY(SUCCEEDED(flds->get_Item(CComVariant(L"tipka"), &f))); + wxCHECK(GetKeySequence(f, ks.seq), false); + } + + return true; +} diff --git a/ZRColaCompile/dbsource.h b/ZRColaCompile/dbsource.h index 711d97f..69c330a 100644 --- a/ZRColaCompile/dbsource.h +++ b/ZRColaCompile/dbsource.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace ZRCola { @@ -40,6 +41,27 @@ namespace ZRCola { std::wstring str; ///< Decomposed string }; + + /// + /// Key sequence + /// + class keyseq { + public: + /// + /// Key code + /// + struct keycode { + wchar_t key; ///< Key + bool shift; ///< Shift modifier + bool ctrl; ///< Ctrl modifier + bool alt; ///< Alt modifier + }; + + public: + wchar_t chr; ///< Character + std::vector seq; ///< Key sequence + }; + public: DBSource(); virtual ~DBSource(); @@ -93,19 +115,6 @@ namespace ZRCola { } - /// - /// Gets encoded Unicode string from ZRCola.zrc database - /// - /// \param[in] f Data field - /// \param[out] str Output string - /// - /// \returns - /// - true when successful - /// - false otherwise - /// - bool GetUnicodeString(const CComPtr& f, std::wstring& str) const; - - /// /// Gets encoded Unicode character from ZRCola.zrc database /// @@ -116,7 +125,33 @@ namespace ZRCola { /// - true when successful /// - false otherwise /// - bool GetUnicodeCharacter(const CComPtr& f, wchar_t& chr) const; + bool GetUnicodeCharacter(const ATL::CComPtr& f, wchar_t& chr) const; + + + /// + /// Gets encoded Unicode string from ZRCola.zrc database + /// + /// \param[in] f Data field + /// \param[out] str Output string + /// + /// \returns + /// - true when successful + /// - false otherwise + /// + bool GetUnicodeString(const ATL::CComPtr& f, std::wstring& str) const; + + + /// + /// Gets encoded key sequence from ZRCola.zrc database + /// + /// \param[in] f Data field + /// \param[out] seq Output sequence + /// + /// \returns + /// - true when successful + /// - false otherwise + /// + bool GetKeySequence(const ATL::CComPtr& f, std::vector& seq) const; /// @@ -143,8 +178,34 @@ namespace ZRCola { /// bool GetTranslation(const ATL::CComPtr& rs, translation& t) const; + + /// + /// Returns key sequences + /// + /// \param[out] rs Recordset with results + /// + /// \returns + /// - true when query succeeds + /// - false otherwise + /// + bool SelectKeySequences(ATL::CComPtr& rs) const; + + + /// + /// Returns key sequence data + /// + /// \param[in] rs Recordset with results + /// \param[out] ks Key sequence + /// + /// \returns + /// - true when succeeded + /// - false otherwise + /// + bool GetKeySequence(const ATL::CComPtr& rs, keyseq& ks) const; + protected: - std::basic_string filename; ///< the database filename - ATL::CComPtr m_db; ///< the database + std::basic_string m_filename; ///< Database filename + ATL::CComPtr m_db; ///< Database + _locale_t m_locale; ///< Database locale }; }; diff --git a/ZRColaCompile/main.cpp b/ZRColaCompile/main.cpp index ee8cc74..068f68c 100644 --- a/ZRColaCompile/main.cpp +++ b/ZRColaCompile/main.cpp @@ -24,18 +24,18 @@ /// Writes translation database to a stream /// /// \param[in] stream Output stream -/// \param[in] t_db Translation database +/// \param[in] db Translation database /// /// \returns The stream \p stream /// -inline std::ostream& operator <<(std::ostream& stream, const ZRCola::translation_db &t_db) +inline std::ostream& operator <<(std::ostream& stream, const ZRCola::translation_db &db) { - assert(t_db.idxComp.size() == t_db.idxDecomp.size()); + assert(db.idxComp.size() == db.idxDecomp.size()); unsigned __int32 count; // Write index count. - std::vector::size_type trans_count = t_db.idxComp.size(); + std::vector::size_type trans_count = db.idxComp.size(); #if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) // 4G check if (trans_count > 0xffffffff) { @@ -49,14 +49,14 @@ inline std::ostream& operator <<(std::ostream& stream, const ZRCola::translation // Write composition index. if (stream.fail()) return stream; - stream.write((const char*)t_db.idxComp.data(), sizeof(unsigned __int32)*count); + stream.write((const char*)db.idxComp.data(), sizeof(unsigned __int32)*count); // Write decomposition index. if (stream.fail()) return stream; - stream.write((const char*)t_db.idxDecomp.data(), sizeof(unsigned __int32)*count); + stream.write((const char*)db.idxDecomp.data(), sizeof(unsigned __int32)*count); // Write data count. - std::vector::size_type data_count = t_db.data.size(); + std::vector::size_type data_count = db.data.size(); #if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) // 4G check if (data_count > 0xffffffff) { @@ -70,7 +70,63 @@ inline std::ostream& operator <<(std::ostream& stream, const ZRCola::translation // Write data. if (stream.fail()) return stream; - stream.write((const char*)t_db.data.data(), sizeof(wchar_t)*count); + stream.write((const char*)db.data.data(), sizeof(unsigned __int16)*count); + + return stream; +} + + +/// +/// Writes key sequence database to a stream +/// +/// \param[in] stream Output stream +/// \param[in] db Key sequence database +/// +/// \returns The stream \p stream +/// +inline std::ostream& operator <<(std::ostream& stream, const ZRCola::keyseq_db &db) +{ + assert(db.idxChr.size() == db.idxKey.size()); + + unsigned __int32 count; + + // Write index count. + std::vector::size_type ks_count = db.idxChr.size(); +#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) + // 4G check + if (ks_count > 0xffffffff) { + stream.setstate(std::ios_base::failbit); + return stream; + } +#endif + if (stream.fail()) return stream; + count = (unsigned __int32)ks_count; + stream.write((const char*)&count, sizeof(count)); + + // Write character index. + if (stream.fail()) return stream; + stream.write((const char*)db.idxChr.data(), sizeof(unsigned __int32)*count); + + // Write key index. + if (stream.fail()) return stream; + stream.write((const char*)db.idxKey.data(), sizeof(unsigned __int32)*count); + + // Write data count. + std::vector::size_type data_count = db.data.size(); +#if defined(_WIN64) || defined(__x86_64__) || defined(__ppc64__) + // 4G check + if (data_count > 0xffffffff) { + stream.setstate(std::ios_base::failbit); + return stream; + } +#endif + if (stream.fail()) return stream; + count = (unsigned __int32)data_count; + stream.write((const char*)&count, sizeof(count)); + + // Write data. + if (stream.fail()) return stream; + stream.write((const char*)db.data.data(), sizeof(unsigned __int16)*count); return stream; } @@ -93,17 +149,42 @@ inline std::ostream& operator <<(std::ostream& stream, const ZRCola::translation /// The function does not treat \\0 characters as terminators for performance reasons. /// Therefore \p count_a and \p count_b must represent exact string lengths. /// -static inline int CompareBinary(const wchar_t *str_a, size_t count_a, const wchar_t *str_b, size_t count_b) +static inline int CompareSequence(const wchar_t *str_a, size_t count_a, const wchar_t *str_b, size_t count_b) { for (size_t i = 0; ; i++) { - if (i >= count_a && i >= count_b) break; + if (i >= count_a && i >= count_b) return 0; else if (i >= count_a && i < count_b) return -1; else if (i < count_a && i >= count_b) return +1; else if (str_a[i] < str_b[i]) return -1; else if (str_a[i] > str_b[i]) return +1; } +} - return 0; + +/// +/// Compares two key sequences +/// +/// \param[in] seq_a First key sequence +/// \param[in] count_a Number of keys in sequence \p seq_a +/// \param[in] seq_b Second key sequence +/// \param[in] count_b Number of keys in sequence \p seq_b +/// +/// \returns +/// - <0 when seq_a < seq_b +/// - =0 when seq_a == seq_b +/// - >0 when seq_a > seq_b +/// +static inline int CompareSequence(const ZRCola::keyseq_db::keyseq::key_t *seq_a, size_t count_a, const ZRCola::keyseq_db::keyseq::key_t *seq_b, size_t count_b) +{ + for (size_t i = 0; ; i++) { + if (i >= count_a && i >= count_b) return 0; + else if (i >= count_a && i < count_b) return -1; + else if (i < count_a && i >= count_b) return +1; + else if (seq_a[i].key < seq_b[i].key ) return -1; + else if (seq_a[i].key > seq_b[i].key ) return +1; + else if (seq_a[i].modifiers < seq_b[i].modifiers) return -1; + else if (seq_a[i].modifiers > seq_b[i].modifiers) return +1; + } } @@ -122,10 +203,10 @@ static inline int CompareBinary(const wchar_t *str_a, size_t count_a, const wcha static int __cdecl CompareCompositionIndex(void *data, const void *a, const void *b) { const ZRCola::translation_db::translation - &trans_a = (const ZRCola::translation_db::translation&)((const wchar_t*)data)[*(const unsigned __int32*)a], - &trans_b = (const ZRCola::translation_db::translation&)((const wchar_t*)data)[*(const unsigned __int32*)b]; + &trans_a = (const ZRCola::translation_db::translation&)((const unsigned __int16*)data)[*(const unsigned __int32*)a], + &trans_b = (const ZRCola::translation_db::translation&)((const unsigned __int16*)data)[*(const unsigned __int32*)b]; - int r = CompareBinary(trans_a.str, trans_a.str_len, trans_b.str, trans_b.str_len); + int r = CompareSequence(trans_a.str, trans_a.str_len, trans_b.str, trans_b.str_len); if (r != 0) return r; if (trans_a.chr < trans_b.chr) return -1; @@ -150,13 +231,66 @@ static int __cdecl CompareCompositionIndex(void *data, const void *a, const void static int __cdecl CompareDecompositionIndex(void *data, const void *a, const void *b) { const ZRCola::translation_db::translation - &trans_a = (const ZRCola::translation_db::translation&)((const wchar_t*)data)[*(const unsigned __int32*)a], - &trans_b = (const ZRCola::translation_db::translation&)((const wchar_t*)data)[*(const unsigned __int32*)b]; + &trans_a = (const ZRCola::translation_db::translation&)((const unsigned __int16*)data)[*(const unsigned __int32*)a], + &trans_b = (const ZRCola::translation_db::translation&)((const unsigned __int16*)data)[*(const unsigned __int32*)b]; if (trans_a.chr < trans_b.chr) return -1; else if (trans_a.chr > trans_b.chr) return +1; - return CompareBinary(trans_a.str, trans_a.str_len, trans_b.str, trans_b.str_len); + return CompareSequence(trans_a.str, trans_a.str_len, trans_b.str, trans_b.str_len); +} + + +/// +/// Function to use in \c qsort_s for key sequence index sorting +/// +/// \param[in] data Pointer to key sequence data +/// \param[in] a Pointer to first key sequence index element +/// \param[in] b Pointer to second key sequence index element +/// +/// \returns +/// - <0 when a < b +/// - =0 when a == b +/// - >0 when a > b +/// +static int __cdecl CompareKeySequenceChar(void *data, const void *a, const void *b) +{ + const ZRCola::keyseq_db::keyseq + &ks_a = (const ZRCola::keyseq_db::keyseq&)((const unsigned __int16*)data)[*(const unsigned __int32*)a], + &ks_b = (const ZRCola::keyseq_db::keyseq&)((const unsigned __int16*)data)[*(const unsigned __int32*)b]; + + if (ks_a.chr < ks_b.chr) return -1; + else if (ks_a.chr > ks_b.chr) return +1; + + return CompareSequence(ks_a.seq, ks_a.seq_len, ks_b.seq, ks_b.seq_len); +} + + +/// +/// Function to use in \c qsort_s for key sequence index sorting +/// +/// \param[in] data Pointer to key sequence data +/// \param[in] a Pointer to first key sequence index element +/// \param[in] b Pointer to second key sequence index element +/// +/// \returns +/// - <0 when a < b +/// - =0 when a == b +/// - >0 when a > b +/// +static int __cdecl CompareKeySequenceKey(void *data, const void *a, const void *b) +{ + const ZRCola::keyseq_db::keyseq + &ks_a = (const ZRCola::keyseq_db::keyseq&)((const unsigned __int16*)data)[*(const unsigned __int32*)a], + &ks_b = (const ZRCola::keyseq_db::keyseq&)((const unsigned __int16*)data)[*(const unsigned __int32*)b]; + + int r = CompareSequence(ks_a.seq, ks_a.seq_len, ks_b.seq, ks_b.seq_len); + if (r != 0) return r; + + if (ks_a.chr < ks_b.chr) return -1; + else if (ks_a.chr > ks_b.chr) return +1; + + return 0; } @@ -239,31 +373,30 @@ int _tmain(int argc, _TCHAR *argv[]) // Get translations. ATL::CComPtr rs; if (src.SelectTranslations(rs)) { - size_t trans_count = src.GetRecordsetCount(rs); - if (trans_count < 0xffffffff) { // 4G check (-1 is reserved for error condition) + size_t count = src.GetRecordsetCount(rs); + if (count < 0xffffffff) { // 4G check (-1 is reserved for error condition) ZRCola::DBSource::translation trans; - ZRCola::translation_db t_db; + ZRCola::translation_db db; // Preallocate memory. - t_db.idxComp .reserve(trans_count); - t_db.idxDecomp.reserve(trans_count); - t_db.data .reserve(trans_count*4); + db.idxComp .reserve(count); + db.idxDecomp.reserve(count); + db.data .reserve(count*4); // Parse translations and build index and data. while (!ZRCola::DBSource::IsEOF(rs)) { // Read translation from the database. if (src.GetTranslation(rs, trans)) { // Add translation to index and data. - unsigned __int32 ti; - ti = t_db.data.size(); - t_db.data.push_back(trans.chr); + unsigned __int32 idx = db.data.size(); + db.data.push_back(trans.chr); std::wstring::size_type n = trans.str.length(); wxASSERT_MSG(n <= 0xffff, wxT("transformation string too long")); - t_db.data.push_back((wchar_t)n); + db.data.push_back((unsigned __int16)n); for (std::wstring::size_type i = 0; i < n; i++) - t_db.data.push_back(trans.str[i]); - t_db.idxComp .push_back(ti); - t_db.idxDecomp.push_back(ti); + db.data.push_back(trans.str[i]); + db.idxComp .push_back(idx); + db.idxDecomp.push_back(idx); } else has_errors = true; @@ -271,11 +404,11 @@ int _tmain(int argc, _TCHAR *argv[]) } // Sort indices. - qsort_s(t_db.idxComp .data(), trans_count, sizeof(unsigned __int32), CompareCompositionIndex , t_db.data.data()); - qsort_s(t_db.idxDecomp.data(), trans_count, sizeof(unsigned __int32), CompareDecompositionIndex, t_db.data.data()); + qsort_s(db.idxComp .data(), count, sizeof(unsigned __int32), CompareCompositionIndex , db.data.data()); + qsort_s(db.idxDecomp.data(), count, sizeof(unsigned __int32), CompareDecompositionIndex, db.data.data()); // Write translations to file. - dst << ZRCola::translation_rec(t_db); + dst << ZRCola::translation_rec(db); } else { _ftprintf(stderr, wxT("%s: error ZCC0004: Error getting translation count from database or too many translations.\n"), (LPCTSTR)filenameIn.c_str()); has_errors = true; @@ -286,10 +419,67 @@ int _tmain(int argc, _TCHAR *argv[]) } } + + { + // Get key sequences. + ATL::CComPtr rs; + if (src.SelectKeySequences(rs)) { + size_t count = src.GetRecordsetCount(rs); + if (count < 0xffffffff) { // 4G check (-1 is reserved for error condition) + ZRCola::DBSource::keyseq ks; + ZRCola::keyseq_db db; + + // Preallocate memory. + db.idxChr.reserve(count); + db.idxKey.reserve(count); + db.data .reserve(count*4); + + // Parse translations and build index and data. + while (!ZRCola::DBSource::IsEOF(rs)) { + // Read translation from the database. + if (src.GetKeySequence(rs, ks)) { + // Add translation to index and data. + unsigned __int32 idx = db.data.size(); + db.data.push_back(ks.chr); + std::vector::size_type n = ks.seq.size(); + wxASSERT_MSG(n <= 0xffff, wxT("key sequence too long")); + db.data.push_back((unsigned __int16)n); + for (std::vector::size_type i = 0; i < n; i++) { + const ZRCola::DBSource::keyseq::keycode &kc = ks.seq[i]; + db.data.push_back(kc.key); + db.data.push_back( + (kc.shift ? ZRCola::keyseq_db::keyseq::SHIFT : 0) | + (kc.ctrl ? ZRCola::keyseq_db::keyseq::CTRL : 0) | + (kc.alt ? ZRCola::keyseq_db::keyseq::ALT : 0)); + } + db.idxChr.push_back(idx); + db.idxKey.push_back(idx); + } else + has_errors = true; + + wxVERIFY(SUCCEEDED(rs->MoveNext())); + } + + // Sort indices. + qsort_s(db.idxChr.data(), count, sizeof(unsigned __int32), CompareKeySequenceChar, db.data.data()); + qsort_s(db.idxKey.data(), count, sizeof(unsigned __int32), CompareKeySequenceKey , db.data.data()); + + // Write translations to file. + dst << ZRCola::keyseq_rec(db); + } else { + _ftprintf(stderr, wxT("%s: error ZCC0006: Error getting key sequence count from database or too many key sequences.\n"), (LPCTSTR)filenameIn.c_str()); + has_errors = true; + } + } else { + _ftprintf(stderr, wxT("%s: error ZCC0005: Error getting key sequences from database. Please make sure the file is ZRCola.zrc compatible.\n"), (LPCTSTR)filenameIn.c_str()); + has_errors = true; + } + } + stdex::idrec::close(dst, dst_start); if (dst.fail()) { - _ftprintf(stderr, wxT("%s: error ZCC0005: Writing to output file failed.\n"), (LPCTSTR)filenameOut.c_str()); + _ftprintf(stderr, wxT("%s: error ZCC0007: Writing to output file failed.\n"), (LPCTSTR)filenameOut.c_str()); has_errors = true; } diff --git a/ZRColaCompile/stdafx.h b/ZRColaCompile/stdafx.h index 404e319..13563e7 100644 --- a/ZRColaCompile/stdafx.h +++ b/ZRColaCompile/stdafx.h @@ -23,6 +23,7 @@ #include "dbsource.h" #include +#include #include #include diff --git a/lib/libZRColaUI/build/libZRColaUI.props b/lib/libZRColaUI/build/libZRColaUI.props index dd8e6f9..6d6fcf5 100644 --- a/lib/libZRColaUI/build/libZRColaUI.props +++ b/lib/libZRColaUI/build/libZRColaUI.props @@ -10,6 +10,7 @@ LIBZRCOLAUI;%(PreprocessorDefinitions) + ..\..\stdex\include;..\..\libZRCola\include;%(AdditionalIncludeDirectories) diff --git a/lib/libZRColaUI/build/libZRColaUI.vcxproj b/lib/libZRColaUI/build/libZRColaUI.vcxproj index 4d2b2a0..5b4e48a 100644 --- a/lib/libZRColaUI/build/libZRColaUI.vcxproj +++ b/lib/libZRColaUI/build/libZRColaUI.vcxproj @@ -19,6 +19,7 @@ + Create Create @@ -28,6 +29,7 @@ + diff --git a/lib/libZRColaUI/build/libZRColaUI.vcxproj.filters b/lib/libZRColaUI/build/libZRColaUI.vcxproj.filters index e0a2fd7..605e170 100644 --- a/lib/libZRColaUI/build/libZRColaUI.vcxproj.filters +++ b/lib/libZRColaUI/build/libZRColaUI.vcxproj.filters @@ -18,6 +18,9 @@ Source Files + + Source Files + @@ -26,6 +29,9 @@ Header Files + + Header Files + diff --git a/lib/libZRColaUI/include/zrcolaui/common.h b/lib/libZRColaUI/include/zrcolaui/common.h index 18605eb..16df1fd 100644 --- a/lib/libZRColaUI/include/zrcolaui/common.h +++ b/lib/libZRColaUI/include/zrcolaui/common.h @@ -24,16 +24,8 @@ /// Public function calling convention /// #ifdef LIBZRCOLAUI -#define ZRCOLAUI_API __declspec(dllexport) +#define ZRCOLAUI_API __declspec(dllexport) #else -#define ZRCOLAUI_API __declspec(dllimport) +#define ZRCOLAUI_API __declspec(dllimport) #endif #define ZRCOLA_NOVTABLE __declspec(novtable) -#pragma warning(push) -#pragma warning(disable: 4251) - - -namespace ZRCola { -}; - -#pragma warning(pop) diff --git a/lib/libZRColaUI/include/zrcolaui/keyboard.h b/lib/libZRColaUI/include/zrcolaui/keyboard.h new file mode 100644 index 0000000..13a24f4 --- /dev/null +++ b/lib/libZRColaUI/include/zrcolaui/keyboard.h @@ -0,0 +1,113 @@ +/* + 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 . +*/ + +#pragma once + +#include "common.h" +#include + +#include +#include +#include + +#pragma warning(push) +#pragma warning(disable: 4251) + + +namespace ZRCola { + /// + /// Key sequence database + /// + class ZRCOLAUI_API keyseq_db { + public: +#pragma pack(push) +#pragma pack(2) +#pragma warning(push) +#pragma warning(disable: 4200) + /// + /// Key sequence data + /// + struct keyseq { + enum modifiers_t { + SHIFT = 1<<0, ///< SHIFT key was pressed + CTRL = 1<<1, ///< CTRL key was pressed + ALT = 1<<2, ///< ALT key was pressed + }; + + wchar_t chr; ///< Character + unsigned __int16 seq_len; ///< \c seq length + struct key_t { + wchar_t key; ///< Key + unsigned __int16 modifiers; ///< Modifiers (bitwise combination of SHIFT, CTRL and ALT) + } seq[]; ///< Key sequence + }; +#pragma warning(pop) +#pragma pack(pop) + + std::vector idxChr; ///< Character index + std::vector idxKey; ///< Key index + std::vector data; ///< Key sequences data + }; + + + typedef ZRCOLAUI_API stdex::idrec::record keyseq_rec; +}; + + +const ZRCola::recordid_t stdex::idrec::record::id = *(ZRCola::recordid_t*)"KEY"; + + +/// +/// Reads key sequence database from a stream +/// +/// \param[in] stream Input stream +/// \param[out] db Key sequence database +/// +/// \returns The stream \p stream +/// +inline std::istream& operator >>(_In_ std::istream& stream, _Out_ ZRCola::keyseq_db &db) +{ + unsigned __int32 count; + + // Read index count. + stream.read((char*)&count, sizeof(count)); + if (!stream.good()) return stream; + + // Read character index. + db.idxChr.resize(count); + stream.read((char*)db.idxChr.data(), sizeof(unsigned __int32)*count); + if (!stream.good()) return stream; + + // Read key index. + db.idxKey.resize(count); + stream.read((char*)db.idxKey.data(), sizeof(unsigned __int32)*count); + if (!stream.good()) return stream; + + // Read data count. + stream.read((char*)&count, sizeof(count)); + if (!stream.good()) return stream; + + // Read data. + db.data.resize(count); + stream.read((char*)db.data.data(), sizeof(unsigned __int16)*count); + + return stream; +} + +#pragma warning(pop) diff --git a/lib/libZRColaUI/src/keyboard.cpp b/lib/libZRColaUI/src/keyboard.cpp new file mode 100644 index 0000000..ac111f1 --- /dev/null +++ b/lib/libZRColaUI/src/keyboard.cpp @@ -0,0 +1,20 @@ +/* + 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" diff --git a/lib/libZRColaUI/src/stdafx.h b/lib/libZRColaUI/src/stdafx.h index 6a78f14..d4f84ec 100644 --- a/lib/libZRColaUI/src/stdafx.h +++ b/lib/libZRColaUI/src/stdafx.h @@ -20,3 +20,4 @@ #pragma once #include "../../../include/zrcola.h" +#include "../include/zrcolaui/keyboard.h" diff --git a/output/data/ZRCola.zrcdb b/output/data/ZRCola.zrcdb index eb9bbd4..14c5484 100644 Binary files a/output/data/ZRCola.zrcdb and b/output/data/ZRCola.zrcdb differ