/* * * UNICON - The Console Chinese & I18N * Copyright (c) 1999-2002 * * This file is part of UNICON, a console Chinese & I18N * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * See the file COPYING directory of this archive * Author: see CREDITS */ #include #include #include #include #include #include #include #include #include #include #include #define error printf static void UnloadInputMethod (hz_input_table *p); void ResetInput (HzInputTable_T *pClient); static void GetAssociatePhrases (HzInputTable_T *pClient, unsigned char *p); static void FindMatchKey (HzInputTable_T *pClient); static void FillAssociateChars(HzInputTable_T *pClient, int index); static void FillMatchChars (HzInputTable_T *pClient, int j); extern int FindAssociateKey (HzInputTable_T *pClient, u_char *pStr); extern void LoadPhrase (HzInputTable_T *pClient, int phrno, char *tt); extern long GetAssociatePhraseIndex (HzInputTable_T *pClient, int index, u_long *nPhrase); extern int AppendPhrase (HzInputTable_T *p, char *szCode, char *szPhrase); extern hz_input_table* LoadInputMethod(char *filename); static void FillAllMatchChars (HzInputTable_T *pClient, int j); /*************************************************************************** * variable defines * ***************************************************************************/ /* 6 bit a key mask */ unsigned long mask[]= { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3F000000, 0x3FFC0000, 0x3FFFF000, 0x3FFFFFC0, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF }; /*************************************************************************** * public function * ***************************************************************************/ static void GetAssociatePhrases (HzInputTable_T *pClient, unsigned char *p) { int len = strlen(p); if (pClient->InputCount <= pClient->InputMatch) /* All Match */ { // index = (int)p[len-2] * 256 + p[len-1]; ResetInput (pClient); if (pClient->UseAssociateMode) { FindAssociateKey (pClient, (u_char*) &p[len-2]); pClient->CurrentPageIndex = pClient->StartKey; pClient->MultiPageMode = 0; FillAssociateChars (pClient, pClient->StartKey); if (pClient->CurSelNum > 0) { pClient->IsAssociateMode = 1; } } } else { int nCount = pClient->InputCount - pClient->InputMatch, nMatch = pClient->InputMatch, i; pClient->MultiPageMode = pClient->NextPageIndex = pClient->CurrentPageIndex = 0; pClient->InputCount = pClient->InputMatch = 0; for(i = 0; i < nCount; i++) pClient->save_InpKey[i] = pClient->InpKey[nMatch+i]; bzero(pClient->InpKey, sizeof(pClient->InpKey)); for(i = 1; i <= nCount; i++) /* feed the additional keys */ { pClient->InpKey[pClient->InputCount] = pClient->save_InpKey[pClient->InputCount++]; if (pClient->InputCount <= pClient->InputMatch+1) { FindMatchKey (pClient); pClient->MultiPageMode = 0; pClient->CurrentPageIndex = pClient->StartKey; FillMatchChars (pClient, pClient->StartKey); } } if (pClient->InputMatch == 0) /* left key has no match, delete */ { ResetInput (pClient); return; } } } void ResetInput (HzInputTable_T *pClient) { bzero (pClient->InpKey, sizeof(pClient->InpKey) ); bzero (pClient->seltab, sizeof(pClient->seltab) ); bzero (pClient->seltab_phrase, sizeof(pClient->seltab_phrase) ); pClient->MultiPageMode = 0, pClient->NextPageIndex = 0, pClient->CurrentPageIndex = 0; pClient->CurSelNum = 0, pClient->InputCount = 0, pClient->InputMatch = 0; pClient->IsAssociateMode = 0; /* lian xiang */ } /* After add/delete a char, search the matched char/phrase, update the pClient->StartKey/pClient->EndKey key, save the related keys at first, if no match is found, we may restore its original value */ static void FindMatchKey (HzInputTable_T *pClient) { // pClient->bIsFinishSorting = 0; pClient->save_StartKey = pClient->StartKey; pClient->save_EndKey = pClient->EndKey; pClient->save_MultiPageMode = pClient->MultiPageMode; pClient->save_NextPageIndex = pClient->NextPageIndex; pClient->save_CurrentPageIndex = pClient->CurrentPageIndex; pClient->val1 = pClient->InpKey[4] | (pClient->InpKey[3]<<6) | (pClient->InpKey[2]<<12) | (pClient->InpKey[1]<<18) | (pClient->InpKey[0]<<24); pClient->val2 = pClient->InpKey[9] | (pClient->InpKey[8]<<6) | (pClient->InpKey[7]<<12) | (pClient->InpKey[6]<<18) | (pClient->InpKey[5]<<24); if (pClient->InputCount == 1) pClient->StartKey = pClient->cur_table->KeyIndex[pClient->InpKey[0]]; else pClient->StartKey = pClient->CharIndex[pClient->InputCount-1]; pClient->EndKey = pClient->cur_table->KeyIndex[pClient->InpKey[0]+1]; for (; pClient->StartKey < pClient->EndKey; pClient->StartKey++) { pClient->key1 = (pClient->cur_table->item[pClient->StartKey].key1 & mask[pClient->InputCount+5]); pClient->key2 = (pClient->cur_table->item[pClient->StartKey].key2 & mask[pClient->InputCount]); if (pClient->key1 > pClient->val1) break; if (pClient->key1 < pClient->val1) continue; if (pClient->key2 < pClient->val2) continue; break; } pClient->CharIndex[pClient->InputCount] = pClient->StartKey; // Add by Arthur Ma, 04-26-2000 FillAllMatchChars (pClient, pClient->StartKey); } /* Find the matched chars/phrases and fill it into SelTab The starting checked index is j The Selection Line 1xxx 2xxx, 80-20=60 60/2=30 chinese chars only 0-9 Selection can contain only 30 chinese chars SelectionXMax-SelectionX-20 #define SelectionXMax 78 #define MAX_SEL_LENGTH (SelectionXMax-SelectionX-18) */ static int IsSelectDup (HzInputTable_T *pClient, int CurSelNum, char *szPhrase) { int i; if (szPhrase[0] == '\0') return 1; for (i = 0; i < CurSelNum; i++) if (strcmp (pClient->seltab[i], szPhrase) == 0) return 1; return 0; } static void FillAssociateChars(HzInputTable_T *pClient, int index) { unsigned char str[25]; int CurLen = 0; pClient->CurSelNum = 0; while (pClient->CurSelNum < pClient->cur_table->MaxDupSel && index < pClient->EndKey) { u_long nPhrase; if (GetAssociatePhraseIndex (pClient, index, &nPhrase) == 0) continue; LoadPhrase (pClient, nPhrase, str); if (IsSelectDup (pClient, pClient->CurSelNum, str) == 0) { CurLen += (strlen(str) + 2); if (CurLen >= pClient->MAX_SEL_LENGTH - 2) break; strcpy(pClient->seltab[pClient->CurSelNum],str); pClient->seltab_phrase[pClient->CurSelNum] = nPhrase; CurLen += (strlen(pClient->seltab[pClient->CurSelNum++]) + 2); } index++; } /* check if more than one page */ if (index < pClient->EndKey && pClient->CurSelNum == pClient->cur_table->MaxDupSel) { /* has another matched key, so enter pClient->MultiPageMode, has more pages */ pClient->NextPageIndex = index; pClient->MultiPageMode = 1; } else if (pClient->MultiPageMode) pClient->NextPageIndex = pClient->StartKey; /* rotate selection */ else pClient->MultiPageMode = 0; } static HzInputTable_T *pDefaultClient = NULL; static int qcmp (void *t1, void *t2) { long c1, c2, k1, k2; long *a = (long *) t1, *b = (long *) t2; int n1, n2, m1, m2; static char p1[256], p2[256]; n1 = pDefaultClient->cur_table->item[*a].nPhrase; n2 = pDefaultClient->cur_table->item[*b].nPhrase; TL_GetPhrase (pDefaultClient->pSysPhrase, n1, p1); TL_GetPhrase (pDefaultClient->pSysPhrase, n2, p2); m1 = strlen (p1); m2 = strlen (p2); if (m1 > m2) return 1; else if (m1 < m2) return -1; TL_GetUserSelectPhraseCount (pDefaultClient->pSysPhrase, n1, &k1); TL_GetUserSelectPhraseCount (pDefaultClient->pSysPhrase, n2, &k2); if (k1 < k2) return 1; else if (k1 > k2) return -1; TL_GetPhraseFreq (pDefaultClient->pSysPhrase, n1, &c1); TL_GetPhraseFreq (pDefaultClient->pSysPhrase, n2, &c2); if (c1 < c2) return 1; else if (c1 > c2) return -1; return 0; } static void SortAllOutput (HzInputTable_T *pClient) { pDefaultClient = pClient; qsort (pClient->OutputPhrase, pClient->TotalOutputPhrase, sizeof(long), qcmp); } static void FillAllMatchChars (HzInputTable_T *pClient, int j) { int i = 0; while ((pClient->cur_table->item[j].key1 & mask[pClient->InputCount+5]) == pClient->val1 && (pClient->cur_table->item[j].key2 & mask[pClient->InputCount]) == pClient->val2 && j < pClient->EndKey) { pClient->OutputPhrase[i++] = j++; } pClient->TotalOutputPhrase = i; SortAllOutput (pClient); pClient->EndKey = i; pClient->StartKey = 0; } static void ResortCharsAfterSelect (HzInputTable_T *pClient, int nSelNum) { /* long n = pClient->seltab_phrase[SelNum]; long t = pClient->OutputPhrase[0]; u_long count; if (TL_GetUserSelectPhraseCount (n, &count) == 0 || count < 3) return; if (TL_GetUserPhraseFreq (n, &count) == 0) */ } static int GetMatchCharsIndex (HzInputTable_T *pClient, int j) { long n; for (n = 0; n < pClient->TotalOutputPhrase; n++) { if (pClient->OutputPhrase[n] == j) return n; } assert (0); return 0; } static void FillMatchChars (HzInputTable_T *pClient, int j) { int SelNum = 0, CurLen = 0; int n = j; do { if (n >= pClient->TotalOutputPhrase) break; j = pClient->OutputPhrase[n]; if (SelNum < pClient->cur_table->MaxDupSel) { LoadPhrase (pClient, pClient->cur_table->item[j].nPhrase, pClient->seltab[SelNum]); if (IsSelectDup (pClient, SelNum, pClient->seltab[SelNum]) == 0) { CurLen += (strlen(pClient->seltab[SelNum]) + 2); if (CurLen >= pClient->MAX_SEL_LENGTH - 2) break; // printf ("phase = %s\n", pClient->seltab[SelNum]); pClient->seltab_phrase[SelNum] = pClient->cur_table->item[j].nPhrase; SelNum++; } } else break; n++; } while (1); // printf ("SelNum = %d, n = %d\n", SelNum, n); if (SelNum == 0) /* some match found */ { pClient->StartKey = pClient->save_StartKey; pClient->EndKey = pClient->save_EndKey; pClient->MultiPageMode = pClient->save_MultiPageMode; pClient->NextPageIndex = pClient->save_NextPageIndex; pClient->CurrentPageIndex = pClient->save_CurrentPageIndex; return; /* keep the original selection */ } pClient->CurSelNum = SelNum; for(SelNum = pClient->CurSelNum; SelNum < 16; SelNum++) { pClient->seltab[SelNum][0] = '\0'; /* zero out the unused area */ pClient->seltab_phrase[SelNum] = -1; /* zero out the unused area */ } pClient->InputMatch = pClient->InputCount;/* until now, have some matches */ /* check if more than one page */ if (n < pClient->TotalOutputPhrase) { pClient->NextPageIndex = n; //j; pClient->MultiPageMode = 1; } else if (pClient->MultiPageMode) { pClient->MultiPageMode = 1; // pClient->NextPageIndex = pClient->StartKey; } else pClient->MultiPageMode = 0; } void Simulate_putstr (char *p, HzInputTable_T *pClient, int SelNum) { int index,len = strlen(p); ResortCharsAfterSelect (pClient, SelNum); if (pClient->InputCount <= pClient->InputMatch) /* All Match */ { index = (int)p[len-2] * 256 + p[len-1]; ResetInput (pClient); if (pClient->UseAssociateMode) { FindAssociateKey (pClient, &p[len-2]); pClient->CurrentPageIndex = pClient->StartKey; pClient->MultiPageMode = 0; FillAssociateChars (pClient, pClient->StartKey); if (pClient->CurSelNum > 0) { pClient->IsAssociateMode = 1; } } } else { int nCount = pClient->InputCount - pClient->InputMatch, nMatch = pClient->InputMatch,i; pClient->MultiPageMode = pClient->NextPageIndex = pClient->CurrentPageIndex = 0; pClient->InputCount = pClient->InputMatch = 0; for(i = 0; i < nCount; i++) pClient->save_InpKey[i] = pClient->InpKey[nMatch+i]; bzero(pClient->InpKey, sizeof(pClient->InpKey)); for(i = 1; i <= nCount; i++) /* feed the additional keys */ { pClient->InpKey[pClient->InputCount] = pClient->save_InpKey[pClient->InputCount++]; if (pClient->InputCount <= pClient->InputMatch+1) { FindMatchKey (pClient); pClient->MultiPageMode = 0; pClient->CurrentPageIndex = pClient->StartKey; FillMatchChars (pClient, pClient->StartKey); } } if (pClient->InputMatch == 0) /* left key has no match, delete */ { ResetInput (pClient); return; } } } int TL_KeyFilter (HzInputTable_T *pClient, u_char key, char *buf, int *len) { int inkey = 0,vv; char *is_sel_key = (char*)0; // long b = 0; inkey = pClient->cur_table->KeyMap[key]; is_sel_key = strchr( pClient->cur_table->selkey, key); if (inkey != 0 || is_sel_key != 0) goto do_our_filter; switch (key) { case '\010': /* BackSpace Ctrl+H */ case '\177': /* BackSpace */ if ( pClient->InputCount > 0 ) { pClient->InpKey[--pClient->InputCount]=0; if (pClient->InputCount == 0) { ResetInput (pClient); } else if (pClient->InputCount < pClient->InputMatch) { FindMatchKey (pClient); pClient->MultiPageMode = 0; pClient->CurrentPageIndex = pClient->StartKey; FillMatchChars (pClient, pClient->StartKey); } return 1; } else return 0; case '\033': /* ESCAPE */ /* case 0x09: */ /* tab key */ if (pClient->InputCount > 0) { ResetInput (pClient); return 1; } else return 0; case '<': case '-': case ',': case '[': if (pClient->InputCount == 0) return 0; if ( pClient->MultiPageMode ) { if (pClient->CurrentPageIndex > pClient->StartKey) { pClient->CurrentPageIndex = pClient->CurrentPageIndex - pClient->cur_table->MaxDupSel; if (pClient->CurrentPageIndex < pClient->StartKey) pClient->CurrentPageIndex = pClient->StartKey; } else pClient->CurrentPageIndex = pClient->StartKey; if (pClient->IsAssociateMode) FillAssociateChars (pClient, pClient->CurrentPageIndex); else FillMatchChars (pClient, pClient->CurrentPageIndex); return 1; } else return 1; break; case '>': case ']': case '.': case '=': if (pClient->InputCount == 0) return 0; if ( pClient->MultiPageMode ) { pClient->CurrentPageIndex = pClient->NextPageIndex; if (pClient->IsAssociateMode) FillAssociateChars (pClient, pClient->CurrentPageIndex); else FillMatchChars (pClient, pClient->CurrentPageIndex); return 1; } else return 1; case ' ': if ( pClient->CurSelNum == 0 ) return 0; if ( pClient->seltab[0][0] ) { strcpy (buf, pClient->seltab[0]); *len = strlen (buf); AdjustPhraseOrder (pClient, pClient->seltab_phrase[0]); Simulate_putstr (buf, pClient, 0); return 2; } return 0; default: return 0; } /* switch */ do_our_filter: // inkey = pClient->cur_table->KeyMap[key]; // is_sel_key = strchr( pClient->cur_table->selkey, key); vv = is_sel_key - pClient->cur_table->selkey; /* selkey index, strchr may return NULL */ /* if a key is simultaneously inkey & is_sel_key, then selkey first? */ if ( (!inkey && !is_sel_key) || (!inkey && is_sel_key && (pClient->CurSelNum == 0 || pClient->seltab[vv][0] == 0)) ) { pClient->IsAssociateMode = 0; ResetInput (pClient); return 0; } if (is_sel_key && pClient->CurSelNum > 0 && pClient->seltab[vv][0]) { strcpy (buf, pClient->seltab[vv]); *len = strlen (buf); AdjustPhraseOrder (pClient, pClient->seltab_phrase[vv]); Simulate_putstr (buf, pClient, vv); return 2; } /* now it must be inkey? */ pClient->IsAssociateMode = 0; if ( inkey >= 1 && pClient->InputCount < MAX_INPUT_LENGTH ) pClient->InpKey[pClient->InputCount++] = inkey; if (pClient->InputCount <= pClient->InputMatch+1) { FindMatchKey (pClient); pClient->CurrentPageIndex = pClient->StartKey; pClient->MultiPageMode = 0; FillMatchChars(pClient, pClient->StartKey); if (pClient->InputCount >= pClient->cur_table->MaxPress && pClient->CurSelNum == 1 && pClient->cur_table->last_full) { // left only one selection strcpy (buf, pClient->seltab[0]); *len = strlen (buf); AdjustPhraseOrder (pClient, pClient->seltab_phrase[0]); Simulate_putstr (buf, pClient, 0); return 2; } return 1; } return 1; } long TL_KeyPressed (HzInputTable_T *pClient, u_char key) { int inkey = 0,vv; char *is_sel_key = (char*)0; switch ( key ) { case '\177': /* BackSpace */ if (pClient->InputCount > 0 ) { pClient->InpKey[--pClient->InputCount]=0; if (pClient->InputCount == 0) { ResetInput (pClient); return 1; } else if (pClient->InputCount < pClient->InputMatch) { FindMatchKey (pClient); pClient->MultiPageMode = 0; pClient->CurrentPageIndex = pClient->StartKey; FillMatchChars (pClient, pClient->StartKey); } } else return 0; break; case '\033': /* ESCAPE */ ResetInput (pClient); break; case '[': if (pClient->MultiPageMode) { if (pClient->CurrentPageIndex > pClient->StartKey) pClient->CurrentPageIndex = pClient->CurrentPageIndex - pClient->cur_table->MaxDupSel; else pClient->CurrentPageIndex = pClient->StartKey; if (pClient->IsAssociateMode) FillAssociateChars (pClient, pClient->CurrentPageIndex); else FillMatchChars (pClient, pClient->CurrentPageIndex); } else return 0; break; case ']': if (pClient->MultiPageMode) { pClient->CurrentPageIndex = pClient->NextPageIndex; if (pClient->IsAssociateMode) FillAssociateChars (pClient, pClient->CurrentPageIndex); else FillMatchChars(pClient, pClient->CurrentPageIndex); } else return 0; break; case ' ': ResetInput(pClient); return 0; default: inkey = pClient->cur_table->KeyMap[key]; is_sel_key = strchr( pClient->cur_table->selkey, key); vv = is_sel_key - pClient->cur_table->selkey; /* selkey index, strchr may return NULL */ /* if a key is simultaneously inkey & is_sel_key, then selkey first? */ if ( (!inkey && !is_sel_key) || (!inkey && is_sel_key && (pClient->CurSelNum == 0 || pClient->seltab[vv][0] == 0)) ) { pClient->IsAssociateMode = 0; ResetInput(pClient); break; } if (is_sel_key && pClient->CurSelNum > 0 && pClient->seltab[vv][0]) { // ResetInput(pClient); break; } /* now it must be inkey? */ pClient->IsAssociateMode = 0; if (inkey >= 1 && pClient->InputCount < MAX_INPUT_LENGTH) pClient->InpKey[pClient->InputCount++] = inkey; if (pClient->InputCount <= pClient->InputMatch+1) { FindMatchKey (pClient); pClient->CurrentPageIndex = pClient->StartKey; pClient->MultiPageMode = 0; FillMatchChars (pClient, pClient->StartKey); } else return 0; break; } /* switch */ return (long) pClient->CurSelNum; } char *szGetSelItem (HzInputTable_T *pClient, int vv) { if (pClient->CurSelNum > 0 && pClient->seltab[vv][0]) return pClient->seltab[vv]; return NULL; } /************************************************************************* * public function * *************************************************************************/ void TL_InputInit (HzInputTable_T *p) { // p->bIsFinishSorting = 0; p->TotalOutputPhrase = 0; p->IsHanziInput = 0; // default is ascii input p->IsFullCharBackup = 0; p->IsFullCommaInEnglish = 0; p->IsFullChar = 0; p->IsFullCommaInChinese = 0; p->IsFullComma = 0; p->current_method = 0; p->cur_table = NULL; p->CurSelNum = 0; p->UseAssociateMode = 0; p->IsHanziInputBackup = 0; p->IsSysMenu = 0; p->IsHelpMenu = 0; p->MaxSelectLen = 72; p->MAX_SEL_LENGTH = p->MaxSelectLen - 16; p->InputCount = 0; } static void InputCleanup (hz_input_table *p) { UnloadInputMethod (p); // UnloadSysPhrase (); // UnloadUserPhrase (); } static void UnloadInputMethod (hz_input_table *p) { int i; if (p == NULL) return; free (p->item); for (i = 0; i < p->TotalAssociatePhrase; i++) if (p->pAssociatePhrase[i].pPhrase != NULL) free (p->pAssociatePhrase[i].pPhrase); free (p); } hz_input_table* TL_LoadMethod (char *szName) { hz_input_table *p; p = LoadInputMethod (szName); return p; } extern void UnloadInputMethod (hz_input_table *p); void TL_UnloadMethod (hz_input_table *p) { UnloadInputMethod (p); } char * TL_DoSelectItem (HzInputTable_T *p, u_long vv, char *s) { if (vv < p->CurSelNum && p->seltab[vv][0]) { strcpy (s, p->seltab[vv]); GetAssociatePhrases (p, s); // ResetInput (p); return s; } return NULL; } int TL_ConfigureInputArea (HzInputTable_T *p, int MaxSelectLen) { p->MaxSelectLen = MaxSelectLen; p->MAX_SEL_LENGTH = p->MaxSelectLen - 8; return 1; } int TL_GetInputDisplay (HzInputTable_T * p, char *buf) { int i, len = 9; char c; char *q = buf; if (p->InputCount == 0) return 0; for( i = 0; i <= len ; i++) { if (i < p->InputCount) c = p->cur_table->KeyName[p->InpKey[i]]; else c= ' '; if (i == p->InputMatch && p->InputCount > p->InputMatch && i != 0) *q++ = '-'; *q++ = c; } *q = '\0'; return 1; } int TL_GetSelectDisplay (HzInputTable_T * p, char *buf) { int i, len; char buf1[256]; buf[0] = '\0'; if (p->CurSelNum == 0) return 0; /* not first page */ if (p->MultiPageMode && p->CurrentPageIndex != p->StartKey) strcat (buf, "< "); for (i = 0; i < p->CurSelNum; i++) { if (i != 9) sprintf (buf1, "%d%s ", i + 1, p->seltab[i]); else sprintf (buf1, "0%s ", p->seltab[i]); len = strlen(buf1); strcat (buf, buf1); } /* not last page */ if (p->MultiPageMode && p->NextPageIndex != p->StartKey) strcat (buf, "> "); return i; }