1# vim:set et sts=4 sw=4: 2# -*- coding: utf-8 -*- 3# 4# ibus-anthy - The Anthy engine for IBus 5# 6# Copyright (c) 2007-2008 Peng Huang <shawn.p.huang@gmail.com> 7# Copyright (c) 2010-2017 Takao Fujiwara <takao.fujiwara1@gmail.com> 8# Copyright (c) 2007-2017 Red Hat, Inc. 9# 10# This program is free software; you can redistribute it and/or modify 11# it under the terms of the GNU General Public License as published by 12# the Free Software Foundation; either version 2 of the License, or 13# (at your option) any later version. 14# 15# This program is distributed in the hope that it will be useful, 16# but WITHOUT ANY WARRANTY; without even the implied warranty of 17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18# GNU General Public License for more details. 19# 20# You should have received a copy of the GNU General Public License along 21# with this program; if not, write to the Free Software Foundation, Inc., 22# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23 24import romaji 25import kana 26import thumb 27 28from segment import unichar_half_to_full 29 30HalfSymbolTable = {} 31for i in range(32, 127): 32 if not chr(i).isalnum(): 33 HalfSymbolTable[unichar_half_to_full(chr(i))] = chr(i) 34 35HalfNumberTable = {} 36for i in range(10): 37 HalfNumberTable[unichar_half_to_full(str(i))] = str(i) 38 39PeriodTable = {'。': '.', '、': ',', '。': '.', '、': ','} 40 41SymbolTable = {} 42SymbolTable[0] = {'「': '「', '」': '」', '/': '/'} 43SymbolTable[1] = {'「': '「', '」': '」', '/': '・'} 44SymbolTable[2] = {'「': '[', '」': ']', '/': '/'} 45SymbolTable[3] = {'「': '[', '」': ']', '/': '・'} 46 47TYPING_MODE_ROMAJI, \ 48TYPING_MODE_KANA, \ 49TYPING_MODE_THUMB_SHIFT = list(range(3)) 50 51class JaString: 52 _prefs = None 53 _mode = TYPING_MODE_ROMAJI 54 _shift = False 55 _unshift = False 56 57 def __init__(self, mode=TYPING_MODE_ROMAJI, latin_with_shift=True): 58 self._init_mode(mode) 59 if mode == TYPING_MODE_ROMAJI: 60 romaji.RomajiSegment.SET_LATIN_WITH_SHIFT(latin_with_shift) 61 62 @classmethod 63 def _init_mode(cls, mode): 64 cls._mode = mode 65 cls._shift = False 66 cls._unshift = False 67 cls.__cursor = 0 68 cls.__segments = list() 69 if mode == TYPING_MODE_ROMAJI: 70 romaji.RomajiSegment.INIT_ROMAJI_TYPING_RULE(cls._prefs) 71 elif mode == TYPING_MODE_KANA: 72 kana.KanaSegment.INIT_KANA_TYPING_RULE(cls._prefs) 73 elif mode == TYPING_MODE_THUMB_SHIFT: 74 thumb.ThumbShiftSegment.INIT_THUMB_TYPING_RULE(cls._prefs) 75 76 @classmethod 77 def SET_PREFS(cls, prefs): 78 cls._prefs = prefs 79 80 @classmethod 81 def RESET(cls, prefs, section, key, value): 82 cls._prefs = prefs 83 if section == 'kana-typing-rule': 84 mode = TYPING_MODE_KANA 85 kana.KanaSegment.RESET(prefs, section, key, value) 86 cls._init_mode(mode) 87 if section == 'common' and key == 'latin-with-shift': 88 romaji.RomajiSegment.SET_LATIN_WITH_SHIFT(value) 89 90 def set_shift(self, shift): 91 self._shift = shift 92 93 def set_hiragana_katakana(self, mode): 94 if mode and self._mode == TYPING_MODE_ROMAJI: 95 self._unshift = True 96 97 def insert(self, c): 98 segment_before = None 99 segment_after = None 100 new_segments = None 101 102 if self.__cursor >= 1: 103 segment_before = self.__segments[self.__cursor - 1] 104 if self.__cursor < len(self.__segments): 105 segment_after = self.__segments[self.__cursor] 106 if segment_before and not segment_before.is_finished(): 107 if type(segment_before) == romaji.RomajiSegment: 108 new_segments = segment_before.append(c, 109 self._shift, 110 self._unshift) 111 self._unshift = False 112 else: 113 new_segments = segment_before.append(c) 114 elif segment_after and not segment_after.is_finished(): 115 if type(segment_after) == romaji.RomajiSegment: 116 new_segments = segment_after.prepend(c, 117 self._shift, 118 self._unshift) 119 self._unshift = False 120 else: 121 new_segments = segment_after.prepend(c) 122 else: 123 if c != '\0' and c != '': 124 if self._mode == TYPING_MODE_ROMAJI: 125 new_segments = [romaji.RomajiSegment(c, 126 '', 127 self._shift, 128 self._unshift)] 129 self._unshift = False 130 elif self._mode == TYPING_MODE_KANA: 131 # kana mode doesn't have shift latin in MS. 132 new_segments = [kana.KanaSegment(c)] 133 elif self._mode == TYPING_MODE_THUMB_SHIFT: 134 new_segments = [thumb.ThumbShiftSegment(c)] 135 if new_segments: 136 self.__segments[self.__cursor:self.__cursor] = new_segments 137 self.__cursor += len(new_segments) 138 139 def remove_before(self): 140 index = self.__cursor - 1 141 if index >= 0: 142 segment = self.__segments[index] 143 segment.pop() 144 if segment.is_empty(): 145 del self.__segments[index] 146 self.__cursor = index 147 return True 148 149 return False 150 151 def remove_after(self): 152 index = self.__cursor 153 if index < len(self.__segments): 154 segment = self.__segments[index] 155 segment.pop() 156 if segment.is_empty(): 157 del self.__segments[index] 158 return True 159 160 return False 161 162 def get_string(self, type): 163 pass 164 165 def move_cursor(self, delta): 166 self.__cursor += delta 167 if self.__cursor < 0: 168 self.__cursor = 0 169 elif self.__cursor > len(self.__segments): 170 self.__cursor = len(self.__segments) 171 172 # hiragana segments are not char lengths. 173 # e.g. 'ya' is 1 segment and 1 char and 'kya' is 1 segment and 2 chars. 174 def move_cursor_hiragana_length(self, length): 175 delta = length 176 if delta < 0: 177 if self.__cursor >= len(self.__segments): 178 delta = delta + (self.__cursor - len(self.__segments) + 1) 179 self.__cursor = len(self.__segments) - 1 180 while delta < 0: 181 text = str(self.__segments[self.__cursor].to_hiragana()) 182 if len(text) > -delta: 183 break 184 delta = delta + len(text) 185 self.__cursor = self.__cursor - 1 186 else: 187 if self.__cursor >= len(self.__segments): 188 self.__cursor = len(self.__segments) 189 return 190 while delta > 0: 191 text = str(self.__segments[self.__cursor].to_hiragana()) 192 if len(text) > delta: 193 break 194 delta = delta - len(text) 195 self.__cursor = self.__cursor + 1 196 197 def move_cursor_katakana_length(self, length): 198 delta = length 199 if delta < 0: 200 if self.__cursor >= len(self.__segments): 201 delta = delta + (self.__cursor - len(self.__segments) + 1) 202 self.__cursor = len(self.__segments) - 1 203 while delta < 0: 204 text = str(self.__segments[self.__cursor].to_katanaka()) 205 if len(text) > -delta: 206 break 207 delta = delta + len(text) 208 self.__cursor = self.__cursor - 1 209 else: 210 if self.__cursor >= len(self.__segments): 211 self.__cursor = len(self.__segments) 212 return 213 while delta > 0: 214 text = str(self.__segments[self.__cursor].to_katanaka()) 215 if len(text) > delta: 216 break 217 delta = delta - len(text) 218 self.__cursor = self.__cursor + 1 219 220 def move_cursor_half_with_katakana_length(self, length): 221 delta = length 222 if delta < 0: 223 if self.__cursor >= len(self.__segments): 224 delta = delta + (self.__cursor - len(self.__segments) + 1) 225 self.__cursor = len(self.__segments) - 1 226 while delta < 0: 227 text = str(self.__segments[self.__cursor].to_half_width_katakana()) 228 if len(text) > -delta: 229 break 230 delta = delta + len(text) 231 self.__cursor = self.__cursor - 1 232 else: 233 if self.__cursor >= len(self.__segments): 234 self.__cursor = len(self.__segments) 235 return 236 while delta > 0: 237 text = str(self.__segments[self.__cursor].to_half_width_katakana()) 238 if len(text) > delta: 239 break 240 delta = delta - len(text) 241 self.__cursor = self.__cursor + 1 242 243 def _chk_text(self, s): 244 period = self._prefs.get_value('common', 'period-style') 245 symbol = self._prefs.get_value('common', 'symbol-style') 246 half_symbol = self._prefs.get_value('common', 'half-width-symbol') 247 half_number = self._prefs.get_value('common', 'half-width-number') 248 ret = '' 249 for c in s: 250 c = c if not period else PeriodTable.get(c, c) 251 # thumb_left + '2' and '/' are different 252 if self._mode != TYPING_MODE_THUMB_SHIFT: 253 c = c if not symbol else SymbolTable[symbol].get(c, c) 254 c = c if not half_symbol else HalfSymbolTable.get(c, c) 255 c = c if not half_number else HalfNumberTable.get(c, c) 256 ret += c 257 return ret 258 259 def get_hiragana(self, commit=False): 260 conv = lambda s: s.to_hiragana() 261 R = lambda s: s if not (commit and s[-1:] == 'n') else s[:-1] + 'ん' 262 text_before = R(''.join(map(conv, self.__segments[:self.__cursor]))) 263 text_after = R(''.join(map(conv, self.__segments[self.__cursor:]))) 264 return self._chk_text(text_before + text_after), len(text_before) 265 266 def get_katakana(self, commit=False): 267 conv = lambda s: s.to_katakana() 268 R = lambda s: s if not (commit and s[-1:] == 'n') else s[:-1] + 'ン' 269 text_before = R(''.join(map(conv, self.__segments[:self.__cursor]))) 270 text_after = R(''.join(map(conv, self.__segments[self.__cursor:]))) 271 return self._chk_text(text_before + text_after), len(text_before) 272 273 def get_half_width_katakana(self, commit=False): 274 conv = lambda s: s.to_half_width_katakana() 275 R = lambda s: s if not (commit and s[-1:] == 'n') else s[:-1] + 'ン' 276 text_before = R(''.join(map(conv, self.__segments[:self.__cursor]))) 277 text_after = R(''.join(map(conv, self.__segments[self.__cursor:]))) 278 return self._chk_text(text_before + text_after), len(text_before) 279 280 def get_latin(self): 281 conv = lambda s: s.to_latin() 282 text_before = ''.join(map(conv, self.__segments[:self.__cursor])) 283 text_after = ''.join(map(conv, self.__segments[self.__cursor:])) 284 return text_before + text_after, len(text_before) 285 286 def get_wide_latin(self): 287 conv = lambda s: s.to_wide_latin() 288 text_before = ''.join(map(conv, self.__segments[:self.__cursor])) 289 text_after = ''.join(map(conv, self.__segments[self.__cursor:])) 290 return text_before + text_after, len(text_before) 291 292 def is_empty(self): 293 return all([s.is_empty() for s in self.__segments]) 294 295 def get_raw(self, start, end): 296 i = 0 297 r = '' 298 for s in self.__segments: 299 if i >= end: 300 break 301 elif start <= i: 302 r += s.to_latin() 303 i += len(s.to_hiragana()) 304 return r 305