Home | History | Annotate | Download | only in src
      1 /*
      2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
      3  *
      4  * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved.
      5  *
      6  * The contents of this file are subject to the terms of either the GNU Lesser
      7  * General Public License Version 2.1 only ("LGPL") or the Common Development and
      8  * Distribution License ("CDDL")(collectively, the "License"). You may not use this
      9  * file except in compliance with the License. You can obtain a copy of the CDDL at
     10  * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at
     11  * http://www.opensource.org/licenses/lgpl-license.php. See the License for the
     12  * specific language governing permissions and limitations under the License. When
     13  * distributing the software, include this License Header Notice in each file and
     14  * include the full text of the License in the License file as well as the
     15  * following notice:
     16  *
     17  * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE
     18  * (CDDL)
     19  * For Covered Software in this distribution, this License shall be governed by the
     20  * laws of the State of California (excluding conflict-of-law provisions).
     21  * Any litigation relating to this License shall be subject to the jurisdiction of
     22  * the Federal Courts of the Northern District of California and the state courts
     23  * of the State of California, with venue lying in Santa Clara County, California.
     24  *
     25  * Contributor(s):
     26  *
     27  * If you wish your version of this file to be governed by only the CDDL or only
     28  * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to
     29  * include this software in this distribution under the [CDDL or LGPL Version 2.1]
     30  * license." If you don't indicate a single choice of license, a recipient has the
     31  * option to distribute your version of this file under either the CDDL or the LGPL
     32  * Version 2.1, or to extend the choice of license to its licensees as provided
     33  * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL
     34  * Version 2 license, then the option applies only if the new code is made subject
     35  * to such option by the copyright holder.
     36  */
     37 
     38 #ifdef HAVE_CONFIG_H
     39 #include <config.h>
     40 #endif
     41 
     42 #include <locale.h>
     43 #include <ctype.h>
     44 
     45 #include "imi_view_modern.h"
     46 #include "imi_uiobjects.h"
     47 
     48 #include "imi_keys.h"
     49 
     50 CIMIModernView::CIMIModernView()
     51     : CIMIView(), m_SyllableStr(), m_bNewSyll(false), m_SyllBone(),
     52       m_CandiList(), m_CandiFirst(0), m_CursorIdx(-1)
     53 {
     54 }
     55 
     56 CIMIModernView::~CIMIModernView()
     57 {
     58 }
     59 
     60 int
     61 CIMIModernView::getViewType(void)
     62     { return CIMIViewFactory::SVT_MODERN; }
     63 
     64 
     65 void
     66 CIMIModernView::attachIC(CIMIContext* pIC)
     67 {
     68     CIMIView::attachIC(pIC);
     69 
     70     CSunpinyinOptions *ppref = dynamic_cast<CSunpinyinOptions*>(m_pPref);
     71 
     72     m_pIC->setLeft2RightSelection(false);
     73     if (ppref) {
     74         m_pIC->enableGBK(ppref->m_GBK);
     75         m_pIC->setHistoryPower(ppref->m_MemoryPower);
     76         m_pIC->enableContextRanking(ppref->m_ContextRanking);
     77     }
     78 
     79     m_SyllableStr.clear();
     80     m_SyllBone = m_pIC->getLastBone();
     81     m_CandiList.clear();
     82     m_CandiFirst = 0;
     83     m_bNewSyll = true;
     84     m_CursorIdx = -1;
     85 }
     86 
     87 unsigned
     88 CIMIModernView::clearIC(void)
     89 {
     90     if (!m_pIC->isEmpty()) {
     91         m_pIC->clear();
     92         m_SyllableStr.clear();
     93         m_bNewSyll = true;
     94         m_SyllBone = m_pIC->getLastBone();
     95         m_CandiFirst = 0;
     96         m_pIC->getCandidates(m_SyllBone, m_CandiList);
     97         m_CursorIdx = -1;
     98         return (PREEDIT_MASK | CANDIDATE_MASK);
     99     }
    100 
    101     m_SyllableStr.clear();
    102     return 0;
    103 }
    104 
    105 void
    106 CIMIModernView::setStatusAttrValue(int key, int value)
    107 {
    108     if (key == CIMIWinHandler::STATUS_ID_CN) {
    109         if (m_CN != (value != 0)) {
    110             unsigned int changeMasks = 0;
    111 
    112             if (m_SyllableStr.size() != 0) {
    113                 if (m_pIC->isValidSyllable(m_SyllableStr)) {
    114                     convertSyllable(CBone::USER_BOUNDARY, m_SyllableStr.size(), changeMasks);
    115                 } else {
    116                     cancelSyllable(changeMasks);
    117                 }
    118             }
    119             updateWindows(changeMasks);
    120 
    121             m_CN = (value != 0);
    122             if (mp_winHandler)
    123                 mp_winHandler->updateStatus(key, value);
    124         }
    125         return;
    126     }
    127     CIMIView::setStatusAttrValue(key, value);
    128 }
    129 
    130 /**
    131  * NOTE: We can not use keyrelease information because IIIM do not give
    132  * such type event.
    133  *    - If KEY_PRESS type event then
    134  *        -# m_nKeysWithShift = 1
    135  *        -# If LShift pressed, m_nKeysWithShift <-- 0, return
    136  *        -# Process Key Press event according whether or not in syllable editing mode
    137  *        .
    138  *    - If LShift released and (m_nKeysWithShift == 0), SwitchCNState()
    139  */
    140 int
    141 CIMIModernView::onKeyEvent(unsigned keycode, unsigned keyvalue, unsigned modifier)
    142 {
    143     unsigned int changeMasks = 0;
    144 
    145     #ifdef DEBUG
    146         printf("ModernView got a key (0x%x-0x%x-0x%x)...", keycode, keyvalue, modifier);
    147         fflush(stdout);
    148     #endif
    149 
    150     //Clear other mask bit we do not care
    151     modifier &= (IM_SHIFT_MASK | IM_CTRL_MASK | IM_ALT_MASK);
    152 
    153     // On ALT+SHIFT, switch CN/EN status, but return the key event back as we do not use it
    154     if ((keycode == IM_VK_SHIFT) && (modifier == IM_ALT_MASK)) {
    155         setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN, (!m_CN)?1:0);
    156         return 0;
    157     }
    158 
    159     if (keycode == IM_VK_SHIFT || keycode == IM_VK_CONTROL || keycode == IM_VK_ALT)
    160         return 0;
    161 
    162     if ((keyvalue == IM_VK_PERIOD) && (modifier == IM_CTRL_MASK)) {
    163         changeMasks |= KEYEVENT_USED;
    164         setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, (!m_FullPunc)?1:0);
    165 
    166     } else if ((keyvalue == IM_VK_SPACE) && (modifier == IM_SHIFT_MASK)) {
    167         changeMasks |= KEYEVENT_USED;
    168         setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSIMBOL, (!m_FullSimbol)?1:0);
    169 
    170     } else if ((modifier & (IM_CTRL_MASK | IM_ALT_MASK)) == 0) {
    171         if (m_SyllableStr.size() > 0)
    172             pressKeyOnSyllable(keycode, keyvalue, modifier, changeMasks);
    173         else
    174             pressKeyOnSentence(keycode, keyvalue, modifier, changeMasks);
    175     }
    176 
    177     #ifdef DEBUG
    178         printf("   |-->(Mask=0x%x)\n", changeMasks);
    179         fflush(stdout);
    180     #endif
    181 
    182     updateWindows(changeMasks);
    183 
    184     return ((changeMasks & KEYEVENT_USED) != 0)?1:0;
    185 }
    186 
    187 void
    188 CIMIModernView::getPreeditString(IPreeditString& ps)
    189 {
    190     int i, SyllPos, SyllSize, caret;
    191 
    192     ps.clear();
    193     caret = getSentence(ps.getString(), &SyllPos, &SyllSize);
    194     if (m_CursorIdx >= 0 && m_CursorIdx < m_SyllableStr.size())
    195         caret -= m_SyllableStr.size()-m_CursorIdx;
    196     ps.setCaret(caret);
    197 
    198     IPreeditString::CCharTypeVec& ctv = ps.getCharTypeVec();
    199     for (i = 0; i < SyllPos; ++i)
    200         ctv.push_back(IPreeditString::HANZI_CHAR);
    201     for (SyllPos += SyllSize; i < SyllPos; ++i)
    202         ctv.push_back(IPreeditString::PINYIN_CHAR);
    203     for (; i < ps.size(); ++i)
    204         ctv.push_back(IPreeditString::HANZI_CHAR);
    205 }
    206 
    207 void
    208 CIMIModernView::getCandidateList(ICandidateList& cl, int start, int size)
    209 {
    210     cl.clear();
    211     ICandidateList::CCandiStrings& css = cl.getCandiStrings();
    212     ICandidateList::CCandiTypeVec& cts = cl.getCandiTypeVec();
    213 
    214     cl.setFirst(start);
    215     cl.setTotal(m_CandiList.size());
    216 
    217     size = start+size;
    218     if (size > m_CandiList.size())
    219         size = m_CandiList.size();
    220     for (; start < size; ++start) {
    221         css.push_back(m_CandiList[start].m_String);
    222         cts.push_back( (start == 0)?(ICandidateList::BEST_WORD):(ICandidateList::NORMAL_WORD) );
    223     }
    224 }
    225 
    226 void
    227 CIMIModernView::updateWindows(unsigned int mask)
    228 {
    229     if (mp_winHandler) {
    230         if ((mask & PREEDIT_MASK) != 0) {
    231             CPreEditString ps;
    232             getPreeditString(ps);
    233             mp_winHandler->updatePreedit(&ps);
    234         }
    235 
    236         if ((mask & CANDIDATE_MASK) != 0) {
    237             CCandidateList cl;
    238             if (m_SyllableStr.size() == 0)
    239                 getCandidateList(cl, m_CandiFirst, s_CandiWindowSize);
    240             else
    241                 m_CandiFirst = 0;
    242             mp_winHandler->updateCandidates(&cl);
    243         }
    244     }
    245 }
    246 
    247 void
    248 CIMIModernView::cancelSyllable(unsigned int& mask)
    249 {
    250     mask |= PREEDIT_MASK | CANDIDATE_MASK;
    251     m_CandiFirst = 0;
    252     m_SyllableStr.clear();
    253     m_CursorIdx = -1;
    254 }
    255 
    256 void
    257 CIMIModernView::convertSyllable(int boundaryType, int len, unsigned int& mask)
    258 {
    259     tryCommitLeftSentence(mask);
    260 
    261     std::list<CBone> newBones;
    262     newBones.push_front(CBone(m_SyllableStr.c_str(), len, boundaryType, CBone::NODE_PINYIN));
    263 
    264     CSkeletonIter it2 = m_SyllBone;
    265     if (!m_bNewSyll)
    266         ++it2;
    267 
    268     if (m_pIC->modify(m_SyllBone, it2, newBones)) {
    269         m_CandiFirst = 0;
    270         m_SyllBone = it2;
    271         m_pIC->getCandidates(m_SyllBone, m_CandiList);
    272     }
    273     m_bNewSyll = true;
    274 
    275     if (len < m_SyllableStr.size()) {
    276         wstring tmpstr(m_SyllableStr.c_str()+len);
    277         if (m_CursorIdx > 0)
    278             m_CursorIdx -= len;
    279         m_SyllableStr = tmpstr;
    280     } else {
    281         m_SyllableStr.clear();
    282         m_CursorIdx = -1;
    283     }
    284     mask |= PREEDIT_MASK | CANDIDATE_MASK;
    285 }
    286 
    287 void
    288 CIMIModernView::makeSelection(int idx, unsigned int& mask)
    289 {
    290     CCandidate& cand = m_CandiList[m_CandiFirst + idx];
    291     m_pIC->makeSelection(cand);
    292     m_SyllBone = cand.m_BoneEnd;
    293     while (m_SyllBone->m_BoneType != CBone::NODE_PINYIN &&
    294            m_SyllBone->m_BoneType != CBone::NODE_TAIL)
    295         ++m_SyllBone;
    296     m_CandiFirst = 0;
    297     m_pIC->getCandidates(m_SyllBone, m_CandiList);
    298     mask |= PREEDIT_MASK | CANDIDATE_MASK;
    299 }
    300 
    301 void
    302 CIMIModernView::eraseBone(CSkeletonIter boneIt, unsigned int& mask)
    303 {
    304     std::list<CBone> dummyBoneList;
    305     CSkeletonIter it = ++CSkeletonIter(boneIt);
    306     m_pIC->modify(boneIt, it, dummyBoneList);
    307 
    308     m_SyllBone = it;
    309     m_pIC->getCandidates(m_SyllBone, m_CandiList);
    310     m_CandiFirst = 0;
    311 
    312     mask |= PREEDIT_MASK | CANDIDATE_MASK;
    313 }
    314 
    315 void
    316 CIMIModernView::insertNonPinyinBone(CBone& bone, unsigned int& mask)
    317 {
    318     tryCommitLeftSentence(mask);
    319 
    320     mask |= PREEDIT_MASK | CANDIDATE_MASK;
    321     std::list<CBone> newBones;
    322     newBones.push_back(bone);
    323     if (m_pIC->modify(m_SyllBone, m_SyllBone, newBones)) {
    324         m_CandiFirst = 0;
    325         m_pIC->getCandidates(m_SyllBone, m_CandiList);
    326     }
    327 }
    328 
    329 unsigned
    330 CIMIModernView::doCommit(bool bConvert)
    331 {
    332     wstring bs;
    333 
    334     if (bConvert) {
    335         getIC()->memorize();
    336         getSentence(bs);
    337         mp_winHandler->commit(bs.c_str());
    338     } else {
    339         CSkeletonIter ite = m_pIC->getLastBone();
    340         for (CSkeletonIter it = m_pIC->getFirstBone(); it != ite; ++it)
    341             bs += it->m_String;
    342         bs += m_SyllableStr;
    343         mp_winHandler->commit(bs.c_str());
    344     }
    345 
    346     return PREEDIT_MASK | CANDIDATE_MASK;
    347 }
    348 
    349 void
    350 CIMIModernView::tryCommitLeftSentence(unsigned int& mask)
    351 {
    352     CSkeletonIter firstBone = m_pIC->getSkeleton().begin();
    353     CSkeletonIter sntnceEnd = m_SyllBone;
    354 
    355     bool needCommit = false;
    356     while (sntnceEnd != firstBone) {
    357         --sntnceEnd;
    358         if (sntnceEnd->m_BoneType == CBone::NODE_PUNC &&
    359                 isTermPuncWide(sntnceEnd->m_String[0])) {
    360             needCommit = true;
    361             break;
    362         }
    363     }
    364 
    365     if (needCommit) {
    366         wstring bs;
    367         ++sntnceEnd;
    368         m_pIC->getBestSentence(bs, firstBone, sntnceEnd);
    369 
    370         mp_winHandler->commit(bs.c_str());
    371 
    372         std::list<CBone> dummyBoneList;
    373         m_pIC->modify(firstBone, sntnceEnd, dummyBoneList, false);
    374 
    375         mask |= PREEDIT_MASK | CANDIDATE_MASK;
    376     }
    377 }
    378 
    379 void
    380 CIMIModernView::commitChar(TWCHAR ch)
    381 {
    382     TWCHAR wa[2] = {ch, 0};
    383 
    384     mp_winHandler->commit(wa);
    385 }
    386 
    387 int
    388 CIMIModernView::getSentence(wstring& wstr, int* pSyllPos, int* pSyllSize)
    389 {
    390     wstr.clear();
    391     CSkeletonIter it = m_SyllBone;
    392     m_pIC->getBestSentence(wstr, m_pIC->getSkeleton().begin(), it);
    393     if (pSyllSize)
    394         *pSyllSize = m_SyllableStr.size();
    395     if (pSyllPos)
    396         *pSyllPos = wstr.size();
    397     if (m_SyllableStr.size() > 0)
    398         wstr += m_SyllableStr;
    399     int caret = wstr.size();
    400     if (m_SyllableStr.size() > 0 && !m_bNewSyll)
    401         ++it;
    402     wstring tmp;
    403     m_pIC->getBestSentence(tmp, it, m_pIC->getLastBone());
    404     wstr += tmp;
    405     return caret;
    406 }
    407 
    408 /**
    409  * -# ESC   -->  cancel current syllable
    410  * -# SPACE  '  RETURN  --> try to commit current syllable (untouch if can not commit)
    411  * -# BACKSPACE  --> try to erase the previouse PINYIN char in the syllable. If
    412  *                   all PINYIN are erased, then:
    413  *    - if modifying syllable, erase the bone.
    414  *    - if inserting syllable, back to non-syllable editing mode.
    415  *    - return
    416  *    .
    417  * -# a-z   --> try to seg pinyin, if could be seged, the new PINYIN char are
    418  *              appended and if have some syllable to commit, commit it. if the
    419  *              newly input PINYIN char can not be accepted, just skip it. NOTE:
    420  *              after commit, even if previous is modifying state, the new syllable
    421  *              editing mode should be inserting. return.
    422  * -# printable char
    423  *    - If current syllable is not commitable, skip it
    424  *    - else
    425  *        -# commit current syllable
    426  *        -# call pressNormalKey()
    427  *        .
    428  *    .
    429  */
    430 void
    431 CIMIModernView::pressKeyOnSyllable(unsigned keycode, unsigned keyvalue, unsigned modifier, unsigned int& mask)
    432 {
    433     mask |= KEYEVENT_USED;
    434     if (keycode == IM_VK_ESCAPE) {
    435         m_bNewSyll = true;
    436         cancelSyllable(mask);
    437     } else if (keycode == IM_VK_ENTER) {
    438         mask |= KEYEVENT_USED | doCommit(modifier != IM_SHIFT_MASK);
    439         mask |= clearIC();
    440     } else if (keyvalue == IM_VK_SPACE || keyvalue == '\'') {
    441         if (m_CursorIdx != 0 || keyvalue == IM_VK_SPACE) {
    442             wstring syllable = m_SyllableStr;
    443             if (m_CursorIdx > 0 && m_CursorIdx < m_SyllableStr.size() && keyvalue == '\'')
    444                 syllable.resize(m_CursorIdx);
    445             if (m_pIC->isValidSyllable(syllable)) {
    446                 convertSyllable(CBone::USER_BOUNDARY, syllable.size(), mask);
    447             }
    448         }
    449     } else if (keycode == IM_VK_LEFT) {
    450         if (m_CursorIdx != 0) {
    451             mask |= PREEDIT_MASK;
    452             m_CursorIdx = (m_CursorIdx < 0 ? m_SyllableStr.size() : m_CursorIdx) - 1;
    453         }
    454     } else if (keycode == IM_VK_RIGHT) {
    455         if (m_CursorIdx >= 0 && m_CursorIdx < m_SyllableStr.size()) {
    456             mask |= PREEDIT_MASK;
    457             if (++m_CursorIdx == m_SyllableStr.size())
    458                 m_CursorIdx = -1;
    459         }
    460     } else if (keycode == IM_VK_HOME) {
    461         if (m_CursorIdx != 0) {
    462             mask |= PREEDIT_MASK;
    463             m_CursorIdx = 0;
    464         }
    465     } else if (keycode == IM_VK_END) {
    466         if ( !(m_CursorIdx < 0 || m_CursorIdx == m_SyllableStr.size()) ) {
    467             mask |= PREEDIT_MASK;
    468             m_CursorIdx = -1;
    469         }
    470     } else if (keycode == IM_VK_BACK_SPACE) {
    471         if (m_CursorIdx != 0) {
    472             mask |= PREEDIT_MASK;
    473             m_SyllableStr.erase(m_CursorIdx < 0 ? m_SyllableStr.size() - 1 : --m_CursorIdx, 1);
    474             if (m_SyllableStr.size() == 0) {
    475                 if (!m_bNewSyll)
    476                     eraseBone(m_SyllBone, mask);
    477                 m_bNewSyll = true;
    478             } else if (!m_bNewSyll) {
    479                 m_SyllBone->m_String = m_SyllableStr;
    480             }
    481          }
    482     } else if (keycode == IM_VK_DELETE) {
    483         if ( !(m_CursorIdx < 0 || m_CursorIdx == m_SyllableStr.size()) ) {
    484             mask |= PREEDIT_MASK;
    485             m_SyllableStr.erase(m_CursorIdx, 1);
    486             if (m_CursorIdx >= m_SyllableStr.size())
    487                 m_CursorIdx = -1;
    488             if (m_SyllableStr.size() == 0) {
    489                 if (!m_bNewSyll)
    490                     eraseBone(m_SyllBone, mask);
    491                 m_bNewSyll = true;
    492             } else if (!m_bNewSyll) {
    493                 m_SyllBone->m_String = m_SyllableStr;
    494             }
    495          }
    496     } else if (keyvalue >= 'a' && keyvalue <= 'z') {
    497         std::list<CBone> newBones;
    498         TWCHAR wa[2] = {keyvalue, 0};
    499         m_SyllableStr.insert( m_CursorIdx < 0 ? m_SyllableStr.size() : m_CursorIdx++, wa );
    500         bool valid_py_char = m_pIC->segPinyinSimplest(m_SyllableStr, newBones);
    501 
    502         if (!valid_py_char) //invalid pinyin character
    503             m_SyllableStr.erase( m_CursorIdx < 0 ? m_SyllableStr.size() - 1 : --m_CursorIdx, 1);
    504 
    505         if (newBones.size() > 1) {
    506             if (!m_bNewSyll)
    507                 m_SyllBone->m_String = m_SyllableStr;
    508             convertSyllable(CBone::AUTO_BOUNDARY, newBones.front().m_String.size(), mask);
    509             if (!valid_py_char) {
    510                 pressNormalKey(keycode, keyvalue, modifier, mask);
    511             }
    512         } else {
    513             mask |= PREEDIT_MASK;
    514         }
    515     } else if (keyvalue < 0x7f && keyvalue > 0x20) {
    516         if (m_pIC->isValidSyllable(m_SyllableStr)) {
    517             convertSyllable(CBone::AUTO_BOUNDARY, m_SyllableStr.size(), mask);
    518             pressNormalKey(keycode, keyvalue, modifier, mask);
    519         }
    520     }
    521 }
    522 
    523 /**
    524  * -# if (IC's skeleton is empty) and (the keyValue is not printable)
    525  *       --> give it back to IM framework
    526  * -# ESACPE  --> cancel all sentence (clear IC, update UI)
    527  * -# RETURN  --> commit the sentence
    528  * -# LEFT_ARROW RIGHT_ARROW  HOME  END  --> move caret
    529  * -# DEL BACKSPACE  --> erase properiate bone
    530  * -# ENState --> call pressNormalKey
    531  * -# ` (KEY left to key 1)  and CN State and current bone contains only one HANZI -->
    532  *    - Entering current's characters syllable modifying mode
    533  *    - else ignore it (CNState)
    534  *    .
    535  * -# SPACE:
    536  *    - if candidates displayed, select the candi, and seek new candidate-list position
    537  *    - (now, it's CNState) commit the sentence
    538  *    .
    539  * -# 1-8:
    540  *     - if candidates displayed, select the candi, seek new position
    541  *     - (CNState), Processing normal key
    542  *     .
    543  * -# =/-
    544  *     - If candidates displayed, move its display window
    545  *     - (CNState), Processing normal key
    546  *     .
    547  * -# a-z  --> entering syllable editing inserting mode, return
    548  * -# call pressNormalKey
    549  */
    550 void
    551 CIMIModernView::pressKeyOnSentence(unsigned keycode, unsigned keyvalue, unsigned modifier, unsigned int& mask)
    552 {
    553     //unsigned int key = event->keyval;
    554     CSunpinyinOptions *ppref = dynamic_cast<CSunpinyinOptions*>(m_pPref);
    555 
    556     if (m_pIC->isEmpty() && !(keyvalue >= 'a' && keyvalue <= 'z'))
    557       return pressNormalKey(keycode, keyvalue, modifier, mask);
    558 
    559     if (keycode == IM_VK_ESCAPE) {
    560         mask |= KEYEVENT_USED | clearIC();
    561 
    562     } else if (keycode == IM_VK_ENTER) {
    563         mask |= KEYEVENT_USED | doCommit(modifier != IM_SHIFT_MASK);
    564         mask |= clearIC();
    565 
    566     } else if (keycode == IM_VK_HOME || keycode == IM_VK_LEFT) {
    567         mask |= KEYEVENT_USED;
    568         if (m_SyllBone != m_pIC->getSkeleton().begin()){
    569             if (keycode != IM_VK_HOME)
    570                 --m_SyllBone;
    571             else
    572                 m_SyllBone = m_pIC->getSkeleton().begin();
    573             m_CandiFirst = 0;
    574             m_pIC->getCandidates(m_SyllBone, m_CandiList);
    575             mask |= PREEDIT_MASK | CANDIDATE_MASK;
    576         }
    577     } else if (keycode == IM_VK_END || keycode == IM_VK_RIGHT) {
    578         mask |= KEYEVENT_USED;
    579         if (m_SyllBone != m_pIC->getLastBone()){
    580             if (keycode != IM_VK_END)
    581                 ++m_SyllBone;
    582             else
    583                 m_SyllBone = m_pIC->getLastBone();
    584             m_CandiFirst = 0;
    585             m_pIC->getCandidates(m_SyllBone, m_CandiList);
    586             mask |= KEYEVENT_USED | PREEDIT_MASK | CANDIDATE_MASK;
    587         }
    588     } else if (keycode == IM_VK_DELETE) {
    589         mask |= KEYEVENT_USED;
    590         if (m_SyllBone != m_pIC->getLastBone()){
    591             eraseBone(m_SyllBone, mask);
    592         }
    593     } else if (keycode == IM_VK_BACK_SPACE) {
    594         mask |= KEYEVENT_USED;
    595         if (m_SyllBone != m_pIC->getSkeleton().begin()){
    596             eraseBone(--CSkeletonIter(m_SyllBone), mask);
    597         }
    598     } else if (!m_CN) {
    599         pressNormalKey(keycode, keyvalue, modifier, mask);
    600     } else if (keycode == IM_VK_BACK_QUOTE) {
    601         mask |= KEYEVENT_USED;
    602         if (m_SyllBone->m_BoneType == CBone::NODE_PINYIN) {
    603             m_bNewSyll = false;
    604             m_SyllableStr = m_SyllBone->m_String;
    605             mask |= PREEDIT_MASK | CANDIDATE_MASK;
    606         }
    607     } else if (keyvalue == IM_VK_SPACE) {
    608         mask |= KEYEVENT_USED;
    609         if (m_CandiList.size() > 0) {
    610             makeSelection(0, mask);
    611         } else { // (m_CN is true)
    612             mask |= doCommit();
    613             mask |= clearIC();
    614         }
    615     } else if ((keyvalue >= '1' && keyvalue < '1'+s_CandiWindowSize) ||
    616                (keyvalue == '0' && s_CandiWindowSize == 10))  {
    617         mask |= KEYEVENT_USED;
    618         if (m_CandiList.size() > m_CandiFirst + (keyvalue == '0' ? 9 : keyvalue-'1')) {
    619             makeSelection(keyvalue == '0' ? 9 : keyvalue-'1', mask);
    620         } else {
    621             pressNormalKey(keycode, keyvalue, modifier, mask);
    622         }
    623     } else if ((ppref != NULL) && (ppref->isPageUpKey(keycode, keyvalue, modifier))) {
    624         mask |= KEYEVENT_USED;
    625         if (m_CandiList.size() && m_CandiFirst > 0) {
    626             m_CandiFirst -= s_CandiWindowSize;
    627             if (m_CandiFirst < 0) m_CandiFirst = 0;
    628             mask |= CANDIDATE_MASK;
    629         }
    630     } else if ((ppref != NULL) && (ppref->isPageDnKey(keycode, keyvalue, modifier))) {
    631         mask |= KEYEVENT_USED;
    632         if (m_CandiFirst + s_CandiWindowSize < m_CandiList.size()) {
    633             m_CandiFirst += s_CandiWindowSize;
    634             mask |= CANDIDATE_MASK;
    635         }
    636     } else if (keyvalue >= 'a' && keyvalue <= 'z') {
    637         mask |= KEYEVENT_USED;
    638         m_bNewSyll = true;
    639         m_SyllableStr += TWCHAR(keyvalue);
    640 
    641         std::list<CBone> newBones;
    642         if (m_pIC->segPinyinSimplest(m_SyllableStr, newBones)) {
    643             mask |= PREEDIT_MASK | CANDIDATE_MASK; //hide the candi-window
    644         } else {  //invalid Pinyin character, like u, i
    645             m_SyllableStr.clear();
    646             pressNormalKey(keycode, keyvalue, modifier, mask);
    647         }
    648     } else {
    649         pressNormalKey(keycode, keyvalue, modifier, mask);
    650     }
    651 }
    652 
    653 int
    654 CIMIModernView::onCandidatePageRequest(int pgno, bool relative)
    655 {
    656     unsigned changeMasks = 0;
    657     int      ncandi, lastpgidx;
    658 
    659     if (!m_pIC->isEmpty()) {
    660         changeMasks |= KEYEVENT_USED;
    661         int sz = m_CandiList.size();
    662         if (sz > 0) {
    663            lastpgidx = (sz-1)/s_CandiWindowSize * s_CandiWindowSize;
    664            if (relative == true) {
    665                 ncandi = m_CandiFirst + pgno*s_CandiWindowSize;
    666                 if (ncandi >= sz)
    667                    ncandi = lastpgidx;
    668                 if (ncandi < 0)
    669                     ncandi = 0;
    670                 if (ncandi != m_CandiFirst) {
    671                     m_CandiFirst = ncandi;
    672                     changeMasks |= CANDIDATE_MASK;
    673                 }
    674             } else {
    675                 if (pgno < 0) { //last page
    676                     ncandi = lastpgidx;
    677                 } else {
    678                     ncandi = pgno * s_CandiWindowSize;
    679                     if (ncandi > lastpgidx)
    680                         ncandi = lastpgidx;
    681                 }
    682                 if (ncandi != m_CandiFirst) {
    683                     m_CandiFirst = ncandi;
    684                     changeMasks |= CANDIDATE_MASK;
    685                 }
    686             }
    687         }
    688     }
    689 
    690     updateWindows(changeMasks);
    691     return 0;
    692 }
    693 
    694 int
    695 CIMIModernView::onCandidateSelectRequest(int index)
    696 {
    697     unsigned changeMasks = 0;
    698 
    699     if (!m_pIC->isEmpty()) {
    700         makeSelection(index, changeMasks);
    701     }
    702     updateWindows(changeMasks);
    703     return 0;
    704 }
    705 
    706 /**
    707  * I will change the core to make that every punc/simbol/Eng char be a single
    708  * bone soon. this could make this function much simpler.
    709  * -# ch <--  the char of the key press
    710  * -# not printable, return (nothing changed, the event key is not processed by IMI)
    711  * -# word id <-- 0
    712  * -# if is PuncChar and FullPuncState,
    713  *    - convert ch into Chinese Punc,
    714  *    - get its word id
    715  * -# if is Simbol char, convert ch into Chinese Simbol
    716  * -# insert the char or commit the char
    717  *     - if (IC's Skeleton is not empty)
    718  *         - create a bone and insert it into IC's skeleton
    719  *         - if ch in (A-Z) and in CN state, change the state into EN automatically
    720  *         .
    721  *     - else commit the char directly
    722  */
    723 void
    724 CIMIModernView::pressNormalKey(unsigned keycode, unsigned keyvalue, unsigned modifier, unsigned int& mask)
    725 {
    726     //unsigned int key = event->keyval;
    727     unsigned int bone_type = CBone::NODE_ASCII;
    728 
    729     if (keyvalue <= 0x20 || keyvalue >= 0x7F )
    730         return;
    731     if (m_FullSimbol)
    732         keyvalue = (unsigned int)(getFullSimbol(TWCHAR(keyvalue)));
    733     if (m_FullPunc) {
    734         keyvalue = (unsigned int)(getFullPunc(TWCHAR(keyvalue)));
    735         bone_type = CBone::NODE_PUNC;
    736     }
    737     if (m_pIC->isEmpty()) {
    738         mask |= KEYEVENT_USED;
    739         commitChar(TWCHAR(keyvalue));
    740     } else {
    741         mask |= KEYEVENT_USED;
    742         CBone bone(CBone::AUTO_BOUNDARY, bone_type);
    743         bone.m_String.clear();
    744         bone.m_String += TWCHAR(keyvalue);
    745         insertNonPinyinBone(bone, mask);
    746     }
    747 }
    748