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