Tizen 2.1 base
[framework/uifw/ise-engine-anthy.git] / src / scim_anthy_conversion.cpp
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2005 Takuro Ashie
4  *
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)
8  *  any later version.
9  *
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.
14  *
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.
18  */
19
20 #include <string.h>
21
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"
27
28 using namespace scim_anthy;
29
30 static void rotate_case (String &str);
31
32
33 //
34 // ConversionSegment Class
35 //
36 ConversionSegment::ConversionSegment (WideString str, int cand_id,
37                                       unsigned int reading_len)
38     : m_string      (str),
39       m_cand_id     (cand_id),
40       m_reading_len (reading_len)
41 {
42 }
43
44 ConversionSegment::~ConversionSegment ()
45 {
46 }
47
48 WideString &
49 ConversionSegment::get_string (void)
50 {
51     return m_string;
52 }
53
54
55 int
56 ConversionSegment::get_candidate_id (void)
57 {
58     return m_cand_id;
59 }
60
61 unsigned int
62 ConversionSegment::get_reading_length (void)
63 {
64     return m_reading_len;
65 }
66
67 void
68 ConversionSegment::set (WideString str, int cand_id)
69 {
70     m_string  = str;
71     m_cand_id = cand_id;
72 }
73
74 void
75 ConversionSegment::set_reading_length (unsigned int len)
76 {
77     m_reading_len = len;
78 }
79
80
81
82 //
83 // Conversion Class
84 //
85 Conversion::Conversion (AnthyInstance &anthy, Reading &reading)
86     : m_anthy              (anthy),
87       m_reading            (reading),
88       m_anthy_context      (anthy_create_context()),
89       m_start_id           (0),
90       m_cur_segment        (-1),
91       m_predicting         (false)
92 {
93     set_dict_encoding (String (SCIM_ANTHY_CONFIG_DICT_ENCODING_DEFAULT));
94 }
95
96 Conversion::~Conversion ()
97 {
98     anthy_release_context (m_anthy_context);
99 }
100
101
102
103 //
104 // starting and finishing
105 //
106 void
107 Conversion::convert (WideString source, CandidateType ctype,
108                      bool single_segment)
109 {
110     if (is_converting ())
111         return;
112
113     clear ();
114
115     String dest;
116
117     // convert
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 ());
123     }
124
125     if (single_segment)
126         join_all_segments ();
127
128     // get information about conversion string
129     anthy_get_stat (m_anthy_context, &conv_stat);
130     if (conv_stat.nr_segment <= 0)
131         return;
132
133     // select first segment
134     m_cur_segment = 0;
135
136     // create segments
137     m_segments.clear();
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,
143                                seg_stat.seg_len));
144     }
145 }
146
147 void
148 Conversion::convert (CandidateType ctype, bool single_segment)
149 {
150     convert (m_reading.get (), ctype, single_segment);
151 }
152
153 void
154 Conversion::convert (const WideString &source, bool single_segment)
155 {
156     convert (source, SCIM_ANTHY_CANDIDATE_DEFAULT, single_segment);
157 }
158
159 void
160 Conversion::predict (void)
161 {
162     clear ();
163
164 #ifdef HAS_ANTHY_PREDICTION
165     String str;
166
167     m_iconv.convert (str, m_reading.get ());
168     anthy_set_prediction_string (m_anthy_context, str.c_str ());
169
170     struct anthy_prediction_stat ps;
171     anthy_get_prediction_stat (m_anthy_context, &ps);
172     if (ps.nr_prediction > 0)
173         m_predicting = true;
174     else
175         anthy_reset_context (m_anthy_context);
176 #endif /* HAS_ANTHY_PREDICTION */
177 }
178
179 void
180 Conversion::clear (int segment_id)
181 {
182     if (segment_id < 0 || m_segments.size () <= 0 ||
183         segment_id >= (int) m_segments.size () - 1)
184     {
185         // complete clear
186
187         anthy_reset_context (m_anthy_context);
188
189         m_segments.clear ();
190
191         m_start_id    = 0;
192         m_cur_segment = -1;
193         m_predicting  = false;
194
195     } else {
196         // partial clear
197
198         // remove stored segments
199         ConversionSegments::iterator it = m_segments.begin ();
200         m_segments.erase (it, it + segment_id + 1);
201
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)
207                 m_cur_segment = 0;
208         }
209
210         // adjust offset
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;
216         }
217         m_reading.erase (0, clear_len, true);
218         m_start_id = new_start_segment_id;
219     }
220 }
221
222 void
223 Conversion::commit (int segment_id, bool learn)
224 {
225     if (!is_converting ()) return;
226
227     // learn
228     for (unsigned int i = m_start_id;
229          learn &&
230              i < m_segments.size () &&
231              (segment_id < 0 || (int) i <= segment_id);
232          i++)
233     {
234         if (m_segments[i].get_candidate_id () >= 0)
235             anthy_commit_segment (m_anthy_context, i,
236                                   m_segments[i].get_candidate_id ());
237     }
238
239     clear (segment_id);
240 }
241
242
243
244 //
245 // getting status
246 //
247 bool
248 Conversion::is_converting (void)
249 {
250     if (m_segments.size () > 0)
251         return true;
252     else
253         return false;
254 }
255
256 bool
257 Conversion::is_predicting (void)
258 {
259     return m_predicting;
260 }
261
262 WideString
263 Conversion::get (void)
264 {
265     WideString str;
266     ConversionSegments::iterator it;
267     for (it = m_segments.begin (); it != m_segments.end (); it++)
268         str += it->get_string ();
269     return str;
270 }
271
272 unsigned int
273 Conversion::get_length (void)
274 {
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 ();
279     return len;
280 }
281
282 AttributeList
283 Conversion::get_attribute_list (void)
284 {
285     AttributeList attrs;
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 ();
290          it++, seg_id++)
291     {
292         // create attribute for this segment
293         if (it->get_string().length () <= 0) {
294             pos += it->get_string().length ();
295             continue;
296         }
297
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);
304         } else {
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);
310         }
311
312         pos += it->get_string().length ();
313     }
314
315     return attrs;
316 }
317
318
319
320 //
321 // segments of the converted sentence
322 //
323 int
324 Conversion::get_nr_segments (void)
325 {
326     if (!is_converting ()) return 0;
327
328     struct anthy_conv_stat conv_stat;
329     anthy_get_stat (m_anthy_context, &conv_stat);
330
331     return conv_stat.nr_segment - m_start_id;
332 }
333
334 WideString
335 Conversion::get_segment_string (int segment_id, int candidate_id)
336 {
337     if (segment_id < 0) {
338         if (m_cur_segment < 0)
339             return WideString ();
340         else
341             segment_id = m_cur_segment;
342     }
343
344     struct anthy_conv_stat conv_stat;
345     anthy_get_stat (m_anthy_context, &conv_stat);
346
347     if (conv_stat.nr_segment <= 0)
348         return WideString ();
349
350     if (m_start_id < 0 ||
351         m_start_id >= conv_stat.nr_segment)
352     {
353         return WideString (); // error
354     }
355
356     if (segment_id < 0 ||
357         segment_id + m_start_id >= conv_stat.nr_segment)
358     {
359         return WideString (); //error
360     }
361
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;
368     }
369
370     int real_seg = segment_id + m_start_id;
371     int cand;
372     if (candidate_id <= SCIM_ANTHY_LAST_SPECIAL_CANDIDATE)
373         cand = m_segments[segment_id].get_candidate_id ();
374     else
375         cand = candidate_id;
376
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);
380
381     // get string of this segment
382     WideString segment_str;
383     if (cand < 0) {
384         get_reading_substr (segment_str, segment_id, cand,
385                             real_seg_start, seg_stat.seg_len);
386     } else {
387         int len = anthy_get_segment (m_anthy_context, real_seg, cand, NULL, 0);
388         if (len > 0) {
389             char buf[len + 1];
390             anthy_get_segment (m_anthy_context, real_seg, cand, buf, len + 1);
391             buf[len] = '\0';
392             m_iconv.convert (segment_str, buf, len);
393         }
394     }
395
396     return segment_str;
397 }
398
399 int
400 Conversion::get_selected_segment (void)
401 {
402     return m_cur_segment;
403 }
404
405 void
406 Conversion::select_segment (int segment_id)
407 {
408     if (!is_converting ()) return;
409
410     if (segment_id < 0) {
411         m_cur_segment = -1;
412         return;
413     }
414
415     struct anthy_conv_stat conv_stat;
416     anthy_get_stat (m_anthy_context, &conv_stat);
417
418     int real_segment_id = segment_id + m_start_id;
419
420     if (segment_id >= 0 && real_segment_id < conv_stat.nr_segment)
421         m_cur_segment = segment_id;
422 }
423
424 int
425 Conversion::get_segment_size (int segment_id)
426 {
427     if (!is_converting ()) return -1;
428
429     struct anthy_conv_stat conv_stat;
430     anthy_get_stat (m_anthy_context, &conv_stat);
431
432     if (segment_id < 0) {
433         if (m_cur_segment < 0)
434             return -1;
435         else
436             segment_id = m_cur_segment;
437     }
438     int real_segment_id = segment_id + m_start_id;
439
440     if (real_segment_id >= conv_stat.nr_segment)
441         return -1;
442
443     struct anthy_segment_stat seg_stat;
444     anthy_get_segment_stat (m_anthy_context, real_segment_id, &seg_stat);
445
446     return seg_stat.seg_len;
447 }
448
449 void
450 Conversion::resize_segment (int relative_size, int segment_id)
451 {
452     if (is_predicting ()) return;
453     if (!is_converting ()) return;
454
455     struct anthy_conv_stat conv_stat;
456     anthy_get_stat (m_anthy_context, &conv_stat);
457
458     int real_segment_id;
459
460     if (segment_id < 0) {
461         if (m_cur_segment < 0)
462             return;
463         else
464             segment_id = m_cur_segment;
465         real_segment_id = segment_id + m_start_id;
466     } else {
467         real_segment_id = segment_id + m_start_id;
468         if (m_cur_segment > segment_id)
469             m_cur_segment = segment_id;
470     }
471
472     if (real_segment_id >= conv_stat.nr_segment)
473         return;
474
475     // do resize
476     anthy_resize_segment (m_anthy_context, real_segment_id, relative_size);
477
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,
488                                seg_stat.seg_len));
489     }
490 }
491
492 unsigned int
493 Conversion::get_segment_position (int segment_id)
494 {
495     if (segment_id < 0) {
496         if (m_cur_segment < 0)
497             return get_length ();
498         else
499             segment_id = m_cur_segment;
500     }
501
502     unsigned int pos = 0;
503
504     for (unsigned int i = 0;
505          (int) i < m_cur_segment && i < m_segments.size ();
506          i++)
507     {
508         pos += m_segments[i].get_string().length ();
509     }
510
511     return pos;
512 }
513
514
515
516 //
517 // candidates for a segment or prediction
518 //
519 void
520 Conversion::get_candidates (CommonLookupTable &table, int segment_id)
521 {
522     table.clear ();
523
524     if (is_predicting ()) {
525 #ifdef HAS_ANTHY_PREDICTION
526         String str;
527         struct anthy_prediction_stat ps;
528
529         anthy_get_prediction_stat (m_anthy_context, &ps);
530
531         for (int i = 0; i < ps.nr_prediction; i++) {
532             int len = anthy_get_prediction (m_anthy_context, i, NULL, 0);
533             if (len <= 0)
534                 continue;
535
536             char buf[len + 1];
537             anthy_get_prediction (m_anthy_context, i, buf, len + 1);
538             buf[len] = '\0';
539
540             WideString cand;
541             m_iconv.convert (cand, buf);
542
543             table.append_candidate (cand);
544         }
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);
549
550         if (conv_stat.nr_segment <= 0)
551             return;
552
553         if (segment_id < 0) {
554             if (m_cur_segment < 0)
555                 return;
556             else
557                 segment_id = m_cur_segment;
558         }
559         int real_segment_id = segment_id + m_start_id;
560
561         if (real_segment_id >= conv_stat.nr_segment)
562             return;
563
564         struct anthy_segment_stat seg_stat;
565         anthy_get_segment_stat (m_anthy_context, real_segment_id, &seg_stat);
566
567         for (int i = 0; i < seg_stat.nr_candidate; i++) {
568             int len = anthy_get_segment (m_anthy_context, real_segment_id, i,
569                                          NULL, 0);
570             if (len <= 0)
571                 continue;
572
573             char buf[len + 1];
574             anthy_get_segment (m_anthy_context, real_segment_id,
575                                i, buf, len + 1);
576
577             WideString cand_wide;
578             m_iconv.convert (cand_wide, buf, len);
579
580             table.append_candidate (cand_wide);
581         }
582
583         table.set_cursor_pos (get_selected_candidate ());
584     }
585 }
586
587 int
588 Conversion::get_selected_candidate (int segment_id)
589 {
590     if (is_predicting ()) {
591 #ifdef HAS_ANTHY_PREDICTION
592         struct anthy_prediction_stat ps;
593
594         anthy_get_prediction_stat (m_anthy_context, &ps);
595
596         if (ps.nr_prediction <= 0)
597             return -1;
598
599         if (segment_id < 0) {
600             if (m_cur_segment < 0)
601                 return -1;
602             else
603                 segment_id = m_cur_segment;
604         } else if (segment_id >= ps.nr_prediction) {
605             return -1;
606         }
607
608         return m_segments[segment_id].get_candidate_id ();
609 #endif /* HAS_ANTHY_PREDICTION */
610
611     } else if (is_converting ()) {
612         struct anthy_conv_stat cs;
613
614         anthy_get_stat (m_anthy_context, &cs);
615
616         if (cs.nr_segment <= 0)
617             return -1;
618
619         if (segment_id < 0) {
620             if (m_cur_segment < 0)
621                 return -1;
622             else
623                 segment_id = m_cur_segment;
624         } else if (segment_id >= cs.nr_segment) {
625             return -1;
626         }
627
628         return m_segments[segment_id].get_candidate_id ();
629     }
630
631     return -1;
632 }
633
634 void
635 Conversion::select_candidate (int candidate_id, int segment_id)
636 {
637     if (is_predicting ()) {
638 #ifdef HAS_ANTHY_PREDICTION
639         if (candidate_id < SCIM_ANTHY_CANDIDATE_DEFAULT)
640             return;
641
642         struct anthy_prediction_stat ps;
643         anthy_get_prediction_stat (m_anthy_context, &ps);
644
645         if (ps.nr_prediction <= 0)
646             return;
647
648         if (!is_converting ()) {
649             m_cur_segment = 0;
650             m_segments.push_back (ConversionSegment (
651                                       get_prediction_string (0), 0,
652                                       m_reading.get_length ()));
653         }
654
655         if (candidate_id < ps.nr_prediction) {
656             m_segments[0].set (get_prediction_string (candidate_id),
657                                candidate_id);
658         }
659 #endif /* HAS_ANTHY_PREDICTION */
660
661     } else if (is_converting ()) {
662         if (candidate_id <= SCIM_ANTHY_LAST_SPECIAL_CANDIDATE)
663             return;
664
665         struct anthy_conv_stat conv_stat;
666         anthy_get_stat (m_anthy_context, &conv_stat);
667
668         if (conv_stat.nr_segment <= 0)
669             return;
670
671         if (segment_id < 0) {
672             if (m_cur_segment < 0)
673                 return;
674             else
675                 segment_id = m_cur_segment;
676         }
677         int real_segment_id = segment_id + m_start_id;
678
679         if (segment_id >= conv_stat.nr_segment)
680             return;
681
682         struct anthy_segment_stat seg_stat;
683         anthy_get_segment_stat (m_anthy_context, real_segment_id, &seg_stat);
684
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;
690                 break;
691             default:
692                 candidate_id = SCIM_ANTHY_CANDIDATE_HALF_KATAKANA;
693                 break;
694             }
695         }
696
697         if (candidate_id < seg_stat.nr_candidate) {
698             m_segments[segment_id].set (get_segment_string (segment_id,
699                                                             candidate_id),
700                                         candidate_id);
701         }
702     }
703 }
704
705
706
707 //
708 // preferences
709 //
710 bool
711 Conversion::set_dict_encoding (String type)
712 {
713 #ifdef HAS_ANTHY_CONTEXT_SET_ENCODING
714     if (!strcasecmp (type.c_str (), "UTF-8") ||
715         !strcasecmp (type.c_str (), "UTF8"))
716     {
717         anthy_context_set_encoding (m_anthy_context, ANTHY_UTF8_ENCODING);
718     } else {
719         anthy_context_set_encoding (m_anthy_context, ANTHY_EUC_JP_ENCODING);
720     }
721 #endif /* HAS_ANTHY_CONTEXT_SET_ENCODING */
722
723     if (m_iconv.set_encoding (type.c_str ())) {
724         return true;
725     } else {
726         return m_iconv.set_encoding ("EUC-JP");
727     }
728 }
729
730
731
732 //
733 // Utilities
734 //
735 void
736 Conversion::get_reading_substr (WideString &string,
737                                 int segment_id,
738                                 int candidate_id,
739                                 int seg_start,
740                                 int seg_len)
741 {
742     int prev_cand = 0;
743
744     if (segment_id < (int) m_segments.size ())
745         prev_cand = m_segments[segment_id].get_candidate_id ();
746
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 ());
751             rotate_case (str);
752             string = utf8_mbstowcs (str);
753         } else {
754             string = m_reading.get (seg_start, seg_len,
755                                     SCIM_ANTHY_STRING_LATIN);
756         }
757         break;
758
759     case SCIM_ANTHY_CANDIDATE_WIDE_LATIN:
760         if (prev_cand == SCIM_ANTHY_CANDIDATE_WIDE_LATIN) {
761             String str;
762             util_convert_to_half (str, m_segments[segment_id].get_string ());
763             rotate_case (str);
764             util_convert_to_wide (string, str);
765         } else {
766             string = m_reading.get (seg_start, seg_len,
767                                     SCIM_ANTHY_STRING_WIDE_LATIN);
768         }
769         break;
770
771     case SCIM_ANTHY_CANDIDATE_KATAKANA:
772         string = m_reading.get (seg_start, seg_len,
773                                 SCIM_ANTHY_STRING_KATAKANA);
774         break;
775
776     case SCIM_ANTHY_CANDIDATE_HALF_KATAKANA:
777         string = m_reading.get (seg_start, seg_len,
778                                 SCIM_ANTHY_STRING_HALF_KATAKANA);
779         break;
780
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);
785         break;
786
787     case SCIM_ANTHY_CANDIDATE_HIRAGANA:
788     default:
789         string = m_reading.get (seg_start, seg_len,
790                                 SCIM_ANTHY_STRING_HIRAGANA);
791         break;
792     }
793 }
794
795 WideString
796 Conversion::get_prediction_string (int candidate_id)
797 {
798 #ifdef HAS_ANTHY_PREDICTION
799     if (!is_predicting ())
800         return WideString ();
801
802     struct anthy_prediction_stat ps;
803     anthy_get_prediction_stat (m_anthy_context, &ps);
804
805     if (ps.nr_prediction <= 0)
806         return WideString ();
807
808     int len = anthy_get_prediction (m_anthy_context, candidate_id, NULL, 0);
809     if (len <= 0)
810         return WideString ();
811
812     char buf[len + 1];
813     anthy_get_prediction (m_anthy_context, candidate_id, buf, len + 1);
814     buf[len] = '\0';
815
816     WideString cand;
817     m_iconv.convert (cand, buf);
818
819     return cand;
820 #else /* HAS_ANTHY_PREDICTION */
821     return WideString ();
822 #endif /* HAS_ANTHY_PREDICTION */
823 }
824
825 void
826 Conversion::join_all_segments (void)
827 {
828     do {
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;
832
833         if (nr_seg > 1)
834             anthy_resize_segment (m_anthy_context, m_start_id, 1);
835         else
836             break;
837     } while (true);
838 }
839
840 static void
841 rotate_case (String &str)
842 {
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])))
847         {
848             is_mixed = true;
849             break;
850         }
851     }
852
853     if (is_mixed) {
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])) {
858         // ANTHY -> Anthy
859         for (unsigned int i = 1; i < str.length (); i++)
860             str[i] = tolower (str[i]);
861     } else {
862         // anthy -> ANTHY
863         for (unsigned int i = 0; i < str.length (); i++)
864             str[i] = toupper (str[i]);
865     }
866 }