diff options
author | Peng Huang <shawn.p.huang@gmail.com> | 2010-02-07 13:25:35 +0800 |
---|---|---|
committer | Peng Huang <shawn.p.huang@gmail.com> | 2010-02-10 11:18:02 +0800 |
commit | a6ec9cf51f8ddcf3f295ebe8f74b1c3a53248fae (patch) | |
tree | 4caa0e549bd1827da503af9ff490ab2709eb73f0 | |
parent | efa0c8f199b0e058b3cc5f88f0d6e20c382570b3 (diff) | |
download | ibus-libpinyin-a6ec9cf51f8ddcf3f295ebe8f74b1c3a53248fae.tar.gz ibus-libpinyin-a6ec9cf51f8ddcf3f295ebe8f74b1c3a53248fae.tar.xz ibus-libpinyin-a6ec9cf51f8ddcf3f295ebe8f74b1c3a53248fae.zip |
Refactory classes, and use sigc++ to make code clear.
-rw-r--r-- | configure.ac | 5 | ||||
-rw-r--r-- | src/DoublePinyinEditor.cc | 39 | ||||
-rw-r--r-- | src/DoublePinyinEditor.h | 4 | ||||
-rw-r--r-- | src/Editor.cc | 136 | ||||
-rw-r--r-- | src/Editor.h | 106 | ||||
-rw-r--r-- | src/FullPinyinEditor.cc | 39 | ||||
-rw-r--r-- | src/FullPinyinEditor.h | 13 | ||||
-rw-r--r-- | src/Makefile.am | 28 | ||||
-rw-r--r-- | src/PhraseEditor.cc | 30 | ||||
-rw-r--r-- | src/PhraseEditor.h | 17 | ||||
-rw-r--r-- | src/PinyinArray.h | 2 | ||||
-rw-r--r-- | src/PinyinEditor.cc | 566 | ||||
-rw-r--r-- | src/PinyinEditor.h | 58 | ||||
-rw-r--r-- | src/PinyinEngine.cc | 1133 | ||||
-rw-r--r-- | src/PinyinEngine.h | 67 | ||||
-rw-r--r-- | src/PinyinProperties.cc | 152 | ||||
-rw-r--r-- | src/PinyinProperties.h | 58 | ||||
-rw-r--r-- | src/RawEditor.h | 76 | ||||
-rw-r--r-- | src/Text.h | 1 |
19 files changed, 1428 insertions, 1102 deletions
diff --git a/configure.ac b/configure.ac index 6825d8a..af39dfd 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,11 @@ PKG_CHECK_MODULES(UUID, [ uuid ]) +# check uuid +PKG_CHECK_MODULES(SIGC, [ + sigc++-2.0 +]) + # check env AC_PATH_PROG(ENV, env) AC_SUBST(ENV) diff --git a/src/DoublePinyinEditor.cc b/src/DoublePinyinEditor.cc index 1bbfff1..6970815 100644 --- a/src/DoublePinyinEditor.cc +++ b/src/DoublePinyinEditor.cc @@ -5,7 +5,8 @@ namespace PY { #include "DoublePinyinTable.h" -DoublePinyinEditor::DoublePinyinEditor (void) +DoublePinyinEditor::DoublePinyinEditor (PinyinProperties & props) + : PinyinEditor (props) { } @@ -46,19 +47,19 @@ gboolean DoublePinyinEditor::insert (gint ch) { /* is full */ - if (G_UNLIKELY (m_text.length () >= MAX_PINYIN_LEN)) + if (G_UNLIKELY (m_buffer.length () >= MAX_PINYIN_LEN)) return FALSE; gint id = char_to_id (ch); if (id < 0) return FALSE; - m_text.insert (m_cursor++, ch); + m_buffer.insert (m_cursor++, ch); if (m_cursor != m_pinyin_len + 2) return TRUE; - const Pinyin *pinyin = isPinyin (m_text[m_cursor - 2], ch); + const Pinyin *pinyin = isPinyin (m_buffer[m_cursor - 2], ch); if (pinyin == NULL) return TRUE; m_pinyin.append (pinyin, m_pinyin_len, 2); @@ -73,7 +74,7 @@ DoublePinyinEditor::removeCharBefore (void) return FALSE; m_cursor --; - m_text.erase (m_cursor, 1); + m_buffer.erase (m_cursor, 1); if (m_cursor < m_pinyin_len) { m_pinyin.pop (); @@ -86,10 +87,10 @@ DoublePinyinEditor::removeCharBefore (void) gboolean DoublePinyinEditor::removeCharAfter (void) { - if (G_UNLIKELY (m_cursor == m_text.length ())) + if (G_UNLIKELY (m_cursor == m_buffer.length ())) return FALSE; - m_text.erase (m_cursor, 1); + m_buffer.erase (m_cursor, 1); return TRUE; } @@ -111,7 +112,7 @@ DoublePinyinEditor::removeWordBefore (void) m_pinyin_len -= 2; } - m_text.erase (cursor, m_cursor - cursor); + m_buffer.erase (cursor, m_cursor - cursor); m_cursor = cursor; return TRUE; } @@ -119,10 +120,10 @@ DoublePinyinEditor::removeWordBefore (void) gboolean DoublePinyinEditor::removeWordAfter (void) { - if (G_UNLIKELY (m_cursor == m_text.length ())) + if (G_UNLIKELY (m_cursor == m_buffer.length ())) return FALSE; - m_text.erase (m_cursor, -1); + m_buffer.erase (m_cursor, -1); return TRUE; } @@ -144,7 +145,7 @@ DoublePinyinEditor::moveCursorLeft (void) gboolean DoublePinyinEditor::moveCursorRight (void) { - if (G_UNLIKELY (m_cursor == m_text.length ())) + if (G_UNLIKELY (m_cursor == m_buffer.length ())) return FALSE; m_cursor ++; @@ -192,16 +193,16 @@ DoublePinyinEditor::moveCursorToBegin (void) gboolean DoublePinyinEditor::moveCursorToEnd (void) { - if (G_UNLIKELY (m_cursor == m_text.length ())) + if (G_UNLIKELY (m_cursor == m_buffer.length ())) return FALSE; - m_cursor = m_text.length (); + m_cursor = m_buffer.length (); updatePinyin (); return TRUE; } -gboolean +void DoublePinyinEditor::reset (void) { gboolean retval = FALSE; @@ -210,22 +211,20 @@ DoublePinyinEditor::reset (void) retval = TRUE; } - if (m_text.length () != 0) { - m_text.truncate (0); + if (m_buffer.length () != 0) { + m_buffer.truncate (0); retval = TRUE; } if (retval) updatePinyin (); - - return retval; } void DoublePinyinEditor::updatePinyin (void) { - if (G_UNLIKELY (m_text.isEmpty ())) { + if (G_UNLIKELY (m_buffer.isEmpty ())) { m_pinyin.removeAll (); m_pinyin_len = 0; return; @@ -234,7 +233,7 @@ DoublePinyinEditor::updatePinyin (void) m_pinyin.removeAll (); m_pinyin_len = 0; for (guint i = 0; i + 1 < m_cursor; i+= 2) { - const Pinyin *pinyin = isPinyin (m_text[i], m_text[i + 1]); + const Pinyin *pinyin = isPinyin (m_buffer[i], m_buffer[i + 1]); if (pinyin == NULL) break; m_pinyin.append (pinyin, m_pinyin_len, 2); diff --git a/src/DoublePinyinEditor.h b/src/DoublePinyinEditor.h index 75d419a..69cd980 100644 --- a/src/DoublePinyinEditor.h +++ b/src/DoublePinyinEditor.h @@ -8,7 +8,7 @@ namespace PY { class DoublePinyinEditor : public PinyinEditor { public: - DoublePinyinEditor (void); + DoublePinyinEditor (PinyinProperties & props); gboolean insert (gint ch); @@ -24,7 +24,7 @@ public: gboolean moveCursorToBegin (void); gboolean moveCursorToEnd (void); - gboolean reset (void); + void reset (void); private: void updatePinyin (void); const Pinyin *isPinyin (gchar i, gchar j); diff --git a/src/Editor.cc b/src/Editor.cc new file mode 100644 index 0000000..86aae8c --- /dev/null +++ b/src/Editor.cc @@ -0,0 +1,136 @@ +#include "Editor.h" + +namespace PY { + +Editor::Editor (PinyinProperties & props) + : m_text (128), + m_cursor (0), + m_props (props) +{ +} + +Editor::~Editor (void) +{ +} + +gboolean +Editor::processKeyEvent (guint keyval, guint keycode, guint modifiers) +{ + /* ignore release key events */ + if (modifiers & IBUS_RELEASE_MASK) + return FALSE; + + modifiers &= (IBUS_SHIFT_MASK | + IBUS_CONTROL_MASK | + IBUS_MOD1_MASK | + IBUS_SUPER_MASK | + IBUS_HYPER_MASK | + IBUS_META_MASK); + /* ignore key events with some masks */ + if (modifiers != 0) + return TRUE; + + if (keyval >= IBUS_exclam && keyval <= IBUS_asciitilde) { + /* char key */ + m_text.insert (m_cursor++, keyval); + update (); + return TRUE; + } + else { + /* control key */ + if (!m_text) + return FALSE; + } + + switch (keyval) { + case IBUS_BackSpace: + if (m_cursor > 0) { + m_text.erase (--m_cursor, 1); + update (); + } + return TRUE; + case IBUS_Delete: + case IBUS_KP_Delete: + if (m_cursor < m_text.length ()) { + m_text.erase (m_cursor, 1); + update (); + } + return TRUE; + case IBUS_Left: + case IBUS_KP_Left: + if (!m_text) + return FALSE; + if (m_cursor > 0) { + m_cursor --; + update (); + } + return TRUE; + case IBUS_Right: + case IBUS_KP_Right: + if (m_cursor < m_text.length ()) { + m_cursor ++; + update (); + } + return TRUE; + case IBUS_Return: + case IBUS_KP_Enter: + { + StaticText text (m_text); + commitText (text); + reset (); + } + return TRUE; + case IBUS_Escape: + reset (); + return TRUE; + default: + g_debug ("Unknown keyval %d", keyval); + return TRUE; + } +} + +void +Editor::reset (void) +{ + gboolean need_update = (m_cursor != 0 || m_text); + m_cursor = 0; + m_text = ""; + if (need_update) + update (); +} + +void +Editor::pageUp (void) +{ +} + +void +Editor::pageDown (void) +{ +} + +void +Editor::cursorUp (void) +{ +} + +void +Editor::cursorDown (void) +{ +} + +void +Editor::update (void) +{ + if (m_text) { + StaticText text (m_text); + text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1); + updatePreeditText (text, m_cursor, TRUE); + } + else { + hidePreeditText (); + } +} + +}; + diff --git a/src/Editor.h b/src/Editor.h new file mode 100644 index 0000000..205cba7 --- /dev/null +++ b/src/Editor.h @@ -0,0 +1,106 @@ +#ifndef __PY_EDITOR_H_ +#define __PY_EDITOR_H_ + +#include <glib.h> +#include <sigc++/sigc++.h> +#include "Text.h" +#include "LookupTable.h" +#include "PinyinProperties.h" + +namespace PY { + +class Editor { +public: + Editor (PinyinProperties & prop); + virtual ~Editor (void); + + virtual gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers); + virtual void pageUp (void); + virtual void pageDown (void); + virtual void cursorUp (void); + virtual void cursorDown (void); + virtual void update (void); + virtual void reset (void); + + /* signals */ + sigc::signal <void, Text &> signalCommitText (void) { return m_signal_commit_text; } + sigc::signal <void, Text &, guint, gboolean> signalUpdatePreeditText (void) { return m_signal_update_preedit_text; } + sigc::signal <void> signalShowPreeditText (void) { return m_signal_show_preedit_text; } + sigc::signal <void> signalHidePreeditText (void) { return m_signal_hide_preedit_text; } + sigc::signal <void, Text &, gboolean> signalUpdateAuxiliaryText (void) { return m_signal_update_auxiliary_text; } + sigc::signal <void> signalShowAuxiliaryText (void) { return m_signal_show_auxiliary_text; } + sigc::signal <void> signalHideAuxiliaryText (void) { return m_signal_hide_auxiliary_text; } + sigc::signal <void, LookupTable &, gboolean> signalUpdateLookupTable (void) { return m_signal_update_lookup_table; } + sigc::signal <void, LookupTable &, gboolean> signalUpdateLookupTableFast (void) { return m_signal_update_lookup_table_fast; } + sigc::signal <void> signalShowLookupTable (void) { return m_signal_show_lookup_table; } + sigc::signal <void> signalHideLookupTable (void) { return m_signal_hide_lookup_table; } + +protected: + /* methods */ + void commitText (Text & text) { + m_signal_commit_text.emit (text); + } + + void updatePreeditText (Text & text, guint cursor, gboolean visible) { + m_signal_update_preedit_text.emit (text, cursor, visible); + } + + void showPreeditText (void) { + m_signal_show_preedit_text.emit (); + } + + void hidePreeditText (void) { + m_signal_hide_preedit_text.emit (); + } + + void updateAuxiliaryText (Text & text, gboolean visible) { + m_signal_update_auxiliary_text.emit (text, visible); + } + + void showAuxiliaryText (void) { + m_signal_show_auxiliary_text.emit (); + } + + void hideAuxiliaryText (void) { + m_signal_hide_auxiliary_text.emit (); + } + + void updateLookupTable (LookupTable & table, gboolean visible) { + m_signal_update_lookup_table.emit (table, visible); + } + + void updateLookupTableFast (LookupTable & table, gboolean visible) { + m_signal_update_lookup_table_fast.emit (table, visible); + } + + void showLookupTable (void) { + m_signal_show_lookup_table.emit (); + } + + void hideLookupTable (void) { + m_signal_hide_lookup_table.emit (); + } + +protected: + /* signals */ + sigc::signal <void, Text &> m_signal_commit_text; + sigc::signal <void, Text &, guint, gboolean> m_signal_update_preedit_text; + sigc::signal <void> m_signal_show_preedit_text; + sigc::signal <void> m_signal_hide_preedit_text; + sigc::signal <void, Text &, gboolean> m_signal_update_auxiliary_text; + sigc::signal <void> m_signal_show_auxiliary_text; + sigc::signal <void> m_signal_hide_auxiliary_text; + sigc::signal <void, LookupTable &, gboolean> m_signal_update_lookup_table; + sigc::signal <void, LookupTable &, gboolean> m_signal_update_lookup_table_fast; + sigc::signal <void> m_signal_show_lookup_table; + sigc::signal <void> m_signal_hide_lookup_table; + +protected: + String m_text; + guint m_cursor; + PinyinProperties & m_props; +}; + +}; + +#endif diff --git a/src/FullPinyinEditor.cc b/src/FullPinyinEditor.cc index f1de074..ede35a9 100644 --- a/src/FullPinyinEditor.cc +++ b/src/FullPinyinEditor.cc @@ -3,12 +3,16 @@ namespace PY { +FullPinyinEditor::FullPinyinEditor (PinyinProperties & props) + : PinyinEditor (props) +{ +} -FullPinyinEditor::FullPinyinEditor (void) +FullPinyinEditor::~FullPinyinEditor (void) { } -gboolean +void FullPinyinEditor::reset (void) { gboolean retval = FALSE; @@ -22,10 +26,10 @@ FullPinyinEditor::reset (void) retval = TRUE; } - if (retval) + if (retval) { updatePinyin (); - - return retval; + update (); + } } gboolean @@ -47,6 +51,7 @@ FullPinyinEditor::insert (gint ch) updatePinyin (); } } + update (); return TRUE; } @@ -60,6 +65,7 @@ FullPinyinEditor::removeCharBefore (void) m_text.erase (m_cursor, 1); updatePinyin (); + update (); return TRUE; } @@ -71,6 +77,7 @@ FullPinyinEditor::removeCharAfter (void) return FALSE; m_text.erase (m_cursor, 1); + update (); return TRUE; } @@ -95,6 +102,8 @@ FullPinyinEditor::removeWordBefore (void) m_text.erase (cursor, m_cursor - cursor); m_cursor = cursor; + updatePhraseEditor (); + update (); return TRUE; } @@ -105,6 +114,7 @@ FullPinyinEditor::removeWordAfter (void) return FALSE; m_text.erase (m_cursor, -1); + update (); return TRUE; } @@ -116,7 +126,7 @@ FullPinyinEditor::moveCursorLeft (void) m_cursor --; updatePinyin (); - + update (); return TRUE; } @@ -128,6 +138,7 @@ FullPinyinEditor::moveCursorRight (void) m_cursor ++; updatePinyin (); + update (); return TRUE; } @@ -147,6 +158,8 @@ FullPinyinEditor::moveCursorLeftByWord (void) m_cursor -= p.len; m_pinyin_len -= p.len; m_pinyin.pop (); + updatePhraseEditor (); + update (); return TRUE; } @@ -166,6 +179,8 @@ FullPinyinEditor::moveCursorToBegin (void) m_cursor = 0; m_pinyin.removeAll (); m_pinyin_len = 0; + updatePhraseEditor (); + update (); return TRUE; } @@ -178,6 +193,7 @@ FullPinyinEditor::moveCursorToEnd (void) m_cursor = m_text.length (); updatePinyin (); + update (); return TRUE; } @@ -196,8 +212,15 @@ FullPinyinEditor::updatePinyin (void) m_pinyin, // result MAX_PHRASE_LEN); // max result length } -} -}; + updatePhraseEditor (); +} +#if 0 +gboolean +FullPinyinEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers) +{ +} +#endif +}; diff --git a/src/FullPinyinEditor.h b/src/FullPinyinEditor.h index 81e6e1f..9e95fa8 100644 --- a/src/FullPinyinEditor.h +++ b/src/FullPinyinEditor.h @@ -8,8 +8,17 @@ namespace PY { class FullPinyinEditor : public PinyinEditor { public: - FullPinyinEditor (void); + FullPinyinEditor (PinyinProperties & props); + ~FullPinyinEditor (void); +public: + /* virtual functions */ +#if 0 + virtual gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers); +#endif + virtual void reset (void); + +protected: gboolean insert (gint ch); gboolean removeCharBefore (void); @@ -24,8 +33,6 @@ public: gboolean moveCursorToBegin (void); gboolean moveCursorToEnd (void); - gboolean reset (void); - private: void updatePinyin (void); diff --git a/src/Makefile.am b/src/Makefile.am index dfcc153..08f578c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,17 +20,17 @@ # @MAINTAINER_MODE_FALSE@skip_gentable=test -f $@ || -AM_CFLAGS = \ - @IBUS_CFLAGS@ \ - @SQLITE_CFLAGS@ \ - -DPKGDATADIR=\"$(pkgdatadir)\" \ - $(NULL) -AM_LDADD = \ - @IBUS_LIBS@ \ - @SQLITE_LIBS@ \ - $(NULL) - -AM_CXXFLAGS = $(AM_CFLAGS) +# AM_CFLAGS = \ +# @IBUS_CFLAGS@ \ +# @SQLITE_CFLAGS@ \ +# -DPKGDATADIR=\"$(pkgdatadir)\" \ +# $(NULL) +# AM_CXXFLAGS = $(AM_CFLAGS) +# AM_LDADD = \ +# @IBUS_LIBS@ \ +# @SQLITE_LIBS@ \ +# $(NULL) +# libexec_PROGRAMS = ibus-engine-pinyin ibus_engine_c_sources = \ @@ -38,6 +38,7 @@ ibus_engine_c_sources = \ CustomPhrase.cc \ Database.cc \ DoublePinyinEditor.cc \ + Editor.cc \ Engine.cc \ FullPinyinEditor.cc \ HalfFullConverter.cc \ @@ -46,6 +47,7 @@ ibus_engine_c_sources = \ PinyinEditor.cc \ PinyinEngine.cc \ PinyinParser.cc \ + PinyinProperties.cc \ SimpTradConverter.cc \ SpecialTable.cc \ $(NULL) @@ -61,6 +63,7 @@ ibus_engine_h_sources = \ Database.h \ DoublePinyinEditor.h \ DoublePinyinTable.h \ + Editor.h \ Engine.h \ FullPinyinEditor.h \ HalfFullConverter.h \ @@ -74,6 +77,7 @@ ibus_engine_h_sources = \ PinyinEngine.h \ PinyinParser.h \ PinyinParserTable.h \ + PinyinProperties.h \ Pointer.h \ RawEditor.h \ Regex.h \ @@ -97,6 +101,7 @@ ibus_engine_pinyin_CXXFLAGS = \ @IBUS_CFLAGS@ \ @SQLITE_CFLAGS@ \ @UUID_CFLAGS@ \ + @SIGC_CFLAGS@ \ -DGETTEXT_PACKAGE=\"@GETTEXT_PACKAGE@\" \ -DPKGDATADIR=\"$(pkgdatadir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ @@ -105,6 +110,7 @@ ibus_engine_pinyin_LDADD = \ @IBUS_LIBS@ \ @SQLITE_LIBS@ \ @UUID_LIBS@ \ + @SIGC_LIBS@ \ $(NULL) BUILT_SOURCES = \ diff --git a/src/PhraseEditor.cc b/src/PhraseEditor.cc index 6248ab5..81bf249 100644 --- a/src/PhraseEditor.cc +++ b/src/PhraseEditor.cc @@ -4,17 +4,16 @@ namespace PY { -/* init static members */ Database PhraseEditor::m_database; -PhraseEditor::PhraseEditor (void) +PhraseEditor::PhraseEditor (PinyinProperties & props) : m_candidates (32), m_selected_phrases (8), m_selected_string (32), m_candidate_0_phrases (8), m_pinyin (16), m_cursor (0), - m_mode_simp (Config::initSimpChinese ()) + m_props (props) { } @@ -22,12 +21,12 @@ PhraseEditor::~PhraseEditor (void) { } -void +gboolean PhraseEditor::update (const PinyinArray &pinyin) { /* the length of pinyin must not bigger than MAX_PHRASE_LEN */ g_assert (pinyin.length () <= MAX_PHRASE_LEN); - +#if 0 gboolean diff = FALSE; if (m_cursor > pinyin.length ()) { @@ -42,16 +41,19 @@ PhraseEditor::update (const PinyinArray &pinyin) } } - m_pinyin = pinyin; - - if (diff) { - /* FIXME, should not remove all phrases1 */ - m_selected_phrases.removeAll (); - m_selected_string.truncate (0); - m_cursor = 0; + if (diff == FALSE){ + return FALSE; } +#endif + + m_pinyin = pinyin; + m_cursor = 0; + /* FIXME, should not remove all phrases1 */ + m_selected_phrases.removeAll (); + m_selected_string.truncate (0); updateCandidates (); + return TRUE; } gboolean @@ -71,7 +73,7 @@ PhraseEditor::selectCandidate (guint i) if (G_LIKELY (i == 0)) { m_selected_phrases << m_candidate_0_phrases; - if (G_LIKELY (m_mode_simp)) + if (G_LIKELY (m_props.modeSimp ())) m_selected_string << m_candidates[0].phrase; else SimpTradConverter::simpToTrad (m_candidates[0].phrase, m_selected_string); @@ -79,7 +81,7 @@ PhraseEditor::selectCandidate (guint i) } else { m_selected_phrases << m_candidates[i]; - if (G_LIKELY (m_mode_simp)) + if (G_LIKELY (m_props.modeSimp ())) m_selected_string << m_candidates[i].phrase; else SimpTradConverter::simpToTrad (m_candidates[i].phrase, m_selected_string); diff --git a/src/PhraseEditor.h b/src/PhraseEditor.h index b7051c0..313d408 100644 --- a/src/PhraseEditor.h +++ b/src/PhraseEditor.h @@ -4,13 +4,14 @@ #include "String.h" #include "Database.h" #include "PhraseArray.h" +#include "PinyinProperties.h" namespace PY { class PhraseEditor { public: - PhraseEditor(void); - ~PhraseEditor(void); + PhraseEditor (PinyinProperties & props); + ~PhraseEditor (void); const String & selectedString (void) const { return m_selected_string; } const PinyinArray & pinyin (void) const { return m_pinyin; } @@ -41,15 +42,7 @@ public: m_cursor = 0; } - gboolean modeSimp (void) const { - return m_mode_simp; - } - - void setModeSimp (gboolean mode) { - m_mode_simp = mode; - } - - void update (const PinyinArray &pinyin); + gboolean update (const PinyinArray &pinyin); gboolean selectCandidate (guint i); gboolean resetCandidate (guint i); void commit (void) { @@ -77,7 +70,7 @@ private: PhraseArray m_candidate_0_phrases; // the first candidate in phrase array format PinyinArray m_pinyin; guint m_cursor; - gboolean m_mode_simp; + PinyinProperties & m_props; private: static Database m_database; diff --git a/src/PinyinArray.h b/src/PinyinArray.h index a3c1bba..c02c5d6 100644 --- a/src/PinyinArray.h +++ b/src/PinyinArray.h @@ -33,7 +33,7 @@ struct PinyinSegment { class PinyinArray: public Array<PinyinSegment> { public: - PinyinArray (guint init_size) : Array<PinyinSegment> (init_size) {} + PinyinArray (guint init_size = 0) : Array<PinyinSegment> (init_size) {} void append (const Pinyin *pinyin, guint begin, guint len) { push_back (PinyinSegment (pinyin, begin, len)); } diff --git a/src/PinyinEditor.cc b/src/PinyinEditor.cc index fd4760c..3992db8 100644 --- a/src/PinyinEditor.cc +++ b/src/PinyinEditor.cc @@ -1,19 +1,573 @@ #include "Config.h" #include "PinyinEditor.h" +#include "SimpTradConverter.h" +#include "HalfFullConverter.h" namespace PY { #define MAX_PINYIN_LEN 64 - +/* init static members */ PinyinParser PinyinEditor::m_parser; -PinyinEditor::PinyinEditor (void) - : m_text (MAX_PINYIN_LEN), - m_cursor (0), - m_pinyin (MAX_PHRASE_LEN), - m_pinyin_len (0) +PinyinEditor::PinyinEditor (PinyinProperties & props) + : m_pinyin (MAX_PHRASE_LEN), + m_pinyin_len (0), + m_buffer (64), + m_lookup_table (Config::pageSize ()), + m_phrase_editor (props), + Editor (props) +{ +} + +#define CMSHM_MASK \ + (IBUS_CONTROL_MASK | \ + IBUS_MOD1_MASK | \ + IBUS_SUPER_MASK | \ + IBUS_HYPER_MASK | \ + IBUS_META_MASK) + +#define CMSHM_FILTER(modifiers) \ + (modifiers & (CMSHM_MASK)) + +/** + * process pinyin + */ +inline gboolean +PinyinEditor::processPinyin (guint keyval, guint keycode, guint modifiers) +{ + if (G_UNLIKELY (CMSHM_FILTER(modifiers) != 0)) + return m_text ? TRUE : FALSE; + + insert (keyval); + return TRUE; +} + +/** + * process numbers + */ +inline gboolean +PinyinEditor::processNumber (guint keyval, guint keycode, guint modifiers) +{ + guint i; + + if (!m_text) + return FALSE; + + switch (keyval) { + case IBUS_0: + case IBUS_KP_0: + i = 9; + break; + case IBUS_1 ... IBUS_9: + i = keyval - IBUS_1; + break; + case IBUS_KP_1 ... IBUS_KP_9: + i = keyval - IBUS_KP_1; + break; + default: + g_return_val_if_reached (FALSE); + } + if (modifiers == 0) + selectCandidateInPage (i); + else if ((modifiers & ~ IBUS_LOCK_MASK) == IBUS_CONTROL_MASK) + resetCandidateInPage (i); + return TRUE; +} + +inline gboolean +PinyinEditor::processSpace (guint keyval, guint keycode, guint modifiers) +{ + if (!m_text) + return FALSE; + if (CMSHM_FILTER (modifiers) != 0) + return TRUE; + + if (m_phrase_editor.pinyinExistsAfterCursor ()) { + selectCandidate (m_lookup_table.cursorPos ()); + } + else { + commit (); + } + return TRUE; +} + +inline gboolean +PinyinEditor::processPunct (guint keyval, guint keycode, guint modifiers) +{ + return TRUE; +} + +inline gboolean +PinyinEditor::processOthers (guint keyval, guint keycode, guint modifiers) { + if (m_text.isEmpty ()) + return FALSE; + + /* ignore numlock */ + modifiers = CMSHM_FILTER (modifiers); + + if (modifiers != 0 && modifiers != IBUS_CONTROL_MASK) + return TRUE; + + + /* process some cursor control keys */ + gboolean _update = FALSE; + if (modifiers == 0) { + switch (keyval) { + case IBUS_Shift_L: + if (Config::shiftSelectCandidate ()) { + selectCandidateInPage (1); + } + break; + + case IBUS_Shift_R: + if (Config::shiftSelectCandidate ()) { + selectCandidateInPage (2); + } + break; + + case IBUS_Return: + case IBUS_KP_Enter: + commit (); + break; + + case IBUS_BackSpace: + removeCharBefore (); + break; + + case IBUS_Delete: + case IBUS_KP_Delete: + removeCharAfter (); + break; + + case IBUS_Left: + case IBUS_KP_Left: + moveCursorLeft (); + break; + + case IBUS_Right: + case IBUS_KP_Right: + moveCursorRight (); + break; + + case IBUS_Home: + case IBUS_KP_Home: + moveCursorToBegin (); + break; + + case IBUS_End: + case IBUS_KP_End: + moveCursorToEnd (); + break; + + case IBUS_Up: + case IBUS_KP_Up: + cursorUp (); break; + + case IBUS_Down: + case IBUS_KP_Down: + cursorDown (); break; + + case IBUS_Page_Up: + case IBUS_KP_Page_Up: + pageUp (); break; + + case IBUS_Page_Down: + case IBUS_KP_Page_Down: + pageDown (); break; + + case IBUS_Escape: + reset (); break; + default: + break; + } + } + else { + switch (keyval) { + case IBUS_BackSpace: + removeWordBefore (); + break; + + case IBUS_Delete: + case IBUS_KP_Delete: + removeWordAfter (); + break; + + case IBUS_Left: + case IBUS_KP_Left: + moveCursorLeftByWord (); + break; + + case IBUS_Right: + case IBUS_KP_Right: + moveCursorToEnd (); + break; + + default: + break; + }; + } + return TRUE; +} + +gboolean +PinyinEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers) +{ + gboolean result = FALSE; + + if (modifiers & IBUS_RELEASE_MASK) { + return m_text.isEmpty () ? FALSE : TRUE; + } + + modifiers &= (IBUS_SHIFT_MASK | + IBUS_CONTROL_MASK | + IBUS_MOD1_MASK | + IBUS_SUPER_MASK | + IBUS_HYPER_MASK | + IBUS_META_MASK | + IBUS_LOCK_MASK); + + switch (keyval) { + /* letters */ + case IBUS_a ... IBUS_z: + return processPinyin (keyval, keycode, modifiers); + case IBUS_0 ... IBUS_9: + case IBUS_KP_0 ... IBUS_KP_9: + return processNumber (keyval, keycode, modifiers); + /* space */ + case IBUS_space: + return processSpace (keyval, keycode, modifiers); + /* others */ + default: + return processOthers (keyval, keycode, modifiers); + } +} + +void +PinyinEditor::updatePreeditText (void) +{ + /* preedit text = selected phrases + highlight candidate + rest text */ + if (G_UNLIKELY (m_phrase_editor.isEmpty () && m_text.isEmpty ())) { + hidePreeditText (); + return; + } + + if (m_cursor == m_text.length ()) + updatePreeditTextInTypingMode (); + else + updatePreeditTextInEditingMode (); +} + +void +PinyinEditor::updatePreeditTextInTypingMode (void) +{ + m_buffer.truncate (0); + + /* add select phrases */ + if (G_UNLIKELY (m_phrase_editor.selectedString ())) { + m_buffer << m_phrase_editor.selectedString (); + } + + /* add highlight candidate */ + guint candidate_begin = m_buffer.utf8Length (); + guint candidate_length = 0; + if (m_phrase_editor.candidates ().length () > 0) { + if (m_lookup_table.cursorPos () == 0 && !m_props.modeSimp ()) { + const PhraseArray & phrases = m_phrase_editor.candidate0 (); + candidate_length = 0; + for (guint i = 0; i < phrases.length (); i++) { + candidate_length += phrases[i].len; + SimpTradConverter::simpToTrad (phrases[i], m_buffer); + } + } + else { + const Phrase & candidate = m_phrase_editor.candidate (m_lookup_table.cursorPos ()); + candidate_length = candidate.len; + if (m_props.modeSimp ()) + m_buffer << candidate; + else + SimpTradConverter::simpToTrad (candidate, m_buffer); + } + } + + /* add rest text */ + const PinyinArray & pinyin = m_phrase_editor.pinyin (); + if (candidate_begin + candidate_length < pinyin.length ()) + m_buffer << ((const gchar *) textAfterPinyin ( + candidate_begin + candidate_length)); + else + m_buffer << ((const gchar *) textAfterPinyin ()); + + StaticText preedit_text (m_buffer); + /* underline */ + preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1); + + /* candidate */ + if (candidate_length != 0) { + preedit_text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x00000000, + candidate_begin, candidate_begin + candidate_length); + preedit_text.appendAttribute (IBUS_ATTR_TYPE_BACKGROUND, 0x00c8c8f0, + candidate_begin, candidate_begin + candidate_length); + } + // ibus_engine_update_preedit_text (m_engine, preedit_text, m_buffer.utf8Length (), TRUE); + Editor::updatePreeditText (preedit_text, candidate_begin, TRUE); +} + +void +PinyinEditor::updatePreeditTextInEditingMode (void) +{ + m_buffer.truncate (0); + + /* add select phrases */ + if (G_UNLIKELY (m_phrase_editor.selectedString ())) { + m_buffer << m_phrase_editor.selectedString (); + } + + /* add highlight candidate */ + const PinyinArray & pinyin = m_phrase_editor.pinyin (); + guint candidate_begin = m_buffer.utf8Length (); + guint candidate_length = 0; + guint candidate_pinyin_end = 0; + if (m_phrase_editor.candidates ().length () > 0) { + const Phrase & candidate = m_phrase_editor.candidate (m_lookup_table.cursorPos ()); + candidate_length = candidate.len; + + m_buffer << pinyin[candidate_begin]->sheng << pinyin[candidate_begin]->yun; + + for (guint i = 1; i < candidate_length; i++) { + m_buffer << ' ' << pinyin[candidate_begin + i]->sheng << pinyin[candidate_begin + i]->yun; + } + candidate_pinyin_end = m_buffer.utf8Length (); + } + + /* add rest text */ + if (candidate_begin + candidate_length < pinyin.length ()) { + if (m_buffer) + m_buffer << ' ' ; + m_buffer << textAfterPinyin (candidate_begin + candidate_length); + } + else { + const gchar * p = textAfterPinyin (); + if (*p != '\0') { + if (m_buffer) + m_buffer << ' '; + m_buffer << p; + } + } + + StaticText preedit_text (m_buffer); + /* underline */ + preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1); + + /* candidate */ + if (candidate_length != 0) { + preedit_text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x00000000, + candidate_begin, candidate_pinyin_end); + preedit_text.appendAttribute (IBUS_ATTR_TYPE_BACKGROUND, 0x00c8c8f0, + candidate_begin, candidate_pinyin_end); + } + // ibus_engine_update_preedit_text (m_engine, preedit_text, m_buffer.utf8Length (), TRUE); + Editor::updatePreeditText (preedit_text, candidate_begin, TRUE); +} + +void +PinyinEditor::updateAuxiliaryText (void) +{ + /* clear pinyin array */ + if (G_UNLIKELY (m_text.isEmpty () || + m_cursor == m_pinyin.length ())) { + hideAuxiliaryText (); + return; + } + + // guint cursor_pos; + m_buffer.truncate (0); + for (guint i = m_phrase_editor.cursor (); i < m_pinyin.length (); ++i) { + if (G_LIKELY (i != m_phrase_editor.cursor ())) + m_buffer << ' '; + const Pinyin *p = m_pinyin[i]; + m_buffer << p->sheng; + m_buffer << p->yun; + } + + if (G_UNLIKELY (m_pinyin_len == m_cursor)) { + /* aux = pinyin + non-pinyin */ + // cursor_pos = m_buffer.utf8Length (); + m_buffer << '|' << textAfterPinyin (); + } + else { + /* aux = pinyin + non-pinyin before cursor + non-pinyin after cursor */ + m_buffer.append (textAfterPinyin (), + m_cursor - m_pinyin_len); + // cursor_pos = m_buffer.utf8Length (); + m_buffer << '|' << textAfterCursor (); + } + + StaticText aux_text (m_buffer); + Editor::updateAuxiliaryText (aux_text, TRUE); +} + +void +PinyinEditor::updateLookupTable (void) +{ + m_lookup_table.clear (); + m_lookup_table.setPageSize (Config::pageSize ()); + + guint candidate_nr = m_phrase_editor.candidates ().length (); + + if (G_UNLIKELY (candidate_nr == 0)) { + hideLookupTable (); + return; + } + + if (G_LIKELY (m_props.modeSimp () || !Config::tradCandidate ())) { + for (guint i = 0; i < candidate_nr; i++) { + StaticText text (m_phrase_editor.candidate (i)); + if (m_phrase_editor.candidateIsUserPhease (i)) + text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1); + m_lookup_table.appendCandidate (text); + } + } + else { + for (guint i = 0; i < candidate_nr; i++) { + m_buffer.truncate (0); + SimpTradConverter::simpToTrad (m_phrase_editor.candidate (i), m_buffer); + Text text (m_buffer); + if (m_phrase_editor.candidateIsUserPhease (i)) + text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1); + m_lookup_table.appendCandidate (text); + } + } + + updateLookupTableFast (m_lookup_table, TRUE); +} + +void +PinyinEditor::pageUp (void) +{ + if (m_lookup_table.pageUp ()) { + updateLookupTableFast (m_lookup_table, TRUE); + updatePreeditText (); + } +} + +void +PinyinEditor::pageDown (void) +{ + if (m_lookup_table.pageDown ()) { + updateLookupTableFast (m_lookup_table, TRUE); + updatePreeditText (); + } +} + +void +PinyinEditor::cursorUp (void) +{ + if (m_lookup_table.cursorUp ()) { + updateLookupTableFast (m_lookup_table, TRUE); + updatePreeditText (); + } +} + +void +PinyinEditor::cursorDown (void) +{ + if (m_lookup_table.cursorDown ()) { + updateLookupTableFast (m_lookup_table, TRUE); + updatePreeditText (); + } +} + +void +PinyinEditor::update (void) +{ + updatePreeditText (); + updateAuxiliaryText (); + updateLookupTable (); +} + +void +PinyinEditor::updatePhraseEditor (void) +{ + m_phrase_editor.update (m_pinyin); +} + +inline void +PinyinEditor::commit (const gchar *str) +{ + StaticText text(str); + commitText (text); +} + +void +PinyinEditor::commit (void) +{ + if (G_UNLIKELY (isEmpty ())) + return; + + m_buffer.truncate (0); + m_buffer << m_phrase_editor.selectedString (); + + const gchar *p = textAfterPinyin (m_buffer.utf8Length ()); + if (G_UNLIKELY (m_props.modeFull ())) { + while (*p != '\0') { + m_buffer.appendUnichar (HalfFullConverter::toFull (*p++)); + } + } + else { + m_buffer << p; + } + m_phrase_editor.commit (); + reset (); + commit ((const gchar *)m_buffer); +} + +inline gboolean +PinyinEditor::selectCandidate (guint i) +{ + if (m_phrase_editor.selectCandidate (i)) { + if ((!m_phrase_editor.pinyinExistsAfterCursor ()) && + *textAfterPinyin () == '\0') { + commit (); + } + else + update (); + return TRUE; + } + return FALSE; +} + +inline gboolean +PinyinEditor::selectCandidateInPage (guint i) +{ + guint page_size = m_lookup_table.pageSize (); + guint cursor_pos = m_lookup_table.cursorPos (); + + if (G_UNLIKELY (i >= page_size)) + return FALSE; + i += (cursor_pos / page_size) * page_size; + + return selectCandidate (i); +} + +inline gboolean +PinyinEditor::resetCandidate (guint i) +{ + if (m_phrase_editor.resetCandidate (i)) { + update (); + } + return TRUE; +} + +inline gboolean +PinyinEditor::resetCandidateInPage (guint i) +{ + guint page_size = m_lookup_table.pageSize (); + guint cursor_pos = m_lookup_table.cursorPos (); + i += (cursor_pos / page_size) * page_size; + + return resetCandidate (i); } }; diff --git a/src/PinyinEditor.h b/src/PinyinEditor.h index 457e07f..1b7d609 100644 --- a/src/PinyinEditor.h +++ b/src/PinyinEditor.h @@ -2,18 +2,55 @@ #define __PY_PINYIN_EDITOR_H_ #include <glib.h> -#include "String.h" +#include "Editor.h" +#include "Database.h" #include "PinyinParser.h" -#include "Regex.h" +#include "PhraseEditor.h" + +namespace PY { #define MAX_PINYIN_LEN 64 -namespace PY { +class PinyinEditor : public Editor { +public: + PinyinEditor (PinyinProperties & props); -class PinyinEditor { public: - PinyinEditor (void); - + /* virtual functions */ + virtual gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers); + virtual void pageUp (void); + virtual void pageDown (void); + virtual void cursorUp (void); + virtual void cursorDown (void); + virtual void update (void); +#if 0 + virtual void reset (void); +#endif +protected: + + gboolean processPinyin (guint keyval, guint keycode, guint modifiers); + gboolean processCapitalLetter (guint keyval, guint keycode, guint modifiers); + gboolean processNumber (guint keyval, guint keycode, guint modifiers); + gboolean processPunct (guint keyval, guint keycode, guint modifiers); + gboolean processSpace (guint keyval, guint keycode, guint modifiers); + gboolean processOthers (guint keyval, guint keycode, guint modifiers); + + void updatePreeditText (void); + void updatePreeditTextInTypingMode (void); + void updatePreeditTextInEditingMode (void); + void updateAuxiliaryText (void); + void updateLookupTable (void); + + void updatePhraseEditor (void); + + gboolean selectCandidate (guint i); + gboolean selectCandidateInPage (guint i); + gboolean resetCandidate (guint i); + gboolean resetCandidateInPage (guint i); + + void commit (void); + void commit (const gchar *str); + const String & text (void) const { return m_text; } const gchar * textAfterPinyin (void) const { return (const gchar *)m_text + m_pinyin_len; } const gchar * textAfterPinyin (guint i) const { @@ -25,7 +62,7 @@ public: } const gchar * textAfterCursor (void) const { return (const gchar *)m_text + m_cursor; } guint cursor (void) const { return m_cursor; } - gboolean isEmpty (void) const { return m_text.isEmpty (); } + gboolean isEmpty (void) const { return m_buffer.isEmpty (); } const PinyinArray & pinyin (void) const { return m_pinyin; } guint pinyinLength (void) const { return m_pinyin_len; } operator gboolean (void) const { return !isEmpty (); } @@ -42,18 +79,17 @@ public: virtual gboolean moveCursorRightByWord (void) = 0; virtual gboolean moveCursorToBegin (void) = 0; virtual gboolean moveCursorToEnd (void) = 0; - virtual gboolean reset (void) = 0; protected: - String m_text; // text buffer - guint m_cursor; // cursor pos in char PinyinArray m_pinyin; // pinyin array guint m_pinyin_len; // pinyin length in char + String m_buffer; // temp buffer + PhraseEditor m_phrase_editor; + LookupTable m_lookup_table; protected: static PinyinParser m_parser; }; - }; #endif diff --git a/src/PinyinEngine.cc b/src/PinyinEngine.cc index dd8d6ec..8c0e25c 100644 --- a/src/PinyinEngine.cc +++ b/src/PinyinEngine.cc @@ -19,12 +19,7 @@ namespace PY { /* constructor */ PinyinEngine::PinyinEngine (IBusEngine *engine) : m_engine (engine), - m_pinyin_editor (NULL), m_need_update (0), - m_lookup_table (Config::pageSize ()), - m_mode_chinese (Config::initChinese ()), - m_mode_full (Config::initFull ()), - m_mode_full_punct (Config::initFullPunct ()), m_quote (TRUE), m_double_quote (TRUE), m_prev_pressed_key (0), @@ -32,83 +27,30 @@ PinyinEngine::PinyinEngine (IBusEngine *engine) m_prev_commited_char (0), m_input_mode (MODE_INIT) { - /* */ + gint i; + /* create editors */ if (Config::doublePinyin ()) - m_pinyin_editor = new DoublePinyinEditor (); + m_editors[MODE_INIT] = new DoublePinyinEditor (m_props); else - m_pinyin_editor = new FullPinyinEditor (); - - /* create properties */ - m_prop_chinese = ibus_property_new ("mode.chinese", - PROP_TYPE_NORMAL, - StaticText ("CN"), - m_mode_chinese ? - PKGDATADIR"/icons/chinese.svg" : - PKGDATADIR"/icons/english.svg", - StaticText (_("Chinese")), - TRUE, - TRUE, - PROP_STATE_UNCHECKED, - NULL); - m_props.append (m_prop_chinese); - - m_prop_full = ibus_property_new ("mode.full", - PROP_TYPE_NORMAL, - StaticText (m_mode_full? "Aa" : "Aa"), - m_mode_full ? - PKGDATADIR"/icons/full.svg" : - PKGDATADIR"/icons/half.svg", - StaticText (_("Full/Half width")), - TRUE, - TRUE, - PROP_STATE_UNCHECKED, - NULL); - m_props.append (m_prop_full); - - m_prop_full_punct = ibus_property_new ("mode.full_punct", - PROP_TYPE_NORMAL, - StaticText (m_mode_full_punct ? ",。" : ",."), - m_mode_full_punct ? - PKGDATADIR"/icons/full-punct.svg" : - PKGDATADIR"/icons/half-punct.svg", - StaticText (_("Full/Half width punctuation")), - TRUE, - TRUE, - PROP_STATE_UNCHECKED, - NULL); - m_props.append (m_prop_full_punct); - - m_prop_simp = ibus_property_new ("mode.simp", - PROP_TYPE_NORMAL, - StaticText (m_phrase_editor.modeSimp () ? "简" : "繁"), - m_phrase_editor.modeSimp () ? - PKGDATADIR"/icons/simp-chinese.svg" : - PKGDATADIR"/icons/trad-chinese.svg", - StaticText (_("Simplfied/Traditional Chinese")), - TRUE, - TRUE, - PROP_STATE_UNCHECKED, - NULL); - m_props.append (m_prop_simp); + m_editors[MODE_INIT] = new FullPinyinEditor (m_props); + for (i = MODE_RAW; i < MODE_LAST; i++) { + m_editors[i] = new RawEditor (m_props); + } - m_prop_setup = ibus_property_new ("setup", - PROP_TYPE_NORMAL, - StaticText (_("Pinyin preferences")), - "ibus-setup", - StaticText (_("Pinyin preferences")), - TRUE, - TRUE, - PROP_STATE_UNCHECKED, - NULL); - m_props.append (m_prop_setup); + m_props.signalUpdateProperty ().connect (sigc::mem_fun (*this, &PinyinEngine::slotUpdateProperty)); + for (i = MODE_INIT; i < MODE_LAST; i++) { + connectEditorSignals (m_editors[i]); + } } /* destructor */ PinyinEngine::~PinyinEngine (void) { - delete m_pinyin_editor; + for (gint i = 0; i < MODE_LAST; i++) { + delete m_editors[i]; + } } #define CMSHM_MASK \ @@ -121,145 +63,13 @@ PinyinEngine::~PinyinEngine (void) #define CMSHM_FILTER(modifiers) \ (modifiers & (CMSHM_MASK)) -/** - * process pinyin - */ -inline gboolean -PinyinEngine::processPinyin (guint keyval, guint keycode, guint modifiers) -{ - if (G_UNLIKELY (CMSHM_FILTER(modifiers) != 0)) - return FALSE; - - if (G_UNLIKELY (!m_mode_chinese)) { - commit (m_mode_full ? HalfFullConverter::toFull (keyval) : (gchar) keyval); - return TRUE; - } - - if (m_pinyin_editor->insert (keyval)) { - updatePhraseEditor (); - updateUI (FALSE); - } - return TRUE; -} - -/** - * process capital letters - */ -inline gboolean -PinyinEngine::processCapitalLetter (guint keyval, guint keycode, guint modifiers) -{ - if (G_UNLIKELY (CMSHM_FILTER (modifiers) != 0)) - return FALSE; - - if (modifiers & IBUS_SHIFT_MASK) - return processPinyin (keyval, keycode, modifiers); - - if (m_mode_chinese && ! isEmpty ()) { - if (!Config::autoCommit ()) - return TRUE; - if (m_phrase_editor.pinyinExistsAfterCursor ()) { - selectCandidate (m_lookup_table.cursorPos ()); - } - commit (); - } - - commit (m_mode_full ? HalfFullConverter::toFull (keyval) : (gchar) keyval); - return TRUE; -} - -/** - * process numbers - */ -inline gboolean -PinyinEngine::processNumber (guint keyval, guint keycode, guint modifiers) -{ - guint ch; - - switch (keyval) { - case IBUS_0 ... IBUS_9: - ch = '0' + keyval - IBUS_0; - break; - case IBUS_KP_0 ... IBUS_KP_9: - ch = '0' + keyval - IBUS_KP_0; - break; - default: - g_return_val_if_reached (FALSE); - break; - } - - /* English mode */ - if (G_UNLIKELY (!m_mode_chinese)) { - if (G_UNLIKELY (CMSHM_FILTER (modifiers) != 0)) - return FALSE; - commit ((gunichar) m_mode_full ? HalfFullConverter::toFull (ch) : ch); - return TRUE; - } - - /* Chinese mode, if empty */ - if (G_UNLIKELY (isEmpty ())) { - if (G_UNLIKELY (CMSHM_FILTER (modifiers) != 0)) - return FALSE; - commit ((gunichar) m_mode_full ? HalfFullConverter::toFull (ch) : ch); - return TRUE; - } - - /* Chinese mode, if has candidates */ - guint i; - switch (keyval) { - case IBUS_0: - case IBUS_KP_0: - i = 9; - break; - case IBUS_1 ... IBUS_9: - i = keyval - IBUS_1; - break; - case IBUS_KP_1 ... IBUS_KP_9: - i = keyval - IBUS_KP_1; - break; - default: - g_return_val_if_reached (FALSE); - } - - if (modifiers == 0) - selectCandidateInPage (i); - else if ((modifiers & ~ IBUS_LOCK_MASK) == IBUS_CONTROL_MASK) - resetCandidateInPage (i); - return TRUE; -} - -inline gboolean -PinyinEngine::processSpace (guint keyval, guint keycode, guint modifiers) -{ - if (CMSHM_FILTER (modifiers) != 0) - return FALSE; - - if (G_UNLIKELY (modifiers & IBUS_SHIFT_MASK)) { - toggleModeFull (); - return TRUE; - } - - /* Chinese mode */ - if (G_UNLIKELY (m_mode_chinese && !isEmpty ())) { - if (m_phrase_editor.pinyinExistsAfterCursor ()) { - selectCandidate (m_lookup_table.cursorPos ()); - } - else { - commit (); - } - } - else { - commit (m_mode_full ? " " : " "); - } - return TRUE; -} - inline gboolean PinyinEngine::processPunct (guint keyval, guint keycode, guint modifiers) { guint cmshm_modifiers = CMSHM_FILTER (modifiers); if (G_UNLIKELY (keyval == IBUS_period && cmshm_modifiers == IBUS_CONTROL_MASK)) { - toggleModeFullPunct (); + m_props.toggleModeFullPunct (); return TRUE; } @@ -268,368 +78,147 @@ PinyinEngine::processPunct (guint keyval, guint keycode, guint modifiers) return FALSE; /* English mode */ - if (G_UNLIKELY (!m_mode_chinese)) { - if (G_UNLIKELY (m_mode_full)) + if (G_UNLIKELY (!m_props.modeChinese ())) { + if (G_UNLIKELY (m_props.modeFull ())) commit (HalfFullConverter::toFull (keyval)); else commit (keyval); return TRUE; } - - /* Chinese mode */ - if (G_UNLIKELY (!isEmpty ())) { - switch (keyval) { - case IBUS_apostrophe: - return processPinyin (keyval, keycode, modifiers); - case IBUS_comma: - if (Config::commaPeriodPage ()) { - pageUp (); - return TRUE; - } - break; - case IBUS_minus: - if (Config::minusEqualPage ()) { - pageUp (); + else { + /* Chinese mode */ + if (m_props.modeFullPunct ()) { + switch (keyval) { + case '`': + commit ("·"); return TRUE; + case '~': + commit ("~"); return TRUE; + case '!': + commit ("!"); return TRUE; + // case '@': + // case '#': + case '$': + commit ("¥"); return TRUE; + // case '%': + case '^': + commit ("……"); return TRUE; + // case '&': + // case '*': + case '(': + commit ("("); return TRUE; + case ')': + commit (")"); return TRUE; + // case '-': + case '_': + commit ("——"); return TRUE; + // case '=': + // case '+': + case '[': + commit ("【"); return TRUE; + case ']': + commit ("】"); return TRUE; + case '{': + commit ("『"); return TRUE; + case '}': + commit ("』"); return TRUE; + case '\\': + commit ("、"); return TRUE; + // case '|': + case ';': + commit (";"); return TRUE; + case ':': + commit (":"); return TRUE; + case '\'': + commit (m_quote ? "‘" : "’"); + m_quote = !m_quote; return TRUE; - } - break; - case IBUS_period: - if (Config::commaPeriodPage ()) { - pageDown (); + case '"': + commit (m_double_quote ? "“" : "”"); + m_double_quote = !m_double_quote; return TRUE; - } - break; - case IBUS_equal: - if (Config::minusEqualPage ()) { - pageDown (); + case ',': + commit (","); return TRUE; + case '.': + if (m_prev_commited_char >= '0' && m_prev_commited_char <= '9') + commit (keyval); + else + commit ("。"); return TRUE; + case '<': + commit ("《"); return TRUE; + case '>': + commit ("》"); return TRUE; + // case '/': + case '?': + commit ("?"); return TRUE; } - break; - case IBUS_semicolon: - if (G_UNLIKELY (Config::doublePinyin ())) { - /* double pinyin need process ';' */ - if (processPinyin (keyval, keycode, modifiers)) - return TRUE; - } - break; - } - - if (G_LIKELY (!Config::autoCommit ())) - return TRUE; - - if (m_phrase_editor.pinyinExistsAfterCursor ()) { - selectCandidate (m_lookup_table.cursorPos ()); - } - commit (); - } - - g_assert (isEmpty ()); - - if (m_mode_full_punct) { - switch (keyval) { - case '`': - commit ("·"); return TRUE; - case '~': - commit ("~"); return TRUE; - case '!': - commit ("!"); return TRUE; - // case '@': - // case '#': - case '$': - commit ("¥"); return TRUE; - // case '%': - case '^': - commit ("……"); return TRUE; - // case '&': - // case '*': - case '(': - commit ("("); return TRUE; - case ')': - commit (")"); return TRUE; - // case '-': - case '_': - commit ("——"); return TRUE; - // case '=': - // case '+': - case '[': - commit ("【"); return TRUE; - case ']': - commit ("】"); return TRUE; - case '{': - commit ("『"); return TRUE; - case '}': - commit ("』"); return TRUE; - case '\\': - commit ("、"); return TRUE; - // case '|': - case ';': - commit (";"); return TRUE; - case ':': - commit (":"); return TRUE; - case '\'': - commit (m_quote ? "‘" : "’"); - m_quote = !m_quote; - return TRUE; - case '"': - commit (m_double_quote ? "“" : "”"); - m_double_quote = !m_double_quote; - return TRUE; - case ',': - commit (","); return TRUE; - case '.': - if (m_prev_commited_char >= '0' && m_prev_commited_char <= '9') - commit (keyval); - else - commit ("。"); - return TRUE; - case '<': - commit ("《"); return TRUE; - case '>': - commit ("》"); return TRUE; - // case '/': - case '?': - commit ("?"); return TRUE; } + commit (m_props.modeFull () ? HalfFullConverter::toFull (keyval) : keyval); } - - commit (m_mode_full ? HalfFullConverter::toFull (keyval) : keyval); return TRUE; } -inline gboolean -PinyinEngine::processOthers (guint keyval, guint keycode, guint modifiers) +gboolean +PinyinEngine::processKeyEvent (guint keyval, guint keycode, guint modifiers) { - if (G_UNLIKELY (isEmpty ())) - return FALSE; - - /* ignore numlock */ - modifiers &= ~IBUS_MOD2_MASK; - - /* process some cursor control keys */ - gboolean _update = FALSE; - switch (keyval) { - case IBUS_Shift_L: - if (Config::shiftSelectCandidate () && - m_mode_chinese) { - selectCandidateInPage (1); - } - break; - - case IBUS_Shift_R: - if (Config::shiftSelectCandidate () && - m_mode_chinese) { - selectCandidateInPage (2); - } - break; - - case IBUS_Return: - case IBUS_KP_Enter: - commit (); - break; - - case IBUS_BackSpace: - if (G_LIKELY (modifiers == 0)) - _update = m_pinyin_editor->removeCharBefore (); - else if (G_LIKELY (modifiers == IBUS_CONTROL_MASK)) - _update = m_pinyin_editor->removeWordBefore (); - break; - - case IBUS_Delete: - case IBUS_KP_Delete: - if (G_LIKELY (modifiers == 0)) - _update = m_pinyin_editor->removeCharAfter (); - else if (G_LIKELY (modifiers == IBUS_CONTROL_MASK)) - _update = m_pinyin_editor->removeWordAfter (); - break; - - case IBUS_Left: - case IBUS_KP_Left: - if (G_LIKELY (modifiers == 0)) { - // move left single char - _update = m_pinyin_editor->moveCursorLeft (); - } - else if (G_LIKELY (modifiers == IBUS_CONTROL_MASK)) { - // move left one pinyin - _update = m_pinyin_editor->moveCursorLeftByWord (); - } - break; + gboolean retval; - case IBUS_Right: - case IBUS_KP_Right: - if (G_LIKELY (modifiers == 0)) { - // move right single char - _update = m_pinyin_editor->moveCursorRight (); - } - else if (G_LIKELY (modifiers == IBUS_CONTROL_MASK)) { - // move right to end - _update = m_pinyin_editor->moveCursorToEnd (); - } - break; + retval = m_editors[m_input_mode]->processKeyEvent (keyval, keycode, modifiers); - case IBUS_Home: - case IBUS_KP_Home: - if (G_LIKELY (modifiers == 0)) { - // move to begin - _update = m_pinyin_editor->moveCursorToBegin (); - } - break; + if (retval == FALSE) { + // ignore release event + if (modifiers & IBUS_RELEASE_MASK) { + if (m_prev_pressed_key != keyval || m_prev_pressed_key_result != FALSE) + return TRUE; - case IBUS_End: - case IBUS_KP_End: - if (G_LIKELY (modifiers == 0)) { - // move to end - _update = m_pinyin_editor->moveCursorToEnd (); + switch (keyval) { + case IBUS_Shift_L: + case IBUS_Shift_R: + m_props.toggleModeChinese (); + return TRUE; + default: + return TRUE; + } } - break; - - case IBUS_Up: - case IBUS_KP_Up: - cursorUp (); break; - case IBUS_Down: - case IBUS_KP_Down: - cursorDown (); break; - case IBUS_Page_Up: - case IBUS_KP_Page_Up: - pageUp (); break; - case IBUS_Page_Down: - case IBUS_KP_Page_Down: - pageDown (); break; - case IBUS_Escape: - reset (); break; - } - if (G_LIKELY (_update)) { - updatePhraseEditor (); - updateUI (FALSE); - } - return TRUE; -} - -inline gboolean -PinyinEngine::processInitMode (guint keyval, guint keycode, guint modifiers) -{ - gboolean retval = FALSE; - - // ignore release event - if (modifiers & IBUS_RELEASE_MASK) { - if (m_prev_pressed_key != keyval || m_prev_pressed_key_result != FALSE) - return TRUE; + modifiers &= (IBUS_SHIFT_MASK | + IBUS_CONTROL_MASK | + IBUS_MOD1_MASK | + IBUS_SUPER_MASK | + IBUS_HYPER_MASK | + IBUS_META_MASK); switch (keyval) { - case IBUS_Shift_L: - case IBUS_Shift_R: - if (isEmpty ()) - toggleModeChinese (); - return TRUE; - default: - return TRUE; + /* letters */ + case IBUS_a ... IBUS_z: + case IBUS_A ... IBUS_Z: + /* numbers */ + case IBUS_0 ... IBUS_9: + case IBUS_KP_0 ... IBUS_KP_9: + if (modifiers == 0) { + commit (m_props.modeFull () ? HalfFullConverter::toFull (keyval) : (gchar) keyval); + retval = TRUE; + } + break; + /* punct */ + case IBUS_exclam ... IBUS_slash: + case IBUS_colon ... IBUS_at: + case IBUS_bracketleft ... IBUS_quoteleft: + case IBUS_braceleft ... IBUS_asciitilde: + retval = processPunct (keyval, keycode, modifiers); + break; + /* space */ + case IBUS_space: + if (modifiers == 0) { + commit (m_props.modeFull () ? " " : " "); + retval = TRUE; + } + break; + /* others */ + default: + break; } } - modifiers &= (IBUS_SHIFT_MASK | - IBUS_CONTROL_MASK | - IBUS_MOD1_MASK | - IBUS_SUPER_MASK | - IBUS_HYPER_MASK | - IBUS_META_MASK | - IBUS_LOCK_MASK); - - switch (keyval) { - /* letters */ - case IBUS_a ... IBUS_z: - retval = processPinyin (keyval, keycode, modifiers); - break; - case IBUS_A ... IBUS_Z: - retval = processCapitalLetter (keyval, keycode, modifiers); - break; - /* numbers */ - case IBUS_0 ... IBUS_9: - case IBUS_KP_0 ... IBUS_KP_9: - retval = processNumber (keyval, keycode, modifiers); - break; - /* punct */ - case IBUS_exclam ... IBUS_slash: - case IBUS_colon ... IBUS_at: - case IBUS_bracketleft ... IBUS_quoteleft: - case IBUS_braceleft ... IBUS_asciitilde: - retval = processPunct (keyval, keycode, modifiers); - break; - /* space */ - case IBUS_space: - retval = processSpace (keyval, keycode, modifiers); - break; - /* others */ - default: - retval = processOthers (keyval, keycode, modifiers); - break; - } - - return retval; -} - -inline gboolean -PinyinEngine::processRawMode (guint keyval, guint keycode, guint modifiers) -{ - gboolean update = TRUE; - gboolean retval = TRUE; - - switch (keyval) { - case IBUS_Escape: - reset (); - break; - default: - retval = m_raw_editor.processKeyEvent (keyval, keycode, modifiers, update); - if (update) - updatePreeditTextInRawMode (); - break; - } - - return retval; -} - -inline gboolean -PinyinEngine::processEnglishMode (guint keyval, guint keycode, guint modifiers) -{ - return TRUE; -} - -inline gboolean -PinyinEngine::processStrokeMode (guint keyval, guint keycode, guint modifiers) -{ - return TRUE; -} - -inline gboolean -PinyinEngine::processExtensionMode (guint keyval, guint keycode, guint modifiers) -{ - return TRUE; -} - -gboolean -PinyinEngine::processKeyEvent (guint keyval, guint keycode, guint modifiers) -{ - gboolean retval; - - switch (m_input_mode) { - case MODE_INIT: - retval = processInitMode (keyval, keycode, modifiers); - break; - case MODE_RAW: - retval = processRawMode (keyval, keycode, modifiers); - break; - case MODE_ENGLISH: - retval = processEnglishMode (keyval, keycode, modifiers); - break; - case MODE_STROKE: - retval = processStrokeMode (keyval, keycode, modifiers); - break; - case MODE_EXTENSION: - retval = processExtensionMode (keyval, keycode, modifiers); - break; - default: - g_assert_not_reached (); - break; - }; - m_prev_pressed_key = keyval; m_prev_pressed_key_result = retval; return retval; @@ -640,101 +229,43 @@ PinyinEngine::focusIn (void) { /* reset pinyin parser */ if (Config::doublePinyin ()) { - if (dynamic_cast <DoublePinyinEditor *> (m_pinyin_editor) == NULL) - delete m_pinyin_editor; - m_pinyin_editor = new DoublePinyinEditor (); + if (dynamic_cast <DoublePinyinEditor *> (m_editors[MODE_INIT]) == NULL) + delete m_editors[MODE_INIT]; + m_editors[MODE_INIT] = new DoublePinyinEditor (m_props); + connectEditorSignals (m_editors[MODE_INIT]); } else { - if (dynamic_cast <FullPinyinEditor *> (m_pinyin_editor) == NULL) - delete m_pinyin_editor; - m_pinyin_editor = new FullPinyinEditor (); + if (dynamic_cast <FullPinyinEditor *> (m_editors[MODE_INIT]) == NULL) + delete m_editors[MODE_INIT]; + m_editors[MODE_INIT] = new FullPinyinEditor (m_props); + connectEditorSignals (m_editors[MODE_INIT]); } - - ibus_engine_register_properties (m_engine, m_props); + ibus_engine_register_properties (m_engine, m_props.properties ()); } void PinyinEngine::pageUp (void) { - if (m_lookup_table.pageUp ()) { - ibus_engine_update_lookup_table_fast (m_engine, m_lookup_table, TRUE); - updatePreeditText (); - } + m_editors[m_input_mode]->pageUp (); } void PinyinEngine::pageDown (void) { - if (m_lookup_table.pageDown ()) { - ibus_engine_update_lookup_table_fast (m_engine, m_lookup_table, TRUE); - updatePreeditText (); - } + m_editors[m_input_mode]->pageDown (); } void PinyinEngine::cursorUp (void) { - if (m_lookup_table.cursorUp ()) { - ibus_engine_update_lookup_table_fast (m_engine, m_lookup_table, TRUE); - updatePreeditText (); - } + m_editors[m_input_mode]->cursorUp (); } void PinyinEngine::cursorDown (void) { - if (m_lookup_table.cursorDown ()) { - ibus_engine_update_lookup_table_fast (m_engine, m_lookup_table, TRUE); - updatePreeditText (); - } -} - -inline void -PinyinEngine::toggleModeChinese (void) -{ - m_mode_chinese = ! m_mode_chinese; - m_prop_chinese.setLabel (m_mode_chinese ? "CN" : "EN"); - m_prop_chinese.setIcon (m_mode_chinese ? - PKGDATADIR"/icons/chinese.svg" : - PKGDATADIR"/icons/english.svg"); - ibus_engine_update_property (m_engine, m_prop_chinese); - - m_prop_full_punct.setSensitive (m_mode_chinese); - ibus_engine_update_property (m_engine, m_prop_full_punct); -} - -inline void -PinyinEngine::toggleModeFull (void) -{ - m_mode_full = !m_mode_full; - m_prop_full.setLabel (m_mode_full ? "Aa" : "Aa"); - m_prop_full.setIcon (m_mode_full ? - PKGDATADIR"/icons/full.svg" : - PKGDATADIR"/icons/half.svg"); - ibus_engine_update_property (m_engine, m_prop_full); -} - -inline void -PinyinEngine::toggleModeFullPunct (void) -{ - m_mode_full_punct = !m_mode_full_punct; - m_prop_full_punct.setLabel (m_mode_full_punct ? ",。" : ",."); - m_prop_full_punct.setIcon (m_mode_full_punct ? - PKGDATADIR"/icons/full-punct.svg" : - PKGDATADIR"/icons/half-punct.svg"); - ibus_engine_update_property (m_engine, m_prop_full_punct); -} - -inline void -PinyinEngine::toggleModeSimp (void) -{ - m_phrase_editor.setModeSimp(!m_phrase_editor.modeSimp ()); - m_prop_simp.setLabel (m_phrase_editor.modeSimp () ? "简" : "繁"); - m_prop_simp.setIcon (m_phrase_editor.modeSimp () ? - PKGDATADIR"/icons/simp-chinese.svg" : - PKGDATADIR"/icons/trad-chinese.svg"); - ibus_engine_update_property (m_engine, m_prop_simp); + m_editors[m_input_mode]->cursorDown (); } inline void @@ -743,365 +274,157 @@ PinyinEngine::showSetupDialog (void) g_spawn_command_line_async (LIBEXECDIR"/ibus-setup-pinyin", NULL); } -void +gboolean PinyinEngine::propertyActivate (const gchar *prop_name, guint prop_state) { - const static StaticString mode_chinese ("mode.chinese"); - const static StaticString mode_full ("mode.full"); - const static StaticString mode_full_punct ("mode.full_punct"); - const static StaticString mode_simp ("mode.simp"); const static StaticString setup ("setup"); - - if (mode_chinese == prop_name) { - toggleModeChinese (); - } - else if (mode_full == prop_name) { - toggleModeFull (); - } - else if (mode_full_punct == prop_name) { - toggleModeFullPunct (); - } - else if (mode_simp == prop_name) { - toggleModeSimp (); + if (m_props.propertyActivate (prop_name, prop_state)) { + return TRUE; } else if (setup == prop_name) { showSetupDialog (); + return TRUE; } + return FALSE; } void PinyinEngine::candidateClicked (guint index, guint button, guint state) { +#if 0 selectCandidateInPage (index); +#endif } -void -PinyinEngine::updatePreeditText (void) +inline void +PinyinEngine::commit (gchar ch) { - switch (m_input_mode) { - case MODE_INIT: - updatePreeditTextInInitMode (); - break; - case MODE_RAW: - updatePreeditTextInRawMode (); - break; - default: - break; - }; + gchar str[2] = {ch, 0}; + ibus_engine_commit_text (m_engine, StaticText (str)); + m_prev_commited_char = ch; } -void -PinyinEngine::updatePreeditTextInInitMode (void) +inline void +PinyinEngine::commit (gunichar ch) { - /* preedit text = selected phrases + highlight candidate + rest text */ - if (G_UNLIKELY (m_phrase_editor.isEmpty () && m_pinyin_editor->isEmpty ())) { - ibus_engine_hide_preedit_text (m_engine); - return; - } - - if (m_pinyin_editor->cursor () == m_pinyin_editor->text ().length ()) - updatePreeditTextInInitTypingMode (); - else - updatePreeditTextInInitEditingMode (); + ibus_engine_commit_text (m_engine, Text (ch)); + m_prev_commited_char = ch; } -void -PinyinEngine::updatePreeditTextInRawMode (void) +inline void +PinyinEngine::commit (const gchar *str) { - StaticText preedit_text (m_raw_editor); - preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1); - ibus_engine_update_preedit_text (m_engine, preedit_text, m_raw_editor.cursor (), TRUE); + ibus_engine_commit_text (m_engine, StaticText (str)); + m_prev_commited_char = 0; } -void -PinyinEngine::updatePreeditTextInInitTypingMode (void) +inline void +PinyinEngine::commit (const String &str) { - m_buffer.truncate (0); - - /* add select phrases */ - if (G_UNLIKELY (m_phrase_editor.selectedString ())) { - m_buffer << m_phrase_editor.selectedString (); - } - - /* add highlight candidate */ - guint candidate_begin = m_buffer.utf8Length (); - guint candidate_length = 0; - if (m_phrase_editor.candidates ().length () > 0) { - if (m_lookup_table.cursorPos () == 0 && !m_phrase_editor.modeSimp ()) { - const PhraseArray & phrases = m_phrase_editor.candidate0 (); - candidate_length = 0; - for (guint i = 0; i < phrases.length (); i++) { - candidate_length += phrases[i].len; - SimpTradConverter::simpToTrad (phrases[i], m_buffer); - } - } - else { - const Phrase & candidate = m_phrase_editor.candidate (m_lookup_table.cursorPos ()); - candidate_length = candidate.len; - if (m_phrase_editor.modeSimp ()) - m_buffer << candidate; - else - SimpTradConverter::simpToTrad (candidate, m_buffer); - } - } - - /* add rest text */ - const PinyinArray & pinyin = m_phrase_editor.pinyin (); - if (candidate_begin + candidate_length < pinyin.length ()) - m_buffer << ((const gchar *) m_pinyin_editor->textAfterPinyin ( - candidate_begin + candidate_length)); - else - m_buffer << ((const gchar *) m_pinyin_editor->textAfterPinyin ()); - - StaticText preedit_text (m_buffer); - /* underline */ - preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1); - - /* candidate */ - if (candidate_length != 0) { - preedit_text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x00000000, - candidate_begin, candidate_begin + candidate_length); - preedit_text.appendAttribute (IBUS_ATTR_TYPE_BACKGROUND, 0x00c8c8f0, - candidate_begin, candidate_begin + candidate_length); - } - // ibus_engine_update_preedit_text (m_engine, preedit_text, m_buffer.utf8Length (), TRUE); - ibus_engine_update_preedit_text (m_engine, preedit_text, candidate_begin, TRUE); + commit ((const gchar *)str); } void -PinyinEngine::updatePreeditTextInInitEditingMode (void) +PinyinEngine::slotCommitText (Text & text) { - m_buffer.truncate (0); - - /* add select phrases */ - if (G_UNLIKELY (m_phrase_editor.selectedString ())) { - m_buffer << m_phrase_editor.selectedString (); - } - - /* add highlight candidate */ - const PinyinArray & pinyin = m_phrase_editor.pinyin (); - guint candidate_begin = m_buffer.utf8Length (); - guint candidate_length = 0; - guint candidate_pinyin_end = 0; - if (m_phrase_editor.candidates ().length () > 0) { - const Phrase & candidate = m_phrase_editor.candidate (m_lookup_table.cursorPos ()); - candidate_length = candidate.len; - - m_buffer << pinyin[candidate_begin]->sheng << pinyin[candidate_begin]->yun; - - for (guint i = 1; i < candidate_length; i++) { - m_buffer << ' ' << pinyin[candidate_begin + i]->sheng << pinyin[candidate_begin + i]->yun; - } - candidate_pinyin_end = m_buffer.utf8Length (); - } - - /* add rest text */ - if (candidate_begin + candidate_length < pinyin.length ()) { - if (m_buffer) - m_buffer << ' ' ; - m_buffer << m_pinyin_editor->textAfterPinyin (candidate_begin + candidate_length); - } - else { - const gchar * p = m_pinyin_editor->textAfterPinyin (); - if (*p != '\0') { - if (m_buffer) - m_buffer << ' '; - m_buffer << p; - } - } - - StaticText preedit_text (m_buffer); - /* underline */ - preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1); - - /* candidate */ - if (candidate_length != 0) { - preedit_text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x00000000, - candidate_begin, candidate_pinyin_end); - preedit_text.appendAttribute (IBUS_ATTR_TYPE_BACKGROUND, 0x00c8c8f0, - candidate_begin, candidate_pinyin_end); - } - // ibus_engine_update_preedit_text (m_engine, preedit_text, m_buffer.utf8Length (), TRUE); - ibus_engine_update_preedit_text (m_engine, preedit_text, candidate_begin, TRUE); + ibus_engine_commit_text (m_engine, text); } void -PinyinEngine::updateAuxiliaryText (void) +PinyinEngine::slotUpdatePreeditText (Text & text, guint cursor, gboolean visible) { - - /* clear pinyin array */ - if (G_UNLIKELY (isEmpty () || - m_phrase_editor.cursor () == m_pinyin_editor->pinyin ().length ())) { - ibus_engine_hide_auxiliary_text (m_engine); - return; - } - - // guint cursor_pos; - - m_buffer.truncate (0); - for (guint i = m_phrase_editor.cursor (); i < m_pinyin_editor->pinyin().length (); ++i) { - if (G_LIKELY (i != m_phrase_editor.cursor ())) - m_buffer << ' '; - const Pinyin *p = m_pinyin_editor->pinyin()[i]; - m_buffer << p->sheng; - m_buffer << p->yun; - } - - if (G_UNLIKELY (m_pinyin_editor->pinyinLength () == m_pinyin_editor->cursor ())) { - /* aux = pinyin + non-pinyin */ - // cursor_pos = m_buffer.utf8Length (); - m_buffer << '|' << m_pinyin_editor->textAfterPinyin (); - } - else { - /* aux = pinyin + non-pinyin before cursor + non-pinyin after cursor */ - m_buffer.append (m_pinyin_editor->textAfterPinyin (), - m_pinyin_editor->cursor () - m_pinyin_editor->pinyinLength ()); - // cursor_pos = m_buffer.utf8Length (); - m_buffer << '|' << m_pinyin_editor->textAfterCursor (); - } - - StaticText aux_text (m_buffer); - ibus_engine_update_auxiliary_text (m_engine, aux_text, TRUE); + ibus_engine_update_preedit_text (m_engine, text, cursor, visible); } void -PinyinEngine::updateLookupTable (void) +PinyinEngine::slotShowPreeditText (void) { - m_lookup_table.clear (); - m_lookup_table.setPageSize (Config::pageSize ()); - - guint candidate_nr = m_phrase_editor.candidates ().length (); - - if (G_UNLIKELY (candidate_nr == 0)) { - ibus_engine_hide_lookup_table (m_engine); - return; - } - - if (G_LIKELY (m_phrase_editor.modeSimp () || !Config::tradCandidate ())) { - for (guint i = 0; i < candidate_nr; i++) { - StaticText text (m_phrase_editor.candidate (i)); - if (m_phrase_editor.candidateIsUserPhease (i)) - text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1); - m_lookup_table.appendCandidate (text); - } - } - else { - for (guint i = 0; i < candidate_nr; i++) { - m_buffer.truncate (0); - SimpTradConverter::simpToTrad (m_phrase_editor.candidate (i), m_buffer); - Text text (m_buffer); - if (m_phrase_editor.candidateIsUserPhease (i)) - text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x000000ef, 0, -1); - m_lookup_table.appendCandidate (text); - } - } - ibus_engine_update_lookup_table_fast (m_engine, - m_lookup_table, - TRUE); + ibus_engine_show_preedit_text (m_engine); } void -PinyinEngine::updatePhraseEditor (void) +PinyinEngine::slotHidePreeditText (void) { - m_phrase_editor.update (m_pinyin_editor->pinyin ()); + ibus_engine_hide_preedit_text (m_engine); } -inline void -PinyinEngine::commit (gchar ch) +void +PinyinEngine::slotUpdateAuxiliaryText (Text & text, gboolean visible) { - gchar str[2] = {ch, 0}; - ibus_engine_commit_text (m_engine, StaticText (str)); - m_prev_commited_char = ch; + ibus_engine_update_auxiliary_text (m_engine, text, visible); } -inline void -PinyinEngine::commit (gunichar ch) +void +PinyinEngine::slotShowAuxiliaryText (void) { - ibus_engine_commit_text (m_engine, Text (ch)); - m_prev_commited_char = ch; + ibus_engine_show_auxiliary_text (m_engine); } -inline void -PinyinEngine::commit (const gchar *str) +void +PinyinEngine::slotHideAuxiliaryText (void) { - ibus_engine_commit_text (m_engine, StaticText (str)); - m_prev_commited_char = 0; + ibus_engine_hide_auxiliary_text (m_engine); } -inline void -PinyinEngine::commit (const String &str) +void +PinyinEngine::slotUpdateLookupTable (LookupTable & table, gboolean visible) { - commit ((const gchar *)str); + ibus_engine_update_lookup_table (m_engine, table, visible); } -inline void -PinyinEngine::commit (void) +void +PinyinEngine::slotUpdateLookupTableFast (LookupTable & table, gboolean visible) { - if (G_UNLIKELY (m_pinyin_editor->isEmpty ())) - return; - - m_buffer.truncate (0); - m_buffer << m_phrase_editor.selectedString (); - - const gchar *p = m_pinyin_editor->textAfterPinyin (m_buffer.utf8Length ()); - if (G_UNLIKELY (m_mode_full)) { - while (*p != '\0') { - m_buffer.appendUnichar (HalfFullConverter::toFull (*p++)); - } - } - else { - m_buffer << p; - } - m_phrase_editor.commit (); - reset (); - commit ((const gchar *)m_buffer); + ibus_engine_update_lookup_table_fast (m_engine, table, visible); } -inline gboolean -PinyinEngine::selectCandidate (guint i) +void +PinyinEngine::slotShowLookupTable (void) { - if (m_phrase_editor.selectCandidate (i)) { - if ((!m_phrase_editor.pinyinExistsAfterCursor ()) && - *m_pinyin_editor->textAfterPinyin () == '\0') { - commit (); - } - else - updateUI (); - return TRUE; - } - return FALSE; + ibus_engine_show_lookup_table (m_engine); } -inline gboolean -PinyinEngine::selectCandidateInPage (guint i) +void +PinyinEngine::slotHideLookupTable (void) { - guint page_size = m_lookup_table.pageSize (); - guint cursor_pos = m_lookup_table.cursorPos (); - - if (G_UNLIKELY (i >= page_size)) - return FALSE; - i += (cursor_pos / page_size) * page_size; - - return selectCandidate (i); + ibus_engine_hide_lookup_table (m_engine); } -inline gboolean -PinyinEngine::resetCandidate (guint i) +void +PinyinEngine::slotUpdateProperty (Property & prop) { - if (m_phrase_editor.resetCandidate (i)) { - updateUI (); - } - return TRUE; + ibus_engine_update_property (m_engine, prop); } -inline gboolean -PinyinEngine::resetCandidateInPage (guint i) -{ - guint page_size = m_lookup_table.pageSize (); - guint cursor_pos = m_lookup_table.cursorPos (); - i += (cursor_pos / page_size) * page_size; +void +PinyinEngine::connectEditorSignals (Editor *editor) +{ + editor->signalCommitText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotCommitText)); + + editor->signalUpdatePreeditText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotUpdatePreeditText)); + editor->signalShowPreeditText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotShowPreeditText)); + editor->signalHidePreeditText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotHidePreeditText)); + + editor->signalUpdateAuxiliaryText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotUpdateAuxiliaryText)); + editor->signalShowAuxiliaryText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotShowAuxiliaryText)); + editor->signalHideAuxiliaryText ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotHideAuxiliaryText)); + + editor->signalUpdateLookupTable ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotUpdateLookupTable)); + editor->signalUpdateLookupTableFast ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotUpdateLookupTableFast)); + editor->signalShowLookupTable ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotShowLookupTable)); + editor->signalHideLookupTable ().connect ( + sigc::mem_fun (*this, &PinyinEngine::slotHideLookupTable)); - return resetCandidate (i); } }; diff --git a/src/PinyinEngine.h b/src/PinyinEngine.h index 2f89dad..1618888 100644 --- a/src/PinyinEngine.h +++ b/src/PinyinEngine.h @@ -11,6 +11,8 @@ #include "LookupTable.h" #include "Property.h" #include "Config.h" +#include "Editor.h" +#include "PinyinProperties.h" namespace PY { @@ -28,9 +30,9 @@ public: void reset (gboolean need_update = TRUE) { resetQuote (); m_input_mode = MODE_INIT; - m_pinyin_editor->reset (); - m_phrase_editor.reset (); - m_raw_editor.reset (); + for (gint i = 0; i < MODE_LAST; i++) { + m_editors[i]->reset (); + } updateUI (need_update); } @@ -46,10 +48,11 @@ public: void cursorUp (void); void cursorDown (void); - void propertyActivate (const gchar *prop_name, guint prop_state); + gboolean propertyActivate (const gchar *prop_name, guint prop_state); void candidateClicked (guint index, guint button, guint state); void updateUI (gboolean now = TRUE) { + #if 0 if (G_UNLIKELY (now || m_need_update >= 4)) { updateLookupTable (); updateAuxiliaryText (); @@ -61,23 +64,21 @@ public: } m_need_update ++; } + #endif } private: - gboolean processInitMode (guint keyval, guint keycode, guint modifiers); - gboolean processRawMode (guint keyval, guint keycode, guint modifiers); - gboolean processEnglishMode (guint keyval, guint keycode, guint modifiers); - gboolean processStrokeMode (guint keyval, guint keycode, guint modifiers); - gboolean processExtensionMode (guint keyval, guint keycode, guint modifiers); + gboolean processPunct (guint keyval, guint keycode, guint modifiers); +#if 0 gboolean processPinyin (guint keyval, guint keycode, guint modifiers); gboolean processCapitalLetter (guint keyval, guint keycode, guint modifiers); gboolean processNumber (guint keyval, guint keycode, guint modifiers); - gboolean processPunct (guint keyval, guint keycode, guint modifiers); gboolean processSpace (guint keyval, guint keycode, guint modifiers); gboolean processOthers (guint keyval, guint keycode, guint modifiers); +#endif private: - gboolean isEmpty (void) { return m_pinyin_editor->isEmpty (); } + // gboolean isEmpty (void) { return m_pinyin_editor->isEmpty (); } void commit (void); void commit (gchar ch); @@ -91,18 +92,6 @@ private: void toggleModeSimp (void); void showSetupDialog (void); - gboolean selectCandidate (guint i); - gboolean selectCandidateInPage (guint i); - gboolean resetCandidate (guint i); - gboolean resetCandidateInPage (guint i); - void updatePreeditText (void); - void updatePreeditTextInInitMode (void); - void updatePreeditTextInRawMode (void); - void updatePreeditTextInInitEditingMode (void); - void updatePreeditTextInInitTypingMode (void); - void updateAuxiliaryText (void); - void updateLookupTable (void); - void updatePhraseEditor (void); static gboolean delayUpdateUIHandler (PinyinEngine *pinyin) { if (pinyin->m_need_update > 0) @@ -110,26 +99,31 @@ private: return FALSE; } + void connectEditorSignals (Editor *editor); + +private: + void slotCommitText (Text & text); + void slotUpdatePreeditText (Text & text, guint cursor, gboolean visible); + void slotShowPreeditText (void); + void slotHidePreeditText (void); + void slotUpdateAuxiliaryText (Text & text, gboolean visible); + void slotShowAuxiliaryText (void); + void slotHideAuxiliaryText (void); + void slotUpdateLookupTable (LookupTable &table, gboolean visible); + void slotUpdateLookupTableFast (LookupTable &table, gboolean visible); + void slotShowLookupTable (void); + void slotHideLookupTable (void); + void slotUpdateProperty (Property & prop); + private: Pointer<IBusEngine> m_engine; // engine pointer - PinyinEditor *m_pinyin_editor; // pinyin editor - PhraseEditor m_phrase_editor; // phrase editor String m_buffer; // string buffer gint m_need_update; // need update preedit, aux, or lookup table LookupTable m_lookup_table; - Property m_prop_chinese; - Property m_prop_full; - Property m_prop_full_punct; - Property m_prop_simp; - Property m_prop_setup; - PropList m_props; - - gboolean m_mode_chinese; - gboolean m_mode_full; - gboolean m_mode_full_punct; + PinyinProperties m_props; gboolean m_quote; gboolean m_double_quote; @@ -144,9 +138,10 @@ private: MODE_ENGLISH, // press v into English input mode MODE_STROKE, // press u into stroke input mode MODE_EXTENSION, // press i into extension input mode + MODE_LAST, } m_input_mode; - RawEditor m_raw_editor; + Editor *m_editors[MODE_LAST]; }; }; diff --git a/src/PinyinProperties.cc b/src/PinyinProperties.cc new file mode 100644 index 0000000..c8ceb0e --- /dev/null +++ b/src/PinyinProperties.cc @@ -0,0 +1,152 @@ +#include "Util.h" +#include "PinyinProperties.h" + +namespace PY { + +PinyinProperties::PinyinProperties (void) + : m_mode_chinese (Config::initChinese ()), + m_mode_full (Config::initFull ()), + m_mode_full_punct (Config::initFullPunct ()), + m_mode_simp (Config::initSimpChinese ()) +{ + /* create properties */ + m_prop_chinese = ibus_property_new ("mode.chinese", + PROP_TYPE_NORMAL, + StaticText ("CN"), + m_mode_chinese ? + PKGDATADIR"/icons/chinese.svg" : + PKGDATADIR"/icons/english.svg", + StaticText (_("Chinese")), + TRUE, + TRUE, + PROP_STATE_UNCHECKED, + NULL); + m_props.append (m_prop_chinese); + + m_prop_full = ibus_property_new ("mode.full", + PROP_TYPE_NORMAL, + StaticText (m_mode_full? "Aa" : "Aa"), + m_mode_full ? + PKGDATADIR"/icons/full.svg" : + PKGDATADIR"/icons/half.svg", + StaticText (_("Full/Half width")), + TRUE, + TRUE, + PROP_STATE_UNCHECKED, + NULL); + m_props.append (m_prop_full); + + m_prop_full_punct = ibus_property_new ("mode.full_punct", + PROP_TYPE_NORMAL, + StaticText (m_mode_full_punct ? ",。" : ",."), + m_mode_full_punct ? + PKGDATADIR"/icons/full-punct.svg" : + PKGDATADIR"/icons/half-punct.svg", + StaticText (_("Full/Half width punctuation")), + TRUE, + TRUE, + PROP_STATE_UNCHECKED, + NULL); + m_props.append (m_prop_full_punct); + + m_prop_simp = ibus_property_new ("mode.simp", + PROP_TYPE_NORMAL, + StaticText (m_mode_simp ? "简" : "繁"), + m_mode_simp ? + PKGDATADIR"/icons/simp-chinese.svg" : + PKGDATADIR"/icons/trad-chinese.svg", + StaticText (_("Simplfied/Traditional Chinese")), + TRUE, + TRUE, + PROP_STATE_UNCHECKED, + NULL); + m_props.append (m_prop_simp); + + m_prop_setup = ibus_property_new ("setup", + PROP_TYPE_NORMAL, + StaticText (_("Pinyin preferences")), + "ibus-setup", + StaticText (_("Pinyin preferences")), + TRUE, + TRUE, + PROP_STATE_UNCHECKED, + NULL); + m_props.append (m_prop_setup); + +} + +void +PinyinProperties::toggleModeChinese (void) +{ + m_mode_chinese = ! m_mode_chinese; + m_prop_chinese.setLabel (m_mode_chinese ? "CN" : "EN"); + m_prop_chinese.setIcon (m_mode_chinese ? + PKGDATADIR"/icons/chinese.svg" : + PKGDATADIR"/icons/english.svg"); + updateProperty (m_prop_chinese); + + m_prop_full_punct.setSensitive (m_mode_chinese); + updateProperty (m_prop_full_punct); +} + +void +PinyinProperties::toggleModeFull (void) +{ + m_mode_full = !m_mode_full; + m_prop_full.setLabel (m_mode_full ? "Aa" : "Aa"); + m_prop_full.setIcon (m_mode_full ? + PKGDATADIR"/icons/full.svg" : + PKGDATADIR"/icons/half.svg"); + updateProperty (m_prop_full); +} + +void +PinyinProperties::toggleModeFullPunct (void) +{ + m_mode_full_punct = !m_mode_full_punct; + m_prop_full_punct.setLabel (m_mode_full_punct ? ",。" : ",."); + m_prop_full_punct.setIcon (m_mode_full_punct ? + PKGDATADIR"/icons/full-punct.svg" : + PKGDATADIR"/icons/half-punct.svg"); + updateProperty (m_prop_full_punct); +} + +void +PinyinProperties::toggleModeSimp (void) +{ + m_mode_simp = ! m_mode_simp; + m_prop_simp.setLabel (m_mode_simp ? "简" : "繁"); + m_prop_simp.setIcon (m_mode_simp ? + PKGDATADIR"/icons/simp-chinese.svg" : + PKGDATADIR"/icons/trad-chinese.svg"); + updateProperty (m_prop_simp); +} + +gboolean +PinyinProperties::propertyActivate (const gchar *prop_name, guint prop_state) { + const static StaticString mode_chinese ("mode.chinese"); + const static StaticString mode_full ("mode.full"); + const static StaticString mode_full_punct ("mode.full_punct"); + const static StaticString mode_simp ("mode.simp"); + + if (mode_chinese == prop_name) { + toggleModeChinese (); + return TRUE; + } + else if (mode_full == prop_name) { + toggleModeFull (); + return TRUE; + } + else if (mode_full_punct == prop_name) { + toggleModeFullPunct (); + return TRUE; + } + else if (mode_simp == prop_name) { + toggleModeSimp (); + return TRUE; + } + return FALSE; +} + + +}; diff --git a/src/PinyinProperties.h b/src/PinyinProperties.h new file mode 100644 index 0000000..0466ee8 --- /dev/null +++ b/src/PinyinProperties.h @@ -0,0 +1,58 @@ +#ifndef __PY_PINYIN_PROPERTIES_H_ +#define __PY_PINYIN_PROPERTIES_H_ + +#include <ibus.h> +#include <sigc++/sigc++.h> +#include <libintl.h> +#include "Text.h" +#include "Property.h" +#include "Config.h" + +#define _(text) (dgettext (GETTEXT_PACKAGE, text)) + +namespace PY { + +class PinyinProperties { +public: + PinyinProperties (void); + + void toggleModeChinese (void); + void toggleModeFull (void); + void toggleModeFullPunct (void); + void toggleModeSimp (void); + gboolean propertyActivate (const gchar *prop_name, guint prop_state); + + gboolean modeChinese (void) { return m_mode_chinese; } + gboolean modeFull (void) { return m_mode_full; } + gboolean modeFullPunct (void) { return m_mode_full_punct; } + gboolean modeSimp (void) { return m_mode_simp; } + PropList & properties (void) { return m_props; } + + sigc::signal<void, Property &> signalUpdateProperty (void) { + return m_signal_update_property; + } + +private: + sigc::signal<void, Property &> m_signal_update_property; + void updateProperty (Property & prop) { + m_signal_update_property.emit (prop); + } + +private: + gboolean m_mode_chinese; + gboolean m_mode_full; + gboolean m_mode_full_punct; + gboolean m_mode_simp; + + /* properties */ + Property m_prop_chinese; + Property m_prop_full; + Property m_prop_full_punct; + Property m_prop_simp; + Property m_prop_setup; + PropList m_props; +}; + +}; + +#endif diff --git a/src/RawEditor.h b/src/RawEditor.h index e6e3885..38a158f 100644 --- a/src/RawEditor.h +++ b/src/RawEditor.h @@ -3,83 +3,13 @@ #define __PY_RAW_EDITOR__ #include <glib.h> -#include "String.h" +#include "Editor.h" namespace PY { -class RawEditor { +class RawEditor : public Editor { public: - RawEditor () : m_cursor (0) {} - - gboolean processKeyEvent (guint keyval, guint keycode, guint modifiers, gboolean &update) { - update = FALSE; - if (modifiers & IBUS_RELEASE_MASK) - return FALSE; - - modifiers &= (IBUS_SHIFT_MASK | - IBUS_CONTROL_MASK | - IBUS_MOD1_MASK | - IBUS_SUPER_MASK | - IBUS_HYPER_MASK | - IBUS_META_MASK); - if (modifiers != 0) - return TRUE; - - switch (keyval) { - case IBUS_exclam ... IBUS_asciitilde: - m_text.insert (m_cursor++, keyval); - update = TRUE; - return TRUE; - case IBUS_BackSpace: - if (m_cursor > 0) { - m_text.erase (--m_cursor, 1); - update = TRUE; - return TRUE; - } - return FALSE; - case IBUS_Delete: - case IBUS_KP_Delete: - if (m_cursor < m_text.length ()) { - m_text.erase (m_cursor, 1); - update = TRUE; - return TRUE; - } - return FALSE; - case IBUS_Left: - case IBUS_KP_Left: - if (m_cursor > 0) { - m_cursor --; - update = TRUE; - return TRUE; - } - return FALSE; - case IBUS_Right: - case IBUS_KP_Right: - if (m_cursor < m_text.length ()) { - m_cursor ++; - update = TRUE; - return TRUE; - } - return FALSE; - default: - break; - } - return TRUE; - } - - void reset (void) { - m_text.truncate (0); - m_cursor = 0; - } - - guint cursor (void) const { return m_cursor; } - operator const String & (void) const { - return m_text; - } - -protected: - String m_text; - guint m_cursor; + RawEditor (PinyinProperties &props) : Editor (props) {} }; }; @@ -3,6 +3,7 @@ #include <ibus.h> #include "Pointer.h" +#include "String.h" namespace PY { |