Tizen 2.1 base
[platform/core/uifw/ise-engine-sunpinyin.git] / src / ime-core / imi_view_classic.cpp
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()
48     : CIMIView(), m_cursorFrIdx(0), m_candiFrIdx(0),
49       m_candiPageFirst(0)
50 {
51 }
52
53 CIMIClassicView::~CIMIClassicView()
54 {
55 }
56
57 size_t CIMIClassicView::top_candidate_threshold = 5;
58
59 void
60 CIMIClassicView::attachIC(CIMIContext* pIC)
61 {
62     CIMIView::attachIC(pIC);
63     clearIC();
64 }
65
66 unsigned
67 CIMIClassicView::clearIC(void)
68 {
69     if (!m_pIC->isEmpty()) {
70         m_cursorFrIdx = m_candiFrIdx = m_candiPageFirst = 0;
71
72         m_pIC->clear();
73         m_pPySegmentor->clear();
74         m_candiList.clear();
75         //m_tailSentence.clear ();
76         m_sentences.clear();
77         m_tails.clear();
78         return PREEDIT_MASK | CANDIDATE_MASK;
79     }
80     return 0;
81 }
82
83 void
84 CIMIClassicView::updateWindows(unsigned mask)
85 {
86     if (!m_pWinHandler)
87         return;
88
89     if (mask & PREEDIT_MASK) {
90         m_uiPreeditString.clear();
91         getPreeditString(m_uiPreeditString);
92         // m_pWinHandler->updatePreedit(&ps);
93         handlerUpdatePreedit(&m_uiPreeditString);
94     }
95
96     if ((mask & PREEDIT_MASK) || (mask & CANDIDATE_MASK)) {
97         // calculate all possible best sentences
98         m_sentences.clear();
99         int best_rank = -1;
100         for (size_t i = 0; i < m_pIC->getNBest(); i++) {
101             wstring sentence;
102             unsigned word_num = m_pIC->getBestSentence(sentence, i,
103                                                        m_candiFrIdx);
104             if (word_num == 0) goto pass;  // when sentence is not worthy of
105
106 #ifdef DEBUG
107             printf("%d ", i);
108             print_wide(sentence.c_str());
109             printf("\n");
110 #endif
111
112             for (size_t j = 0; j < m_sentences.size(); j++) {
113                 if (sentence == m_sentences[j].second) goto pass;
114             }
115
116             if (best_rank < 0 && word_num > 1) {
117                 best_rank = i;
118             }
119             m_sentences.push_back(std::make_pair(i, sentence));
120         pass:
121             continue;
122         }
123         // build all possible tails with best_rank
124         std::vector<CCandidates> tails =
125             m_pIC->getBestSentenceTails(best_rank, m_candiFrIdx);
126         m_tails.clear();
127         for (size_t i = 0; i < tails.size(); i++) {
128             CCandidates& tail = tails[i];
129             wstring tail_text;
130             // translate this tail into text
131             for (size_t j = 0; j < tail.size(); j++) {
132                 tail_text += tail[j].m_cwstr;
133             }
134             m_tails.push_back(std::make_pair(tail_text, tail));
135         }
136     }
137
138     if (mask & CANDIDATE_MASK) {
139         m_uiCandidateList.clear();
140         getCandidateList(m_uiCandidateList, m_candiPageFirst,
141                          m_candiWindowSize);
142         // m_pWinHandler->updateCandidates(&cl);
143         handlerUpdateCandidates(&m_uiPreeditString, &m_uiCandidateList);
144     }
145 }
146
147 bool
148 CIMIClassicView::onKeyEvent(const CKeyEvent& key)
149 {
150     unsigned changeMasks = 0;
151
152     unsigned keycode = key.code;
153     unsigned keyvalue = key.value;
154     unsigned modifiers = key.modifiers;
155
156 #ifdef DEBUG
157     printf("Classic View got a key (0x%x-0x%x-0x%x)...",
158            keycode, keyvalue, modifiers);
159     if (((modifiers & IM_CTRL_MASK) != 0) &&
160         (keyvalue == 'P' || keyvalue == 'p'))
161         m_pIC->printLattice();
162 #endif
163
164     if (m_pHotkeyProfile && m_pHotkeyProfile->isModeSwitchKey(key)) {
165         setStatusAttrValue(CIMIWinHandler::STATUS_ID_CN, (!m_bCN) ? 1 : 0);
166         if (!m_pIC->isEmpty()) {
167             changeMasks |= CANDIDATE_MASK | PREEDIT_MASK;
168             clearIC();
169         }
170     } else if (m_pHotkeyProfile && m_pHotkeyProfile->isPunctSwitchKey(key)) {
171         // On CTRL+. switch Full/Half punc
172         changeMasks |= KEYEVENT_USED;
173         setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLPUNC,
174                            (!m_bFullPunct) ? 1 : 0);
175     } else if (m_pHotkeyProfile && m_pHotkeyProfile->isSymbolSwitchKey(key)) {
176         // On SHIFT+SPACE switch Full/Half symbol
177         changeMasks |= KEYEVENT_USED;
178         setStatusAttrValue(CIMIWinHandler::STATUS_ID_FULLSYMBOL,
179                            (!m_bFullSymbol) ? 1 : 0);
180     } else if (modifiers == IM_CTRL_MASK && keycode == IM_VK_LEFT) {
181         // move left
182         if (!m_pIC->isEmpty()) {
183             changeMasks |= KEYEVENT_USED;
184             _moveLeft(changeMasks);
185         }
186     } else if (modifiers == IM_CTRL_MASK && keycode == IM_VK_RIGHT) {
187         // move right
188         if (!m_pIC->isEmpty()) {
189             changeMasks |= KEYEVENT_USED;
190             _moveRight(changeMasks);
191         }
192     } else if (((modifiers == 0 && keycode == IM_VK_PAGE_UP)
193                 || (m_pHotkeyProfile && m_pHotkeyProfile->isPageUpKey(key)))
194                && !m_pIC->isEmpty()) {
195         changeMasks |= KEYEVENT_USED;
196         if (m_candiPageFirst > 0) {
197             m_candiPageFirst -= m_candiWindowSize;
198             if (m_candiPageFirst < 0) m_candiPageFirst = 0;
199             changeMasks |= CANDIDATE_MASK;
200         }
201     } else if (((modifiers == 0 && keycode == IM_VK_PAGE_DOWN)
202                 || (m_pHotkeyProfile && m_pHotkeyProfile->isPageDownKey(key)))
203                && !m_pIC->isEmpty()) {
204         changeMasks |= KEYEVENT_USED;
205         if (m_candiPageFirst + m_candiWindowSize < candidateListSize()) {
206             m_candiPageFirst += m_candiWindowSize;
207             changeMasks |= CANDIDATE_MASK;
208         }
209     } else if (m_pHotkeyProfile
210               && m_pHotkeyProfile->isCandiDeleteKey(key, m_candiWindowSize)
211               && !m_pIC->isEmpty()) {
212         changeMasks |= KEYEVENT_USED;
213         unsigned sel = (keyvalue == '0' ? 9 : keyvalue - '1');
214         deleteCandidate(sel, changeMasks);
215         goto PROCESSED;
216     } else if ((modifiers &
217                 (IM_CTRL_MASK | IM_ALT_MASK | IM_SUPER_MASK |
218                  IM_RELEASE_MASK)) == 0) {
219         if ((keyvalue >= '0' && keyvalue <= '9')
220             && (m_candiWindowSize >= 10
221                 || keyvalue < ('1' + m_candiWindowSize))) {
222             // try to make selection
223             if (!m_pIC->isEmpty()) {
224                 changeMasks |= KEYEVENT_USED;
225                 unsigned sel = (keyvalue == '0' ? 9 : keyvalue - '1');
226                 makeSelection(sel, changeMasks);
227             } else if (m_smartPunct) {
228                 m_pIC->omitNextPunct();
229             }
230             goto PROCESSED;
231         }
232
233         if (keyvalue > 0x60 && keyvalue < 0x7b) {
234             /* islower(keyvalue) */
235             changeMasks |= KEYEVENT_USED;
236             _insert(keyvalue, changeMasks);
237         } else if (keyvalue > 0x20 && keyvalue < 0x7f) {
238             /* isprint(keyvalue) && !isspace(keyvalue) */
239             changeMasks |= KEYEVENT_USED;
240             if (m_pIC->isEmpty()) {
241                 _insert(keyvalue, changeMasks);
242                 _doCommit();
243                 clearIC();
244             } else {
245                 _insert(keyvalue, changeMasks);
246             }
247         } else if (keycode == IM_VK_BACK_SPACE || keycode == IM_VK_DELETE) {
248             if (!m_pIC->isEmpty()) {
249                 changeMasks |= KEYEVENT_USED;
250                 _erase(keycode == IM_VK_BACK_SPACE, changeMasks);
251             }
252         } else if (keycode == IM_VK_SPACE) {
253             if (!m_pIC->isEmpty()) {
254                 changeMasks |= KEYEVENT_USED;
255                 makeSelection(0, changeMasks);
256             } else {
257                 wstring wstr = (m_pIC->fullPuncOp())(keyvalue);
258                 if (wstr.size()) {
259                     _commitString(wstr);
260                     changeMasks |= KEYEVENT_USED;
261                 }
262             }
263         } else if (keycode == IM_VK_ENTER) {
264             if (!m_pIC->isEmpty()) {
265                 changeMasks |= KEYEVENT_USED | CANDIDATE_MASK | PREEDIT_MASK;
266                 _doCommit(false);
267                 clearIC();
268             }
269         } else if (keycode == IM_VK_ESCAPE) {
270             if (!m_pIC->isEmpty()) {
271                 changeMasks |= KEYEVENT_USED | CANDIDATE_MASK | PREEDIT_MASK;
272                 clearIC();
273             }
274         } else if (keycode == IM_VK_LEFT) { // move left syllable
275             if (!m_pIC->isEmpty()) {
276                 changeMasks |= KEYEVENT_USED;
277                 _moveLeftSyllable(changeMasks);
278             }
279         } else if (keycode == IM_VK_RIGHT) { // move right syllable
280             if (!m_pIC->isEmpty()) {
281                 changeMasks |= KEYEVENT_USED;
282                 _moveRightSyllable(changeMasks);
283             }
284         } else if (keycode == IM_VK_HOME) { // move home
285             if (!m_pIC->isEmpty()) {
286                 changeMasks |= KEYEVENT_USED;
287                 _moveHome(changeMasks);
288             }
289         } else if (keycode == IM_VK_END) { // move end
290             if (!m_pIC->isEmpty()) {
291                 changeMasks |= KEYEVENT_USED;
292                 _moveEnd(changeMasks);
293             }
294         }
295     } else {
296         goto RETURN;
297     }
298
299 PROCESSED:;
300     m_pHotkeyProfile->rememberLastKey(key);
301
302 RETURN:;
303
304 #ifdef DEBUG
305     printf("   |-->(Mask=0x%x)\n", changeMasks);
306 #endif
307
308     updateWindows(changeMasks);
309     return changeMasks & KEYEVENT_USED;
310 }
311
312 int
313 CIMIClassicView::onCandidatePageRequest(int pgno, bool relative)
314 {
315     unsigned changeMasks = 0;
316     int ncandi, lastpgidx;
317
318     if (!m_pIC->isEmpty()) {
319         changeMasks |= KEYEVENT_USED;
320         size_t sz = candidateListSize();
321         lastpgidx = (sz - 1) / m_candiWindowSize * m_candiWindowSize;
322         if (relative == true) {
323             ncandi = m_candiPageFirst + pgno * m_candiWindowSize;
324             if (ncandi >= (int) sz)
325                 ncandi = lastpgidx;
326             if (ncandi < 0)
327                 ncandi = 0;
328             if (ncandi != (int) m_candiPageFirst) {
329                 m_candiPageFirst = ncandi;
330                 changeMasks |= CANDIDATE_MASK;
331             }
332         } else {
333             if (pgno == -1) { //last page
334                 ncandi = lastpgidx;
335             } else {
336                 ncandi = pgno * m_candiWindowSize;
337                 if (ncandi > lastpgidx)
338                     ncandi = lastpgidx;
339             }
340             if (ncandi != (int) m_candiPageFirst) {
341                 m_candiPageFirst = ncandi;
342                 changeMasks |= CANDIDATE_MASK;
343             }
344         }
345     }
346
347     updateWindows(changeMasks);
348     return 0;
349 }
350
351 int
352 CIMIClassicView::onCandidateSelectRequest(int index)
353 {
354     unsigned changeMasks = 0;
355
356     if (!m_pIC->isEmpty())
357         makeSelection(index, changeMasks);
358
359     updateWindows(changeMasks);
360     return 0;
361 }
362
363 void
364 CIMIClassicView::getPreeditString(IPreeditString& ps)
365 {
366     ps.clear();
367
368     wstring &wstr = ps.getString();
369     IPreeditString::CCharTypeVec& charTypes = ps.getCharTypeVec();
370
371     m_pIC->getSelectedSentence(wstr, 0, m_candiFrIdx);
372
373     int caret = wstr.size();
374     charTypes.reserve(caret);
375     for (int i = 0; i < caret; ++i)
376         charTypes.push_back(
377             IPreeditString::HANZI_CHAR | IPreeditString::USER_CHOICE);
378
379     const wstring& pystr = m_pPySegmentor->getInputBuffer();
380     std::vector<unsigned>& seg_path = m_pIC->getBestSegPath();
381
382     if (pystr.empty())
383         return;
384
385     CLattice& lattice = m_pIC->getLattice();
386     unsigned last = 0, len = 0;
387     for (std::vector<unsigned>::iterator it = seg_path.begin();
388          it != seg_path.end(); ++it) {
389         len = *it - last;
390         if (*it <= m_candiFrIdx) {
391             last = *it;
392             continue;
393         }
394         if (last < m_cursorFrIdx && m_cursorFrIdx <= last + len)
395             caret = wstr.size() + (m_cursorFrIdx - last);
396
397         CLatticeFrame &fr = lattice[last + len];
398         int ct = IPreeditString::PINYIN_CHAR;
399         if (fr.isSyllableSepFrame()) {
400             ct = IPreeditString::BOUNDARY | IPreeditString::USER_CHOICE;
401         } else if (fr.m_type == CLatticeFrame::ASCII) {
402             ct = IPreeditString::ASCII_CHAR;
403         } else if (fr.m_type == CLatticeFrame::SYMBOL) {
404             ct = IPreeditString::SYMBOL_CHAR;
405         }
406
407         wstr.insert(wstr.end(), pystr.begin() + last,
408                     pystr.begin() + last + len);
409         for (size_t c = 0; c < len; ++c)
410             charTypes.push_back(ct);
411
412         if (fr.isSyllableFrame() && !fr.isSyllableSepFrame()) {
413             if (it != seg_path.end() - 1
414                 && !lattice[last + len + 1].isSyllableSepFrame()) {
415                 wstr.push_back(' ');
416                 charTypes.push_back(IPreeditString::BOUNDARY);
417             }
418         }
419         last = *it;
420     }
421     // segment path might be missing or incomplete, therefore append the input
422     // buffer left to wstr
423     wstr.insert(wstr.end(), pystr.begin() + last, pystr.end());
424
425     ps.setCaret(caret);
426 }
427
428 void
429 CIMIClassicView::getCandidateList(ICandidateList& cl, int start, int size)
430 {
431     cl.clear();
432     cl.setSize(size);
433
434     cl.setFirst(start);
435     cl.setTotal(candidateListSize());
436
437     // sentences
438     for (size_t i = 0; i < m_sentences.size(); i++) {
439         cl.pushBackCandidate(m_sentences[i].second,
440                              ICandidateList::BEST_TAIL, i);
441     }
442     // possible tails
443     for (size_t i = 0; i < m_tails.size(); i++) {
444         cl.pushBackCandidate(m_tails[i].first,
445                              ICandidateList::OTHER_BEST_TAIL, i);
446     }
447     // word candidates
448     for (size_t i = 0; i < m_candiList.size(); i++) {
449         if (i == 0) {
450             cl.pushBackCandidate(m_candiList[i].m_cwstr,
451                                  ICandidateList::BEST_WORD, i);
452         } else {
453             cl.pushBackCandidate(m_candiList[i].m_cwstr,
454                                  ICandidateList::NORMAL_WORD, i);
455         }
456     }
457 }
458
459 void
460 CIMIClassicView::_insert(unsigned keyvalue, unsigned &changeMasks)
461 {
462     changeMasks |= KEYEVENT_USED;
463
464     if (m_pPySegmentor->getInputBuffer().size() >= MAX_LATTICE_LENGTH - 1)
465         return;
466
467     if (m_cursorFrIdx == m_pIC->getLastFrIdx())
468         m_pPySegmentor->push(keyvalue);
469     else
470         m_pPySegmentor->insertAt(m_cursorFrIdx, keyvalue);
471
472     m_cursorFrIdx++;
473
474     if (m_pIC->buildLattice(m_pPySegmentor))
475         _getCandidates();
476
477     changeMasks |= PREEDIT_MASK | CANDIDATE_MASK;
478 }
479
480 void
481 CIMIClassicView::_erase(bool backward, unsigned &changeMasks)
482 {
483     if (backward) {
484         // if possible to cancel the last selection
485         if (m_backspaceCancel) {
486             if (m_candiFrIdx > 0) {
487                 m_candiFrIdx = m_pIC->cancelSelection(m_candiFrIdx, true);
488                 goto done;
489             }
490         }
491         if (m_cursorFrIdx == m_pIC->getLastFrIdx()) {
492             m_pPySegmentor->pop();
493         } else if (m_cursorFrIdx > 0) {
494             m_pPySegmentor->deleteAt(m_cursorFrIdx - 1, backward);
495         } else {
496             return;
497         }
498         _moveLeft(changeMasks, true);
499     } else {
500         if (m_cursorFrIdx == m_pIC->getLastFrIdx() - 1) {
501             m_pPySegmentor->pop();
502         } else if (m_cursorFrIdx < m_pIC->getLastFrIdx() - 1) {
503             m_pPySegmentor->deleteAt(m_cursorFrIdx - 1, backward);
504         } else {
505             return;
506         }
507     }
508 done:
509     if (m_pIC->buildLattice(m_pPySegmentor))
510         _getCandidates();
511
512     changeMasks |= PREEDIT_MASK | CANDIDATE_MASK | KEYEVENT_USED;
513 }
514
515 void
516 CIMIClassicView::_getCandidates()
517 {
518     m_candiPageFirst = 0;
519     m_pIC->getCandidates(m_candiFrIdx, m_candiList);
520 }
521
522 void
523 CIMIClassicView::_commitChar(TWCHAR ch)
524 {
525     TWCHAR wa[2] = { ch, 0 };
526     m_pWinHandler->commit(wa);
527 }
528
529 void
530 CIMIClassicView::_commitString(const wstring& wstr)
531 {
532     m_pWinHandler->commit(wstr.c_str());
533 }
534
535 void
536 CIMIClassicView::_doCommit(bool bConvert)
537 {
538     wstring bs;
539
540     if (bConvert) {
541         m_pIC->memorize();
542         m_pIC->getSelectedSentence(bs);
543         handlerCommit(bs.c_str());
544     } else {
545         bs += m_pPySegmentor->getInputBuffer();
546         handlerCommit(bs.c_str());
547     }
548 }
549
550 unsigned
551 CIMIClassicView::_moveLeft(unsigned& mask, bool searchAgain)
552 {
553     if (m_cursorFrIdx == 0)
554         return _moveEnd(mask);
555
556     mask |= PREEDIT_MASK;
557     if (m_cursorFrIdx == m_candiFrIdx) {
558         mask |= CANDIDATE_MASK;
559         m_candiFrIdx = m_pIC->cancelSelection(m_candiFrIdx, searchAgain);
560         _getCandidates();
561     }
562
563     return --m_cursorFrIdx;
564 }
565
566 unsigned
567 CIMIClassicView::_moveLeftSyllable(unsigned& mask, bool searchAgain)
568 {
569     if (m_cursorFrIdx == 0)
570         return _moveEnd(mask);
571
572     mask |= PREEDIT_MASK;
573
574     if (m_cursorFrIdx == m_candiFrIdx) {
575         mask |= CANDIDATE_MASK;
576         m_candiFrIdx = m_pIC->cancelSelection(m_candiFrIdx, searchAgain);
577         _getCandidates();
578     }
579
580     std::vector<unsigned>& seg_path = m_pIC->getBestSegPath();
581     std::vector<unsigned>::iterator it =
582         std::upper_bound(seg_path.begin(), seg_path.end(), m_cursorFrIdx - 1);
583     return m_cursorFrIdx = *(--it);
584 }
585
586 unsigned
587 CIMIClassicView::_moveHome(unsigned& mask, bool searchAgain)
588 {
589     if (m_cursorFrIdx == 0)
590         return 0;
591
592     mask |= PREEDIT_MASK;
593
594     if (m_candiFrIdx != 0) {
595         std::vector<unsigned>& best_path = m_pIC->getBestPath();
596         std::vector<unsigned>::iterator it = best_path.begin();
597         std::vector<unsigned>::iterator ite = best_path.end();
598         CLattice& lattice = m_pIC->getLattice();
599
600         for (; it != ite; ++it) {
601             if (lattice[*it].m_bwType & CLatticeFrame::USER_SELECTED)
602                 m_pIC->cancelSelection(*it, false);
603         }
604
605         mask |= CANDIDATE_MASK;
606         m_candiFrIdx = 0;
607         _getCandidates();
608
609         if (searchAgain) m_pIC->searchFrom();
610     }
611
612     m_cursorFrIdx = 0;
613     return 0;
614 }
615
616 unsigned
617 CIMIClassicView::_moveRight(unsigned& mask)
618 {
619     if (m_cursorFrIdx < m_pIC->getLastFrIdx()) {
620         mask |= PREEDIT_MASK;
621         ++m_cursorFrIdx;
622         return m_cursorFrIdx;
623     }
624
625     return _moveHome(mask);
626 }
627
628 unsigned
629 CIMIClassicView::_moveRightSyllable(unsigned& mask)
630 {
631     if (m_cursorFrIdx < m_pIC->getLastFrIdx()) {
632         mask |= PREEDIT_MASK;
633
634         std::vector<unsigned>& seg_path = m_pIC->getBestSegPath();
635         std::vector<unsigned>::iterator it = std::upper_bound(
636             seg_path.begin(), seg_path.end(), m_cursorFrIdx);
637         m_cursorFrIdx = *it;
638         return m_cursorFrIdx;
639     }
640
641     return _moveHome(mask);
642 }
643
644 unsigned
645 CIMIClassicView::_moveEnd(unsigned& mask)
646 {
647     if (m_cursorFrIdx < m_pIC->getLastFrIdx()) {
648         mask |= PREEDIT_MASK;
649         m_cursorFrIdx = m_pIC->getLastFrIdx();
650     }
651
652     return m_cursorFrIdx;
653 }
654
655 void
656 CIMIClassicView::makeSelection(int candiIdx, unsigned& mask)
657 {
658     if (m_candiList.size() == 0 || m_sentences.size() == 0) {
659         // user might delete all the left over pinyin characters, this will
660         // make m_candiList empty
661         // 0 or space choices should commit previous selected candidates
662         mask |= PREEDIT_MASK | CANDIDATE_MASK;
663         _doCommit();
664         clearIC();
665         return;
666     }
667
668     // candiIdx += m_candiPageFirst;
669     if (candiIdx >= m_uiCandidateList.size()) {
670         return;
671     }
672     int idx = m_uiCandidateList.getUserIndex(candiIdx);
673     int type = m_uiCandidateList.getCandiTypeVec()[candiIdx];
674     bool selected = false;
675
676     mask |= PREEDIT_MASK | CANDIDATE_MASK;
677     if (type == ICandidateList::BEST_TAIL) {
678         // get the rank of that sentence and select it
679         m_pIC->selectSentence(m_sentences[idx].first);
680         _doCommit();
681         clearIC();
682     } else if (type == ICandidateList::OTHER_BEST_TAIL) {
683         CCandidates& tail = m_tails[idx].second;
684         for (size_t i = 0; i < tail.size(); i++) {
685             m_pIC->makeSelection(tail[i]);
686         }
687         m_candiFrIdx = tail.back().m_end;
688         selected = true;
689     } else if (type == ICandidateList::BEST_WORD
690                || type == ICandidateList::NORMAL_WORD) {
691         CCandidate& candi = m_candiList[idx];
692         m_pIC->makeSelection(candi);
693         m_candiFrIdx = candi.m_end;
694         selected = true;
695     } else if (type == ICandidateList::PLUGIN_TAIL) {
696         handlerCommit(m_uiCandidateList.getCandiStrings()[candiIdx]);
697         clearIC();
698     }
699
700     if (selected) {
701         if (m_cursorFrIdx < m_candiFrIdx)
702             m_cursorFrIdx = m_candiFrIdx;
703
704         CLattice& lattice = m_pIC->getLattice();
705         while (m_candiFrIdx < m_pIC->getLastFrIdx() &&
706                !lattice[m_candiFrIdx + 1].isUnusedFrame() &&
707                !lattice[m_candiFrIdx + 1].isSyllableFrame()) {
708             ++m_candiFrIdx;
709             lattice[m_candiFrIdx].m_bwType |= CLatticeFrame::IGNORED;
710         }
711
712         if (m_candiFrIdx == m_pIC->getLastFrIdx()) {
713             _doCommit();
714             clearIC();
715         } else {
716             m_candiPageFirst = 0;
717             _getCandidates();
718         }
719     }
720 }
721
722 void
723 CIMIClassicView::deleteCandidate(int candiIdx, unsigned& mask)
724 {
725     // candiIdx += m_candiPageFirst;
726     int idx = m_uiCandidateList.getUserIndex(candiIdx);
727     int type = m_uiCandidateList.getCandiTypeVec()[candiIdx];
728
729     if (type == ICandidateList::BEST_TAIL) {
730         // try to remove candidate 0 which is a calculated sentence
731         std::vector<unsigned> wids;
732         m_pIC->getSelectedSentence(wids, m_candiFrIdx);
733         m_pIC->removeFromHistoryCache(wids);
734
735         /* if the sentence wid length is 1, also delete this word */
736         if (wids.size() == 1) {
737             unsigned wid = wids[0];
738             m_pIC->deleteCandidateByWID(wid);
739         }
740     } else if (type == ICandidateList::BEST_WORD
741                || type == ICandidateList::NORMAL_WORD) {
742         // remove an ordinary candidate
743         CCandidate& candi = m_candiList[idx];
744         m_pIC->deleteCandidate(candi);
745     }
746
747     _getCandidates();
748     mask |= PREEDIT_MASK | CANDIDATE_MASK;
749 }