Refactory classes, and use sigc++ to make code clear.
authorPeng Huang <shawn.p.huang@gmail.com>
Sun, 7 Feb 2010 05:25:35 +0000 (13:25 +0800)
committerPeng Huang <shawn.p.huang@gmail.com>
Wed, 10 Feb 2010 03:18:02 +0000 (11:18 +0800)
19 files changed:
configure.ac
src/DoublePinyinEditor.cc
src/DoublePinyinEditor.h
src/Editor.cc [new file with mode: 0644]
src/Editor.h [new file with mode: 0644]
src/FullPinyinEditor.cc
src/FullPinyinEditor.h
src/Makefile.am
src/PhraseEditor.cc
src/PhraseEditor.h
src/PinyinArray.h
src/PinyinEditor.cc
src/PinyinEditor.h
src/PinyinEngine.cc
src/PinyinEngine.h
src/PinyinProperties.cc [new file with mode: 0644]
src/PinyinProperties.h [new file with mode: 0644]
src/RawEditor.h
src/Text.h

index 6825d8a..af39dfd 100644 (file)
@@ -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)
index 1bbfff1..6970815 100644 (file)
@@ -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);
index 75d419a..69cd980 100644 (file)
@@ -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 (file)
index 0000000..86aae8c
--- /dev/null
@@ -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 (file)
index 0000000..205cba7
--- /dev/null
@@ -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
index f1de074..ede35a9 100644 (file)
@@ -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
 
+};
index 81e6e1f..9e95fa8 100644 (file)
@@ -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);
 
index dfcc153..08f578c 100644 (file)
 
 # @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 = \
index 6248ab5..81bf249 100644 (file)
@@ -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);
index b7051c0..313d408 100644 (file)
@@ -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;
index a3c1bba..c02c5d6 100644 (file)
@@ -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));
     }
index fd4760c..3992db8 100644 (file)
 #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);
 }
 
 };
index 457e07f..1b7d609 100644 (file)
@@ -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
index dd8d6ec..8c0e25c 100644 (file)
@@ -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);
 }
 
 };
index 2f89dad..1618888 100644 (file)
@@ -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 (file)
index 0000000..c8ceb0e
--- /dev/null
@@ -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 (file)
index 0000000..0466ee8
--- /dev/null
@@ -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
index e6e3885..38a158f 100644 (file)
@@ -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) {}
 };
 
 };
index 91f5e99..35a0b8b 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <ibus.h>
 #include "Pointer.h"
+#include "String.h"
 
 namespace PY {