My Cantonese Input Method (version 20100717) Download

Data in DBF format (version 20100523) Download

Browse Data Online

*
* written by: Chang Man Wai, 2004
* useful sites: http://www.lexiconer.com/
*
* 06SEP2008 : close canton.dbf as soon as possible
* 04NOV2008 : prevent clearing the clipboard when hitting reset
* 30JAN2009 : show touched, use fixed width font in listbox
* 01FEB2009 : Double-click to email, add text1.interactivechange()
* 03APR009 : Change button "NewLine" to "History",  add property buffer to class MyForm,
*       pressing it would recall all outputed Chinese Big5 and then the buffer be cleared
*       hitting cmdReset would also remember the history
* 13APR2009: Make cmdClip to remember history as well (like cmdReset)
* 10MAY2009: handle adnormal shutdown via "ON SHUTDOWN" and ReleaseType
* 24AUG2009: smarter buffer handling for cmdClip, cmdHistory and cmdReset
* 24SEP2009: show all possible keys text1 is empty
* 15OCT2009: bugfix for text1.valid() when handling blank key input
*
* Useful links: 
* http://input.foruto.com/cie/cie_bd.htm
*
*
#Define K_F1 28
#Define K_ESC 27
#Define K_ENTER 13
parameters m.p1, m.p2

on shutdown do myquit
on error
set debug on
set help on

Set Escape Off
Set Talk Off
Set Notify Off
Set Safety Off
Set Exact On

Clear All
Close All
Clear

Public g_quit
Local oform

g_quit=.F.

#if .f.
*-- Turn off DDE error messages.
=ddesetoption( "SAFETY", .f.)

*-- Check to see if DDE service was already started.
achan = ddeinitiate( "pkf1", "System" )
IF achan # -1
  *-- You'll probably want a more informative message.
  WAIT WINDOW "Program already running."
ELSE
  *-- If we got here, the service was not started, so start it.
  =ddesetservice( "pkf1", "DEFINE" )
#endif
  oform=Createobject("myform")
  If Type("oform")="O"
  	Set Sysmenu To _Msm_edit
  	oform.Show()
  	Read Events
  Endif

#if .f.
  *-- At the end of the program, turn off the service, so we can get in
  *-- next time.
  =ddesetservice( "pkf1", "RELEASE" )
ENDIF
#endif

Set Sysmenu To Default
Set Safety On
Set Talk On
Set Notify On
Set Escape On

If g_quit
	* clean temporary files for Vi$ta
	Quit
Endif
Return

procedure myquit
local xx
	on shutdown
	on error *
	close all
	xx=sys(2023)+"*.tmp"
	if g_quit
		delete files &xx
	endif
	on error
	quit
return

Define Class mytextbox As TextBox
	FontSize=16
	Format="!K"
	lastvalue=""
	IntegralHeight=.t.
Enddefine

Define Class mylabel As Label
	AutoSize=.T.
	Caption=""
	FontName="Courier New"
	FontSize=14
Enddefine

Define Class mycommandbutton As CommandButton
	Height=25
	Width=100
	TabStop=.F.
Enddefine

Define Class myform As Form
	Caption="CHANG's Cantonese Input Method"
	KeyPreview=.T.
	ShowWindow=2
	AutoCenter=.T.
	MaxButton=.f.
	ShowTips=.t.
	MaxButton=.f.
	otoolbar=.F.
	icon="chang.ico"
	Width=400
	Buffer=""

	Add Object text1 As mytextbox With ;
		top=8,Left=10,Height=29, Width=130, FontName="Courier New", Value=""
	Add Object list1 As ListBox With ;
		top=60,Left=10,Width=130,Height=177, ;
		FontName="Courier New", FontSize=16, ;
		rowsourcetype=6, ;
		columncount=1, ;
		columnlines=.f., ;
		rowsource="matched.big5, kissed"
	Add Object edit1 As EditBox With ;
		top=60,Left=145,Width=250,Height=115,Format="!K", ;
		FontSize=12, TabStop=.F., Value="", ;
		ToolTipText="Enter a maths. equation here"
	Add Object lblChangjei As mylabel With ;
		top=15,Left=145
	Add Object lblMatches As mylabel With ;
		top=20,Left=290, Alignment=1
	Add Object lblCalculate As mylabel With ;
		top=43,Left=290,Alignment=1
	Add Object lblCopyRight As Label With ;
		top=212-35, Left=145, Width=200, FontSize=8, Caption="Powered by Visual Foxpro", ForeColor=rgb(255,128,128)
	Add Object lblFreeware As Label With ;
		top=212-35, Left=340, Width=200, FontSize=8, Caption="Freeware", ForeColor=rgb(255,0,0)
	Add Object lblEmail As Label With ;
		top=212-20, Left=145, Width=200, ;
		ToolTipText="Double-click to email me", ;
		FontSize=8, Caption="Email: toylet.toylet@gmail.com", ForeColor=rgb(255,128,128)
	Add Object cmdClip As mycommandbutton With ;
		top=212,Left=145, Width=75, Caption="\This.lastvalue 
		This.lastvalue=This.Value
		thisform.lblCalculate.caption=""
		If "?"$This.Value
			m.thekey=Left(This.Value,At("?",This.Value)-1)
			select 0
			use data\canton noupdate
			if used("matched")
				use in matched
			endif
			Select Distinct thekey As big5, Space(1) As changjei, .F. As leaf , "" as kissed;
				from canton ;
				where thekey=m.thekey ;
				into Cursor matched
			use in canton
			thisform.list1.columnCount=1
		else
			Select modified From remap ;
				where original=This.Value ;
				into Cursor t_dummy
			If !Empty(t_dummy.modified)
				This.Value=t_dummy.modified
			Endif
			Use In t_dummy
			select 0
			use data\canton noupdate alias canton
			if used("matched")
				use in matched
			endif
			Select big5, changjei,.T. As leaf, kissed_mask(touched) as kissed;
				from canton ;
				where thekey=This.Value ;
				order by touched descending ;
				into Cursor matched
			use in canton
			thisform.list1.columnCount=2
		Endif
		select matched
		Thisform.list1.Requery()
		Thisform.lblMatches.Caption=Alltrim(Str(Reccount("matched")))+" matches"
		Thisform.list1.Value=1
	endif
	Return .T.

	Function text1.interactivechange()
	local m.xx
	m.xx=alltrim(this.value)
	select 0
	use data\canton noupdate
	Select distinct thekey As big5, Space(1) As changjei, .F. As leaf , "" as kissed;
		from canton ;
		where (left(thekey,len(m.xx))=m.xx or empty(m.xx)) ;
		and !empty(thekey) ;
		into Cursor matched
	use in canton
	with thisform.list1
		.columncount=1
		.requery()
		.value=1
	endwith
	return .t.

	Function edit1.valid()
	local m.aa, m.errhand
	if !empty(this.value)
		m.aa=alltrim(this.value)
		m.aa=strtran(m.aa,space(1),"")
		m.aa=strtran(m.aa,chr(13),"")
		m.aa=strtran(m.aa,chr(10),"")
		m.errhand=on("error")
		on error *
		m.aa=eval(m.aa)
		on error &errhand
		if type("m.aa")="N"
			if m.aa<>0
				thisform.lblCalculate.caption=floatpnt(m.aa,15,4)
			endif
		else
			thisform.lblCalculate.caption="Not an equation!!"
		endif
		thisform.cmdReset.enabled=.t.
	endif
	return .t.

	Procedure list1.KeyPress
	Lparameter nkeycode, nsac
	Local m.thekey
	If nkeycode=K_ENTER
		* ENTER
		If matched.leaf
			* leaf node, no need to drill further
			thisform.addone(matched.big5)			
			Thisform.edit1.Value=Thisform.edit1.Value+matched.big5
			Thisform.lblChangjei.Caption=matched.changjei
			thisform.cmdReset.enabled=.t.
			Thisform.text1.SetFocus()
			Nodefault
		Else
			* branch node, load the chinese characters of the branch
			m.thekey=matched.big5
			Thisform.text1.Value=m.thekey
			Thisform.text1.lastvalue=m.thekey
			select 0
			use data\canton noupdate
			if used("matched")
				use in matched
			endif
			Select big5, changjei, .T. As leaf, kissed_mask(touched) as kissed ;
				from canton ;
				where thekey=m.thekey ;
				order by touched descending ;
				into Cursor matched
			use in canton
			Thisform.list1.Requery()
			Thisform.lblMatches.Caption=Alltrim(Str(Reccount("matched")))+" matches"
			Thisform.list1.Value=1
		Endif
	Endif
	Endproc

	Procedure cmdClip.Click
	* copy to windows clipboard
	if !empty(thisform.edit1.value)
		_cliptext=thisform.edit1.value
		thisform.buffer=thisform.buffer ;
			+iif(empty(thisform.buffer),"",chr(13)+chr(10)) ;
			+thisform.edit1.value
		thisform.cmdHistory.enabled=.t.
		Thisform.text1.SetFocus()
	endif
	Endproc

	Procedure cmdHistory.Click
	if !empty(thisform.buffer)
		thisform.edit1.value=Thisform.buffer
		thisform.buffer=""
		thisform.cmdReset.enabled=.t.
		this.enabled=!empty(thisform.buffer)
		Thisform.text1.SetFocus()
	endif
	Endproc

	Procedure cmdReset.Click
	local m.lastline
	if !empty(thisform.edit1.value)
		if thisform.lastclip()<>thisform.edit1.value
			thisform.buffer=thisform.buffer ;
				+iif(empty(thisform.buffer),"",chr(13)+chr(10)) ;
				+thisform.edit1.value
		endif
		Thisform.edit1.Value=""
		thisform.cmdHistory.enabled=!empty(thisform.buffer)
		this.enabled=.f.
		Thisform.text1.SetFocus()
	endif
	Endproc
	
	function lastclip
	local m.xx
	m.xx=mline(thisform.buffer,memlines(thisform.buffer))
	return strtran(strtran(m.xx,chr(13),""),chr(10),"")

	Procedure Load
	If !File("data\canton.dbf")
		If !Thisform.readcanton()
			Return .F.
		Endif
	Endif
	Create Cursor remap ( vowel c(5), original c(10), modified c(10) )
	Thisform.makeremap()
	Select 0
	Use data\canton noupdate
	Select distinct thekey As big5, Space(1) As changjei, .F. As leaf , "" as kissed;
		from canton ;
		where !empty(thekey) ;
		into Cursor matched
	use in canton
	Endproc

	Procedure Init
	local m.xx, m.yy
	select 0
	use data\canton noupdate
	count to m.xx for !empty(thekey)
	with Thisform.lblCalculate
		.Caption=Alltrim(Str(m.xx))+" words"+space(2)+Alltrim(Str(reccount("matched")))+" syllables"
	endwith
	use in canton
	with Thisform.list1
		.Value=1
	endwith
	Endproc

	Procedure Activate
	_Screen.Visible=.F.
	Endproc

	Procedure Unload
	Use In matched
	Use In remap
	Endproc

	Procedure KeyPress
	Parameters nkeycode, nsac
	If nkeycode=K_ESC
		Thisform.QueryUnload()
		Nodefault
	Endif
	Endproc

	Procedure QueryUnload
	Local frmQuit
	Local loParam
	if this.ReleaseType=2
		g_quit=.t.
		clear events
		thisform.release()
		nodefault
	else
		loParam=Createobject("MyParam")
		frmQuit=Createobject("quitform",loParam)
		frmQuit.Show()
		Do Case
		Case  loParam.toquit=0
			g_quit=.T.
			Nodefault
			Clear Events
			Thisform.Release()
		Case loParam.toquit=1
			Nodefault
		Case loParam.toquit=2
			Clear Events
		Endcase
	endif
	Endproc

	Procedure Destroy
	_Screen.Visible=.T.
	Endproc

	function addone
	lparameter m.lcBig5
	select 0
	use data\canton shared
	locate for big5=m.lcBig5
	if found()
		select canton
		replace touched with touched+1
	endif
	use in canton
	return .t.

	Function readcanton
	If File("canton.dbf")
		? "CANTON.DBF already existed! Rename it."
		Return .F.
	Endif
	Create Table canton ( thekey c(10), big5 c(2) )
	Create Cursor temp ( Aline c(80) )
	Append From canton.txt Type Sdf
	Select temp
	m.maxlen=0
	Scan
		If Recno() > 2
			xx=At(" ",temp.Aline)
			kk=Left(temp.Aline,xx)
			If !Empty(m.kk)
				If Len(m.kk) > m.maxlen
					m.maxlen=Len(m.kk)
				Endif
				big5=Alltrim(Substr(temp.Aline,xx,xx+20))
				Insert Into canton Values (kk, m.big5)
			Endif
		Endif
	Endscan
	? "max key length=",m.maxlen
	? "word count=",Reccount("canton")
	Close All
	Return .T.

	Procedure readoldcanton
	Select 0
	Use data\canton Shared
	Select 0
	Use ophrase Shared
	Scan
		Replace ophrase.Found With Space(1)
		Select canton
		Locate For big5=ophrase.Code
		If Found()
			Replace canton.canton With ophrase.Sound
		Else
			Insert Into canton ( ;
				big5, canton ;
				) Values ( ;
				ophrase.Code, ophrase.Sound ;
				)
		Endif
	Endscan
	Use In canton
	Use In ophrase
	Endproc

	Procedure readchangjei
	Select 0
	Use data\canton Shared
	Create Cursor t_changjei ( ;
		filler c(4),;
		big5 c(2),;
		radical c(3),;
		stroke c(3),;
		changjei c(10),;
		found c(1);
		)
	Append From changjei.txt Delimited With Tab
	Scan
		Select canton
		Locate For big5=t_changjei.big5
		If Found()
			Select canton
			Replace changjei With t_changjei.changjei
		Else
			Insert Into canton ( ;
				big5, changjei ;
				) Values ( ;
				t_changjei.big5,t_changjei.changjei ;
				)
		Endif
	Endscan
	Select t_changjei
	Brow For Empty(t_changjei.Found)
	Use In t_changjei
	Use In canton
	Endproc

	Procedure makeremap
* missing: DEN, AI, TAG, TIM
	Insert Into remap Values ( "IU", "DIU", "DEW" )
	Insert Into remap Values ( "EONG", "CHEONG", "CHERN" )
	Insert Into remap Values ( "EUNG", "CHEUNG", "CHERN" )
	Insert Into remap Values ( "EUNG", "LEUNG", "LERN" )
	Insert Into remap Values ( "EUNG", "HEUNG", "HERN" )
	Insert Into remap Values ( "EUNG", "KEUNG", "KERN" )
	Insert Into remap Values ( "EUNG", "SHEUNG","SHERN" )
	Insert Into remap Values ( "EUNG", "TSEUNG","JUNK" )
	Insert Into remap Values ( "ANG",  "HANG", "HUNT" )
	Insert Into remap Values ( "EAN",  "DEAN", "DIN" )
	Insert Into remap Values ( "O",    "NGO", "ALL" )
	Insert Into remap Values ( "O",    "LO",  "NO" )		&& LOW, LAW
	Insert Into remap Values ( "O",    "SO",  "SOUL" )
	Insert Into remap Values ( "O",    "KO",  "GO" )
	Insert Into remap Values ( "O",    "HO",  "HALL" )
	Insert Into remap Values ( "O",    "WO",  "WALL" )
	Insert Into remap Values ( "O",    "MO",  "MODE" )
	Insert Into remap Values ( "IN",   "LIN", "LEAN" )
	Insert Into remap Values ( "IN",   "NIN", "LEAN" )
	Insert Into remap Values ( "IN",   "LIN", "LEAN" )
	Insert Into remap Values ( "IN",   "HIN", "HINT" )
	Insert Into remap Values ( "AN",   "SAM", "SUM" )
	Insert Into remap Values ( "AM",   "KAM", "GUM" )
	Insert Into remap Values ( "AM",   "NAM", "LARM" )
	Insert Into remap Values ( "AM",   "LAM", "LUMP" )		&& LUMP, LARM
	Insert Into remap Values ( "AU",   "LAU", "NULL" )		&& NULL, LOUD
	Insert Into remap Values ( "AU",   "SAU", "SHOUT" )
	Insert Into remap Values ( "AU",   "GAU", "COW" )
	Insert Into remap Values ( "AU",   "KAU", "CULT" )
	Insert Into remap Values ( "AU",   "NGAU", "OUT" )
	Insert Into remap Values ( "AU",   "CHAU", "CHOW" )
	Insert Into remap Values ( "AU",   "SHAU", "SHOUT" )
	Insert Into remap Values ( "AN",   "KWAN", "GROUND" )
	Insert Into remap Values ( "AN",   "MAN", "MUNK" )
	Insert Into remap Values ( "AN",   "SAN", "SARM" )		&& SARM, SAND
	Insert Into remap Values ( "AN",   "DAN", "DANT" )		&& DEN, DANT, DOWN
	Insert Into remap Values ( "ENG",  "SENG", "SUNK" )
	Insert Into remap Values ( "IN",   "HIN", "HINT" )
	Insert Into remap Values ( "IN",   "MIN", "MEAN" )
	Insert Into remap Values ( "IN",   "NIN", "LEAN" )
	Insert Into remap Values ( "IN",   "MUN", "MOON" )
	Insert Into remap Values ( "EE",   "MEE", "MEAN" )
	Insert Into remap Values ( "EE",   "LEE", "LAY" )
	Insert Into remap Values ( "ING",  "CHING", "CHAIN" )
	Insert Into remap Values ( "ING",  "NING", "LINK" )
	Insert Into remap Values ( "ING",  "LING", "LINK" )
	Insert Into remap Values ( "ING",  "MING", "MAIN" )
	Insert Into remap Values ( "ONG",  "KWONG", "GONE" )
	Insert Into remap Values ( "ONG",  "TONG", "TOM" )
	Insert Into remap Values ( "ONG",  "KONG", "GONE" )
	Insert Into remap Values ( "ON",   "BON", "BOMB" )
	Insert Into remap Values ( "ONG",  "BONG", "BOMB" )
	Insert Into remap Values ( "ONG",  "WONG", "WARM" )
	Insert Into remap Values ( "UN",   "KWUN", "KOON" )
	Insert Into remap Values ( "UN",   "NUN", "LUNG" )
	Insert Into remap Values ( "UK",   "LUK", "LOOK" )
	Insert Into remap Values ( "UK",   "KUK", "COOK" )
	Insert Into remap Values ( "UNG",  "CHUNG", "JONE" )		&& LUNG, LOAN
	Insert Into remap Values ( "UNG",  "SUNG", "ZONE" )
	Insert Into remap Values ( "UNG",  "KUNG", "CONE" )
	Insert Into remap Values ( "UNG",  "TUNG", "TONE" )		&& TONE, DONT
	Insert Into remap Values ( "UNG",  "FUNG", "PHONE" )
	Insert Into remap Values ( "OI",   "OI", "OIL" )
	Insert Into remap Values ( "OI",   "LOI", "LOY" )
	Insert Into remap Values ( "OI",   "SOI", "SOIL" )
	Insert Into remap Values ( "OI",   "KOI", "COIL" )
	Insert Into remap Values ( "OI",   "TSOI", "CHOI" )
	Insert Into remap Values ( "AAI",  "AI", "EYE" )
	Insert Into remap Values ( "AI",   "AI", "EYE" )
	Insert Into remap Values ( "AI",   "KWAI", "QUITE" )
	Insert Into remap Values ( "AI",   "DAI", "DIE" )
	Insert Into remap Values ( "AI",   "TAI", "TIE" )
	Insert Into remap Values ( "AI",   "WAI", "RIGHT" )
	Insert Into remap Values ( "AI",   "LAI", "LIKE" )		&& LIE, LIKE
	Insert Into remap Values ( "AI",   "SAI", "SITE" )
	Insert Into remap Values ( "AI",   "MAI", "MIND" )
	Insert Into remap Values ( "AI",   "KAI", "GUY" )
	Insert Into remap Values ( "AI",   "FAI", "FINE" )
	Insert Into remap Values ( "AI",   "HAI", "HIGH" )
	Insert Into remap Values ( "SI",   "SI", "SEE" )
	Insert Into remap Values ( "LI",   "LI", "LAY" )
	Insert Into remap Values ( "A",    "WA", "WAH" )
	Insert Into remap Values ( "IU",   "YIU", "YIELD" )
	Insert Into remap Values ( "IU",   "LIU", "NEW" )
	Insert Into remap Values ( "IU",   "SIU", "SILL" )
	Insert Into remap Values ( "IU",   "TIU", "TILL" )
	Insert Into remap Values ( "IU",   "KIU", "KILL" )
	Insert Into remap Values ( "IU",   "SHIU", "SILL" )
	Insert Into remap Values ( "IU",   "HIU", "HILL"  )
	Insert Into remap Values ( "IU",   "GIU", "GILL" )
	Insert Into remap Values ( "IU",   "MIU", "MILL" )
	Insert Into remap Values ( "IU",   "CHIU", "CHEW" )
	Insert Into remap Values ( "AO",   "TAO", "THOUGH" )
	Insert Into remap Values ( "AO",   "DAO", "THOUGH" )
	Insert Into remap Values ( "AO",   "MAO", "MODE" )
	Insert Into remap Values ( "A",    "TA", "TAR" )
	Insert Into remap Values ( "A",    "KA", "CAR" )
	Insert Into remap Values ( "A",    "CHA", "CHAR" )
	Insert Into remap Values ( "A",    "SA", "CZAR" )
	Insert Into remap Values ( "A",    "KWA", "GRA" )
	Insert Into remap Values ( "EI",   "LEI", "LAY" )
	Insert Into remap Values ( "EI",   "SEI", "SAY" )
	Insert Into remap Values ( "AK",   "TAK", "DUCK" )
	Insert Into remap Values ( "UE",   "YUE", "YU" )
	Insert Into remap Values ( "EH",   "YEH", "YEAH" )
	Insert Into remap Values ( "AAK",  "AAK", "ARK" )
	Insert Into remap Values ( "EUK",  "LEUK", "LURK" )
	Insert Into remap Values ( "IC",   "LIC", "LEG" )
	Insert Into remap Values ( "IC",   "VIC", "WAKE" )
	Endproc
Enddefine

Define Class quitform As Form
	Caption="Quit"
	Height=150
	Width=200
	AutoCenter=.T.
	WindowType=1
	ShowWindow=1
	ControlBox=.F.
	oQuit=.F.

	Add Object btnYes As mycommandbutton With ;
		top=10,Left=20,Caption="\ m.maxlen
		m.maxlen=Len(Alltrim(thekey))
	Endif
	? Alltrim(thekey)
	Select canton
	Scan For thekey=t_thekey.thekey
		?? Space(1)+Alltrim(canton.big5)
	Endscan
Endscan
Use In t_thekey
Use In canton

Set Alternate Off
Set Alternate To

? "maximum length:",m.maxlen

Set Talk On
Set Safety On
Endproc

function floatpnt
parameter m.in_prce, m.in_wid, m.in_dec
*
* copied from WAE's PROCEDR2.PRG
*
* m.in_prce = the floating point value
* m.in_wid  = the output width
* m.in_dec  = the maximum precision of m.in_prce
*
* 25/09/01 - MMC, created. beware of the limits...
*
* you can use the following to re-test this function
* set decimal to 5
* for m.ii=0 to 7
*   for m.jj=1 to 5
*     m.vv=10**m.ii+1/10**m.jj
*     ? m.ii, floatpnt(m.vv,7), floatpnt(-m.vv,7)
*   endfor
* endfor
*
private m.prce, m.wid, m.dec
private M.ss, m.xx, o_dec

if m.in_wid-m.in_dec<2
  * you need at least "0.", and "-" sign
  return "I CANT"
endif

o_dec=set("decimal")
set decimal to m.in_dec

m.dec=m.in_dec
m.prce=abs(m.in_prce)
m.wid=m.in_wid
if m.in_prce<0
  m.wid=m.wid-1
endif
if m.wid-m.dec<2
  * "0." has 2 places at least
  m.dec=m.dec-1
endif

m.ss=str(m.prce,15,m.dec)
if m.prce>10**(m.wid-1)
  * the recursive call here is pretty useless. but...
  m.ss=str(m.prce/1000,15)+"K"
else
  * check the width of the part before decimal point
  * trim the mantissa accordingly
  if m.prce<=1
    m.xx=m.dec
  else
    m.xx=m.wid-(int(log10(m.prce))+1)-1
    if m.xx<=0
      m.xx=0
    endif
  endif
  m.ss=str(m.prce,15,m.xx)
endif
m.ss=alltrim(right(m.ss,m.wid))
if m.in_prce<0
  m.ss="-"+m.ss
endif
* removing trailing decimal zero
if "."$m.ss
  do while right(m.ss,1)="0"
    m.ss=left(m.ss,len(m.ss)-1)
  enddo
  if right(m.ss,1)="."
    m.ss=left(m.ss,len(m.ss)-1)
  endif
  if val(m.ss)=0
    m.ss="0"
  endif
endif
set decimal to &o_dec
return padl(m.ss,m.in_wid)

function kissed_mask
lparameter m.touched
local m.xx
if m.touched>9999
	m.touched=9999
endif
if m.touched=0
	m.xx=""
else
	m.xx=str(m.touched,5)
endif
return padr(m.xx,6)

function mailto
local m.lcMail
DECLARE INTEGER ShellExecute ;
	    IN SHELL32.DLL ;
	    INTEGER nWinHandle,;
	    STRING cOperation,;
	    STRING cFileName,;
	    STRING cParameters,;
	    STRING cDirectory,;
	    INTEGER nShowWindow

m.lcMail = "mailto:toylet.toylet@gmail.com"+ ;
  "?CC=&Subject= About Chang's Cantonese Input"+ ;
   "&Body= "
ShellExecute(0,"open",m.lcMail,"","",1)
release dll
return .t.

FUNCTION FirstInstance(pMutex)    && pMutex=Program name
   #DEFINE ERROR_ALREADY_EXISTS 183
   DECLARE INTEGER CreateMutex IN WIN32API INTEGER, INTEGER, STRING @
   DECLARE INTEGER CloseHandle IN WIN32API INTEGER
   DECLARE INTEGER GetLastError IN WIN32API

   nExeHwnd=CreateMutex(0, 1, @pMutex)
   IF GetLastError()=ERROR_ALREADY_EXISTS
      CloseHandle(nExeHwnd)
      RETURN .F.
   ELSE
      RETURN .T.
   ENDIF
ENDFUNC