/python/engine/XingMa/XingMa.py
Python | 1365 lines | 1141 code | 78 blank | 146 comment | 159 complexity | 89bfb9b85227f2726f49eb7b7bf568e0 MD5 | raw file
- # -*- coding: utf-8 -*-
- # vim: set noet ts=4:
- #
- # scim-python
- #
- # Copyright (c) 2008-2008 Yu Yuwei <acevery@gmail.com>
- #
- #
- # This library is free software; you can redistribute it and/or
- # modify it under the terms of the GNU Lesser General Public
- # License as published by the Free Software Foundation; either
- # version 2 of the License, or (at your option) any later version.
- #
- # This library is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public
- # License along with this program; if not, write to the
- # Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- # Boston, MA 02111-1307 USA
- #
- # $Id: $
- #
- import scim
- from scim import IMEngine
- from scim import IMEngineFactory
- from scim import Property
- from scim import Attribute
- from scim import KeyCode
- from scim import KeyMask
- import scim.ascii as ascii
- import XMSQLiteDB
- import XMDict
- import re
- patt_edit = re.compile (r'(.*)###(.*)###(.*)')
- patt_uncommit = re.compile (r'(.*)@@@(.*)')
- from gettext import dgettext
- _ = lambda a : dgettext ("scim-python", a)
- N_ = lambda a : a
- class Editor:
- '''Hold user inputs chars and preedit string'''
- def __init__ (self,phrase_table_index,valid_input_chars, max_key_length, database, parser = XMDict.Parse, deparser = XMDict.Deparse, max_length = 64):
- self.db = database
- self._pt = phrase_table_index
- self._parser = parser
- self._deparser = deparser
- self._max_key_len = int(max_key_length)
- self._max_length = max_length
- self._valid_input_chars = valid_input_chars
- #
- # below vals will be reset in self.clear()
- #
- # we hold this: [str,str,...]
- # self._chars: hold user input in xingma mode (valid,invalid,prevalid)
- self._chars = [[],[],[]]
- #self._t_chars: hold total input for xingma mode for input check
- self._t_chars = []
- # self._u_chars: hold user input but not manual comitted chars
- self._u_chars = []
- # self._xmkey_list: hold XingMa_Key objects transform from user input chars
- self._xmkey_list = []
- # self._strings: hold preedit strings
- self._strings = []
- # self._cursor: the caret position in preedit phrases
- self._cursor = [0,0]
- # self._candidates: hold candidates selected from database [[now],[pre]]
- self._candidates = [[],[]]
- self._lookup_table = scim.LookupTable(XingMaEngine._page_size)
- self._lookup_table.fix_page_size (True)
- # self._py_mode: whether in pinyin mode
- self._py_mode = False
- # self._zi: the last Zi commit to preedit
- self._zi = u''
- # self._caret: caret position in lookup_table
- self._caret = 0
- # self._onechar: whether we only select single character
- self._onechar = False
- def clear (self):
- '''Remove data holded'''
- self.over_input ()
- self._t_chars = []
- self._strings = []
- self._cursor = [0,0]
- self._py_mode = False
- self._zi = u''
- self.update_candidates
-
- def is_empty (self):
- return len(self._t_chars) == 0
- def clear_input (self):
- '''
- Remove input characters held for XingMa mode,
- '''
- self._chars = [[],[],[]]
- self._xmkey_list = []
- self._lookup_table.clear()
- self._lookup_table.show_cursor(False)
- self._candidates = [[],[]]
-
- def over_input (self):
- '''
- Remove input characters held for XingMa mode,
- '''
- self.clear_input ()
- self._u_chars = []
-
- def set_parser (self, parser):
- '''change input parser'''
- self.clear ()
- self._parser = parser
-
- def add_input (self,c):
- '''add input character'''
- if len (self._t_chars) == self._max_length:
- return True
- self._zi = u''
- if self._cursor[1]:
- self.split_phrase()
- if (len (self._chars[0]) == self._max_key_len and (not self._py_mode)) or ( len (self._chars[0]) == 6 and self._py_mode ) :
- self.auto_commit_to_preedit()
- res = self.add_input (c)
- return res
- elif self._chars[1]:
- self._chars[1].append (c)
- else:
- if (not self._py_mode and ( c in self._valid_input_chars)) or\
- (self._py_mode and (c in u'abcdefghijklmnopqrstuvwxyz')):
- try:
- self._xmkey_list += self._parser (c)
- self._chars[0].append (c)
- except:
- self._chars[1].append (c)
- else:
- self._chars[1].append (c)
- self._t_chars.append(c)
- res = self.update_candidates ()
- return res
- def pop_input (self):
- '''remove and display last input char held'''
- _c =''
- if self._chars[1]:
- _c = self._chars[1].pop ()
- elif self._chars[0]:
- _c = self._chars[0].pop ()
- self._xmkey_list.pop()
- if (not self._chars[0]) and self._u_chars:
- self._chars[0] = self._u_chars.pop()
- self._chars[1] = self._chars[1][:-1]
- self._xmkey_list = self._parser (self._chars[0])
- self._strings.pop (self._cursor[0] - 1 )
- self._cursor[0] -= 1
- self._t_chars.pop()
- self.update_candidates ()
- return _c
-
- def get_input_chars (self):
- '''get characters held, valid and invalid'''
- return self._chars[0] + self._chars[1]
- def get_input_chars_string (self):
- '''Get valid input char string'''
- return u''.join(map(str,self._t_chars))
- def get_all_input_strings (self):
- '''Get all uncommit input characters, used in English mode or direct commit'''
- return u''.join( map(u''.join, self._u_chars + [self._chars[0]] \
- + [self._chars[1]]) )
-
- def get_index(self,key):
- '''Get the index of key in database table'''
- return self._pt.index(key)
- def split_phrase (self):
- '''Splite current phrase into two phrase'''
- _head = u''
- _end = u''
- try:
- _head = self._strings[self._cursor[0]][:self._cursor[1]]
- _end = self._strings[self._cursor[0]][self._cursor[1]:]
- self._strings.pop(self._cursor[0])
- self._strings.insert(self._cursor[0],_head)
- self._strings.insert(self._cursor[0]+1,_end)
- self._cursor[0] +=1
- self._cursor[1] = 0
- except:
- pass
-
- def remove_before_string (self):
- '''Remove string before cursor'''
- if self._cursor[1] != 0:
- self.split_phrase()
- if self._cursor[0] > 0:
- self._strings.pop(self._cursor[0]-1)
- self._cursor[0] -= 1
- else:
- pass
- # if we remove all characters in preedit string, we need to clear the self._t_chars
- if self._cursor == [0,0]:
- self._t_chars =[]
-
- def remove_after_string (self):
- '''Remove string after cursor'''
- if self._cursor[1] != 0:
- self.split_phrase()
- if self._cursor[0] >= len (self._strings):
- pass
- else:
- self._strings.pop(self._cursor[0])
-
- def remove_before_char (self):
- '''Remove character before cursor'''
- if self._cursor[1] > 0:
- _str = self._strings[ self._cursor[0] ]
- self._strings[ self._cursor[0] ] = _str[ : self._cursor[1]-1] + _str[ self._cursor[1] :]
- self._cursor[1] -= 1
- else:
- if self._cursor[0] == 0:
- pass
- else:
- if len ( self._strings[self._cursor[0] - 1] ) == 1:
- self.remove_before_string()
- else:
- self._strings[self._cursor[0] - 1] = self._strings[self._cursor[0] - 1][:-1]
- # if we remove all characters in preedit string, we need to clear the self._t_chars
- if self._cursor == [0,0]:
- self._t_chars =[]
- def remove_after_char (self):
- '''Remove character after cursor'''
- if self._cursor[1] == 0:
- if self._cursor[0] == len ( self._strings):
- pass
- else:
- if len( self._strings[ self._cursor[0] ]) == 1:
- self.remove_after_string ()
- else:
- self._strings[ self._cursor[0] ] = self._strings[ self._cursor[0] ][1:]
- else:
- if ( self._cursor[1] + 1 ) == len( self._strings[ self._cursor[0] ] ) :
- self.split_phrase ()
- self.remove_after_string ()
- else:
- string = self._strings[ self._cursor[0] ]
- self._strings[ self._cursor[0] ] = string[:self._cursor[1]] + string[ self._cursor[1] + 1 : ]
- def get_invalid_input_chars (self):
- '''get invalid characters held'''
- return self._chars[1]
- def get_invalid_input_string (self):
- '''get invalid characters in string form'''
- return u''.join (self._chars[1])
-
- def get_preedit_strings (self):
- '''Get preedit strings'''
- if self._candidates[0]:
- if self._py_mode:
- _p_index = 7
- else:
- _p_index = self.get_index ('phrase')
- _candi = u'###' + self._candidates[0][ int (self._lookup_table.get_cursor_pos() ) ][ _p_index ] + u'###'
- else:
- input_chars = self.get_input_chars ()
- if input_chars:
- _candi = u''.join( ['###'] + map( str, input_chars) + ['###'] )
- else:
- _candi = u''
- if self._strings:
- res = u''
- _cursor = self._cursor[0]
- _luc = len (self._u_chars)
- if _luc:
- res =u''.join( self._strings[ : _cursor - _luc] +[u'@@@'] + self._strings[_cursor - _luc : _cursor ] + [ _candi ] + self._strings[ _cursor : ])
- else:
- res = u''.join( self._strings[ : _cursor ] + [ _candi ] + self._strings[ _cursor : ])
- return res
- else:
- return _candi
- def add_caret (self, addstr):
- '''add length to caret position'''
- self._caret += len(addstr)
- def get_caret (self):
- '''Get caret position in preedit strings'''
- self._caret = 0
- if self._cursor[0] and self._strings:
- map (self.add_caret,self._strings[:self._cursor[0]])
- self._caret += self._cursor[1]
- if self._candidates[0]:
- if self._py_mode:
- _p_index = 7
- else:
- _p_index = self.get_index ('phrase')
- _candi =self._candidates[0][ int (self._lookup_table.get_cursor_pos() ) ][ _p_index ]
- else:
- _candi = u''.join( map( str,self.get_input_chars()) )
- self._caret += len( _candi )
- return self._caret
-
- def arrow_left (self):
- '''Process Arrow Left Key Event.
- Update cursor data when move caret left'''
- if self.get_preedit_strings ():
- if not( self.get_input_chars () or self._u_chars ):
- if self._cursor[1] > 0:
- self._cursor[1] -= 1
- else:
- if self._cursor[0] > 0:
- self._cursor[1] = len (self._strings[self._cursor[0]-1]) - 1
- self._cursor[0] -= 1
- else:
- self._cursor[0] = len(self._strings)
- self._cursor[1] = 0
- self.update_candidates ()
- return True
- else:
- return False
-
- def arrow_right (self):
- '''Process Arrow Right Key Event.
- Update cursor data when move caret right'''
- if self.get_preedit_strings ():
- if not( self.get_input_chars () or self._u_chars ):
- if self._cursor[1] == 0:
- if self._cursor[0] == len (self._strings):
- self._cursor[0] = 0
- else:
- self._cursor[1] += 1
- else:
- self._cursor[1] += 1
- if self._cursor[1] == len(self._strings[ self._cursor[0] ]):
- self._cursor[0] += 1
- self._cursor[1] = 0
- self.update_candidates ()
- return True
- else:
- return False
- def control_arrow_left (self):
- '''Process Control + Arrow Left Key Event.
- Update cursor data when move caret to string left'''
- if self.get_preedit_strings ():
- if not( self.get_input_chars () or self._u_chars ):
- if self._cursor[1] == 0:
- if self._cursor[0] == 0:
- self._cursor[0] = len (self._strings) - 1
- else:
- self._cursor[0] -= 1
- else:
- self._cursor[1] = 0
- self.update_candidates ()
- return True
- else:
- return False
-
- def control_arrow_right (self):
- '''Process Control + Arrow Right Key Event.
- Update cursor data when move caret to string right'''
- if self.get_preedit_strings ():
- if not( self.get_input_chars () or self._u_chars ):
- if self._cursor[1] == 0:
- if self._cursor[0] == len (self._strings):
- self._cursor[0] = 1
- else:
- self._cursor[0] += 1
- else:
- self._cursor[0] += 1
- self._cursor[1] = 0
- self.update_candidates ()
- return True
- else:
- return False
- def ap_candidate (self, candi):
- '''append candidate to lookup_table'''
- if not self._py_mode:
- _p_index = self.get_index('phrase')
- _fkey = self.get_index('m0')
- else:
- _p_index = 7
- _fkey = 1
- _phrase = candi[_p_index]
- _xm = u''.join( map(self._deparser , candi[_fkey + len(self._xmkey_list) : _p_index ] ) )
- # further color implementation needed :)
- # here -2 is the pos of num, -1 is the pos of . 0 is the pos of string
- attrs = [scim.Attribute (-2, 1 , scim.ATTR_FOREGROUND, 0x8e2626 )]
- if candi[-2] < 0 :
- # this is a user defined phrase:
- attrs.append ( scim.Attribute (0, len(_phrase), scim.ATTR_FOREGROUND, 0x7700c3 ) )
- elif candi[-1] > 0:
- # this is a sys phrase used by user:
- attrs.append ( scim.Attribute (0, len(_phrase), scim.ATTR_FOREGROUND, 0x000000 ) )
- else:
- # this is a system phrase haven't been used:
- attrs.append ( scim.Attribute (0, len(_phrase), scim.ATTR_FOREGROUND, 0x000000 ) )
- # this is the part of xmkey
- attrs.append( scim.Attribute (len(_phrase), len(_xm), scim.ATTR_FOREGROUND, 0x1973a2 ) )
- self._lookup_table.append_candidate ( _phrase + _xm, attrs )
- self._lookup_table.show_cursor (True)
- def update_candidates (self):
- '''Update lookuptable'''
- if (self._chars[0] == self._chars[2] and self._candidates[0]) \
- or self._chars[1]:
- # if no change in valid input char or we have invalid input,
- # we do not do sql enquery
- pass
- else:
- # do enquiry
- self._lookup_table.clear ()
- self._lookup_table.show_cursor (False)
- if self._xmkey_list:
- # here we need to consider two parts, xingma and pinyin
- # first xingma
- if not self._py_mode:
- self._candidates[0] = self.db.select_words( self._xmkey_list, self._onechar )
- else:
- self._candidates[0] = self.db.select_zi( self._xmkey_list )
- self._chars[2] = self._chars[0][:]
-
- else:
- self._candidates[0] =[]
- if self._candidates[0]:
- map ( self.ap_candidate, self._candidates[0])
- else:
- if self._chars[0]:
- ## old manner:
- #if self._candidates[1]:
- # #print self._candidates[1]
- # self._candidates[0] = self._candidates[1]
- # self._candidates[1] = []
- # last_input = self.pop_input ()
- # self.auto_commit_to_preedit ()
- # res = self.add_input( last_input )
- # return res
- #else:
- # self.pop_input ()
- # self._lookup_table.clear()
- # self._lookup_table.show_cursor (False)
- # return False
- ###################
- ## new manner, we add new char to invalid input
- ## chars
- if not self._chars[1]:
- # we don't have invalid input chars
- # here we need to check the last input
- # is a punctuation or not, if is a punct,
- # then we use old maner to summit the former valid
- # candidate
- if ascii.ispunct (self._chars[0][-1].encode('ascii')):
- ## old manner:
- if self._candidates[1]:
- self._candidates[0] = self._candidates[1]
- self._candidates[1] = []
- last_input = self.pop_input ()
- self.auto_commit_to_preedit ()
- res = self.add_input( last_input )
- return res
- else:
- self.pop_input ()
- self._lookup_table.clear()
- self._lookup_table.show_cursor (False)
- return False
- else:
- # this is not a punct
- self._chars[1].append( self._chars[0].pop() )
- self._xmkey_list.pop()
- else:
- pass
- self._candidates[0] =[]
- else:
- self._lookup_table.clear()
- self._lookup_table.show_cursor (False)
- self._candidates[1] = self._candidates[0]
- return True
- def commit_to_preedit (self):
- '''Add select phrase in lookup table to preedit string'''
- if not self._py_mode:
- _p_index = self.get_index('phrase')
- else:
- _p_index = 7
- try:
- self._strings.insert(self._cursor[0], self._candidates[0][ self.get_cursor_pos() ][_p_index])
- self._cursor [0] += 1
- if self._py_mode:
- self._zi = self._candidates[0][ self.get_cursor_pos() ][_p_index]
- self.over_input ()
- self.update_candidates ()
- except:
- pass
-
- def auto_commit_to_preedit (self):
- '''Add select phrase in lookup table to preedit string'''
- if not self._py_mode:
- _p_index = self.get_index('phrase')
- else:
- _p_index = 7
- try:
- self._u_chars.append( self._chars[0][:] )
- self._strings.insert(self._cursor[0], self._candidates[0][ self.get_cursor_pos() ][_p_index])
- self._cursor [0] += 1
- self.clear_input()
- self.update_candidates ()
- except:
- pass
- def get_aux_strings (self):
- '''Get aux strings'''
- input_chars = self.get_input_chars ()
- if input_chars:
- #aux_string = u' '.join( map( u''.join, self._u_chars + [self._chars[0]] ) )
- aux_string = u''.join (self._chars[0])
- return aux_string
- aux_string = u''
- if self._zi:
- # we have pinyin result
- xmcodes = self.db.find_zi_code(self._zi)
- aux_string = self._zi+u': '
- aux_string = u' '.join(xmcodes)
- # self._zi = u''
- cstr = u''.join(self._strings)
- if self.db.user_can_define_phrase:
- if len (cstr ) > 1:
- aux_string += (u'\t#: ' + self.db.parse_phrase_to_xm (cstr))
- return aux_string
- def arrow_down(self):
- '''Process Arrow Down Key Event
- Move Lookup Table cursor down'''
- res = self._lookup_table.cursor_down()
- self.update_candidates ()
- if not res and self._candidates[0]:
- return True
- return res
-
- def arrow_up(self):
- '''Process Arrow Up Key Event
- Move Lookup Table cursor up'''
- res = self._lookup_table.cursor_up()
- self.update_candidates ()
- if not res and self._candidates[0]:
- return True
- return res
-
- def page_down(self):
- '''Process Page Down Key Event
- Move Lookup Table page down'''
- res = self._lookup_table.page_down()
- self.update_candidates ()
- if not res and self._candidates[0]:
- return True
- return res
-
- def page_up(self):
- '''Process Page Up Key Event
- move Lookup Table page up'''
- res = self._lookup_table.page_up()
- self.update_candidates ()
- if not res and self._candidates[0]:
- return True
- return res
-
- def number (self, index):
- '''Select the candidates in Lookup Table
- index should start from 0'''
- self._lookup_table.set_cursor_pos_in_current_page ( index )
- if index != self._lookup_table.get_cursor_pos_in_current_page ():
- # the index given is out of range we do not commit string
- return False
- self.commit_to_preedit ()
- return True
- def alt_number (self,index):
- '''Remove the candidates in Lookup Table from user_db index should start from 0'''
- cps = self._lookup_table.get_current_page_start()
- pos = cps + index
- if len (self._candidates[0]) > pos:
- # this index is valid
- can = self._candidates[0][pos]
- if can[-2] <0 :
- # freq of this candidate is -1, means this a user phrase
- self.db.remove_phrase (can)
- # make update_candidates do sql enquiry
- self._chars[2].pop()
- self.update_candidates ()
- return True
- else:
- return False
- def get_cursor_pos (self):
- '''get lookup table cursor position'''
- return self._lookup_table.get_cursor_pos()
- def get_lookup_table (self):
- '''Get lookup table'''
- return self._lookup_table
- def is_lt_visible (self):
- '''Check whether lookup table is visible'''
- return self._lookup_table.is_cursor_visible ()
-
- def backspace (self):
- '''Process backspace Key Event'''
- self._zi = u''
- if self.get_input_chars():
- self.pop_input ()
- return True
- elif self.get_preedit_strings ():
- self.remove_before_char ()
- return True
- else:
- return False
-
- def control_backspace (self):
- '''Process control+backspace Key Event'''
- self._zi = u''
- if self.get_input_chars():
- self.over_input ()
- return True
- elif self.get_preedit_strings ():
- self.remove_before_string ()
- return True
- else:
- return False
- def delete (self):
- '''Process delete Key Event'''
- self._zi = u''
- if self.get_input_chars():
- return True
- elif self.get_preedit_strings ():
- self.remove_after_char ()
- return True
- else:
- return False
-
- def control_delete (self):
- '''Process control+delete Key Event'''
- self._zi = u''
- if self.get_input_chars ():
- return True
- elif self.get_preedit_strings ():
- self.remove_after_string ()
- return True
- else:
- return False
- def l_shift (self):
- '''Process Left Shift Key Event as immediately commit to preedit strings'''
- if self._chars[0]:
- self.commit_to_preedit ()
- return True
- else:
- return False
-
- def r_shift (self):
- '''Proess Right Shift Key Event as changed between PinYin Mode and XingMa Mode'''
- self._zi = u''
- if self._chars[0]:
- self.commit_to_preedit ()
- self._py_mode = not (self._py_mode)
- return True
- def space (self):
- '''Process space Key Event
- return (KeyProcessResult,whethercommit,commitstring)'''
- if self._chars[1]:
- # we have invalid input, so do not commit
- return (False,u'')
- if self._t_chars :
- # user has input sth
- self.commit_to_preedit ()
- pstr = self.get_preedit_strings ()
- self.clear()
- return (True,pstr)
- else:
- return (False,u' ')
- def one_candidate (self):
- '''Return true if there is only one candidate'''
- return len(self._candidates[0]) == 1
- class XingMaEngine (IMEngine):
- '''The IMEngine for XingMa'''
-
- # colors
- # _phrase_color = 0xffffff
- # _user_phrase_color = 0xffffff
- # _new_phrase_color = 0xffffff
- # lookup table page size
- _page_size = 6
- def __init__ (self, factory, config, encoding, id, db ):
- IMEngine.__init__ (self, factory, config, encoding, id)
- self._config = config
- self._lookup_table = scim.LookupTable (XingMaEngine._page_size)
- self._lookup_table.fix_page_size (True)
- # this is the backend sql db we need for our IME
- # we receive this db from IMEngineFactory
- #self.db = XMSQLiteDB.XMSQLiteDB( name = dbname )
- self.db = db
- # this is the parer which parse the input string to key object
- self._parser = XMDict.Parse
-
- # 0 = english input mode
- # 1 = xingma input mode
- self._mode = 1
- # self._ime_py: True / False this IME support pinyin mode
- self._ime_py = self.db.get_ime_property ('pinyin_mode')
- if self._ime_py:
- if self._ime_py.lower() == u'true':
- self._ime_py = True
- else:
- self._ime_py = False
- else:
- print 'We coult not find "pinyin_mode" entry in database, is it a outdated database?'
- self._ime_py = False
- self._status = self.db.get_ime_property('status_prompt').encode('utf8')
- # now we check and update the valid input characters
- self._chars = self.db.get_ime_property('valid_input_chars')
- self._valid_input_chars = []
- for _c in self._chars:
- if _c in XMDict.XingMa_List:
- self._valid_input_chars.append(_c)
- del self._chars
- self._pt = self.db.get_phrase_table_index ()
- self._ml = int(self.db.get_ime_property ('max_key_length'))
-
- # Containers we used:
- self._editor = Editor(self._pt, self._valid_input_chars, self._ml, self.db)
- # some other vals we used:
- # self._prev_key: hold the key event last time.
- self._prev_key = None
- self._prev_char = None
- self._double_quotation_state = False
- self._single_quotation_state = False
- # [ENmode,XMmode] we get XMmode properties from db
- self._full_width_letter = [
- False,
- self.db.get_ime_property('def_full_width_letter').lower() == u'true'
- ]
- self._full_width_punct = [
- False,
- self.db.get_ime_property('def_full_width_punct').lower() == u'true'
- ]
- self._direct_commit = False
-
- # some properties we will involved, Property is taken from scim.
- self._status_property = Property ("status", "")
- self._letter_property = Property ("full_letter", "")
- self._punct_property = Property ("full_punct", "")
- self._py_property = Property ("py_mode", "")
- self._onechar_property = Property ("onechar","")
- self._direct_commit_property = Property ("dcommit","")
- #self._setup_property = Property ("setup", _("Setup"))
- self.reset ()
-
- def reset (self):
- self._editor.clear ()
- self._double_quotation_state = False
- self._single_quotation_state = False
- self._prev_key = None
- # do not change onechar mode
- #self._editor._onechar = False
- self._init_properties ()
- self._update_ui ()
- IMEngine.reset (self)
- def _init_properties (self):
- properties= (
- self._status_property,
- self._letter_property,
- self._punct_property,
- self._py_property,
- self._onechar_property,
- self._direct_commit_property
- # self._setup_property
- )
- self.register_properties (properties)
- self._refresh_properties ()
-
- def _refresh_properties (self):
- '''Method used to update properties'''
- # taken and modified from PinYin.py :)
- if self._mode == 1: # refresh mode
- self._status_property.label = _(self._status)
- else:
- self._status_property.label = _("EN")
- if self._full_width_letter[self._mode]:
- self._letter_property.label = ''
- self._letter_property.icon = '/usr/share/scim/icons/full-letter.png'
- else:
- self._letter_property.label = ''
- self._letter_property.icon = '/usr/share/scim/icons/half-letter.png'
- if self._full_width_punct[self._mode]:
- self._punct_property.label = ''
- self._punct_property.icon ='/usr/share/scim/icons/full-punct.png'
- else:
- self._punct_property.label = ''
- self._punct_property.icon ='/usr/share/scim/icons/half-punct.png'
-
- if self._editor._py_mode:
- self._py_property.label = ''
- self._py_property.icon ='/usr/share/scim/icons/py-mode.png'
-
- else:
- self._py_property.label = ''
- self._py_property.icon ='/usr/share/scim/icons/xm-mode.png'
- if self._editor._onechar:
- self._onechar_property.label = ''
- self._onechar_property.icon = '/usr/share/scim/icons/onechar.png'
- else:
- self._onechar_property.label = ''
- self._onechar_property.icon = '/usr/share/scim/icons/phrase.png'
- if self._direct_commit:
- self._direct_commit_property.label = ''
- self._direct_commit_property.icon = '/usr/share/scim/icons/dcommit.png'
- else:
- self._direct_commit_property.label = ''
- self._direct_commit_property.icon = '/usr/share/scim/icons/ncommit.png'
- properties = (
- self._status_property,
- self._letter_property,
- self._punct_property,
- self._py_property,
- self._onechar_property,
- self._direct_commit_property
- )
- # use buildin method to update properties :)
- map (self.update_property, properties)
-
- def _change_mode (self):
- '''Shift input mode, XM -> EN -> XM
- '''
- self._mode = int (not self._mode)
- self.reset ()
- self._update_ui ()
- def trigger_property (self, property):
- '''Shift property'''
- if property == "status":
- self._change_mode ()
- elif property == "full_letter":
- self._full_width_letter [self._mode] = not self._full_width_letter [self._mode]
- elif property == "full_punct":
- self._full_width_punct [self._mode] = not self._full_width_punct [self._mode]
- elif property == 'py_mode' and self._ime_py:
- self._editor.r_shift ()
- elif property == 'onechar':
- self._editor._onechar = not self._editor._onechar
- elif property == 'dcommit':
- self._direct_commit = not self._direct_commit
- self._refresh_properties ()
- # elif property == "setup":
- # Need implementation
- # self.start_helper ("96c07b6f-0c3d-4403-ab57-908dd9b8d513")
- # at last invoke default method
- IMEngine.trigger_property (self, property)
-
- def _update_preedit (self):
- '''Update Preedit String in UI'''
- _str = self._editor.get_preedit_strings ()
- if _str == u'':
- self.hide_preedit_string ()
- else:
- attrs = []
- res = patt_edit.match (_str)
- if res:
- _str = u''
- ures = patt_uncommit.match (res.group(1))
- if ures:
- _str=u''.join (ures.groups())
- lc = len (ures.group(1) )
- lu = len (ures.group(2) )
- attrs.append (scim.Attribute(0,lc,scim.ATTR_FOREGROUND,0x1b3f03) )
- attrs.append (scim.Attribute(lc,lu,scim.ATTR_FOREGROUND,0x0895a2) )
- lg1 = len (_str)
- else:
- _str += res.group (1)
- lg1 = len ( res.group(1) )
- attrs.append (scim.Attribute(0,lg1,scim.ATTR_FOREGROUND,0x1b3f03) )
- _str += res.group(2)
- _str += res.group(3)
- lg2 = len ( res.group(2) )
- lg3 = len ( res.group(3) )
- attrs.append( scim.Attribute(lg1,lg2,scim.ATTR_FOREGROUND,0x0e0ea0) )
- attrs.append( scim.Attribute(lg1+lg2,lg3,scim.ATTR_FOREGROUND,0x1b3f03) )
- else:
- attrs.append( scim.Attribute(0,len(_str),scim.ATTR_FOREGROUND,0x1b3f03) )
-
- self.show_preedit_string ()
- self.update_preedit_string (_str,attrs)
- self.update_preedit_caret ( self._editor.get_caret() )
- self.show_preedit_string ()
-
- def _update_aux (self):
- '''Update Aux String in UI'''
- _ic = self._editor.get_aux_strings ()
- if _ic:
- self.show_aux_string ()
- attrs = [ scim.Attribute(0, len(_ic), scim.ATTR_FOREGROUND,0x9515b5) ]
- #attrs = [ scim.Attribute(0,len(_ic),scim.ATTR_FOREGROUND,0x5540c1)]
- self.update_aux_string (_ic,attrs)
- else:
- self.hide_aux_string ()
- self.update_aux_string (u'')
- def _update_lookup_table (self):
- '''Update Lookup Table in UI'''
- if self._editor.is_lt_visible ():
- self.update_lookup_table ( self._editor.get_lookup_table() )
- self.show_lookup_table ()
- else:
- self.hide_lookup_table ()
- def _update_ui (self):
- '''Update User Interface'''
- self._update_lookup_table ()
- self._update_preedit ()
- self._update_aux ()
-
- def commit_string (self,string):
- IMEngine.commit_string ( self, string )
- self._prev_char = string[-1]
- def _convert_to_full_width (self, c):
- '''convert half width character to full width'''
- if c in [u".", u"\\", u"^", u"_", u"$", u"\"", u"'", u">", u"<" ]:
- if c == u".":
- if self._prev_char and self._prev_char.isdigit () \
- and self._prev_key and chr (self._prev_key.code) == self._prev_char:
- return u"."
- else:
- return u"\u3002"
- elif c == u"\\":
- return u"\u3001"
- elif c == u"^":
- return u"\u2026\u2026"
- elif c == u"_":
- return u"\u2014\u2014"
- elif c == u"$":
- return u"\uffe5"
- elif c == u"\"":
- self._double_quotation_state = not self._double_quotation_state
- if self._double_quotation_state:
- return u"\u201c"
- else:
- return u"\u201d"
- elif c == u"'":
- self._single_quotation_state = not self._single_quotation_state
- if self._single_quotation_state:
- return u"\u2018"
- else:
- return u"\u2019"
- elif c == u"<":
- if self._mode:
- return u"\u300a"
- elif c == u">":
- if self._mode:
- return u"\u300b"
-
- return scim.unichar_half_to_full (c)
-
- def _match_hotkey (self, key, code, mask):
-
- if key.code == code and key.mask == mask:
- if self._prev_key and key.code == self._prev_key.code and key.mask & KeyMask.ReleaseMask:
- return True
- if not key.mask & KeyMask.ReleaseMask:
- return True
- return False
-
- def process_key_event (self, key):
- '''Process Key Events
- Key Events include Key Press and Key Release,
- KeyMask means Key Pressed
- '''
- result = self._process_key_event (key)
- self._prev_key = key
- return result
- def _process_key_event (self, key):
- '''Internal method to process key event'''
- # Match mode switch hotkey
- if not self._editor._t_chars and ( self._match_hotkey (key, KeyCode.KEY_Shift_L, KeyMask.ShiftMask + KeyMask.ReleaseMask)):
- self._change_mode ()
- return True
- # Match full half letter mode switch hotkey
- if self._match_hotkey (key, KeyCode.KEY_space, KeyMask.ShiftMask):
- self.trigger_property ("full_letter")
- return True
-
- # Match full half punct mode switch hotkey
- if self._match_hotkey (key, KeyCode.KEY_period, KeyMask.ControlMask):
- self.trigger_property ("full_punct")
- return True
-
- # we ignore all hotkeys
- # if key.mask & KeyMask.AltMask:
- # return False
- # Ignore key release event
- # if key.mask & KeyMask.ReleaseMask:
- # return True
-
- if self._mode:
- return self._xingma_mode_process_key_event (key)
- else:
- return self._english_mode_process_key_event (key)
- def _english_mode_process_key_event (self, key):
- '''English Mode Process Key Event'''
- # Ignore key release event
- if key.mask & KeyMask.ReleaseMask:
- return True
-
- if key.code >= 128:
- return False
- # we ignore all hotkeys here
- if key.mask & KeyMask.ControlMask+KeyMask.AltMask:
- return False
-
- c = unichr (key.code)
- if ascii.ispunct (key.code): # if key code is a punctation
- if self._full_width_punct[self._mode]:
- self.commit_string (self._convert_to_full_width (c))
- return True
- else:
- self.commit_string (c)
- return True
-
- if self._full_width_letter[self._mode]: # if key code is a letter or digit
- self.commit_string (self._convert_to_full_width (c))
- return True
- else:
- self.commit_string (c)
- return True
-
- # should not reach there
- return False
-
- def _xingma_mode_process_key_event (self, key):
- '''Xingma Mode Process Key Event'''
- cond_letter_translate = lambda (c): \
- self._convert_to_full_width (c) if self._full_width_letter [self._mode] else c
- cond_punct_translate = lambda (c): \
- self._convert_to_full_width (c) if self._full_width_punct [self._mode] else c
-
- # We have to process the pinyin mode change key event here,
- # because we ignore all Release event below.
- if self._match_hotkey (key, KeyCode.KEY_Shift_R, KeyMask.ShiftMask + KeyMask.ReleaseMask) and self._ime_py:
- res = self._editor.r_shift ()
- self._refresh_properties ()
- self._update_ui ()
- return res
- # process commit to preedit
- if self._match_hotkey (key, KeyCode.KEY_Shift_R, KeyMask.ShiftMask + KeyMask.ReleaseMask) or self._match_hotkey (key, KeyCode.KEY_Shift_L, KeyMask.ShiftMask + KeyMask.ReleaseMask):
- res = self._editor.l_shift ()
- self._update_ui ()
- return res
-
- # Match sigle char mode switch hotkey
- if self._match_hotkey (key, KeyCode.KEY_comma, KeyMask.ControlMask):
- self.trigger_property ( u"onechar" )
- return True
-
- # Match direct commit mode switch hotkey
- if self._match_hotkey (key, KeyCode.KEY_slash, KeyMask.ControlMask):
- self.trigger_property ( u"dcommit" )
- return True
- # Ignore key release event now :)
- if key.mask & KeyMask.ReleaseMask:
- return True
- if self._editor.is_empty ():
- # we have not input anything
- if key.code <= 127 and (not key.mask & KeyMask.AltMask + KeyMask.ControlMask):
- if ascii.ispunct (key.code) and ( unichr(key.code) not in self._valid_input_chars ) :
- self.commit_string (cond_punct_translate (unichr (key.code)))
- return True
- if ascii.isdigit (key.code):
- self.commit_string (cond_letter_translate (unichr (key.code)))
- return True
- if key.code in (KeyCode.KEY_Return, KeyCode.KEY_KP_Enter):
- if self._editor.is_empty ():
- return False
- else:
- commit_string = self._editor.get_all_input_strings ()
- self._editor.clear ()
- self._update_ui ()
- self.commit_string (commit_string)
- self._refresh_properties ()
- return True
-
- elif key.code == KeyCode.KEY_space:
- o_py = self._editor._py_mode
- sp_res = self._editor.space ()
- self._update_ui ()
- #return (KeyProcessResult,whethercommit,commitstring)
- if sp_res[0]:
- self.commit_string (sp_res[1])
- self.db.check_phrase (sp_res[1])
- else:
- if sp_res[1] == u' ':
- self.commit_string (cond_letter_translate (u" "))
- if o_py != self._editor._py_mode:
- self._refresh_properties ()
- return True
-
- elif key.code == KeyCode.KEY_BackSpace and key.mask & KeyMask.ControlMask:
- res = self._editor.control_backspace ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_BackSpace:
- res = self._editor.backspace ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_Escape:
- if self._editor.is_empty () and (not self._editor._py_mode) :
- return False
- else:
- self.reset ()
- self._update_ui ()
- return True
-
- elif key.code == KeyCode.KEY_Down :
- res = self._editor.arrow_down ()
- self._update_lookup_table ()
- return res
-
- elif key.code == KeyCode.KEY_Up:
- res = self._editor.arrow_up ()
- self._update_lookup_table ()
- return res
-
- elif key.code == KeyCode.KEY_Left and key.mask & KeyMask.ControlMask:
- res = self._editor.control_arrow_left ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_Right and key.mask & KeyMask.ControlMask:
- res = self._editor.control_arrow_right ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_Left:
- res = self._editor.arrow_left ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_Right:
- res = self._editor.arrow_right ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_Delete and key.mask & KeyMask.ControlMask:
- res = self._editor.control_delete ()
- self._update_ui ()
- return res
-
- elif key.code == KeyCode.KEY_Delete:
- res = self._editor.delete ()
- self._update_ui ()
- return res
- elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9 and self._editor._candidates[0] and key.mask & KeyMask.ControlMask:
- res = self._editor.number (key.code - KeyCode.KEY_1)
- self._update_ui ()
- return res
- elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9 and self._editor._candidates[0] and key.mask & KeyMask.AltMask:
- res = self._editor.alt_number (key.code - KeyCode.KEY_1)
- self._update_ui ()
- return res
- # now we ignore all else hotkeys
- elif key.mask & KeyMask.ControlMask+KeyMask.AltMask:
- return False
- elif key.mask & KeyMask.AltMask:
- return False
- elif unichr(key.code) in self._valid_input_chars or \
- ( self._editor._py_mode and \
- unichr(key.code) in u'abcdefghijklmnopqrstuvwxyz' ):
- if self._direct_commit and len(self._editor._chars[0]) == self._ml:
- # it is time to direct commit
- sp_res = self._editor.space ()
- #return (whethercommit,commitstring)
- if sp_res[0]:
- self.commit_string (sp_res[1])
- self.db.check_phrase (sp_res[1])
-
- res = self._editor.add_input ( unichr(key.code) )
- if not res:
- if ascii.ispunct (key.code):
- key_char = cond_punct_translate (unichr (key.code))
- else:
- key_char = cond_letter_translate (unichr (key.code))
- sp_res = self._editor.space ()
- self._update_ui ()
- #return (KeyProcessResult,whethercommit,commitstring)
- if sp_res[0]:
- self.commit_string (sp_res[1] + key_char)
- self.db.check_phrase (sp_res[1])
- else:
- self.commit_string ( key_char )
- else:
- if self._direct_commit and self._editor.one_candidate () \
- and len(self._editor._chars[0]) == self._ml:
- # it is time to direct commit
- sp_res = self._editor.space ()
- #return (whethercommit,commitstring)
- if sp_res[0]:
- self.commit_string (sp_res[1])
- self.db.check_phrase (sp_res[1])
- self._update_ui ()
- return True
-
- elif key.code in (KeyCode.KEY_equal, KeyCode.KEY_Page_Down, KeyCode.KEY_KP_Page_Down) and self._editor._candidates[0]:
- res = self._editor.page_down()
- self._update_ui ()
- return res
- elif key.code in (KeyCode.KEY_minus, KeyCode.KEY_Page_Up, KeyCode.KEY_KP_Page_Up) and self._editor._candidates[0]:
- res = self._editor.page_up ()
- self._update_ui ()
- return res
-
- elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9 and self._editor._candidates[0]:
- res = self._editor.number (key.code - KeyCode.KEY_1)
- if res:
- commit_string = self._editor.get_preedit_strings ()
- self._editor.clear ()
- self.commit_string (commit_string)
- # modify freq info
- self.db.check_phrase (commit_string)
- self._refresh_properties ()
- self._update_ui ()
- return True
-
- elif key.code <= 127:
- if not self._editor._candidates[0]:
- commit_string = self._editor.get_all_input_strings ()
- else:
- self._editor.commit_to_preedit ()
- commit_string = self._editor.get_preedit_strings ()
- self._editor.clear ()
- if ascii.ispunct (key.code):
- self.commit_string ( commit_string + cond_punct_translate (unichr (key.code)))
- else:
- self.commit_string ( commit_string + cond_letter_translate (unichr (key.code)))
- self._update_ui ()
- return True
- return False
-
- # below for initial test
- def focus_in (self):
- self._init_properties ()
- IMEngine.focus_in (self)
- self._update_ui ()
-
- def focus_out (self):
- IMEngine.focus_out (self)
- def lookup_table_page_up (self):
- if self._editor.page_up ():
- self._update_lookup_table ()
- return True
-
- IMEngine.lookup_table_page_up (self)
- return True
- def lookup_table_page_down (self):
- if self._editor.page_down ():
- self._update_lookup_table ()
- return True
- IMEngine.lookup_table_page_down (self)
- return False
-
- def reload_config (self, config):
- self._lookup_table.set_page_size (XingMaEngine._page_size)
- self.focus_in ()
-
- class Factory (IMEngineFactory):
- """XingMa IM Engine Factory"""
- def __init__ (self, config, db):
- import os.path
- import locale
- IMEngineFactory.__init__ (self, config)
- # this is the backend sql db we need for our IME
- # we need get lots of IME property from this db :)
- #self.db = XMSQLiteDB.XMSQLiteDB( name = database )
-
- udb = os.path.basename(db).replace('.db','-user.db')
- self.db = XMSQLiteDB.XMSQLiteDB( name = db,user_db = udb )
- self.dbname = db
- ulocale = locale.getdefaultlocale ()[0].lower()
- self.name = self.db.get_ime_property ('name.%s' % ulocale)
- if not self.name:
- self.name = _( self.db.get_ime_property ('name') )
- self.uuid = self.db.get_ime_property ('uuid')
- self.authors = self.db.get_ime_property ('author')
- self.icon_file = self.db.get_ime_property ('icon')
- self.credits = self.db.get_ime_property ('credit')
- self.help = _(u"Help For Python XingMa\n\tPlease read http://code.google.com/p/scim-python/wiki/XingMaUserGuide")
- # init factory
- self._config = config
- self.set_languages ( self.db.get_ime_property ('languages') )
- # self.reload_config (config)
- self.db.db.commit()
- def destroy (self):
- '''Destructor, which finish some task for IME'''
- # we need to sync the temp userdb in memory to the user_db on disk
- self.db.sync_usrdb ()
- def create_instance (self, encoding, id):
- engine = XingMaEngine (self, self._config, encoding, id, self.db)
- return engine
- def reload_config (self, config):
- pass
- # XingMaEngine._page_size = \
- # config.read ("/IMEngine/Python/XingMa/PageSize", 5)
- # if XingMaEngine._page_size < 1 or XingMaEngine._page_size > 9:
- # XingMaEngine._page_size = 5
-
- # XingMaEngine._phrase_color = \
- # config.read ("/IMEngine/Python/XingMa/PhraseColor", XingMaEngine._phrase_color)
- # XingMaEngine._new_phrase_color = \
- # config.read ("/IMEngine/Python/XingMa/NewPhraseColor", XingMaEngine._new_phrase_color)
- # XingMaEngine._user_phrase_color = \
- # config.read ("/IMEngine/Python/XingMa/UserPhraseColor", XingMaEngine._user_phrase_color)