1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * Copyright (C) 2005 Takuro Ashie
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 #include "scim_anthy_factory.h"
23 #include "scim_anthy_imengine.h"
24 #include "scim_anthy_conversion.h"
25 #include "scim_anthy_utils.h"
26 #include "scim_anthy_prefs.h"
28 using namespace scim_anthy;
30 static void rotate_case (String &str);
34 // ConversionSegment Class
36 ConversionSegment::ConversionSegment (WideString str, int cand_id,
37 unsigned int reading_len)
40 m_reading_len (reading_len)
44 ConversionSegment::~ConversionSegment ()
49 ConversionSegment::get_string (void)
56 ConversionSegment::get_candidate_id (void)
62 ConversionSegment::get_reading_length (void)
68 ConversionSegment::set (WideString str, int cand_id)
75 ConversionSegment::set_reading_length (unsigned int len)
85 Conversion::Conversion (AnthyInstance &anthy, Reading &reading)
88 m_anthy_context (anthy_create_context()),
93 set_dict_encoding (String (SCIM_ANTHY_CONFIG_DICT_ENCODING_DEFAULT));
96 Conversion::~Conversion ()
98 anthy_release_context (m_anthy_context);
104 // starting and finishing
107 Conversion::convert (WideString source, CandidateType ctype,
110 if (is_converting ())
118 struct anthy_conv_stat conv_stat;
119 anthy_get_stat (m_anthy_context, &conv_stat);
120 if (conv_stat.nr_segment <= 0) {
121 m_iconv.convert (dest, source);
122 anthy_set_string (m_anthy_context, dest.c_str ());
126 join_all_segments ();
128 // get information about conversion string
129 anthy_get_stat (m_anthy_context, &conv_stat);
130 if (conv_stat.nr_segment <= 0)
133 // select first segment
138 for (int i = m_start_id; i < conv_stat.nr_segment; i++) {
139 struct anthy_segment_stat seg_stat;
140 anthy_get_segment_stat (m_anthy_context, i, &seg_stat);
141 m_segments.push_back (
142 ConversionSegment (get_segment_string (i, ctype), ctype,
148 Conversion::convert (CandidateType ctype, bool single_segment)
150 convert (m_reading.get (), ctype, single_segment);
154 Conversion::convert (const WideString &source, bool single_segment)
156 convert (source, SCIM_ANTHY_CANDIDATE_DEFAULT, single_segment);
160 Conversion::predict (void)
164 #ifdef HAS_ANTHY_PREDICTION
167 m_iconv.convert (str, m_reading.get ());
168 anthy_set_prediction_string (m_anthy_context, str.c_str ());
170 struct anthy_prediction_stat ps;
171 anthy_get_prediction_stat (m_anthy_context, &ps);
172 if (ps.nr_prediction > 0)
175 anthy_reset_context (m_anthy_context);
176 #endif /* HAS_ANTHY_PREDICTION */
180 Conversion::clear (int segment_id)
182 if (segment_id < 0 || m_segments.size () <= 0 ||
183 segment_id >= (int) m_segments.size () - 1)
187 anthy_reset_context (m_anthy_context);
193 m_predicting = false;
198 // remove stored segments
199 ConversionSegments::iterator it = m_segments.begin ();
200 m_segments.erase (it, it + segment_id + 1);
202 // adjust selected segment
203 int new_start_segment_id = m_start_id + segment_id + 1;
204 if (m_cur_segment >= 0) {
205 m_cur_segment -= new_start_segment_id - m_start_id;
206 if (m_cur_segment < 0)
211 unsigned int clear_len = 0;
212 for (int i = m_start_id; i < new_start_segment_id; i++) {
213 struct anthy_segment_stat seg_stat;
214 anthy_get_segment_stat (m_anthy_context, i, &seg_stat);
215 clear_len += seg_stat.seg_len;
217 m_reading.erase (0, clear_len, true);
218 m_start_id = new_start_segment_id;
223 Conversion::commit (int segment_id, bool learn)
225 if (!is_converting ()) return;
228 for (unsigned int i = m_start_id;
230 i < m_segments.size () &&
231 (segment_id < 0 || (int) i <= segment_id);
234 if (m_segments[i].get_candidate_id () >= 0)
235 anthy_commit_segment (m_anthy_context, i,
236 m_segments[i].get_candidate_id ());
248 Conversion::is_converting (void)
250 if (m_segments.size () > 0)
257 Conversion::is_predicting (void)
263 Conversion::get (void)
266 ConversionSegments::iterator it;
267 for (it = m_segments.begin (); it != m_segments.end (); it++)
268 str += it->get_string ();
273 Conversion::get_length (void)
275 unsigned int len = 0;
276 ConversionSegments::iterator it;
277 for (it = m_segments.begin (); it != m_segments.end (); it++)
278 len += it->get_string().length ();
283 Conversion::get_attribute_list (void)
286 unsigned int pos = 0, seg_id;
287 ConversionSegments::iterator it;
288 for (it = m_segments.begin (), seg_id = 0;
289 it != m_segments.end ();
292 // create attribute for this segment
293 if (it->get_string().length () <= 0) {
294 pos += it->get_string().length ();
298 if ((int) seg_id == m_cur_segment) {
299 util_create_attributes (
300 attrs, pos, it->get_string().length(),
301 m_anthy.get_factory()->m_selected_segment_style,
302 m_anthy.get_factory()->m_selected_segment_fg_color,
303 m_anthy.get_factory()->m_selected_segment_bg_color);
305 util_create_attributes (
306 attrs, pos, it->get_string().length(),
307 m_anthy.get_factory()->m_conversion_style,
308 m_anthy.get_factory()->m_conversion_fg_color,
309 m_anthy.get_factory()->m_conversion_bg_color);
312 pos += it->get_string().length ();
321 // segments of the converted sentence
324 Conversion::get_nr_segments (void)
326 if (!is_converting ()) return 0;
328 struct anthy_conv_stat conv_stat;
329 anthy_get_stat (m_anthy_context, &conv_stat);
331 return conv_stat.nr_segment - m_start_id;
335 Conversion::get_segment_string (int segment_id, int candidate_id)
337 if (segment_id < 0) {
338 if (m_cur_segment < 0)
339 return WideString ();
341 segment_id = m_cur_segment;
344 struct anthy_conv_stat conv_stat;
345 anthy_get_stat (m_anthy_context, &conv_stat);
347 if (conv_stat.nr_segment <= 0)
348 return WideString ();
350 if (m_start_id < 0 ||
351 m_start_id >= conv_stat.nr_segment)
353 return WideString (); // error
356 if (segment_id < 0 ||
357 segment_id + m_start_id >= conv_stat.nr_segment)
359 return WideString (); //error
362 // character position of the head of segment.
363 unsigned int real_seg_start = 0;
364 for (int i = m_start_id; i < m_start_id + segment_id; i++) {
365 struct anthy_segment_stat seg_stat;
366 anthy_get_segment_stat (m_anthy_context, i, &seg_stat);
367 real_seg_start += seg_stat.seg_len;
370 int real_seg = segment_id + m_start_id;
372 if (candidate_id <= SCIM_ANTHY_LAST_SPECIAL_CANDIDATE)
373 cand = m_segments[segment_id].get_candidate_id ();
377 // get information of this segment
378 struct anthy_segment_stat seg_stat;
379 anthy_get_segment_stat (m_anthy_context, real_seg, &seg_stat);
381 // get string of this segment
382 WideString segment_str;
384 get_reading_substr (segment_str, segment_id, cand,
385 real_seg_start, seg_stat.seg_len);
387 int len = anthy_get_segment (m_anthy_context, real_seg, cand, NULL, 0);
390 anthy_get_segment (m_anthy_context, real_seg, cand, buf, len + 1);
392 m_iconv.convert (segment_str, buf, len);
400 Conversion::get_selected_segment (void)
402 return m_cur_segment;
406 Conversion::select_segment (int segment_id)
408 if (!is_converting ()) return;
410 if (segment_id < 0) {
415 struct anthy_conv_stat conv_stat;
416 anthy_get_stat (m_anthy_context, &conv_stat);
418 int real_segment_id = segment_id + m_start_id;
420 if (segment_id >= 0 && real_segment_id < conv_stat.nr_segment)
421 m_cur_segment = segment_id;
425 Conversion::get_segment_size (int segment_id)
427 if (!is_converting ()) return -1;
429 struct anthy_conv_stat conv_stat;
430 anthy_get_stat (m_anthy_context, &conv_stat);
432 if (segment_id < 0) {
433 if (m_cur_segment < 0)
436 segment_id = m_cur_segment;
438 int real_segment_id = segment_id + m_start_id;
440 if (real_segment_id >= conv_stat.nr_segment)
443 struct anthy_segment_stat seg_stat;
444 anthy_get_segment_stat (m_anthy_context, real_segment_id, &seg_stat);
446 return seg_stat.seg_len;
450 Conversion::resize_segment (int relative_size, int segment_id)
452 if (is_predicting ()) return;
453 if (!is_converting ()) return;
455 struct anthy_conv_stat conv_stat;
456 anthy_get_stat (m_anthy_context, &conv_stat);
460 if (segment_id < 0) {
461 if (m_cur_segment < 0)
464 segment_id = m_cur_segment;
465 real_segment_id = segment_id + m_start_id;
467 real_segment_id = segment_id + m_start_id;
468 if (m_cur_segment > segment_id)
469 m_cur_segment = segment_id;
472 if (real_segment_id >= conv_stat.nr_segment)
476 anthy_resize_segment (m_anthy_context, real_segment_id, relative_size);
478 // reset candidates of trailing segments
479 anthy_get_stat (m_anthy_context, &conv_stat);
480 ConversionSegments::iterator start_iter = m_segments.begin();
481 ConversionSegments::iterator end_iter = m_segments.end();
482 m_segments.erase (start_iter + segment_id, end_iter);
483 for (int i = real_segment_id; i < conv_stat.nr_segment; i++) {
484 struct anthy_segment_stat seg_stat;
485 anthy_get_segment_stat (m_anthy_context, i, &seg_stat);
486 m_segments.push_back (
487 ConversionSegment (get_segment_string (i - m_start_id, 0), 0,
493 Conversion::get_segment_position (int segment_id)
495 if (segment_id < 0) {
496 if (m_cur_segment < 0)
497 return get_length ();
499 segment_id = m_cur_segment;
502 unsigned int pos = 0;
504 for (unsigned int i = 0;
505 (int) i < m_cur_segment && i < m_segments.size ();
508 pos += m_segments[i].get_string().length ();
517 // candidates for a segment or prediction
520 Conversion::get_candidates (CommonLookupTable &table, int segment_id)
524 if (is_predicting ()) {
525 #ifdef HAS_ANTHY_PREDICTION
527 struct anthy_prediction_stat ps;
529 anthy_get_prediction_stat (m_anthy_context, &ps);
531 for (int i = 0; i < ps.nr_prediction; i++) {
532 int len = anthy_get_prediction (m_anthy_context, i, NULL, 0);
537 anthy_get_prediction (m_anthy_context, i, buf, len + 1);
541 m_iconv.convert (cand, buf);
543 table.append_candidate (cand);
545 #endif /* HAS_ANTHY_PREDICTION */
546 } else if (is_converting ()) {
547 struct anthy_conv_stat conv_stat;
548 anthy_get_stat (m_anthy_context, &conv_stat);
550 if (conv_stat.nr_segment <= 0)
553 if (segment_id < 0) {
554 if (m_cur_segment < 0)
557 segment_id = m_cur_segment;
559 int real_segment_id = segment_id + m_start_id;
561 if (real_segment_id >= conv_stat.nr_segment)
564 struct anthy_segment_stat seg_stat;
565 anthy_get_segment_stat (m_anthy_context, real_segment_id, &seg_stat);
567 for (int i = 0; i < seg_stat.nr_candidate; i++) {
568 int len = anthy_get_segment (m_anthy_context, real_segment_id, i,
574 anthy_get_segment (m_anthy_context, real_segment_id,
577 WideString cand_wide;
578 m_iconv.convert (cand_wide, buf, len);
580 table.append_candidate (cand_wide);
583 table.set_cursor_pos (get_selected_candidate ());
588 Conversion::get_selected_candidate (int segment_id)
590 if (is_predicting ()) {
591 #ifdef HAS_ANTHY_PREDICTION
592 struct anthy_prediction_stat ps;
594 anthy_get_prediction_stat (m_anthy_context, &ps);
596 if (ps.nr_prediction <= 0)
599 if (segment_id < 0) {
600 if (m_cur_segment < 0)
603 segment_id = m_cur_segment;
604 } else if (segment_id >= ps.nr_prediction) {
608 return m_segments[segment_id].get_candidate_id ();
609 #endif /* HAS_ANTHY_PREDICTION */
611 } else if (is_converting ()) {
612 struct anthy_conv_stat cs;
614 anthy_get_stat (m_anthy_context, &cs);
616 if (cs.nr_segment <= 0)
619 if (segment_id < 0) {
620 if (m_cur_segment < 0)
623 segment_id = m_cur_segment;
624 } else if (segment_id >= cs.nr_segment) {
628 return m_segments[segment_id].get_candidate_id ();
635 Conversion::select_candidate (int candidate_id, int segment_id)
637 if (is_predicting ()) {
638 #ifdef HAS_ANTHY_PREDICTION
639 if (candidate_id < SCIM_ANTHY_CANDIDATE_DEFAULT)
642 struct anthy_prediction_stat ps;
643 anthy_get_prediction_stat (m_anthy_context, &ps);
645 if (ps.nr_prediction <= 0)
648 if (!is_converting ()) {
650 m_segments.push_back (ConversionSegment (
651 get_prediction_string (0), 0,
652 m_reading.get_length ()));
655 if (candidate_id < ps.nr_prediction) {
656 m_segments[0].set (get_prediction_string (candidate_id),
659 #endif /* HAS_ANTHY_PREDICTION */
661 } else if (is_converting ()) {
662 if (candidate_id <= SCIM_ANTHY_LAST_SPECIAL_CANDIDATE)
665 struct anthy_conv_stat conv_stat;
666 anthy_get_stat (m_anthy_context, &conv_stat);
668 if (conv_stat.nr_segment <= 0)
671 if (segment_id < 0) {
672 if (m_cur_segment < 0)
675 segment_id = m_cur_segment;
677 int real_segment_id = segment_id + m_start_id;
679 if (segment_id >= conv_stat.nr_segment)
682 struct anthy_segment_stat seg_stat;
683 anthy_get_segment_stat (m_anthy_context, real_segment_id, &seg_stat);
685 if (candidate_id == SCIM_ANTHY_CANDIDATE_HALF) {
686 switch (m_segments[segment_id].get_candidate_id ()) {
687 case SCIM_ANTHY_CANDIDATE_LATIN:
688 case SCIM_ANTHY_CANDIDATE_WIDE_LATIN:
689 candidate_id = SCIM_ANTHY_CANDIDATE_LATIN;
692 candidate_id = SCIM_ANTHY_CANDIDATE_HALF_KATAKANA;
697 if (candidate_id < seg_stat.nr_candidate) {
698 m_segments[segment_id].set (get_segment_string (segment_id,
711 Conversion::set_dict_encoding (String type)
713 #ifdef HAS_ANTHY_CONTEXT_SET_ENCODING
714 if (!strcasecmp (type.c_str (), "UTF-8") ||
715 !strcasecmp (type.c_str (), "UTF8"))
717 anthy_context_set_encoding (m_anthy_context, ANTHY_UTF8_ENCODING);
719 anthy_context_set_encoding (m_anthy_context, ANTHY_EUC_JP_ENCODING);
721 #endif /* HAS_ANTHY_CONTEXT_SET_ENCODING */
723 if (m_iconv.set_encoding (type.c_str ())) {
726 return m_iconv.set_encoding ("EUC-JP");
736 Conversion::get_reading_substr (WideString &string,
744 if (segment_id < (int) m_segments.size ())
745 prev_cand = m_segments[segment_id].get_candidate_id ();
747 switch ((CandidateType) candidate_id) {
748 case SCIM_ANTHY_CANDIDATE_LATIN:
749 if (prev_cand == SCIM_ANTHY_CANDIDATE_LATIN) {
750 String str = utf8_wcstombs (m_segments[segment_id].get_string ());
752 string = utf8_mbstowcs (str);
754 string = m_reading.get (seg_start, seg_len,
755 SCIM_ANTHY_STRING_LATIN);
759 case SCIM_ANTHY_CANDIDATE_WIDE_LATIN:
760 if (prev_cand == SCIM_ANTHY_CANDIDATE_WIDE_LATIN) {
762 util_convert_to_half (str, m_segments[segment_id].get_string ());
764 util_convert_to_wide (string, str);
766 string = m_reading.get (seg_start, seg_len,
767 SCIM_ANTHY_STRING_WIDE_LATIN);
771 case SCIM_ANTHY_CANDIDATE_KATAKANA:
772 string = m_reading.get (seg_start, seg_len,
773 SCIM_ANTHY_STRING_KATAKANA);
776 case SCIM_ANTHY_CANDIDATE_HALF_KATAKANA:
777 string = m_reading.get (seg_start, seg_len,
778 SCIM_ANTHY_STRING_HALF_KATAKANA);
781 case SCIM_ANTHY_CANDIDATE_HALF:
782 // shouldn't reach to this entry
783 string = m_reading.get (seg_start, seg_len,
784 SCIM_ANTHY_STRING_HALF_KATAKANA);
787 case SCIM_ANTHY_CANDIDATE_HIRAGANA:
789 string = m_reading.get (seg_start, seg_len,
790 SCIM_ANTHY_STRING_HIRAGANA);
796 Conversion::get_prediction_string (int candidate_id)
798 #ifdef HAS_ANTHY_PREDICTION
799 if (!is_predicting ())
800 return WideString ();
802 struct anthy_prediction_stat ps;
803 anthy_get_prediction_stat (m_anthy_context, &ps);
805 if (ps.nr_prediction <= 0)
806 return WideString ();
808 int len = anthy_get_prediction (m_anthy_context, candidate_id, NULL, 0);
810 return WideString ();
813 anthy_get_prediction (m_anthy_context, candidate_id, buf, len + 1);
817 m_iconv.convert (cand, buf);
820 #else /* HAS_ANTHY_PREDICTION */
821 return WideString ();
822 #endif /* HAS_ANTHY_PREDICTION */
826 Conversion::join_all_segments (void)
829 struct anthy_conv_stat conv_stat;
830 anthy_get_stat (m_anthy_context, &conv_stat);
831 int nr_seg = conv_stat.nr_segment - m_start_id;
834 anthy_resize_segment (m_anthy_context, m_start_id, 1);
841 rotate_case (String &str)
843 bool is_mixed = false;
844 for (unsigned int i = 1; i < str.length (); i++) {
845 if ((isupper (str[0]) && islower (str[i])) ||
846 (islower (str[0]) && isupper (str[i])))
854 // Anthy -> anthy, anThy -> anthy
855 for (unsigned int i = 0; i < str.length (); i++)
856 str[i] = tolower (str[i]);
857 } else if (isupper (str[0])) {
859 for (unsigned int i = 1; i < str.length (); i++)
860 str[i] = tolower (str[i]);
863 for (unsigned int i = 0; i < str.length (); i++)
864 str[i] = toupper (str[i]);