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 "imi_view_classic.h"
     43 #include "imi_uiobjects.h"
     44 
     45 #include "imi_keys.h"
     46 
     47 CIMIClassicView::CIMIClassicView() : CIMIView(),
     48     m_CursorBone(), m_CursorIdx(0), m_CandiBone(),
     49     m_CandiList(), m_CandiFirst(0), m_TailSentence()
     50     { }
     51 
     52 CIMIClassicView::~CIMIClassicView()
     53     { }
     54 
     55 int
     56 CIMIClassicView::getViewType(void)
     57     { return CIMIViewFactory::SVT_CLASSIC; }
     58 
     59 
     60 void
     61 CIMIClassicView::attachIC(CIMIContext* pIC)
     62 {
     63     CIMIView::attachIC(pIC);
     64 
     65     CSunpinyinOptions *ppref = dynamic_cast<CSunpinyinOptions*>(m_pPref);
     66 
     67     m_pIC->setLeft2RightSelection(false);
     68     if (ppref) {
     69         m_pIC->enableGBK(ppref->m_GBK);
     70         m_pIC->setHistoryPower(ppref->m_MemoryPower);
     71         m_pIC->enableContextRanking(ppref->m_ContextRanking);
     72     }
     73 
     74     m_pIC->clear();
     75     m_CursorBone = m_pIC->getLastBone();
     76     m_CursorIdx = 0;
     77     m_CandiBone = m_pIC->getLastBone();
     78     m_CandiList.clear();
     79     m_CandiFirst = 0;
     80 }
     81 
     82 unsigned
     83 CIMIClassicView::clearIC(void)
     84 {
     85     if (!m_pIC->isEmpty()) {
     86         m_pIC->clear();
     87         m_CursorBone = m_pIC->getLastBone();
     88         m_CursorIdx = 0;
     89         m_CandiBone = m_pIC->getLastBone();
     90         m_CandiList.clear();
     91         m_CandiFirst = 0;
     92         m_TailSentence.clear();
     93         return PREEDIT_MASK | CANDIDATE_MASK;
     94     }
     95     return 0;
     96 }
     97 
     98 void
     99 CIMIClassicView::updateWindows(unsigned int mask)
    100 {
    101     if (mp_winHandler) {
    102         if ((mask & PREEDIT_MASK) != 0) {
    103             CPreEditString ps;
    104             getPreeditString(ps);
    105             mp_winHandler->updatePreedit(&ps);
    106         }
    107 
    108         if ((mask & PREEDIT_MASK) != 0 || (mask & CANDIDATE_MASK) != 0) {
    109             int wlen = getSentence(m_TailSentence, m_CandiBone);
    110             if (wlen <= 1) m_TailSentence.clear();
    111         }
    112 
    113         if ((mask & CANDIDATE_MASK) != 0) {
    114             CCandidateList cl;
    115             getCandidateList(cl, m_CandiFirst, s_CandiWindowSize);
    116             mp_winHandler->updateCandidates(&cl);
    117         }
    118     }
    119 }
    120 
    121 void
    122 CIMIClassicView::commitChar(TWCHAR ch)
    123 {
    124     TWCHAR wa[2] = {ch, 0};
    125 
    126     mp_winHandler->commit(wa);
    127 }
    128 
    129 void
    130 CIMIClassicView::doCommit(bool bConvert)
    131 {
    132     wstring bs;
    133 
    134     if (bConvert) {
    135         getIC()->memorize();
    136         getSentence(bs, m_pIC->getFirstBone());
    137         mp_winHandler->commit(bs.c_str());
    138     } else {
    139         CSkeletonIter ite = m_pIC->getLastBone();
    140         for (CSkeletonIter it = m_pIC->getFirstBone(); it != ite; ++it) {
    141             if (it->isPinyinNode())
    142                 bs += it->m_String;
    143             else
    144                 bs += (unsigned)it->m_BoundaryType;
    145             if (it->isPinyinNode() && it->isUserBoundary())
    146                 bs += TWCHAR('\'');
    147         }
    148         mp_winHandler->commit(bs.c_str());
    149     }
    150 }
    151 
    152 int
    153 CIMIClassicView::onKeyEvent(unsigned keycode, unsigned keyvalue, unsigned modifier)
    154 {
    155     unsigned changeMasks = 0;
    156     CSunpinyinOptions *ppref = dynamic_cast<CSunpinyinOptions*>(m_pPref);
    157 
    158     //Clear other mask bit we do not care
    159     modifier &= (IM_SHIFT_MASK | IM_CTRL_MASK | IM_ALT_MASK);
    160 
    161     #ifdef DEBUG
    162         printf("Classic View got a key (0x%x-0x%x-0x%x)...", keycode, keyvalue, modifier);
    163 
    164         if (((modifier & IM_CTRL_MASK) != 0) && (keyvalue == 'P' || keyvalue=='p')) {
    165             m_pIC->print_lattice();
    166         }
    167 
    168         fflush(stdout);
    169     #endif
    170 
    171     if (keycode == IM_VK_SHIFT && modifier == IM_ALT_MASK) {
    172         setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN, (!m_CN)?1:0);
    173         return 0;
    174     } else if (keyvalue == IM_VK_PERIOD && modifier == IM_CTRL_MASK) {
    175         // On CTRL+. switch Full/Half punc
    176         changeMasks |= KEYEVENT_USED;
    177         setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC, (!m_FullPunc)?1:0);
    178 
    179     } else if (keyvalue == IM_VK_SPACE && modifier == IM_SHIFT_MASK) {
    180         // On CTRL+, switch Full/Half simbol
    181         changeMasks |= KEYEVENT_USED;
    182         setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSIMBOL, (!m_FullSimbol)?1:0);
    183 
    184     } else if (modifier == IM_CTRL_MASK && keycode == IM_VK_LEFT) { //move left syllable
    185         if (!m_pIC->isEmpty()) {
    186             changeMasks |= KEYEVENT_USED;
    187             moveLeftSyllable(changeMasks);
    188         }
    189 
    190     } else if (modifier == 0 && keycode == IM_VK_LEFT) { //move left
    191         if (!m_pIC->isEmpty()) {
    192             changeMasks |= KEYEVENT_USED;
    193             moveLeft(changeMasks);
    194         }
    195 
    196     } else if (modifier == IM_CTRL_MASK && keycode == IM_VK_RIGHT) { //move syllable right
    197         if (!m_pIC->isEmpty()) {
    198             changeMasks |= KEYEVENT_USED;
    199             moveRightSyllable(changeMasks);
    200         }
    201 
    202     } else if (modifier == 0 && keycode == IM_VK_RIGHT) { //move right
    203         if (!m_pIC->isEmpty()) {
    204             changeMasks |= KEYEVENT_USED;
    205             moveRight(changeMasks);
    206         }
    207 
    208     } else if ((ppref != NULL) && (ppref->isPageUpKey(keycode, keyvalue, modifier)) && !m_pIC->isEmpty()) {
    209         changeMasks |= KEYEVENT_USED;
    210         int sz = m_CandiList.size() + ((m_TailSentence.size() > 0)?1:0);
    211         if (sz > 0 && m_CandiFirst > 0) {
    212             m_CandiFirst -= s_CandiWindowSize;
    213             if (m_CandiFirst < 0) m_CandiFirst = 0;
    214             changeMasks |= CANDIDATE_MASK;
    215         }
    216 
    217     } else if ((ppref != NULL) && (ppref->isPageDnKey(keycode, keyvalue, modifier)) && !m_pIC->isEmpty()) {
    218         changeMasks |= KEYEVENT_USED;
    219         int sz = m_CandiList.size() + ((m_TailSentence.size() > 0)?1:0);
    220         if (sz > 0 && m_CandiFirst + s_CandiWindowSize < sz) {
    221             m_CandiFirst += s_CandiWindowSize;
    222             changeMasks |= CANDIDATE_MASK;
    223         }
    224 
    225     } else if ((modifier & (IM_CTRL_MASK | IM_ALT_MASK)) == 0) {
    226         if (islower(keyvalue)) { // insert a PINYIN char
    227             changeMasks |= KEYEVENT_USED;
    228             if (m_pIC->getSkeleton().size() < 32) {
    229                 insertPinyin(keyvalue, changeMasks);
    230             }
    231 
    232         } else if (keycode == IM_VK_BACK_SPACE || keycode == IM_VK_DELETE) { // delete
    233             if (!m_pIC->isEmpty()) {
    234                 changeMasks |= KEYEVENT_USED;
    235                 erase((keycode == IM_VK_BACK_SPACE), changeMasks);
    236             }
    237 
    238         } else if (keycode == IM_VK_HOME) { //move home
    239             if (!m_pIC->isEmpty()) {
    240                 changeMasks |= KEYEVENT_USED;
    241                 moveHome(changeMasks);
    242             }
    243         } else if (keycode == IM_VK_END) {  //move end
    244             if (!m_pIC->isEmpty()) {
    245                 changeMasks |= KEYEVENT_USED;
    246                 moveEnd(changeMasks);
    247             }
    248 
    249         } else if (keyvalue == '\'') {
    250             if (!m_pIC->isEmpty()) {
    251                 changeMasks |= KEYEVENT_USED;
    252                 insertBoundary(changeMasks);
    253             }
    254 
    255         } else if (isdigit(keyvalue) &&
    256                    ((s_CandiWindowSize >= 10) ||
    257                     (s_CandiWindowSize < 10 && keyvalue < ('1'+s_CandiWindowSize)))) { // try to make selections
    258 #ifdef DEBUG
    259             printf("onKeyEvent:number: %d, %d, %d\n", keycode, keyvalue, s_CandiWindowSize);
    260 #endif
    261             if (!m_pIC->isEmpty()) {
    262                 changeMasks |= KEYEVENT_USED;
    263                 unsigned selection = (keyvalue == '0' ? 9 : keyvalue-'1');
    264                 makeSelection(selection, changeMasks);
    265             }
    266 
    267         } else if (keyvalue == IM_VK_SPACE) { // select or commit
    268             if (!m_pIC->isEmpty()) {
    269                 changeMasks |= KEYEVENT_USED;
    270                 makeSelection(0, changeMasks);
    271             }
    272 
    273         } else if (keycode == IM_VK_ENTER) { // commit PINYIN string
    274             if (!m_pIC->isEmpty()) {
    275                 changeMasks |= KEYEVENT_USED | CANDIDATE_MASK | PREEDIT_MASK;
    276                 doCommit(false);
    277                 clearIC();
    278             }
    279 
    280         } else if (keycode == IM_VK_ESCAPE) {
    281             if (!m_pIC->isEmpty()) {
    282                 changeMasks |= KEYEVENT_USED | CANDIDATE_MASK | PREEDIT_MASK;
    283                 clearIC();
    284             }
    285         } else if (keyvalue > 0x20 && keyvalue < 0x7F ) { // isprint() && !isblank()
    286             if (m_pIC->getSkeleton().size() < 32)
    287                 pressNormalKey(keyvalue, changeMasks);
    288             else
    289                 changeMasks |= KEYEVENT_USED;
    290         }
    291     }
    292 
    293     #ifdef DEBUG
    294         printf("   |-->(Mask=0x%x)\n", changeMasks);
    295         fflush(stdout);
    296     #endif
    297 
    298     updateWindows(changeMasks);
    299 
    300     return ((changeMasks & KEYEVENT_USED) != 0)?1:0;
    301 }
    302 
    303 int
    304 CIMIClassicView::onCandidatePageRequest(int pgno, bool relative)
    305 {
    306     unsigned changeMasks = 0;
    307     int      ncandi, lastpgidx;
    308 
    309     if (!m_pIC->isEmpty()) {
    310         changeMasks |= KEYEVENT_USED;
    311         int sz = m_CandiList.size() + ((m_TailSentence.size() > 0)?1:0);
    312         if (sz > 0) {
    313            lastpgidx = (sz-1)/s_CandiWindowSize * s_CandiWindowSize;
    314            if (relative == true) {
    315                 ncandi = m_CandiFirst + pgno*s_CandiWindowSize;
    316                 if (ncandi >= sz)
    317                    ncandi = lastpgidx;
    318                 if (ncandi < 0)
    319                     ncandi =0;
    320                 if (ncandi != m_CandiFirst) {
    321                     m_CandiFirst = ncandi;
    322                     changeMasks |= CANDIDATE_MASK;
    323                 }
    324             } else {
    325                 if (pgno == -1) { //last page
    326                     ncandi = lastpgidx;
    327                 } else {
    328                     ncandi = pgno * s_CandiWindowSize;
    329                     if (ncandi > lastpgidx)
    330                         ncandi = lastpgidx;
    331                 }
    332                 if (ncandi != m_CandiFirst) {
    333                     m_CandiFirst = ncandi;
    334                     changeMasks |= CANDIDATE_MASK;
    335                 }
    336             }
    337         }
    338     }
    339 
    340     updateWindows(changeMasks);
    341     return 0;
    342 }
    343 
    344 int
    345 CIMIClassicView::onCandidateSelectRequest(int index)
    346 {
    347     unsigned changeMasks = 0;
    348 
    349     if (!m_pIC->isEmpty()) {
    350         makeSelection(index, changeMasks);
    351     }
    352     updateWindows(changeMasks);
    353     return 0;
    354 }
    355 
    356 
    357 void
    358 CIMIClassicView::pressNormalKey(unsigned keyvalue, unsigned int& mask)
    359 {
    360     unsigned orig_key = keyvalue;
    361     unsigned int bone_type = CBone::NODE_ASCII;
    362 
    363     if (m_FullSimbol) {
    364         keyvalue = (unsigned int)(getFullSimbol(TWCHAR(keyvalue)));
    365     }
    366     if (m_FullPunc) {
    367         keyvalue = (unsigned int)(getFullPunc(TWCHAR(keyvalue)));
    368         bone_type = CBone::NODE_PUNC;
    369     }
    370     mask |= KEYEVENT_USED;
    371     if (m_pIC->isEmpty()) {
    372         commitChar(TWCHAR(keyvalue));
    373     } else {
    374         insertNormalChar(bone_type, orig_key, keyvalue, mask);
    375     }
    376 }
    377 
    378 CSkeletonIter
    379 CIMIClassicView::moveLeft(unsigned int& changeMask, bool searchAgain)
    380 {
    381     CSkeletonIter leftmost = m_pIC->getLastBone();
    382     if (m_CursorIdx > 0) {
    383         --m_CursorIdx;
    384         changeMask |= PREEDIT_MASK;
    385     } else if (m_CursorBone != m_pIC->getFirstBone()) {
    386         changeMask |= PREEDIT_MASK;
    387         if (m_CursorBone-- == m_CandiBone) {
    388             changeMask |= CANDIDATE_MASK;
    389             if (m_CursorBone->isValidPinyinNode()) {
    390                 leftmost = m_CandiBone = m_pIC->cancelSelection(m_CursorBone, searchAgain);
    391             } else {
    392                 m_CandiBone = m_CursorBone;
    393             }
    394             getCandidates();
    395         }
    396 
    397         m_CursorIdx = m_CursorBone->m_String.size();
    398         if (!m_CursorBone->isPinyinNode())
    399             --m_CursorIdx;
    400     } // else we already at head of the string
    401     return leftmost;
    402 }
    403 
    404 CSkeletonIter
    405 CIMIClassicView::moveLeftSyllable(unsigned int& changeMask, bool searchAgain)
    406 {
    407     CSkeletonIter leftmost = m_pIC->getLastBone();
    408     if (m_CursorIdx > 0) {
    409         m_CursorIdx = 0;
    410         changeMask |= PREEDIT_MASK;
    411     } else if (m_CursorBone != m_pIC->getFirstBone()) {
    412         changeMask |= PREEDIT_MASK;
    413         if (m_CursorBone-- == m_CandiBone) {
    414             changeMask |= CANDIDATE_MASK;
    415             if (m_CursorBone->isValidPinyinNode()) {
    416                 leftmost = m_CandiBone = m_pIC->cancelSelection(m_CursorBone, searchAgain);
    417             } else {
    418                 m_CandiBone = m_CursorBone;
    419             }
    420             getCandidates();
    421         }
    422         m_CursorIdx = 0;
    423     }  // else we already at head of the string
    424     return leftmost;
    425 }
    426 
    427 CSkeletonIter
    428 CIMIClassicView::moveHome(unsigned int& changeMask, bool searchAgain)
    429 {
    430     CSkeletonIter leftmost = m_pIC->getLastBone();
    431     CSkeletonIter firstBone = m_pIC->getFirstBone();
    432 
    433     if (m_CursorBone != firstBone || m_CursorIdx != 0)
    434         changeMask |= PREEDIT_MASK;
    435 
    436     if (m_CandiBone != firstBone) {
    437         bool usCanceled = false;
    438         for (CSkeletonIter bit = m_pIC->getFirstBone(); bit != m_CandiBone; ++bit) {
    439             if (bit->isValidPinyinNode() && bit->isUserSelectionStart()) {
    440                 m_pIC->cancelSelection(bit, false);
    441                 usCanceled = true;
    442             }
    443         }
    444         if (usCanceled) {
    445             changeMask |= CANDIDATE_MASK;
    446             leftmost = m_CandiBone = firstBone;
    447             getCandidates();
    448             if (searchAgain) m_pIC->searchFrom(firstBone);
    449         }
    450     }
    451     m_CursorIdx = 0;
    452     m_CursorBone = firstBone;
    453     return leftmost;
    454 }
    455 
    456 void
    457 CIMIClassicView::moveRight(unsigned int& changeMask)
    458 {
    459     CSkeletonIter itLast = m_pIC->getLastBone();
    460     if (m_CursorBone != itLast) {
    461         CSkeletonIter itNext = ++CSkeletonIter(m_CursorBone);
    462         if (m_CursorIdx < m_CursorBone->m_String.size()-1) {
    463             ++m_CursorIdx;
    464             changeMask |= PREEDIT_MASK;
    465         } else if (m_CursorIdx == m_CursorBone->m_String.size()-1) {
    466             if (m_CursorBone->isPinyinNode()) {
    467                 ++m_CursorIdx;
    468                 changeMask |= PREEDIT_MASK;
    469             } else {
    470                 m_CursorIdx = 0;
    471                 ++m_CursorBone;
    472                 changeMask |= PREEDIT_MASK;
    473             }
    474         } else { // m_CursorIdx == m_CursorBone->m_String.size(), must be PinyinNode
    475             if (itNext != itLast || m_CursorBone->isUserBoundary()) {
    476                 m_CursorIdx = 0;
    477                 ++m_CursorBone;
    478                 changeMask |= PREEDIT_MASK;
    479             }
    480         }
    481     }
    482 }
    483 
    484 void
    485 CIMIClassicView::moveRightSyllable(unsigned int& changeMask)
    486 {
    487     CSkeletonIter itLast = m_pIC->getLastBone();
    488     if (m_CursorBone != itLast) {
    489         CSkeletonIter itNext = ++CSkeletonIter(m_CursorBone);
    490         if (itNext == itLast && m_CursorBone->isPinyinNode() && !m_CursorBone->isUserBoundary()) {
    491             if (m_CursorIdx != m_CursorBone->m_String.size()) {
    492                 m_CursorIdx = m_CursorBone->m_String.size();
    493                 changeMask |= PREEDIT_MASK;
    494             } // else we already at tail
    495         } else {
    496             m_CursorIdx = 0;
    497             ++m_CursorBone;
    498             changeMask |= PREEDIT_MASK;
    499         }
    500     }
    501 }
    502 
    503 void
    504 CIMIClassicView::moveEnd(unsigned int& changeMask)
    505 {
    506     CSkeletonIter itLast = m_pIC->getLastBone();
    507     if (m_CursorBone != itLast) {
    508         CSkeletonIter itPrev = --CSkeletonIter(itLast);
    509         if (m_CursorBone != itPrev) {
    510             m_CursorBone = itPrev;
    511             m_CursorIdx = 0;
    512         }
    513         moveRightSyllable(changeMask);
    514     }
    515 }
    516 
    517 void
    518 CIMIClassicView::erase(bool bLeft, unsigned int& changeMask)
    519 {
    520     if (bLeft && m_CursorBone == m_pIC->getFirstBone() && m_CursorIdx == 0) return;
    521     if (!bLeft && cursorAtTail()) return;
    522 
    523     // default stick direction here
    524     bool stickLeft = bLeft;
    525 
    526     if (bLeft)  moveLeft(changeMask);
    527 
    528     changeMask |= PREEDIT_MASK;
    529     if (m_CursorBone->m_String.size() == m_CursorIdx){
    530         if (m_CursorBone->m_BoundaryType == CBone::USER_BOUNDARY) {
    531             CSkeleton skel;
    532             CSkeletonIter old_cursor = m_CursorBone;
    533             skel.push_front(*old_cursor);
    534             skel.front().m_BoundaryType = CBone::AUTO_BOUNDARY;
    535             m_CursorBone = skel.begin();
    536             bool upd_candi = m_pIC->modifyAndReseg(
    537                         old_cursor, ++CSkeletonIter(old_cursor), skel,
    538                         m_CursorBone, m_CursorIdx, m_CandiBone,
    539                         stickLeft, true
    540                     );
    541             if (upd_candi) getCandidates();
    542             changeMask |= CANDIDATE_MASK;
    543         } else {
    544             if (!bLeft) moveRight(changeMask);
    545         }
    546     } else {
    547         CSkeleton skel;
    548         CSkeletonIter old_cursor = m_CursorBone;
    549         skel.push_front(*old_cursor);
    550         skel.front().m_String.erase(m_CursorIdx, 1);
    551         if (skel.front().m_String.size() == 0) {
    552             skel.pop_front();
    553             m_CursorBone = skel.end();
    554             m_CursorIdx = 0;
    555         } else {
    556             if (m_CursorIdx == 0)
    557                 stickLeft = false;
    558             else if (m_CursorIdx == m_CursorBone->m_String.size()-1)
    559                 stickLeft = true;
    560             m_CursorBone = skel.begin();
    561         }
    562         bool upd_candi = m_pIC->modifyAndReseg(
    563                     old_cursor, ++CSkeletonIter(old_cursor), skel,
    564                     m_CursorBone, m_CursorIdx, m_CandiBone,
    565                     stickLeft, true
    566                 );
    567         if (upd_candi) getCandidates();
    568         changeMask |= CANDIDATE_MASK;
    569     }
    570 }
    571 
    572 void
    573 CIMIClassicView::insertPinyin(unsigned int keyvalue, unsigned int& changeMask)
    574 {
    575     CSkeleton skel;
    576     CSkeletonIter cursor1 = m_CursorBone, cursor2 = m_CursorBone;
    577 
    578     changeMask |= PREEDIT_MASK | CANDIDATE_MASK;
    579     if (m_CursorBone == m_pIC->getLastBone()) {
    580         skel.push_front(CBone((const TWCHAR*)&keyvalue, 1, CBone::AUTO_BOUNDARY, CBone::NODE_PINYIN));
    581         m_CursorBone = skel.begin();
    582         m_CursorIdx = 1;
    583     } else if (!m_CursorBone->isPinyinNode()){
    584         // The cursor could not on the tail of non-pinyin nodes
    585         if (m_CursorIdx > 0) {
    586             ++cursor2;
    587             skel.push_back(CBone(*m_CursorBone));
    588             skel.back().m_String.erase(m_CursorIdx);
    589             skel.push_back(CBone((const TWCHAR*)&keyvalue, 1, CBone::AUTO_BOUNDARY, CBone::NODE_PINYIN));
    590             skel.push_back(CBone(*m_CursorBone));
    591             skel.back().m_String.erase(0, m_CursorIdx);
    592             m_CursorBone = skel.begin(); ++m_CursorBone;
    593             m_CursorIdx = 1;
    594         } else {
    595             skel.push_front(CBone((const TWCHAR*)&keyvalue, 1, CBone::AUTO_BOUNDARY, CBone::NODE_PINYIN));
    596             m_CursorBone = skel.begin();
    597             m_CursorIdx = 0;
    598         }
    599     } else {
    600         skel.push_front(*m_CursorBone);
    601         skel.front().m_String.insert(m_CursorIdx, 1, TWCHAR(keyvalue));
    602         m_CursorBone = skel.begin();
    603         ++m_CursorIdx;
    604         ++cursor2;
    605     }
    606 
    607     bool upd_candi = m_pIC->modifyAndReseg(cursor1, cursor2, skel, m_CursorBone, m_CursorIdx, m_CandiBone);
    608 
    609     if (upd_candi) getCandidates();
    610 }
    611 
    612 void
    613 CIMIClassicView::insertBoundary(unsigned int& changeMask)
    614 {
    615     CSkeletonIter itLast = m_pIC->getLastBone();
    616     CSkeletonIter itFirst = m_pIC->getFirstBone();
    617 
    618     if (m_CursorIdx == 0) {
    619         if (m_CursorBone != itFirst) {
    620             CSkeletonIter itPrev = --CSkeletonIter(m_CursorBone);
    621             if (itPrev->isPinyinNode() && itPrev->isAutoBoundary()) {
    622                 changeMask |= PREEDIT_MASK; // Only the color of the boundary needed to be updated
    623                 itPrev->m_BoundaryType = CBone::USER_BOUNDARY;
    624             }
    625         }
    626     } else if (m_CursorBone != itLast && m_CursorIdx == m_CursorBone->m_String.size()) {
    627         if (m_CursorBone->isAutoBoundary()) { // hidden condition: it is a pinyin node
    628             changeMask |= PREEDIT_MASK; // Only the color of the boundary needed to be updated
    629             m_CursorBone->m_BoundaryType = CBone::USER_BOUNDARY;
    630             moveRight(changeMask);
    631         }
    632     } else if (m_CursorBone != itLast && m_CursorIdx < m_CursorBone->m_String.size()) {
    633         if (m_CursorBone->isPinyinNode()) {
    634             changeMask |= PREEDIT_MASK | CANDIDATE_MASK;
    635 
    636             if (m_CursorBone != itFirst) {
    637                 bool saveNonCompleteSyllable = m_pIC->canNonCompleteSyllable();
    638                 m_pIC->setNonCompleteSyllable(false);
    639                 do { // whether current syllable should be connected with the previous one.
    640                     CSkeletonIter itPrev = --CSkeletonIter(m_CursorBone);
    641                     if (!itPrev->isAutoBoundary()) break;
    642 
    643                     std::list<CBone> newBones;
    644                     wstring newSyllable;
    645 
    646                     newSyllable.append(m_CursorBone->m_String, 0, m_CursorIdx);
    647                     m_pIC->segPinyinSimplest(newSyllable, newBones);
    648                     if ( !(newBones.size() == 0 || newBones.front().m_BoneType == CBone::NODE_INCOMPLETE_PINYIN) ) break;
    649 
    650                     newBones.clear();
    651                     newSyllable.insert(0, itPrev->m_String);
    652                     m_pIC->segPinyinSimplest(newSyllable, newBones);
    653                     if (newBones.size() != 1 || newBones.front().m_BoneType != CBone::NODE_PINYIN) break;
    654 
    655                     m_pIC->setNonCompleteSyllable(saveNonCompleteSyllable);
    656 
    657                     newBones.front().m_BoundaryType = CBone::USER_BOUNDARY;
    658                     m_pIC->modify(itPrev, m_CursorBone, newBones);
    659                     if (m_CandiBone == itPrev)
    660                         m_CandiBone = --CSkeletonIter(m_CursorBone);
    661 
    662                     CSkeleton skel;
    663                     CSkeletonIter cursor1 = m_CursorBone;
    664                     CSkeletonIter cursor2 = m_CursorBone;
    665                     ++cursor2;
    666 
    667                     skel.push_back(CBone(*m_CursorBone));
    668                     skel.back().m_String.erase(0, m_CursorIdx);
    669                     m_CursorBone = skel.begin();
    670                     m_CursorIdx = 0;
    671                     m_pIC->modifyAndReseg(
    672                             cursor1, cursor2, skel,
    673                             m_CursorBone, m_CursorIdx, m_CandiBone, false
    674                         );
    675                     getCandidates();
    676                     return;
    677                 } while (false);
    678                 m_pIC->setNonCompleteSyllable(saveNonCompleteSyllable);
    679             }
    680 
    681             CSkeleton skel;
    682             CSkeletonIter cursor1 = m_CursorBone;
    683             CSkeletonIter cursor2 = m_CursorBone;
    684             ++cursor2;
    685 
    686             skel.push_back(CBone(*m_CursorBone));
    687             skel.back().m_String.erase(m_CursorIdx);
    688             skel.back().m_BoundaryType = CBone::USER_BOUNDARY;
    689             skel.push_back(CBone(*m_CursorBone));
    690             skel.back().m_String.erase(0, m_CursorIdx);
    691             m_CursorBone = skel.begin();
    692             ++m_CursorBone;
    693             m_CursorIdx = 0;
    694             bool upd_candi = m_pIC->modifyAndReseg(
    695                     cursor1, cursor2, skel,
    696                     m_CursorBone, m_CursorIdx, m_CandiBone, false
    697                 );
    698             if (upd_candi) getCandidates();
    699         }
    700     }
    701 }
    702 
    703 int
    704 CIMIClassicView::getSentence(wstring& wstr, CSkeletonIter itStart)
    705 {
    706     return m_pIC->getBestSentence(wstr, itStart, m_pIC->getLastBone());
    707 }
    708 
    709 void
    710 CIMIClassicView::insertNormalChar(int boneType, unsigned originalKey, unsigned keyvalue, unsigned int& mask)
    711 {
    712     CSkeleton skel;
    713     CSkeletonIter cursor1;
    714     CSkeletonIter cursor2;
    715     CSkeletonIter ite = m_pIC->getLastBone();
    716     mask |= PREEDIT_MASK | CANDIDATE_MASK;
    717     if (m_CursorBone == ite || m_CursorBone->m_String.size() == 0) {
    718         cursor1 = cursor2 = m_CursorBone;
    719         skel.push_back(CBone((TWCHAR*)&keyvalue, 1, originalKey, boneType));
    720     } else if (m_CursorBone != ite && m_CursorBone->m_String.size() == m_CursorIdx) {
    721         cursor1 = cursor2 = ++m_CursorBone;
    722         skel.push_back(CBone((TWCHAR*)&keyvalue, 1, originalKey, boneType));
    723         m_CursorBone = skel.begin();
    724         ++m_CursorBone;
    725         m_CursorIdx = 0;
    726 
    727     } else {
    728         cursor1 = cursor2 = m_CursorBone; ++cursor2;
    729         skel.push_back(CBone(*m_CursorBone));
    730         skel.back().m_String.erase(m_CursorIdx);
    731         skel.push_back(CBone((TWCHAR*)&keyvalue, 1, originalKey, boneType));
    732         skel.push_back(CBone(*m_CursorBone));
    733         skel.back().m_String.erase(0, m_CursorIdx);
    734         m_CursorBone = skel.begin();
    735         ++(++m_CursorBone);
    736         m_CursorIdx = 0;
    737     }
    738     bool upd_candi = m_pIC->modifyAndReseg(cursor1, cursor2, skel, m_CursorBone,
    739                                            m_CursorIdx, m_CandiBone, false);
    740     if (upd_candi) getCandidates();
    741 }
    742 
    743 void
    744 CIMIClassicView::getCandidates()
    745 {
    746     m_CandiFirst = 0;
    747     m_pIC->getCandidates(m_CandiBone, m_CandiList);
    748 }
    749 
    750 bool
    751 CIMIClassicView::cursorAtTail()
    752 {
    753     CSkeletonIter itLast = m_pIC->getLastBone();
    754     if (m_CursorBone == itLast)
    755         return true;
    756     CSkeletonIter itNext = ++CSkeletonIter(m_CursorBone);
    757     return (itNext == itLast && m_CursorBone->m_String.size() == m_CursorIdx && !m_CursorBone->isUserBoundary());
    758     // Hidden condition m_CursorBone->isPinyinNode() == true
    759 }
    760 
    761 void
    762 CIMIClassicView::makeSelection(int idx, unsigned int& mask)
    763 {
    764     idx += m_CandiFirst;
    765     if (m_TailSentence.size() > 0) --idx;
    766     if (idx < 0) {
    767         mask |= PREEDIT_MASK | CANDIDATE_MASK;
    768         doCommit(true);
    769         clearIC();
    770     } else if (idx < m_CandiList.size()) {
    771         mask |= PREEDIT_MASK | CANDIDATE_MASK;
    772         CCandidate& cand = m_CandiList[idx];
    773         m_pIC->makeSelection(cand);
    774         m_CandiBone = cand.m_BoneEnd;
    775         while (!m_CandiBone->isTailNode() && !m_CandiBone->isPinyinNode())
    776             ++m_CandiBone;
    777         if (m_CandiBone->isTailNode()) {
    778             doCommit(true);
    779             clearIC();
    780         } else {
    781             for (CSkeletonIter it=cand.m_BoneStart; it != m_CandiBone; ++it) {
    782                 if (m_CursorBone == it) {
    783                     m_CursorBone = m_CandiBone;
    784                     m_CursorIdx = 0;
    785                     break;
    786                 }
    787             }
    788             m_CandiFirst = 0;
    789             getCandidates();
    790         }
    791     }
    792 }
    793 
    794 void
    795 CIMIClassicView::getPreeditString(IPreeditString& ps)
    796 {
    797     ps.clear();
    798 
    799     wstring & wstr = ps.getString();
    800     IPreeditString::CCharTypeVec& charTypes = ps.getCharTypeVec();
    801 
    802     m_pIC->getBestSentence(wstr, m_pIC->getFirstBone(), m_CandiBone);
    803 
    804     int caret = wstr.size();
    805     charTypes.reserve(caret);
    806     for (int i=0; i < caret; ++i)
    807         charTypes.push_back(IPreeditString::HANZI_CHAR | IPreeditString::USER_CHOICE);
    808     ps.setCandiStart(caret);
    809 
    810     CSkeletonIter ite = m_pIC->getLastBone();
    811     for (CSkeletonIter it = m_CandiBone; it != ite; ) {
    812         CSkeletonIter curIt = it++;
    813         if (curIt == m_CursorBone)
    814             caret = wstr.size() + m_CursorIdx;
    815 
    816         if (curIt->isPinyinNode())
    817             wstr += curIt->m_String;
    818         else
    819             wstr.push_back((unsigned)curIt->m_BoundaryType);
    820 
    821         int ct = IPreeditString::PINYIN_CHAR;
    822         if (curIt->m_BoneType == CBone::NODE_INVALID_PINYIN)
    823             ct |= IPreeditString::ILLEGAL;
    824         else if (curIt->m_BoneType == CBone::NODE_ASCII)
    825             ct = IPreeditString::ASCII_CHAR;
    826         else if (!curIt->isPinyinNode())
    827             ct = IPreeditString::SIMBOL_CHAR;
    828 
    829         for (int i=0; i < curIt->m_String.size(); ++i)
    830             charTypes.push_back(ct);
    831 
    832         if (curIt->isPinyinNode()) {
    833             if (curIt->m_BoundaryType == CBone::USER_BOUNDARY) {
    834                 wstr += '\'';
    835                 charTypes.push_back(IPreeditString::BOUNDARY);
    836             } else if (it != ite) {
    837                 wstr += ' ';
    838                 charTypes.push_back(IPreeditString::BOUNDARY | IPreeditString::USER_CHOICE);
    839             }
    840         }
    841     }
    842     if (m_CursorBone == ite)
    843         caret = wstr.size();
    844     ps.setCaret(caret);
    845 }
    846 
    847 void
    848 CIMIClassicView::getCandidateList(ICandidateList& cl, int start, int size)
    849 {
    850     cl.clear();
    851     cl.reserve(size);
    852 
    853     int  tscount = (m_TailSentence.size() > 0)?1:0;
    854 
    855     cl.setFirst(start);
    856     cl.setTotal(tscount + m_CandiList.size());
    857     ICandidateList::CCandiStrings& css = cl.getCandiStrings();
    858     ICandidateList::CCandiTypeVec& cts = cl.getCandiTypeVec();
    859 
    860     //Loop used for future n-best sentence candidates usage
    861     for (; start < tscount && size > 0; ++start, --size) {
    862         css.push_back(m_TailSentence);
    863         cts.push_back(ICandidateList::BEST_TAIL);
    864     }
    865 
    866     start -= tscount;
    867     for (int sz=m_CandiList.size(); start < sz && size > 0; ++start, --size) {
    868         css.push_back(m_CandiList[start].m_String);
    869         cts.push_back( (start == 0)?(ICandidateList::BEST_WORD):(ICandidateList::NORMAL_WORD) );
    870     }
    871 }
    872