PageRenderTime 3716ms CodeModel.GetById 513ms app.highlight 2307ms RepoModel.GetById 271ms app.codeStats 0ms

/python/engine/XingMa/XingMa.py

http://scim-python.googlecode.com/
Python | 1365 lines | 1141 code | 78 blank | 146 comment | 127 complexity | 89bfb9b85227f2726f49eb7b7bf568e0 MD5 | raw file
   1# -*- coding: utf-8 -*-
   2# vim: set noet ts=4:
   3#
   4# scim-python
   5#
   6# Copyright (c) 2008-2008 Yu Yuwei <acevery@gmail.com>
   7#
   8#
   9# This library is free software; you can redistribute it and/or
  10# modify it under the terms of the GNU Lesser General Public
  11# License as published by the Free Software Foundation; either
  12# version 2 of the License, or (at your option) any later version.
  13#
  14# This library is distributed in the hope that it will be useful,
  15# but WITHOUT ANY WARRANTY; without even the implied warranty of
  16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17# GNU Lesser General Public License for more details.
  18#
  19# You should have received a copy of the GNU Lesser General Public
  20# License along with this program; if not, write to the
  21# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  22# Boston, MA  02111-1307  USA
  23#
  24# $Id: $
  25#
  26
  27import scim
  28from scim import IMEngine
  29from scim import IMEngineFactory
  30from scim import Property
  31from scim import Attribute
  32from scim import KeyCode
  33from scim import KeyMask
  34import scim.ascii as ascii
  35import XMSQLiteDB
  36import XMDict
  37import re
  38patt_edit = re.compile (r'(.*)###(.*)###(.*)')
  39patt_uncommit = re.compile (r'(.*)@@@(.*)')
  40
  41from gettext import dgettext
  42_  = lambda a : dgettext ("scim-python", a)
  43N_ = lambda a : a
  44
  45class Editor:
  46	'''Hold user inputs chars and preedit string'''
  47	def __init__ (self,phrase_table_index,valid_input_chars, max_key_length, database, parser = XMDict.Parse, deparser = XMDict.Deparse, max_length = 64):
  48		self.db = database
  49		self._pt = phrase_table_index
  50		self._parser = parser
  51		self._deparser = deparser
  52		self._max_key_len = int(max_key_length)
  53		self._max_length = max_length
  54		self._valid_input_chars = valid_input_chars
  55		#
  56		# below vals will be reset in self.clear()
  57		#
  58		# we hold this: [str,str,...]
  59		# self._chars: hold user input in xingma mode (valid,invalid,prevalid)
  60		self._chars = [[],[],[]]
  61		#self._t_chars: hold total input for xingma mode for input check
  62		self._t_chars = []
  63		# self._u_chars: hold user input but not manual comitted chars
  64		self._u_chars = []
  65		# self._xmkey_list: hold XingMa_Key objects transform from user input chars
  66		self._xmkey_list = []
  67		# self._strings: hold preedit strings
  68		self._strings = []
  69		# self._cursor: the caret position in preedit phrases 
  70		self._cursor = [0,0]
  71		# self._candidates: hold candidates selected from database [[now],[pre]]
  72		self._candidates = [[],[]]
  73		self._lookup_table = scim.LookupTable(XingMaEngine._page_size)
  74		self._lookup_table.fix_page_size (True)
  75		# self._py_mode: whether in pinyin mode
  76		self._py_mode = False
  77		# self._zi: the last Zi commit to preedit
  78		self._zi = u''
  79		# self._caret: caret position in lookup_table
  80		self._caret = 0
  81		# self._onechar: whether we only select single character
  82		self._onechar = False
  83
  84	def clear (self):
  85		'''Remove data holded'''
  86		self.over_input ()
  87		self._t_chars = []
  88		self._strings = []
  89		self._cursor = [0,0]
  90		self._py_mode = False
  91		self._zi = u''
  92		self.update_candidates
  93	
  94	def is_empty (self):
  95		return len(self._t_chars) == 0
  96
  97	def clear_input (self):
  98		'''
  99		Remove input characters held for XingMa mode,
 100		'''
 101		self._chars = [[],[],[]]
 102		self._xmkey_list = []
 103		self._lookup_table.clear()
 104		self._lookup_table.show_cursor(False)
 105		self._candidates = [[],[]]
 106	
 107	def over_input (self):
 108		'''
 109		Remove input characters held for XingMa mode,
 110		'''
 111		self.clear_input ()
 112		self._u_chars = []
 113	
 114	def set_parser (self, parser):
 115		'''change input parser'''
 116		self.clear ()
 117		self._parser = parser
 118		
 119	def add_input (self,c):
 120		'''add input character'''
 121		if len (self._t_chars) == self._max_length:
 122			return True
 123		self._zi = u''
 124		if self._cursor[1]:
 125			self.split_phrase()
 126		if (len (self._chars[0]) == self._max_key_len and (not self._py_mode)) or ( len (self._chars[0]) == 6 and self._py_mode ) :
 127			self.auto_commit_to_preedit()
 128			res = self.add_input (c)
 129			return res
 130		elif self._chars[1]:
 131			self._chars[1].append (c)
 132		else:
 133			if (not self._py_mode and ( c in self._valid_input_chars)) or\
 134				(self._py_mode and (c in u'abcdefghijklmnopqrstuvwxyz')):
 135				try:
 136					self._xmkey_list += self._parser (c)
 137					self._chars[0].append (c)
 138				except:
 139					self._chars[1].append (c)
 140			else:
 141				self._chars[1].append (c)
 142		self._t_chars.append(c)
 143		res = self.update_candidates ()
 144		return res
 145
 146	def pop_input (self):
 147		'''remove and display last input char held'''
 148		_c =''
 149		if self._chars[1]:
 150			_c = self._chars[1].pop ()
 151		elif self._chars[0]:
 152			_c = self._chars[0].pop ()
 153			self._xmkey_list.pop()
 154			if (not self._chars[0]) and self._u_chars:
 155				self._chars[0] = self._u_chars.pop()
 156				self._chars[1] = self._chars[1][:-1]
 157				self._xmkey_list = self._parser (self._chars[0])
 158				self._strings.pop (self._cursor[0] - 1 )
 159				self._cursor[0] -= 1
 160		self._t_chars.pop()
 161		self.update_candidates ()
 162		return _c
 163	
 164	def get_input_chars (self):
 165		'''get characters held, valid and invalid'''
 166		return self._chars[0] + self._chars[1]
 167
 168	def get_input_chars_string (self):
 169		'''Get valid input char string'''
 170		return u''.join(map(str,self._t_chars))
 171
 172	def get_all_input_strings (self):
 173		'''Get all uncommit input characters, used in English mode or direct commit'''
 174		return  u''.join( map(u''.join, self._u_chars + [self._chars[0]] \
 175			+ [self._chars[1]]) )
 176	
 177	def get_index(self,key):
 178		'''Get the index of key in database table'''
 179		return self._pt.index(key)
 180
 181	def split_phrase (self):
 182		'''Splite current phrase into two phrase'''
 183		_head = u''
 184		_end = u''
 185		try:
 186			_head = self._strings[self._cursor[0]][:self._cursor[1]]
 187			_end = self._strings[self._cursor[0]][self._cursor[1]:]
 188			self._strings.pop(self._cursor[0])
 189			self._strings.insert(self._cursor[0],_head)
 190			self._strings.insert(self._cursor[0]+1,_end)
 191			self._cursor[0] +=1
 192			self._cursor[1] = 0
 193		except:
 194			pass
 195	
 196	def remove_before_string (self):
 197		'''Remove string before cursor'''
 198		if self._cursor[1] != 0:
 199			self.split_phrase()
 200		if self._cursor[0] > 0:
 201			self._strings.pop(self._cursor[0]-1)
 202			self._cursor[0] -= 1
 203		else:
 204			pass
 205		# if we remove all characters in preedit string, we need to clear the self._t_chars
 206		if self._cursor == [0,0]:
 207			self._t_chars =[]
 208	
 209	def remove_after_string (self):
 210		'''Remove string after cursor'''
 211		if self._cursor[1] != 0:
 212			self.split_phrase()
 213		if self._cursor[0] >= len (self._strings):
 214			pass
 215		else:
 216			self._strings.pop(self._cursor[0])
 217	
 218	def remove_before_char (self):
 219		'''Remove character before cursor'''
 220		if self._cursor[1] > 0:
 221			_str = self._strings[ self._cursor[0] ]
 222			self._strings[ self._cursor[0] ] = _str[ : self._cursor[1]-1] + _str[ self._cursor[1] :]
 223			self._cursor[1] -= 1
 224		else:
 225			if self._cursor[0] == 0:
 226				pass
 227			else:
 228				if len ( self._strings[self._cursor[0] - 1] ) == 1:
 229					self.remove_before_string()
 230				else:
 231					self._strings[self._cursor[0] - 1] = self._strings[self._cursor[0] - 1][:-1]
 232		# if we remove all characters in preedit string, we need to clear the self._t_chars
 233		if self._cursor == [0,0]:
 234			self._t_chars =[]
 235
 236	def remove_after_char (self):
 237		'''Remove character after cursor'''
 238		if self._cursor[1] == 0:
 239			if self._cursor[0] == len ( self._strings):
 240				pass
 241			else:
 242				if len( self._strings[ self._cursor[0] ]) == 1:
 243					self.remove_after_string ()
 244				else:
 245					self._strings[ self._cursor[0] ] = self._strings[ self._cursor[0] ][1:]
 246		else:
 247			if ( self._cursor[1] + 1 ) == len( self._strings[ self._cursor[0] ] ) :
 248				self.split_phrase ()
 249				self.remove_after_string ()
 250			else:
 251				string = self._strings[ self._cursor[0] ]
 252				self._strings[ self._cursor[0] ] = string[:self._cursor[1]] + string[ self._cursor[1] + 1 : ]
 253
 254	def get_invalid_input_chars (self):
 255		'''get invalid characters held'''
 256		return self._chars[1]
 257
 258	def get_invalid_input_string (self):
 259		'''get invalid characters in string form'''
 260		return u''.join (self._chars[1])
 261		
 262	def get_preedit_strings (self):
 263		'''Get preedit strings'''
 264		if self._candidates[0]:
 265			if self._py_mode:
 266				_p_index = 7
 267			else:
 268				_p_index = self.get_index ('phrase')
 269			_candi = u'###' + self._candidates[0][ int (self._lookup_table.get_cursor_pos() ) ][ _p_index ] + u'###' 
 270		else:
 271			input_chars = self.get_input_chars ()
 272			if input_chars:
 273				_candi = u''.join( ['###'] + map( str, input_chars) + ['###'] )
 274			else:
 275				_candi = u''
 276		if self._strings:
 277			res = u''
 278			_cursor = self._cursor[0]
 279			_luc = len (self._u_chars)
 280			if _luc:
 281				res =u''.join( self._strings[ : _cursor - _luc] +[u'@@@'] + self._strings[_cursor - _luc : _cursor ]  + [ _candi  ] + self._strings[ _cursor : ])
 282			else:
 283				res = u''.join( self._strings[ : _cursor ] + [ _candi  ] + self._strings[ _cursor : ])
 284			return res
 285		else:
 286			return _candi
 287	def add_caret (self, addstr):
 288		'''add length to caret position'''
 289		self._caret += len(addstr)
 290
 291	def get_caret (self):
 292		'''Get caret position in preedit strings'''
 293		self._caret = 0
 294		if self._cursor[0] and self._strings:
 295			map (self.add_caret,self._strings[:self._cursor[0]])
 296		self._caret += self._cursor[1]
 297		if self._candidates[0]:
 298			if self._py_mode:
 299				_p_index = 7
 300			else:
 301				_p_index = self.get_index ('phrase')
 302			_candi =self._candidates[0][ int (self._lookup_table.get_cursor_pos() ) ][ _p_index ] 
 303		else:
 304			_candi = u''.join( map( str,self.get_input_chars()) )
 305		self._caret += len( _candi ) 
 306		return self._caret
 307	
 308	def arrow_left (self):
 309		'''Process Arrow Left Key Event.
 310		Update cursor data when move caret left'''
 311		if self.get_preedit_strings ():
 312			if not( self.get_input_chars () or self._u_chars ):
 313				if self._cursor[1] > 0:
 314					self._cursor[1] -= 1
 315				else:
 316					if self._cursor[0] > 0:
 317						self._cursor[1] = len (self._strings[self._cursor[0]-1]) - 1
 318						self._cursor[0] -= 1
 319					else:
 320						self._cursor[0] = len(self._strings)
 321						self._cursor[1] = 0
 322				self.update_candidates ()
 323			return True
 324		else:
 325			return False
 326	
 327	def arrow_right (self):
 328		'''Process Arrow Right Key Event.
 329		Update cursor data when move caret right'''
 330		if self.get_preedit_strings ():
 331			if not( self.get_input_chars () or self._u_chars ):
 332				if self._cursor[1] == 0:
 333					if self._cursor[0] == len (self._strings):
 334						self._cursor[0] = 0
 335					else:
 336						self._cursor[1] += 1
 337				else:
 338					self._cursor[1] += 1
 339				if self._cursor[1] == len(self._strings[ self._cursor[0] ]):
 340					self._cursor[0] += 1
 341					self._cursor[1] = 0
 342				self.update_candidates ()
 343			return True
 344		else:
 345			return False
 346
 347	def control_arrow_left (self):
 348		'''Process Control + Arrow Left Key Event.
 349		Update cursor data when move caret to string left'''
 350		if self.get_preedit_strings ():
 351			if not( self.get_input_chars () or self._u_chars ):
 352				if self._cursor[1] == 0:
 353					if self._cursor[0] == 0:
 354						self._cursor[0] = len (self._strings) - 1
 355					else:
 356						self._cursor[0] -= 1
 357				else:
 358					self._cursor[1] = 0
 359				self.update_candidates ()
 360			return True
 361		else:
 362			return False
 363	
 364	def control_arrow_right (self):
 365		'''Process Control + Arrow Right Key Event.
 366		Update cursor data when move caret to string right'''
 367		if self.get_preedit_strings ():
 368			if not( self.get_input_chars () or self._u_chars ):
 369				if self._cursor[1] == 0:
 370					if self._cursor[0] == len (self._strings):
 371						self._cursor[0] = 1
 372					else:
 373						self._cursor[0] += 1
 374				else:
 375					self._cursor[0] += 1
 376					self._cursor[1] = 0
 377				self.update_candidates ()
 378			return True
 379		else:
 380			return False
 381	def ap_candidate (self, candi):
 382		'''append candidate to lookup_table'''
 383		if not self._py_mode:
 384			_p_index = self.get_index('phrase')
 385			_fkey = self.get_index('m0')
 386		else:
 387			_p_index = 7
 388			_fkey = 1
 389		_phrase = candi[_p_index]
 390		_xm = u''.join( map(self._deparser , candi[_fkey + len(self._xmkey_list) : _p_index ] ) )
 391		# further color implementation needed :)
 392		# here -2 is the pos of num, -1 is the pos of . 0 is the pos of string
 393		attrs = [scim.Attribute (-2, 1 , scim.ATTR_FOREGROUND, 0x8e2626 )]
 394		if candi[-2] < 0 :
 395			# this is a user defined phrase:
 396			attrs.append ( scim.Attribute (0, len(_phrase), scim.ATTR_FOREGROUND, 0x7700c3 ) )
 397		elif candi[-1] > 0:
 398			# this is a sys phrase used by user:
 399			attrs.append ( scim.Attribute (0, len(_phrase), scim.ATTR_FOREGROUND, 0x000000 ) )
 400		else:
 401			# this is a system phrase haven't been used:
 402			attrs.append ( scim.Attribute (0, len(_phrase), scim.ATTR_FOREGROUND, 0x000000 ) )
 403		# this is the part of xmkey
 404		attrs.append( scim.Attribute (len(_phrase), len(_xm), scim.ATTR_FOREGROUND, 0x1973a2 ) )
 405		self._lookup_table.append_candidate ( _phrase + _xm, attrs )
 406		self._lookup_table.show_cursor (True)
 407
 408	def update_candidates (self):
 409		'''Update lookuptable'''
 410		if (self._chars[0] == self._chars[2] and self._candidates[0]) \
 411				or self._chars[1]:
 412			# if no change in valid input char or we have invalid input,
 413			# we do not do sql enquery
 414			pass
 415		else:
 416			# do enquiry
 417			self._lookup_table.clear ()
 418			self._lookup_table.show_cursor (False)
 419			if self._xmkey_list:
 420				# here we need to consider two parts, xingma and pinyin
 421				# first xingma
 422				if not self._py_mode:
 423					self._candidates[0] = self.db.select_words( self._xmkey_list, self._onechar )
 424				else:
 425					self._candidates[0] = self.db.select_zi( self._xmkey_list )
 426				self._chars[2] = self._chars[0][:]
 427					
 428			else:
 429				self._candidates[0] =[]
 430			if self._candidates[0]:
 431				map ( self.ap_candidate, self._candidates[0])
 432			else:
 433				if self._chars[0]:
 434					## old manner:
 435					#if self._candidates[1]:
 436					#	#print self._candidates[1]
 437					#	self._candidates[0] = self._candidates[1]
 438					#	self._candidates[1] = []
 439					#	last_input = self.pop_input ()
 440					#	self.auto_commit_to_preedit ()
 441					#	res = self.add_input( last_input )
 442					#	return res
 443					#else:
 444					#	self.pop_input ()
 445					#	self._lookup_table.clear()
 446					#	self._lookup_table.show_cursor (False)
 447					#	return False
 448					###################
 449					## new manner, we add new char to invalid input
 450					## chars
 451					if not self._chars[1]:
 452						# we don't have invalid input chars
 453						# here we need to check the last input
 454						# is a punctuation or not, if is a punct,
 455						# then we use old maner to summit the former valid
 456						# candidate
 457						if ascii.ispunct (self._chars[0][-1].encode('ascii')):
 458							## old manner:
 459							if self._candidates[1]:
 460								self._candidates[0] = self._candidates[1]
 461								self._candidates[1] = []
 462								last_input = self.pop_input ()
 463								self.auto_commit_to_preedit ()
 464								res = self.add_input( last_input )
 465								return res
 466							else:
 467								self.pop_input ()
 468								self._lookup_table.clear()
 469								self._lookup_table.show_cursor (False)
 470								return False
 471						else:	
 472							# this is not a punct
 473							self._chars[1].append( self._chars[0].pop() )
 474							self._xmkey_list.pop()
 475					else:
 476						pass
 477					self._candidates[0] =[]
 478				else:
 479					self._lookup_table.clear()
 480					self._lookup_table.show_cursor (False)
 481			self._candidates[1] = self._candidates[0]
 482		return True	
 483
 484	def commit_to_preedit (self):
 485		'''Add select phrase in lookup table to preedit string'''
 486		if not self._py_mode:
 487			_p_index = self.get_index('phrase')
 488		else:
 489			_p_index = 7
 490		try:
 491			self._strings.insert(self._cursor[0], self._candidates[0][ self.get_cursor_pos() ][_p_index])
 492			self._cursor [0] += 1
 493			if self._py_mode:
 494				self._zi = self._candidates[0][ self.get_cursor_pos() ][_p_index]
 495			self.over_input ()
 496			self.update_candidates ()
 497		except:
 498			pass
 499	
 500	def auto_commit_to_preedit (self):
 501		'''Add select phrase in lookup table to preedit string'''
 502		if not self._py_mode:
 503			_p_index = self.get_index('phrase')
 504		else:
 505			_p_index = 7
 506		try:
 507			self._u_chars.append( self._chars[0][:] )
 508			self._strings.insert(self._cursor[0], self._candidates[0][ self.get_cursor_pos() ][_p_index])
 509			self._cursor [0] += 1
 510			self.clear_input()
 511			self.update_candidates ()
 512		except:
 513			pass
 514
 515	def get_aux_strings (self):
 516		'''Get aux strings'''
 517		input_chars = self.get_input_chars ()
 518		if input_chars:
 519			#aux_string =  u' '.join( map( u''.join, self._u_chars + [self._chars[0]] ) )
 520			aux_string =   u''.join (self._chars[0]) 
 521			return aux_string
 522
 523		aux_string = u''
 524		if self._zi:
 525			# we have pinyin result
 526			xmcodes = self.db.find_zi_code(self._zi)
 527			aux_string = self._zi+u': '
 528			aux_string = u' '.join(xmcodes)
 529		#	self._zi = u''
 530		cstr = u''.join(self._strings)
 531		if self.db.user_can_define_phrase:
 532			if len (cstr ) > 1:
 533				aux_string += (u'\t#: ' + self.db.parse_phrase_to_xm (cstr))
 534		return aux_string
 535	def arrow_down(self):
 536		'''Process Arrow Down Key Event
 537		Move Lookup Table cursor down'''
 538		res = self._lookup_table.cursor_down()
 539		self.update_candidates ()
 540		if not res and self._candidates[0]:
 541			return True
 542		return res
 543	
 544	def arrow_up(self):
 545		'''Process Arrow Up Key Event
 546		Move Lookup Table cursor up'''
 547		res = self._lookup_table.cursor_up()
 548		self.update_candidates ()
 549		if not res and self._candidates[0]:
 550			return True
 551		return res
 552	
 553	def page_down(self):
 554		'''Process Page Down Key Event
 555		Move Lookup Table page down'''
 556		res = self._lookup_table.page_down()
 557		self.update_candidates ()
 558		if not res and self._candidates[0]:
 559			return True
 560		return res
 561	
 562	def page_up(self):
 563		'''Process Page Up Key Event
 564		move Lookup Table page up'''
 565		res = self._lookup_table.page_up()
 566		self.update_candidates ()
 567		if not res and self._candidates[0]:
 568			return True
 569		return res
 570	
 571	def number (self, index):
 572		'''Select the candidates in Lookup Table
 573		index should start from 0'''
 574		self._lookup_table.set_cursor_pos_in_current_page ( index )
 575		if index != self._lookup_table.get_cursor_pos_in_current_page ():
 576			# the index given is out of range we do not commit string
 577			return False
 578		self.commit_to_preedit ()
 579		return True
 580
 581	def alt_number (self,index):
 582		'''Remove the candidates in Lookup Table from user_db index should start from 0'''
 583		cps = self._lookup_table.get_current_page_start()
 584		pos = cps + index
 585		if  len (self._candidates[0]) > pos:
 586			# this index is valid
 587			can = self._candidates[0][pos]
 588			if can[-2] <0 :
 589				# freq of this candidate is -1, means this a user phrase
 590				self.db.remove_phrase (can)
 591				# make update_candidates do sql enquiry
 592				self._chars[2].pop()
 593				self.update_candidates ()
 594			return True
 595		else:
 596			return False
 597
 598	def get_cursor_pos (self):
 599		'''get lookup table cursor position'''
 600		return self._lookup_table.get_cursor_pos()
 601
 602	def get_lookup_table (self):
 603		'''Get lookup table'''
 604		return self._lookup_table
 605
 606	def is_lt_visible (self):
 607		'''Check whether lookup table is visible'''
 608		return self._lookup_table.is_cursor_visible ()
 609	
 610	def backspace (self):
 611		'''Process backspace Key Event'''
 612		self._zi = u''
 613		if self.get_input_chars():
 614			self.pop_input ()
 615			return True
 616		elif self.get_preedit_strings ():
 617			self.remove_before_char ()
 618			return True
 619		else:
 620			return False
 621	
 622	def control_backspace (self):
 623		'''Process control+backspace Key Event'''
 624		self._zi = u''
 625		if self.get_input_chars():
 626			self.over_input ()
 627			return True
 628		elif self.get_preedit_strings ():
 629			self.remove_before_string ()
 630			return True
 631		else:
 632			return False
 633
 634	def delete (self):
 635		'''Process delete Key Event'''
 636		self._zi = u''
 637		if self.get_input_chars():
 638			return True
 639		elif self.get_preedit_strings ():
 640			self.remove_after_char ()
 641			return True
 642		else:
 643			return False
 644	
 645	def control_delete (self):
 646		'''Process control+delete Key Event'''
 647		self._zi = u''
 648		if self.get_input_chars ():
 649			return True
 650		elif self.get_preedit_strings ():
 651			self.remove_after_string ()
 652			return True
 653		else:
 654			return False
 655
 656	def l_shift (self):
 657		'''Process Left Shift Key Event as immediately commit to preedit strings'''
 658		if self._chars[0]:
 659			self.commit_to_preedit ()
 660			return True
 661		else:
 662			return False
 663	
 664	def r_shift (self):
 665		'''Proess Right Shift Key Event as changed between PinYin Mode and XingMa Mode'''
 666		self._zi = u''
 667		if self._chars[0]:
 668			self.commit_to_preedit ()
 669		self._py_mode = not (self._py_mode)
 670		return True
 671
 672	def space (self):
 673		'''Process space Key Event
 674		return (KeyProcessResult,whethercommit,commitstring)'''
 675		if self._chars[1]:
 676			# we have invalid input, so do not commit 
 677			return (False,u'')
 678		if self._t_chars :
 679			# user has input sth
 680			self.commit_to_preedit ()
 681			pstr = self.get_preedit_strings ()
 682			self.clear()
 683			return (True,pstr)
 684		else:
 685			return (False,u' ')
 686
 687	def one_candidate (self):
 688		'''Return true if there is only one candidate'''
 689		return len(self._candidates[0]) == 1
 690
 691
 692class XingMaEngine (IMEngine):
 693	'''The IMEngine for XingMa'''
 694	
 695	# colors
 696#	_phrase_color 			= 0xffffff
 697#	_user_phrase_color 		= 0xffffff
 698#	_new_phrase_color 		= 0xffffff
 699
 700	# lookup table page size
 701	_page_size = 6
 702
 703	def __init__ (self, factory, config, encoding, id, db ):
 704		IMEngine.__init__ (self, factory, config, encoding, id)
 705		self._config = config
 706		self._lookup_table = scim.LookupTable (XingMaEngine._page_size)
 707		self._lookup_table.fix_page_size (True)
 708		# this is the backend sql db we need for our IME
 709		# we receive this db from IMEngineFactory
 710		#self.db = XMSQLiteDB.XMSQLiteDB( name = dbname )
 711		self.db = db 
 712		# this is the parer which parse the input string to key object
 713		self._parser = XMDict.Parse
 714		
 715		# 0 = english input mode
 716		# 1 = xingma input mode
 717		self._mode = 1
 718		# self._ime_py: True / False this IME support pinyin mode
 719		self._ime_py = self.db.get_ime_property ('pinyin_mode')
 720		if self._ime_py:
 721			if self._ime_py.lower() == u'true':
 722				self._ime_py = True
 723			else:
 724				self._ime_py = False
 725		else:
 726			print 'We coult not find "pinyin_mode" entry in database, is it a outdated database?'
 727			self._ime_py = False
 728
 729		self._status = self.db.get_ime_property('status_prompt').encode('utf8')
 730		# now we check and update the valid input characters
 731		self._chars = self.db.get_ime_property('valid_input_chars')
 732		self._valid_input_chars = []
 733		for _c in self._chars:
 734			if _c in XMDict.XingMa_List:
 735				self._valid_input_chars.append(_c)
 736		del self._chars
 737		self._pt = self.db.get_phrase_table_index ()
 738		self._ml = int(self.db.get_ime_property ('max_key_length'))
 739		
 740		# Containers we used:
 741		self._editor = Editor(self._pt, self._valid_input_chars, self._ml, self.db)
 742
 743		# some other vals we used:
 744		# self._prev_key: hold the key event last time.
 745		self._prev_key = None
 746		self._prev_char = None
 747		self._double_quotation_state = False
 748		self._single_quotation_state = False
 749		# [ENmode,XMmode] we get XMmode properties from db
 750		self._full_width_letter = [
 751				False, 
 752				self.db.get_ime_property('def_full_width_letter').lower() == u'true'
 753				]
 754		self._full_width_punct = [
 755				False,
 756				self.db.get_ime_property('def_full_width_punct').lower() == u'true'
 757				]
 758		self._direct_commit = False
 759		
 760		# some properties we will involved, Property is taken from scim.
 761		self._status_property = Property ("status", "")
 762		self._letter_property = Property ("full_letter", "")
 763		self._punct_property = Property ("full_punct", "")
 764		self._py_property = Property ("py_mode", "")
 765		self._onechar_property = Property ("onechar","")
 766		self._direct_commit_property = Property ("dcommit","")
 767		#self._setup_property = Property ("setup", _("Setup"))
 768		self.reset ()
 769		
 770
 771	def reset (self):
 772		self._editor.clear ()
 773		self._double_quotation_state = False
 774		self._single_quotation_state = False
 775		self._prev_key = None
 776		# do not change onechar mode
 777		#self._editor._onechar = False	
 778		self._init_properties ()
 779		self._update_ui ()
 780		IMEngine.reset (self)
 781
 782	def _init_properties (self):
 783		properties= (
 784			self._status_property,
 785			self._letter_property,
 786			self._punct_property,
 787			self._py_property, 
 788			self._onechar_property,
 789			self._direct_commit_property
 790	#		self._setup_property
 791			)
 792		self.register_properties (properties)
 793		self._refresh_properties ()
 794	
 795	def _refresh_properties (self):
 796		'''Method used to update properties'''
 797		# taken and modified from PinYin.py :)
 798		if self._mode == 1: # refresh mode
 799			self._status_property.label = _(self._status)
 800		else:
 801			self._status_property.label = _("EN")
 802
 803		if self._full_width_letter[self._mode]:
 804			self._letter_property.label = ''
 805			self._letter_property.icon = '/usr/share/scim/icons/full-letter.png'
 806		else:
 807			self._letter_property.label = ''
 808			self._letter_property.icon = '/usr/share/scim/icons/half-letter.png'
 809
 810		if self._full_width_punct[self._mode]:
 811			self._punct_property.label = ''
 812			self._punct_property.icon ='/usr/share/scim/icons/full-punct.png'
 813		else:
 814			self._punct_property.label = '' 
 815			self._punct_property.icon ='/usr/share/scim/icons/half-punct.png'
 816		
 817		if self._editor._py_mode:
 818			self._py_property.label = ''
 819			self._py_property.icon ='/usr/share/scim/icons/py-mode.png'
 820		
 821		else:
 822			self._py_property.label = ''
 823			self._py_property.icon ='/usr/share/scim/icons/xm-mode.png'
 824
 825		if self._editor._onechar:
 826			self._onechar_property.label = ''
 827			self._onechar_property.icon = '/usr/share/scim/icons/onechar.png'
 828		else:
 829			self._onechar_property.label = ''
 830			self._onechar_property.icon = '/usr/share/scim/icons/phrase.png'
 831		if self._direct_commit:
 832			self._direct_commit_property.label = ''
 833			self._direct_commit_property.icon = '/usr/share/scim/icons/dcommit.png'
 834		else:
 835			self._direct_commit_property.label = ''
 836			self._direct_commit_property.icon = '/usr/share/scim/icons/ncommit.png'
 837
 838
 839		properties = (
 840			self._status_property, 
 841			self._letter_property, 
 842			self._punct_property,
 843			self._py_property,
 844			self._onechar_property,
 845			self._direct_commit_property
 846			)
 847		# use buildin method to update properties :)
 848		map (self.update_property, properties)
 849	
 850	def _change_mode (self):
 851		'''Shift input mode, XM -> EN -> XM
 852		'''
 853		self._mode = int (not self._mode)
 854		self.reset ()
 855		self._update_ui ()
 856
 857	def trigger_property (self, property):
 858		'''Shift property'''
 859		if property == "status":
 860			self._change_mode ()
 861		elif property == "full_letter":
 862			self._full_width_letter [self._mode] = not self._full_width_letter [self._mode]
 863		elif property == "full_punct":
 864			self._full_width_punct [self._mode] = not self._full_width_punct [self._mode]
 865		elif property == 'py_mode' and self._ime_py:
 866			self._editor.r_shift ()
 867		elif property == 'onechar':
 868			self._editor._onechar = not self._editor._onechar
 869		elif property == 'dcommit':
 870			self._direct_commit = not self._direct_commit
 871		self._refresh_properties ()
 872	#	elif property == "setup":
 873			# Need implementation
 874	#		self.start_helper ("96c07b6f-0c3d-4403-ab57-908dd9b8d513")
 875		# at last invoke default method 
 876		IMEngine.trigger_property (self, property)
 877	
 878	def _update_preedit (self):
 879		'''Update Preedit String in UI'''
 880		_str = self._editor.get_preedit_strings ()
 881		if _str == u'':
 882			self.hide_preedit_string ()
 883		else:
 884			attrs = []
 885			res = patt_edit.match (_str)
 886			if res:
 887				_str = u''
 888				ures = patt_uncommit.match (res.group(1))
 889				if ures:
 890					_str=u''.join (ures.groups())
 891					lc = len (ures.group(1) )
 892					lu = len (ures.group(2) )
 893					attrs.append (scim.Attribute(0,lc,scim.ATTR_FOREGROUND,0x1b3f03) )
 894					attrs.append (scim.Attribute(lc,lu,scim.ATTR_FOREGROUND,0x0895a2) )
 895					lg1 = len (_str)
 896				else:
 897					_str += res.group (1)
 898					lg1 = len ( res.group(1) )
 899					attrs.append (scim.Attribute(0,lg1,scim.ATTR_FOREGROUND,0x1b3f03) )
 900				_str += res.group(2)
 901				_str += res.group(3)
 902				lg2 = len ( res.group(2) )
 903				lg3 = len ( res.group(3) )
 904				attrs.append( scim.Attribute(lg1,lg2,scim.ATTR_FOREGROUND,0x0e0ea0) )
 905				attrs.append( scim.Attribute(lg1+lg2,lg3,scim.ATTR_FOREGROUND,0x1b3f03) )
 906			else:
 907				attrs.append( scim.Attribute(0,len(_str),scim.ATTR_FOREGROUND,0x1b3f03) )
 908				
 909
 910
 911			self.show_preedit_string ()
 912			self.update_preedit_string (_str,attrs)
 913			self.update_preedit_caret ( self._editor.get_caret() )
 914			self.show_preedit_string ()
 915	
 916	def _update_aux (self):
 917		'''Update Aux String in UI'''
 918		_ic = self._editor.get_aux_strings ()
 919		if _ic:
 920			self.show_aux_string ()
 921			attrs = [ scim.Attribute(0, len(_ic), scim.ATTR_FOREGROUND,0x9515b5) ]
 922			#attrs = [ scim.Attribute(0,len(_ic),scim.ATTR_FOREGROUND,0x5540c1)]
 923
 924			self.update_aux_string (_ic,attrs)
 925		else:
 926			self.hide_aux_string ()
 927			self.update_aux_string (u'')
 928
 929	def _update_lookup_table (self):
 930		'''Update Lookup Table in UI'''
 931		if self._editor.is_lt_visible ():
 932			self.update_lookup_table ( self._editor.get_lookup_table() )
 933			self.show_lookup_table ()
 934		else:
 935			self.hide_lookup_table ()
 936
 937	def _update_ui (self):
 938		'''Update User Interface'''
 939		self._update_lookup_table ()
 940		self._update_preedit ()
 941		self._update_aux ()
 942	
 943	def commit_string (self,string):
 944		IMEngine.commit_string ( self, string )
 945		self._prev_char = string[-1]
 946
 947	def _convert_to_full_width (self, c):
 948		'''convert half width character to full width'''
 949		if c in [u".", u"\\", u"^", u"_", u"$", u"\"", u"'", u">", u"<" ]:
 950			if c == u".":
 951				if self._prev_char and self._prev_char.isdigit () \
 952					and self._prev_key and chr (self._prev_key.code) == self._prev_char:
 953					return u"."
 954				else:
 955					return u"\u3002"
 956			elif c == u"\\":
 957				return u"\u3001"
 958			elif c == u"^":
 959				return u"\u2026\u2026"
 960			elif c == u"_":
 961				return u"\u2014\u2014"
 962			elif c == u"$":
 963				return u"\uffe5"
 964			elif c == u"\"":
 965				self._double_quotation_state = not self._double_quotation_state
 966				if self._double_quotation_state:
 967					return u"\u201c"
 968				else:
 969					return u"\u201d"
 970			elif c == u"'":
 971				self._single_quotation_state = not self._single_quotation_state
 972				if self._single_quotation_state:
 973					return u"\u2018"
 974				else:
 975					return u"\u2019"
 976			elif c == u"<":
 977				if self._mode:
 978					return u"\u300a"
 979			elif c == u">":
 980				if self._mode:
 981					return u"\u300b"
 982			
 983		return scim.unichar_half_to_full (c)
 984	
 985	def _match_hotkey (self, key, code, mask):
 986		
 987		if key.code == code and key.mask == mask:
 988			if self._prev_key and key.code == self._prev_key.code and key.mask & KeyMask.ReleaseMask:
 989				return True
 990			if not key.mask & KeyMask.ReleaseMask:
 991				return True
 992
 993		return False
 994	
 995	def process_key_event (self, key):
 996		'''Process Key Events
 997		Key Events include Key Press and Key Release,
 998		KeyMask means Key Pressed
 999		'''
1000		result = self._process_key_event (key)
1001		self._prev_key = key
1002		return result
1003
1004	def _process_key_event (self, key):
1005		'''Internal method to process key event'''
1006		# Match mode switch hotkey
1007		if not self._editor._t_chars and ( self._match_hotkey (key, KeyCode.KEY_Shift_L, KeyMask.ShiftMask + KeyMask.ReleaseMask)):
1008			self._change_mode ()
1009			return True
1010
1011		# Match full half letter mode switch hotkey
1012		if self._match_hotkey (key, KeyCode.KEY_space, KeyMask.ShiftMask):
1013			self.trigger_property ("full_letter")
1014			return True
1015		
1016		# Match full half punct mode switch hotkey
1017		if self._match_hotkey (key, KeyCode.KEY_period, KeyMask.ControlMask):
1018			self.trigger_property ("full_punct")
1019			return True
1020		
1021		# we ignore all hotkeys
1022#		if key.mask & KeyMask.AltMask:
1023#			return False
1024
1025		# Ignore key release event
1026#		if key.mask & KeyMask.ReleaseMask:
1027#			return True
1028		
1029		if self._mode:
1030			return self._xingma_mode_process_key_event (key)
1031		else:
1032			return self._english_mode_process_key_event (key)
1033
1034	def _english_mode_process_key_event (self, key):
1035		'''English Mode Process Key Event'''
1036		# Ignore key release event
1037		if key.mask & KeyMask.ReleaseMask:
1038			return True
1039		
1040		if key.code >= 128:
1041			return False
1042		# we ignore all hotkeys here	
1043		if key.mask & KeyMask.ControlMask+KeyMask.AltMask:
1044			return False
1045		
1046		c = unichr (key.code)
1047		if ascii.ispunct (key.code): # if key code is a punctation
1048			if self._full_width_punct[self._mode]:
1049				self.commit_string (self._convert_to_full_width (c))
1050				return True
1051			else:
1052				self.commit_string (c)
1053				return True
1054			
1055		if self._full_width_letter[self._mode]: # if key code is a letter or digit
1056			self.commit_string (self._convert_to_full_width (c))
1057			return True
1058		else:
1059			self.commit_string (c)
1060			return True
1061		
1062		# should not reach there
1063		return False
1064	
1065	def _xingma_mode_process_key_event (self, key):
1066		'''Xingma Mode Process Key Event'''
1067		cond_letter_translate = lambda (c): \
1068			self._convert_to_full_width (c) if self._full_width_letter [self._mode] else c
1069		cond_punct_translate = lambda (c): \
1070			self._convert_to_full_width (c) if self._full_width_punct [self._mode] else c
1071		
1072		# We have to process the pinyin mode change key event here,
1073		# because we ignore all Release event below.
1074		if self._match_hotkey (key, KeyCode.KEY_Shift_R, KeyMask.ShiftMask + KeyMask.ReleaseMask) and self._ime_py:
1075			res = self._editor.r_shift ()
1076			self._refresh_properties ()
1077			self._update_ui ()
1078			return res
1079		# process commit to preedit	
1080		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):
1081			res = self._editor.l_shift ()
1082			self._update_ui ()
1083			return res
1084		
1085		# Match sigle char mode switch hotkey
1086		if self._match_hotkey (key, KeyCode.KEY_comma, KeyMask.ControlMask):
1087			self.trigger_property ( u"onechar" )
1088			return True
1089		
1090		# Match direct commit mode switch hotkey
1091		if self._match_hotkey (key, KeyCode.KEY_slash, KeyMask.ControlMask):
1092			self.trigger_property ( u"dcommit" )
1093			return True
1094
1095		# Ignore key release event now :)
1096		if key.mask & KeyMask.ReleaseMask:
1097			return True
1098
1099		if self._editor.is_empty ():
1100			# we have not input anything
1101			if key.code <= 127 and (not key.mask & KeyMask.AltMask + KeyMask.ControlMask):
1102				if ascii.ispunct (key.code) and ( unichr(key.code) not in self._valid_input_chars ) :
1103					self.commit_string (cond_punct_translate (unichr (key.code)))
1104					return True
1105				if ascii.isdigit (key.code):
1106					self.commit_string (cond_letter_translate (unichr (key.code)))
1107					return True
1108
1109		if key.code in (KeyCode.KEY_Return, KeyCode.KEY_KP_Enter):
1110			if self._editor.is_empty ():
1111				return False
1112			else:
1113				commit_string = self._editor.get_all_input_strings ()
1114				self._editor.clear ()
1115				self._update_ui ()
1116				self.commit_string (commit_string)
1117				self._refresh_properties ()
1118				return True
1119		
1120		elif key.code == KeyCode.KEY_space:
1121			o_py = self._editor._py_mode
1122			sp_res = self._editor.space ()
1123			self._update_ui ()
1124			#return (KeyProcessResult,whethercommit,commitstring)
1125			if sp_res[0]:
1126				self.commit_string (sp_res[1])
1127				self.db.check_phrase (sp_res[1])
1128			else:
1129				if sp_res[1] == u' ':
1130					self.commit_string (cond_letter_translate (u" "))
1131			if o_py != self._editor._py_mode:
1132				self._refresh_properties ()
1133			return True
1134		
1135		elif key.code == KeyCode.KEY_BackSpace and key.mask & KeyMask.ControlMask:
1136			res = self._editor.control_backspace ()
1137			self._update_ui ()
1138			return res
1139		
1140		elif key.code == KeyCode.KEY_BackSpace:
1141			res = self._editor.backspace ()
1142			self._update_ui ()
1143			return res
1144		
1145		elif key.code == KeyCode.KEY_Escape:
1146			if self._editor.is_empty () and (not self._editor._py_mode) :
1147				return False
1148			else:
1149				self.reset ()
1150				self._update_ui ()
1151				return True
1152		
1153		elif key.code == KeyCode.KEY_Down :
1154			res = self._editor.arrow_down ()
1155			self._update_lookup_table ()
1156			return res
1157		
1158		elif key.code == KeyCode.KEY_Up:
1159			res = self._editor.arrow_up ()
1160			self._update_lookup_table ()
1161			return res
1162		
1163		elif key.code == KeyCode.KEY_Left and key.mask & KeyMask.ControlMask:
1164			res = self._editor.control_arrow_left ()
1165			self._update_ui ()
1166			return res
1167		
1168		elif key.code == KeyCode.KEY_Right and key.mask & KeyMask.ControlMask:
1169			res = self._editor.control_arrow_right ()
1170			self._update_ui ()
1171			return res
1172		
1173		elif key.code == KeyCode.KEY_Left:
1174			res = self._editor.arrow_left ()
1175			self._update_ui ()
1176			return res
1177		
1178		elif key.code == KeyCode.KEY_Right:
1179			res = self._editor.arrow_right ()
1180			self._update_ui ()
1181			return res
1182		
1183		elif key.code == KeyCode.KEY_Delete and key.mask & KeyMask.ControlMask:
1184			res = self._editor.control_delete ()
1185			self._update_ui ()
1186			return res
1187		
1188		elif key.code == KeyCode.KEY_Delete:
1189			res = self._editor.delete ()
1190			self._update_ui ()
1191			return res
1192
1193		elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9 and self._editor._candidates[0] and key.mask & KeyMask.ControlMask:
1194			res = self._editor.number (key.code - KeyCode.KEY_1)
1195			self._update_ui ()
1196			return res
1197
1198		elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9 and self._editor._candidates[0] and key.mask & KeyMask.AltMask:
1199			res = self._editor.alt_number (key.code - KeyCode.KEY_1)
1200			self._update_ui ()
1201			return res
1202
1203		# now we ignore all else hotkeys
1204		elif key.mask & KeyMask.ControlMask+KeyMask.AltMask:
1205			return False
1206
1207		elif key.mask & KeyMask.AltMask:
1208			return False
1209
1210		elif unichr(key.code) in self._valid_input_chars or \
1211				( self._editor._py_mode and \
1212					unichr(key.code) in u'abcdefghijklmnopqrstuvwxyz' ):
1213			if self._direct_commit and len(self._editor._chars[0]) == self._ml:
1214				# it is time to direct commit
1215				sp_res = self._editor.space ()
1216				#return (whethercommit,commitstring)
1217				if sp_res[0]:
1218					self.commit_string (sp_res[1])
1219					self.db.check_phrase (sp_res[1])
1220			
1221			res = self._editor.add_input ( unichr(key.code) )
1222			if not res:
1223				if ascii.ispunct (key.code):
1224					key_char = cond_punct_translate (unichr (key.code))
1225				else:
1226					key_char = cond_letter_translate (unichr (key.code))
1227				sp_res = self._editor.space ()
1228				self._update_ui ()
1229				#return (KeyProcessResult,whethercommit,commitstring)
1230				if sp_res[0]:
1231					self.commit_string (sp_res[1] + key_char)
1232					self.db.check_phrase (sp_res[1])
1233				else:
1234					self.commit_string ( key_char )
1235
1236			else:
1237				if self._direct_commit and self._editor.one_candidate () \
1238						and len(self._editor._chars[0]) == self._ml:
1239					# it is time to direct commit
1240					sp_res = self._editor.space ()
1241					#return (whethercommit,commitstring)
1242					if sp_res[0]:
1243						self.commit_string (sp_res[1])
1244						self.db.check_phrase (sp_res[1])
1245
1246			self._update_ui ()
1247			return True
1248		
1249		elif key.code in (KeyCode.KEY_equal, KeyCode.KEY_Page_Down, KeyCode.KEY_KP_Page_Down) and self._editor._candidates[0]:
1250			res = self._editor.page_down()
1251			self._update_ui ()
1252			return res
1253
1254		elif key.code in (KeyCode.KEY_minus, KeyCode.KEY_Page_Up, KeyCode.KEY_KP_Page_Up) and self._editor._candidates[0]:
1255			res = self._editor.page_up ()
1256			self._update_ui ()
1257			return res
1258		
1259		elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9 and self._editor._candidates[0]:
1260			res = self._editor.number (key.code - KeyCode.KEY_1)
1261			if res:
1262				commit_string = self._editor.get_preedit_strings ()
1263				self._editor.clear ()
1264				self.commit_string (commit_string)
1265				# modify freq info
1266				self.db.check_phrase (commit_string)
1267				self._refresh_properties ()
1268			self._update_ui ()
1269			return True
1270		
1271		elif key.code <= 127:
1272			if not self._editor._candidates[0]:
1273				commit_string = self._editor.get_all_input_strings ()
1274			else:
1275				self._editor.commit_to_preedit ()
1276				commit_string = self._editor.get_preedit_strings ()
1277			self._editor.clear ()
1278			if ascii.ispunct (key.code):
1279				self.commit_string ( commit_string + cond_punct_translate (unichr (key.code)))
1280			else:
1281				self.commit_string ( commit_string + cond_letter_translate (unichr (key.code)))
1282			self._update_ui ()
1283			return True
1284		return False
1285	
1286	# below for initial test
1287	def focus_in (self):
1288		self._init_properties ()
1289		IMEngine.focus_in (self)
1290		self._update_ui ()
1291	
1292	def focus_out (self):
1293		IMEngine.focus_out (self)
1294
1295	def lookup_table_page_up (self):
1296		if self._editor.page_up ():
1297			self._update_lookup_table ()
1298			return True
1299		
1300		IMEngine.lookup_table_page_up (self)
1301		return True
1302
1303	def lookup_table_page_down (self):
1304		if self._editor.page_down ():
1305			self._update_lookup_table ()
1306			return True
1307		IMEngine.lookup_table_page_down (self)
1308		return False
1309	
1310	def reload_config (self, config):
1311		self._lookup_table.set_page_size (XingMaEngine._page_size)
1312		self.focus_in ()
1313	
1314class Factory (IMEngineFactory):
1315	"""XingMa IM Engine Factory"""
1316	def __init__ (self, config, db):
1317		import os.path
1318		import locale
1319		IMEngineFactory.__init__ (self, config)
1320		# this is the backend sql db we need for our IME
1321		# we need get lots of IME property from this db :)
1322		#self.db = XMSQLiteDB.XMSQLiteDB( name = database )
1323		
1324		udb = os.path.basename(db).replace('.db','-user.db') 
1325		self.db = XMSQLiteDB.XMSQLiteDB( name = db,user_db = udb )
1326		self.dbname = db
1327		ulocale = locale.getdefaultlocale ()[0].lower()
1328		self.name 	=  self.db.get_ime_property ('name.%s' % ulocale) 
1329		if not self.name:
1330			self.name 		= _( self.db.get_ime_property ('name') )
1331		self.uuid 		= self.db.get_ime_property ('uuid')
1332		self.authors	= self.db.get_ime_property ('author')
1333		self.icon_file 	= self.db.get_ime_property ('icon')
1334		self.credits 	= self.db.get_ime_property ('credit')
1335		self.help		= _(u"Help For Python XingMa\n\tPlease read http://code.google.com/p/scim-python/wiki/XingMaUserGuide")
1336
1337		# init factory
1338		self._config	= config
1339		self.set_languages ( self.db.get_ime_property ('languages') )
1340#		self.reload_config (config)
1341		self.db.db.commit()
1342
1343	def destroy (self):
1344		'''Destructor, which finish some task for IME'''
1345		# we need to sync the temp userdb in memory to the user_db on disk
1346		self.db.sync_usrdb ()
1347
1348	def create_instance (self, encoding, id):
1349		engine =  XingMaEngine (self, self._config, encoding, id, self.db)
1350		return engine
1351
1352	def reload_config (self, config):
1353		pass
1354#		XingMaEngine._page_size = \
1355#			config.read ("/IMEngine/Python/XingMa/PageSize", 5)
1356#		if XingMaEngine._page_size < 1 or XingMaEngine._page_size > 9:
1357#			XingMaEngine._page_size = 5
1358		
1359#		XingMaEngine._phrase_color = \
1360#			config.read ("/IMEngine/Python/XingMa/PhraseColor", XingMaEngine._phrase_color)
1361#		XingMaEngine._new_phrase_color = \
1362#			config.read ("/IMEngine/Python/XingMa/NewPhraseColor", XingMaEngine._new_phrase_color)
1363#		XingMaEngine._user_phrase_color = \
1364#			config.read ("/IMEngine/Python/XingMa/UserPhraseColor", XingMaEngine._user_phrase_color)
1365