implement PunctEditor for BopomofoEngine
authorBYVoid <byvoid1@gmail.com>
Sun, 16 May 2010 15:02:07 +0000 (23:02 +0800)
committerBYVoid <byvoid1@gmail.com>
Sun, 16 May 2010 15:02:07 +0000 (23:02 +0800)
src/BopomofoEditor.cc
src/PunctEditor.cc
src/PunctEditor.h

index a93fe24..684180f 100644 (file)
@@ -15,9 +15,9 @@
 namespace PY {
 
 BopomofoEditor::BopomofoEditor (PinyinProperties & props)
-    : PinyinEditor (props)
+    : PinyinEditor (props),
+      m_select_mode (FALSE)
 {
-    m_select_mode = FALSE;
 }
 
 BopomofoEditor::~BopomofoEditor (void)
@@ -251,7 +251,6 @@ BopomofoEditor::processNumber (guint keyval, guint keycode, guint modifiers)
         break;
     default:
         return FALSE;
-        g_return_val_if_reached (FALSE);
     }
 
     selectCandidateInPage (i);
@@ -303,7 +302,7 @@ BopomofoEditor::processNumberWithShift (guint keyval, guint keycode, guint modif
         i = 9;
         break;
     default:
-        g_return_val_if_reached (FALSE);
+        return FALSE;
     }
 
     selectCandidateInPage (i);
@@ -317,6 +316,9 @@ BopomofoEditor::processBopomofo (guint keyval, guint keycode, guint modifiers)
     if (G_UNLIKELY (CMSHM_FILTER(modifiers) != 0))
         return m_text ? TRUE : FALSE;
 
+    if (keyvalToBopomofo(keyval) == BOPOMOFO_ZERO)
+        return FALSE;
+
     m_select_mode = FALSE;
 
     return insert (keyval);
@@ -333,36 +335,18 @@ BopomofoEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
                   IBUS_META_MASK |
                   IBUS_LOCK_MASK);
 
-    if (m_select_mode == TRUE && keyval >= IBUS_0 && keyval <= IBUS_9) {
-        return processNumber (keyval, keycode, modifiers);
-    }
+    if (m_select_mode == TRUE && processNumber (keyval, keycode, modifiers) == TRUE)
+        return TRUE;
+    if (processNumberWithShift (keyval, keycode, modifiers) == TRUE)
+        return TRUE;
+    if (processBopomofo(keyval, keycode ,modifiers))
+        return TRUE;
 
     switch (keyval) {
     /* Bopomofo */
-    case IBUS_a ... IBUS_z:
-    case IBUS_0 ... IBUS_9:
-    case IBUS_comma:
-    case IBUS_period:
-    case IBUS_slash:
-    case IBUS_semicolon:
-    case IBUS_minus:
-        return processBopomofo (keyval, keycode, modifiers);
-
     case IBUS_KP_0 ... IBUS_KP_9:
         return processNumber (keyval, keycode, modifiers);
 
-    case IBUS_exclam:
-    case IBUS_at:
-    case IBUS_numbersign:
-    case IBUS_dollar:
-    case IBUS_percent:
-    case IBUS_asciicircum:
-    case IBUS_ampersand:
-    case IBUS_asterisk:
-    case IBUS_parenleft:
-    case IBUS_parenright:
-        return processNumberWithShift (keyval, keycode, modifiers);
-
     case IBUS_space:
         m_select_mode = TRUE;
         return processSpace (keyval, keycode, modifiers);
index 45c7dba..5304b74 100644 (file)
 #include "PunctEditor.h"
+#include <cstdio>
+
+#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))
 
 namespace PY {
 
 #include "PunctTable.h"
 
 PunctEditor::PunctEditor (PinyinProperties & props)
-    : Editor (props)
+    : Editor (props),
+      m_punct_mode (FALSE),
+      m_lookup_table (Config::pageSize ())
+{
+}
+
+gboolean
+PunctEditor::insert (gchar ch)
 {
+    m_text.insert (m_cursor++, ch);
+    update ();
+    return TRUE;
+}
+
+inline gboolean
+PunctEditor::processSpace (guint keyval, guint keycode, guint modifiers)
+{
+    if (!m_text)
+        return FALSE;
+    if (CMSHM_FILTER (modifiers) != 0)
+        return TRUE;
+    if (m_lookup_table.size () != 0) {
+        selectCandidate (m_lookup_table.cursorPos ());
+    }
+    else {
+        commit ();
+    }
+    return TRUE;
+}
+
+gboolean
+PunctEditor::processPunct (guint keyval, guint keycode, guint modifiers)
+{
+    if (CMSHM_FILTER (modifiers) != 0)
+        return TRUE;
+
+    if (m_punct_mode == FALSE) {
+        if (keyval == IBUS_grave) {
+            m_punct_mode = TRUE;
+            return insert('`');
+        }
+        return FALSE;
+    }
+
+    switch (keyval) {
+    case IBUS_grave:        /* ` */
+    case IBUS_asciitilde:   /* ~ */
+    case IBUS_exclam:       /* ~ */
+    case IBUS_at:           /* @ */
+    case IBUS_numbersign:   /* # */
+    case IBUS_dollar:       /* $ */
+    case IBUS_percent:      /* % */
+    case IBUS_asciicircum:  /* ^ */
+    case IBUS_ampersand:    /* & */
+    case IBUS_asterisk:     /* * */
+    case IBUS_parenleft:    /* ( */
+    case IBUS_parenright:   /* ) */
+    case IBUS_minus:        /* - */
+    case IBUS_underscore:   /* _ */
+    case IBUS_equal:        /* = */
+    case IBUS_plus:         /* + */
+    case IBUS_bracketleft:  /* [ */
+    case IBUS_bracketright: /* ] */
+    case IBUS_braceleft:    /* { */
+    case IBUS_braceright:   /* } */
+    case IBUS_backslash:    /* \ */
+    case IBUS_bar:          /* | */
+    case IBUS_colon:        /* : */
+    case IBUS_semicolon:    /* ; */
+    case IBUS_apostrophe:   /* ' */
+    case IBUS_quotedbl:     /* " */
+    case IBUS_comma:        /* , */
+    case IBUS_period:       /* . */
+    case IBUS_less:         /* < */
+    case IBUS_greater:      /* > */
+    case IBUS_slash:        /* / */
+    case IBUS_question:     /* ? */
+    case IBUS_0...IBUS_9:
+    case IBUS_a...IBUS_z:
+    case IBUS_A...IBUS_Z:
+        return insert(keyval);
+    default:
+        return FALSE;
+    }
 }
 
 gboolean
 PunctEditor::processKeyEvent (guint keyval, guint keycode, guint modifiers)
 {
-    return Editor::processKeyEvent (keyval, keycode, modifiers);
+    modifiers &= (IBUS_SHIFT_MASK |
+                  IBUS_CONTROL_MASK |
+                  IBUS_MOD1_MASK |
+                  IBUS_SUPER_MASK |
+                  IBUS_HYPER_MASK |
+                  IBUS_META_MASK |
+                  IBUS_LOCK_MASK);
+
+    printf("%d %d %d\n",keyval,keycode,modifiers);
+
+    if (processPunct(keyval,keycode,modifiers) == TRUE)
+        return TRUE;
+
+    switch (keyval) {
+    case IBUS_space:
+        return processSpace (keyval, keycode, modifiers);
+
+    case IBUS_Return:
+    case IBUS_KP_Enter:
+        commit ();
+        return TRUE;
+
+    case IBUS_BackSpace:
+        removeCharBefore ();
+        return TRUE;
+
+    case IBUS_Delete:
+    case IBUS_KP_Delete:
+        removeCharAfter ();
+        return TRUE;
+
+    case IBUS_Left:
+    case IBUS_KP_Left:
+        moveCursorLeft ();
+        return TRUE;
+
+    case IBUS_Right:
+    case IBUS_KP_Right:
+        moveCursorRight ();
+        return TRUE;
+
+    case IBUS_Home:
+    case IBUS_KP_Home:
+        moveCursorToBegin ();
+        return TRUE;
+
+    case IBUS_End:
+    case IBUS_KP_End:
+        moveCursorToEnd ();
+        return TRUE;
+
+    case IBUS_Up:
+    case IBUS_KP_Up:
+        cursorUp ();
+        return TRUE;
+
+    case IBUS_Down:
+    case IBUS_KP_Down:
+        cursorDown ();
+        return TRUE;
+
+    case IBUS_Page_Up:
+    case IBUS_KP_Page_Up:
+        pageUp ();
+        return TRUE;
+
+    case IBUS_Page_Down:
+    case IBUS_KP_Page_Down:
+    case IBUS_Tab:
+        pageDown ();
+        return TRUE;
+    default:
+        return Editor::processKeyEvent (keyval, keycode, modifiers);
+    }
 }
 
 void
 PunctEditor::pageUp (void)
 {
-    Editor::pageUp ();
+    if (G_LIKELY (m_lookup_table.pageUp ())) {
+        updateLookupTableFast (m_lookup_table, TRUE);
+        updatePreeditText ();
+        updateAuxiliaryText ();
+    }
 }
 
 void
 PunctEditor::pageDown (void)
 {
-    Editor::pageDown ();
+    if (G_LIKELY(
+            (m_lookup_table.pageDown ()) ||
+            (fillLookupTableByPage () && m_lookup_table.pageDown ()))) {
+        updateLookupTableFast (m_lookup_table, TRUE);
+        updatePreeditText ();
+        updateAuxiliaryText ();
+    }
 }
 
 void
 PunctEditor::cursorUp (void)
 {
-    Editor::cursorUp ();
+    if (G_LIKELY (m_lookup_table.cursorUp ())) {
+        updateLookupTableFast (m_lookup_table, TRUE);
+        updatePreeditText ();
+        updateAuxiliaryText ();
+    }
 }
 
 void
 PunctEditor::cursorDown (void)
 {
-    Editor::cursorDown ();
+    if (G_LIKELY (
+            (m_lookup_table.cursorPos () == m_lookup_table.size () - 1) &&
+            (fillLookupTableByPage () == FALSE))) {
+        return;
+    }
+
+    if (G_LIKELY (m_lookup_table.cursorDown ())) {
+        updateLookupTableFast (m_lookup_table, TRUE);
+        updatePreeditText ();
+        updateAuxiliaryText ();
+    }
 }
 
-void
-PunctEditor::update (void)
+gboolean
+PunctEditor::moveCursorLeft (void)
+{
+    if (G_UNLIKELY (m_cursor == 0))
+        return FALSE;
+    m_cursor --;
+    update();
+    return TRUE;
+}
+
+gboolean
+PunctEditor::moveCursorRight (void)
 {
-    Editor::update ();
+    if (G_UNLIKELY (m_cursor == m_text.length ()))
+        return FALSE;
+    m_cursor ++;
+    update();
+    return TRUE;
+}
+
+gboolean
+PunctEditor::moveCursorToBegin (void)
+{
+    if (G_UNLIKELY (m_cursor == 0))
+        return FALSE;
+    m_cursor = 0;
+    update ();
+    return TRUE;
+}
+
+gboolean
+PunctEditor::moveCursorToEnd (void)
+{
+    if (G_UNLIKELY (m_cursor == m_text.length ()))
+        return FALSE;
+    m_cursor = m_text.length ();
+    update();
+    return TRUE;
+}
+
+gboolean
+PunctEditor::removeCharBefore (void)
+{
+    if (G_UNLIKELY (m_cursor == 0))
+        return FALSE;
+
+    m_cursor --;
+    m_text.erase (m_cursor, 1);
+    if (m_text.empty())
+        m_punct_mode = FALSE;
+
+    update();
+
+    return TRUE;
+}
+
+gboolean
+PunctEditor::removeCharAfter (void)
+{
+    if (G_UNLIKELY (m_cursor == m_text.length ()))
+        return FALSE;
+
+    m_text.erase (m_cursor, 1);
+    if (m_text.empty())
+        m_punct_mode = FALSE;
+
+    update();
+
+    return TRUE;
 }
 
 void
 PunctEditor::reset (void)
 {
+    m_punct_mode = FALSE;
     Editor::reset ();
 }
 
 void
 PunctEditor::candidateClicked (guint index, guint button, guint state)
 {
-    Editor::candidateClicked (index, button, state);
+    selectCandidateInPage(index);
+}
+
+inline void
+PunctEditor::commit (const gchar *str)
+{
+    StaticText text(str);
+    commitText (text);
+}
+
+void
+PunctEditor::commit (void)
+{
+    commit ((const gchar *)m_text);
+    reset();
+}
+
+inline gboolean
+PunctEditor::selectCandidate (guint i)
+{
+    m_buffer.clear ();
+    m_buffer << m_punct_candidates[i];
+    reset();
+    commit ((const gchar *) m_buffer);
+    return FALSE;
+}
+
+inline gboolean
+PunctEditor::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);
+}
+
+void
+PunctEditor::update (void)
+{
+    updateLookupTable ();
+    updatePreeditText ();
+    updateAuxiliaryText ();
+}
+
+void
+PunctEditor::updateLookupTable (void)
+{
+    m_lookup_table.clear ();
+    m_lookup_table.setPageSize (Config::pageSize ());
+    m_lookup_table.setOrientation (Config::orientation ());
+
+    fillLookupTableByPage ();
+    if (m_lookup_table.size ()) {
+        Editor::updateLookupTable (m_lookup_table, TRUE);
+    }
+    else {
+        hideLookupTable ();
+    }
+}
+
+static int
+punct_cmp (const void *p1, const void *p2)
+{
+    const gchar *s1 = (gchar *) p1;
+    const gchar *s2 = **(gchar ***) p2;
+    return strcmp(s1,s2);
+}
+
+void
+PunctEditor::getPunctCandidates (void)
+{
+    const gchar *** brs;
+    const gchar ** res;
+    m_punct_candidates.clear();
+
+    if (m_text.empty())
+        return;
+
+    brs = (const gchar ***) std::bsearch (m_text.c_str() + 1, punct_table,
+                                          G_N_ELEMENTS (punct_table),
+                                          sizeof(punct_table[0]),
+                                          punct_cmp);
+    if (brs == NULL)
+        return;
+
+    for (res = (*brs) + 1 ;*res != NULL; ++res ) {
+        m_punct_candidates.push_back(*res);
+    }
+}
+
+gboolean
+PunctEditor::fillLookupTableByPage (void)
+{
+    guint filled_nr = m_lookup_table.size ();
+    guint page_size = m_lookup_table.pageSize ();
+    guint candidates_count;
+
+    getPunctCandidates();
+    candidates_count = m_punct_candidates.size();
+
+    guint need_nr = MIN (page_size, candidates_count - filled_nr);
+    g_assert (need_nr >= 0);
+    if (need_nr == 0) {
+        return FALSE;
+    }
+
+    for (guint i = filled_nr; i < filled_nr + need_nr; i++) {
+        Text text (m_punct_candidates[i]);
+        text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x004466, 0, -1);
+        m_lookup_table.appendCandidate (text);
+    }
+
+    return TRUE;
+}
+
+void
+PunctEditor::updateAuxiliaryText (void)
+{
+    if (G_UNLIKELY (m_punct_mode == FALSE)) {
+        hideAuxiliaryText ();
+        return;
+    }
+
+    m_buffer.clear();
+    for (String::iterator i = m_text.begin(); i!=m_text.end(); ++i) {
+        if (i - m_text.begin() == m_cursor)
+            m_buffer << '|';
+        m_buffer << *i;
+    }
+    if (m_text.end() - m_text.begin() == m_cursor)
+        m_buffer << '|';
+
+    StaticText aux_text (m_buffer);
+    Editor::updateAuxiliaryText (aux_text, TRUE);
+}
+
+void
+PunctEditor::updatePreeditText (void)
+{
+    if (G_UNLIKELY (m_punct_mode == FALSE )) {
+        hidePreeditText ();
+        return;
+    }
+
+    guint edit_begin = 0;
+    guint edit_end = 0;
+
+    m_buffer.clear ();
+    if (m_lookup_table.size() != 0) {
+        guint cursor = m_lookup_table.cursorPos ();
+        m_buffer << m_punct_candidates[cursor];
+    }
+
+    StaticText preedit_text (m_buffer);
+    /* underline */
+    preedit_text.appendAttribute (IBUS_ATTR_TYPE_UNDERLINE, IBUS_ATTR_UNDERLINE_SINGLE, 0, -1);
+
+    /* candidate */
+    if (edit_begin < edit_end) {
+        preedit_text.appendAttribute (IBUS_ATTR_TYPE_FOREGROUND, 0x00000000,
+                                        edit_begin, edit_end);
+        preedit_text.appendAttribute (IBUS_ATTR_TYPE_BACKGROUND, 0x00c8c8f0,
+                                        edit_begin, edit_end);
+    }
+    Editor::updatePreeditText (preedit_text, edit_begin, TRUE);
 }
 
 };
index e773158..773ee87 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <glib.h>
 #include "Editor.h"
+#include "PhraseEditor.h"
 
 namespace PY {
 
@@ -20,7 +21,32 @@ public:
     virtual void reset (void);
     virtual void candidateClicked (guint index, guint button, guint state);
 
-private:
+    virtual gboolean processPunct (guint keyval, guint keycode, guint modifiers);
+    virtual gboolean processSpace (guint keyval, guint keycode, guint modifiers);
+    virtual gboolean insert (gchar ch);
+    virtual void updateLookupTable (void);
+    virtual gboolean fillLookupTableByPage (void);
+    virtual void updateAuxiliaryText (void);
+    virtual void updatePreeditText (void);
+    virtual void getPunctCandidates (void);
+    virtual gboolean selectCandidate (guint i);
+    virtual gboolean selectCandidateInPage (guint i);
+    virtual void commit (const gchar *str);
+    virtual void commit (void);
+    virtual gboolean removeCharBefore (void);
+    virtual gboolean removeCharAfter (void);
+    virtual gboolean moveCursorLeft (void);
+    virtual gboolean moveCursorRight (void);
+    virtual gboolean moveCursorToBegin (void);
+    virtual gboolean moveCursorToEnd (void);
+
+protected:
+    gboolean m_punct_mode;
+    LookupTable m_lookup_table;
+    //PhraseEditor m_phrase_editor;
+    String m_buffer;
+    std::vector<String> m_punct_candidates;
+
 };
 
 };