/* vi: set sw=4 ts=4: */ /* * CCE - Console Chinese Environment - * Copyright (C) 1998-2003 Rui He (rhe@3eti.com) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* This input method is different than xcin's bimsphone and chewing which will do candidate-autoselect(based on libtabe). It won't do candidate-autoselect, you need to manually select. Since most zhuyin keyboard mappings have conflict between input and select keys(0-9 now) and SPACE is used for 1st tone, we have to stay in Shift-Select state until user press SPACE(sometimes SPACE twice, one for 1st tone and one for Direct-Selct). This is not so convenient! In order to keep the same interface like traditional single-char zhuyin(after tone key, candidates can be selected by 0-9), we will go into Direct-Select state if a tone key is pressed. So for phrase input, usually you just need to enter one tone key for last char, (otherwise you may have conflict, 0-9 as select keys have higher priority than as select keys). It's OK for phrase input since not too many duplicate mappings for phrase. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #define DEFAULT_PINYIN_KEYBOARD KEYBOARD_MAP_PINYIN /* TONEPY */ static int MmapZhuyinTable(ZhuyinModule *inmd, char *pathname); static void CreateZYMsg(ZhuyinModule *inmd) { int i,j; strncpy(inmd->iapybuf, inmd->HZbuf, sizeof(inmd->iapybuf)); if (inmd->EnglishPos > 0) /* in English mode */ { strcat(inmd->iapybuf, inmd->ZYbuf); return; } if (inmd->HZbufLen > 0) strcat(inmd->iapybuf, " "); /* add one space */ if (IS_KB_PINYIN(inmd->KeyboardMode)) { for(i=inmd->CurZYPos; iCurZYNum; i++) { strcat(inmd->iapybuf,inmd->SavedPYStr[i]); /* MAX_PY_LEN = 7 */ if (inmd->SavedPYStr[i][ strlen(inmd->SavedPYStr[i]) - 1 ] == '\'') continue; else strcat(inmd->iapybuf," "); } } else { unsigned char *p = inmd->iapybuf; while(*p) p++; /* skip to the zero */ for(i = inmd->CurZYPos; i < inmd->CurZYNum; i++) { j = 0; while(inmd->CurZYStr[i][j]) { if (inmd->CurZYStr[i][j] <= ZHUYIN_TOTAL_NUM || inmd->CurZYStr[i][j] > ZHUYIN_FIRST_TONE) /* 37: 1st tone use SPACE to show it */ { strncpy(p, (lInfo.sysCoding == CODE_BIG5? Big5PhoneChars : GBPhoneChars) + 2 * (inmd->CurZYStr[i][j]-1), 2); p += 2; } j++; } *p++ = ' '; } *p++ = '\0'; } } #define ENGSTR_MAXLEN (cur_table->SelAreaWidth + cur_table->InputAreaWidth-8) void RefreshZYInputArea(ZhuyinModule *inmd ) { int pos = cur_table->InputAreaX; int len = strlen(inmd->iapybuf); if (inmd->EnglishPos > 0) { u_char *str = inmd->iapybuf; if (len > ENGSTR_MAXLEN) { str += len - ENGSTR_MAXLEN; len = ENGSTR_MAXLEN; } InputAreaOutput(lInfo.sysCoding, pos, "Eng: ", INPUT_FGCOLOR, INPUT_BGCOLOR); pos += 5; InputAreaOutput(lInfo.sysCoding, pos, str, INPUT_FGCOLOR, INPUT_BGCOLOR); pos += len; } else /* output candidates */ { InputAreaOutput(lInfo.sysCoding, pos, inmd->iapybuf, INPUT_FGCOLOR, INPUT_BGCOLOR); pos += strlen(inmd->iapybuf); if (pos > cur_table->SelAreaX) pos = cur_table->SelAreaX; else { for(; pos < cur_table->SelAreaX; pos++) pVideoInfo->input_sput(pos,' ',INPUT_BGCOLOR,INPUT_BGCOLOR); } InputAreaOutput(lInfo.sysCoding, pos, inmd->iahzbuf, INPUT_FGCOLOR, INPUT_BGCOLOR); pos += strlen(inmd->iahzbuf); } for(; pos < cur_table->SelAreaX + cur_table->SelAreaWidth; pos++) pVideoInfo->input_sput(pos,' ',INPUT_BGCOLOR,INPUT_BGCOLOR); } void RefreshZhuyinInput(void) { RefreshZYInputArea((ZhuyinModule *)cur_table->PrivateData); } void ResetZhuyinInput(ZhuyinModule *inmd) { inmd->Keybuf[0] = inmd->ZYbuf[0] = inmd->HZbuf[0] = inmd->HZPYKey[0] ='\0'; inmd->HZbufLen = inmd->ZYbufLen = inmd->KeybufLen = 0; inmd->iapybuf[0] = inmd->iahzbuf[0] = '\0'; inmd->EnglishPos = 0; inmd->TotalSelNum = inmd->CurZYNum = inmd->CurZYPos = 0; inmd->FirstZiNum = -1; /* flag to find again */ } void ChangeZhuyinKeyboardMap(int offset) { ZhuyinModule *inmd = (ZhuyinModule *)cur_table->PrivateData; inmd->KeyboardMode += offset; if (inmd->KeyboardMode > KEYBOARD_MAP_LASTZY) inmd->KeyboardMode = KEYBOARD_MAP_PINYIN; if (IS_KB_ZHUYIN(inmd->KeyboardMode) && lInfo.sysCoding != CODE_GB && lInfo.sysCoding != CODE_GBK && lInfo.sysCoding != CODE_BIG5) inmd->KeyboardMode = DEFAULT_PINYIN_KEYBOARD; cur_table->FirstInputSelectKeyConflict = IS_KB_CONFLICT(inmd->KeyboardMode); ResetZhuyinInput(inmd); if (offset != 0) { strcpy(inmd->iahzbuf, CiyinKeyboardMapStr[inmd->KeyboardMode]); RefreshZYInputArea(inmd); } inmd->iahzbuf[0] = '\0'; /* temporary display */ } hz_input_table* Init_ZhuyinInput(hz_input_table *table, char *filename, int sysCoding) { ZhuyinModule *inmd; table->PrivateData = malloc(sizeof(ZhuyinModule)); /* not zeroed out!!! */ inmd = (ZhuyinModule *)table->PrivateData; if (!inmd || !memset(inmd, 0, sizeof(ZhuyinModule)) ) { error("Failed to init Zhuyin Input Module!\n"); free(table); return NULL; } strcpy(table->cname, Zhuyin_CNameStr[sysCoding]); table->PhraseFileSize = 0; fclose(table->TableFile); inmd->OldCoding = table->encoding; table->encoding = sysCoding; inmd->KeyboardMode = ZhuyinKeyboardMode; if (inmd->KeyboardMode == KEYBOARD_MAP_AUTO || inmd->KeyboardMode > KEYBOARD_MAP_LASTZY) /* 0 */ { if (lInfo.sysCoding == CODE_BIG5) inmd->KeyboardMode = KEYBOARD_MAP_ZOZY; else inmd->KeyboardMode = DEFAULT_PINYIN_KEYBOARD; } if (IS_KB_ZHUYIN(inmd->KeyboardMode) && lInfo.sysCoding != CODE_GB && lInfo.sysCoding != CODE_GBK && lInfo.sysCoding != CODE_BIG5) inmd->KeyboardMode = DEFAULT_PINYIN_KEYBOARD; table->FirstInputSelectKeyConflict = IS_KB_CONFLICT(inmd->KeyboardMode); if (MmapZhuyinTable(inmd, filename)) { free(table->PrivateData); free(table); return NULL; } table->FontMapped = 1; /* already mapped! since pinyin will use system's encoding */ table->auto_select = 0; /* 1; // we dont' need it, right? */ table->item = (ITEM *)-1; /* 0 unmapped, -1 (no table) other: mapped */ ResetZhuyinInput(inmd); table->InputAreaX = cal_input_xpos(table); table->InputAreaWidth = ZHUYIN_AREA_WIDTH; /* 20 */ table->SelAreaX = table->InputAreaX + table->InputAreaWidth; table->SelAreaWidth = dispInfo.tx_avail - table->SelAreaX - 2 - dispInfo.show_terminal * WINDOW_TERMINAL_STR_WIDTH; /* 18 or 0 */ if (table->SelAreaWidth > MAX_CANDIDATE_BUF) table->SelAreaWidth = MAX_CANDIDATE_BUF; table->HZFilter = Zhuyin_HZFilter; table->RefreshInput = RefreshZhuyinInput; table->ChangeKeyboardMap = ChangeZhuyinKeyboardMap; return table; } static int MmapZhuyinTable(ZhuyinModule *inmd, char *pathname) { int fd; char *p; int size; if ( (fd = open(pathname, O_RDWR|CCE_OPENFLAG)) < 0) { warn("File %s can't open for read-write.\n",pathname); return -1; } if (lseek(fd,-4,SEEK_END) == -1 || read(fd, &(inmd->sys_size), sizeof(int)) != sizeof(int) /* || inmd->sys_size != ftell(stream)-4 */) /* error!! */ { warn("%s is not a valid zhuyin phrase file, size=%d.\n",pathname, inmd->sys_size); return -1; } p = (char *)mmap(0, inmd->sys_size /*+ sizeof(hz_input_table) + sizeof(inmd->pytab) */, PROT_READ | PROT_WRITE, MAP_SHARED, fd /* fileno(stream) */, 0); if (p == MAP_FAILED) { warn("Mmapping %s failed. errno=%d %m\n", pathname, errno); return -1; } inmd->MmapPtr = (void *)p; /* save it */ p += sizeof(hz_input_table); inmd->YinTable = (Yin *)p; p += sizeof(Yin) * TOTAL_ZHUYIN_NUM; /* skip the YinTable */ size = *((int *)p); /* ZiLibSize */ p += sizeof(int); inmd->ZiLib = (unsigned short *)p; p += size; /* skip the ZiLib */ inmd->ZiNum = *((int *)p); /* ZiNum */ p += sizeof(int); inmd->ZiTable = (Zi *)p; p += inmd->ZiNum * sizeof(Zi); size = *((int *)p); /* CiLibSize */ p += sizeof(int); inmd->CiLib = p; close(fd); return 0; } void ZhuyinInputCleanup(hz_input_table *table) { ZhuyinModule *inmd = (ZhuyinModule *)(table->PrivateData); if (inmd->MmapPtr != MAP_FAILED && inmd->MmapPtr != NULL) munmap(inmd->MmapPtr, inmd->sys_size); } static u_char *GetPhraseAndConvert(ZhuyinModule *inmd, SelItem *p, u_char *saveOld) { static u_char temp[2*MAX_ZYPHRASE_LEN+1]; int i, len = p->len; u_char *dest; dest = temp; if (saveOld) { *saveOld++ = (u_char)(p->zi >> 8); *saveOld++ = (u_char)(p->zi & 0xFF); } ConvertEncoding_Char(inmd->OldCoding /*CODE_BIG5*/, lInfo.sysCoding, p->zi >> 8, p->zi & 0xFF, dest, dest+1); dest += 2; if (len > 0) { u_char *cp; if (len > MAX_ZYPHRASE_LEN) len = MAX_ZYPHRASE_LEN; cp = p->ptr; for(i = 0; i < len; i++) { if (saveOld) { *saveOld++ = cp[0]; *saveOld++ = cp[1]; } ConvertEncoding_Char(inmd->OldCoding /*CODE_BIG5*/, lInfo.sysCoding, cp[0], cp[1], dest, dest+1); cp += 2; dest += 2; } } *dest = '\0'; if (saveOld) *saveOld = '\0'; return temp; } static void FillForwardSelection(ZhuyinModule *inmd,int startpos) { char *iahzbuf=inmd->iahzbuf; int i,count; char strtmp[2*MAX_ZYPHRASE_LEN+10]; if (startpos > inmd->TotalSelNum - 1 || startpos < 0) return ; /* non-forwardable, keep the iahzbuf intact */ if (startpos == 0) CurrentPage = 0; if (CurrentPage < MAX_PAGE_INDEX) SavedPageIndex[CurrentPage] = startpos; for(i = 0; i < NUM_ZHUYIN_CAND; i++) inmd->CandidateIndex[i] = -1; /* make it invalid first */ iahzbuf[0] = '\0'; if (inmd->TotalSelNum < 1) { return; /* clear the iahzbuf */ } count = 0; /* backup the starting position */ inmd->SelStartPos = startpos; inmd->SelEndPos = startpos - 1; snprintf(inmd->iahzbuf, sizeof(inmd->iahzbuf), inmd->SelStartPos > 0 ? "< %s" : " %s", ( IsDirectSelect ? "" : "Shift-" )); for(; inmd->SelEndPos < inmd->TotalSelNum-1 && count < NUM_ZHUYIN_CAND; inmd->SelEndPos++) { strncpy(SelTab[count], GetPhraseAndConvert(inmd, inmd->CurSel + inmd->SelEndPos + 1, NULL), sizeof(SelTab[0])); for(i = 0; i < count; i++) if (!strcmp(SelTab[i], SelTab[count])) { break; /* try next one */ } if (i < count) continue; /* duplicate */ snprintf(strtmp,sizeof(strtmp),"%d%s ", (count+1)%NUM_ZHUYIN_CAND, SelTab[count]); if ( (strlen(iahzbuf)+strlen(strtmp)+2) <= (u_int)cur_table->SelAreaWidth) { strcat(iahzbuf,strtmp); inmd->CandidateIndex[count++] = inmd->SelEndPos+1; /* record the mapping */ } else break; } i = strlen(iahzbuf); if (inmd->SelEndPos < inmd->TotalSelNum - 1 && count >= 1) { iahzbuf[i] = '>'; iahzbuf[i+1] = '\0'; } } static void FillBackwardSelection(ZhuyinModule *inmd,int lastpos) { char *iahzbuf=inmd->iahzbuf; int count,ialen; char strbuf[2*MAX_ZYPHRASE_LEN+10]; if (lastpos < 0 || lastpos > inmd->TotalSelNum-1) return; /* iahzbuf intact */ iahzbuf[0] = '\0'; if (inmd->TotalSelNum < 1) return; /* clear iahzbuf */ count = 0; inmd->SelEndPos = lastpos; ialen = 2; /* leftmost "< " or " " */ inmd->SelStartPos = lastpos+1; while(inmd->SelStartPos > 0 && count < NUM_ZHUYIN_CAND) { strncpy(strbuf, GetPhraseAndConvert(inmd, inmd->CurSel+inmd->SelStartPos-1,NULL), sizeof (strbuf)); ialen += 2 + strlen(strbuf); if (ialen+2 > cur_table->SelAreaWidth) break; count++; inmd->SelStartPos--; } FillForwardSelection(inmd,inmd->SelStartPos); } static void FillMatchZi(ZhuyinModule *inmd, int index, int tone) { Yin *y = &inmd->YinTable[index]; unsigned short *pZi = inmd->ZiLib + y->offset; int i, j, k; for(i = 0; i < MAX_TONE_NUM; pZi += y->ZiNum[i], i++, tone >>= 1) { if ( y->ZiNum[i] == 0 || (tone & 1) == 0 ) continue; for(j = 0; j < y->ZiNum[i]; j++) { if (inmd->FirstZiNum >= sizeof(inmd->FirstZiAry)/sizeof(inmd->FirstZiAry[0])) break; if (ConvertEncoding_Char( inmd->OldCoding /*CODE_BIG5*/, lInfo.sysCoding, pZi[j] >> 8, pZi[j] & 0xFF, NULL, NULL) <= 0) continue; /* can't convert to target encoding */ for(k = 0; k < inmd->FirstZiNum; k++) if (pZi[j] == inmd->FirstZiAry[k]) { break; } if ( k == inmd->FirstZiNum) inmd->FirstZiAry[inmd->FirstZiNum++] = pZi[j]; } } } static int FindFirstChar(ZhuyinModule *inmd, u_short ZYKey[], u_short ZYMask[]) { int i, j; int key, tone, endkey; inmd->FirstZiNum = 0; /* clear it */ key = ZYKey[0] & 0x07FF & ZYMask[0]; /* this one is the starting key without tone */ tone = (ZYKey[0] & TONE_MASK) >> TONE_OFFSET; if (tone == 0) tone = 0x1F; /* enable all bits */ endkey = (key & 0x07C0) + 0x0040; /* won't bigger than TOTAL_ZHUYIN_NUM */ for(j = key; j < endkey /* TOTAL_ZHUYIN_NUM */; j++) { if ((j & ZYMask[0]) == key) /* matched */ { FillMatchZi(inmd, j, tone); } } for(i = 0; i < inmd->FirstZiNum; i++) { int freq_i, freq_j; u_short zi_i, zi_j; zi_i = inmd->FirstZiAry[i]; freq_i = inmd->ZiTable[ CalculateCharIndex(inmd->OldCoding, zi_i >> 8, zi_i & 0xFF) ].freq; for(j = i+1; j < inmd->FirstZiNum; j++) { zi_j = inmd->FirstZiAry[j]; freq_j = inmd->ZiTable[ CalculateCharIndex(inmd->OldCoding, zi_j >> 8, zi_j & 0xFF)].freq; if (freq_i < freq_j) { freq_i = freq_j; inmd->FirstZiAry[i] = zi_j; inmd->FirstZiAry[j] = zi_i; zi_i = zi_j; } } } return 0; } static int TestPhraseZhuyin(ZhuyinModule *inmd, u_char *Zi, int cilen, int yinlen, u_short ZYKey[], u_short ZYMask[]) { int i,j; int index, key; for(i = 0; i < cilen; i++) { if (ConvertEncoding_Char( inmd->OldCoding /*CODE_BIG5*/, lInfo.sysCoding, Zi[0], Zi[1], NULL, NULL) <= 0) return 0; /* can't convert to target encoding */ if (i < yinlen) { index = CalculateCharIndex( inmd->OldCoding, Zi[0], Zi[1]); key = ZYKey[i] & ZYMask[i]; for(j = 0; j < MAX_YIN_NUM; j++) { if (inmd->ZiTable[index].yin[j] == 0) return 0; /* no match */ if ((inmd->ZiTable[index].yin[j] & ZYMask[i]) == key) { break; /* found */ } } if (j >= MAX_YIN_NUM) /* no matched */ return 0; } Zi += 2; } return 1; } static void SortMatchPhrase(ZhuyinModule *inmd, int start, int end) { int i, j; u_short freq_i, freq_j; SelItem tmpSel; for(i = start; i < end; i++) { freq_i = *(ZYFREQTYPE *)(inmd->CurSel[i].ptr + 2 * inmd->CurSel[i].len); for(j = i+1; j < end; j++) { freq_j = *(ZYFREQTYPE *)(inmd->CurSel[j].ptr + 2 * inmd->CurSel[j].len); if (freq_i < freq_j) { freq_i = freq_j; tmpSel = inmd->CurSel[i]; inmd->CurSel[i] = inmd->CurSel[j]; inmd->CurSel[j] = tmpSel; } } } } static int FindMatchPhrase(ZhuyinModule *inmd, u_short Zi, int cilen, int yinlen, u_short ZYKey[], u_short ZYMask[]) { int i; int index, count, curnum; u_char *p; index = CalculateCharIndex(inmd->OldCoding, Zi >> 8, Zi & 0xFF); count = (cilen == 1 ? inmd->ZiTable[index].phnum.PhraseNum1 : inmd->ZiTable[index].phnum.PhraseNum[cilen]); if (count == 0) return 0; curnum = inmd->TotalSelNum; p = inmd->CiLib + inmd->ZiTable[index].offset; for(i = 1; i < cilen; i++) p += (i == 1 ? inmd->ZiTable[index].phnum.PhraseNum1 : inmd->ZiTable[index].phnum.PhraseNum[i]) *(2*i + sizeof(ZYFREQTYPE)); for(i = 0; i < count; i++) { if (TestPhraseZhuyin(inmd, p, cilen, yinlen, ZYKey, ZYMask)) /* match */ { SelItem *pSel = &inmd->CurSel[inmd->TotalSelNum]; pSel->zi = Zi; pSel->len = cilen; pSel->ptr = p; inmd->TotalSelNum++; if (inmd->TotalSelNum >= MAX_CAND_NUM) break; /* overflow */ } p += 2*cilen + sizeof(ZYFREQTYPE); } return inmd->TotalSelNum - curnum; } static int FindMatchCharPhrase(ZhuyinModule *inmd, u_short ZYKey[], u_short ZYMask[],int ZYNum) { int i, j; int CurSelNum; inmd->TotalSelNum = 0; inmd->iahzbuf[0] = '\0'; /* clear it */ CurrentPage = 0; if (!IS_KB_CONFLICT(inmd->KeyboardMode)) IsDirectSelect = 1; /* pinyin is DirectSlect if last key is 0-5 tone */ else { if (inmd->ZYbufLen == 0 || inmd->ZYbuf[inmd->ZYbufLen-1] >= ZHUYIN_FIRST_TONE) IsDirectSelect = 1; else IsDirectSelect = 0; } if (ZYNum == 1 || inmd->FirstZiNum == -1) /* more than 1, then it's already filled */ FindFirstChar(inmd, ZYKey, ZYMask); if (ZYNum > 1) { for(j = ZYNum; j > 1; j--) { CurSelNum = inmd->TotalSelNum; for(i = 0; i < inmd->FirstZiNum; i++) { FindMatchPhrase(inmd, inmd->FirstZiAry[i], j-1, j-1, ZYKey+1, ZYMask+1); if (inmd->TotalSelNum >= MAX_CAND_NUM) { j = 1; /* end condition for j loop */ break; } } SortMatchPhrase(inmd, CurSelNum, inmd->TotalSelNum); /* sorted */ } } for(i = 0; i < inmd->FirstZiNum; i++) { if (inmd->TotalSelNum >= MAX_CAND_NUM) break; /* overflow */ inmd->CurSel[inmd->TotalSelNum].zi = inmd->FirstZiAry[i]; inmd->CurSel[inmd->TotalSelNum++].len = 0; /* single char */ } return inmd->TotalSelNum; } static void OutputHZBuf(ZhuyinModule *inmd, int index, int hzlen, int tty_fd) { ZYFREQTYPE *pFreq; unsigned char LastChar[2]; if ( hzlen == inmd->HZbufLen ) /* found an existing phrase */ { if (index >= 0) /* index==-1, in PinyinArea */ { if (inmd->CurSel[index].len == 0) /* single char */ { int char_index = CalculateCharIndex(inmd->OldCoding, inmd->CurSel[index].zi >> 8, inmd->CurSel[index].zi & 0xFF); pFreq = &inmd->ZiTable[char_index].freq; } else /* phrase */ { pFreq = (ZYFREQTYPE *)(inmd->CurSel[index].ptr + 2*inmd->CurSel[index].len); } if (*pFreq < MAX_FREQ_ALLOWED) *pFreq += 100; /* How to adjust frequency? */ } } else if(inmd->HZbufLen > 1) /* user just make a new phrase */ { } OutputCJKStr(tty_fd, inmd->HZbuf, 2 * inmd->HZbufLen); LastChar[0] = inmd->HZbuf[2*inmd->HZbufLen-2]; LastChar[1] = inmd->HZbuf[2*inmd->HZbufLen-1]; /* save it, will reset next */ ResetZhuyinInput(inmd); RefreshZYInputArea(inmd); if (IsAssocEnable && cur_table->AssocFile) { FindAssociateKeyAndFill(LastChar); /* using the input method's encoding to find */ } } static void OutputZYBuf(ZhuyinModule *inmd, int tty_fd) { if (inmd->HZbufLen > 0) /* 1 Char 2 Bytes, should we output the selected hanzis? */ OutputCJKStr(tty_fd, inmd->HZbuf, 2 * inmd->HZbufLen); if (inmd->ZYbufLen > 0) OutputCJKStr(tty_fd, inmd->ZYbuf, inmd->ZYbufLen); ResetZhuyinInput(inmd); RefreshZYInputArea(inmd); } static int SelectKeyPressed(ZhuyinModule *inmd, int idx, int tty_fd) { int j, index; int hzlen; index = inmd->CandidateIndex[idx]; /* get the mapping */ if (index > inmd->SelEndPos) return 1; /* out of range selection! we ignore it */ strcat(inmd->HZbuf, GetPhraseAndConvert(inmd, inmd->CurSel + index, inmd->HZbufOldCoding + 2*inmd->HZbufLen)); hzlen = strlen(SelTab[idx])/2; inmd->HZbufLen += hzlen; if (inmd->HZbufLen >= inmd->CurZYNum) { OutputHZBuf(inmd, index, hzlen, tty_fd); } else /* not yet, some unselected zhuyin exist */ { inmd->CurZYPos += hzlen; inmd->FirstZiNum = -1; /* force it to find again */ FindMatchCharPhrase(inmd, inmd->CurZYKey + inmd->CurZYPos, inmd->CurZYMask + inmd->CurZYPos, inmd->CurZYNum - inmd->CurZYPos); FillForwardSelection(inmd,0); inmd->ZYbuf[0] = '\0'; /* put the rest of the pinyin into ZYbuf */ for(j = inmd->CurZYPos; j < inmd->CurZYNum; j++) { if (IS_KB_ZHUYIN(inmd->KeyboardMode)) strcat(inmd->ZYbuf, inmd->CurZYStr[j]); else if (IS_KB_SHUANGPIN(inmd->KeyboardMode)) strcat(inmd->ZYbuf, inmd->CurSPStr[j]); else strcat(inmd->ZYbuf, inmd->SavedPYStr[j]); } inmd->ZYbufLen = strlen(inmd->ZYbuf); CreateZYMsg(inmd); RefreshZYInputArea(inmd); } return 1; } static int ProcessZhuyinKeys(ZhuyinModule *inmd, u_char ch, int tty_fd) { if (IS_KB_ZHUYIN(inmd->KeyboardMode)) { ch = ((u_int)strchr(InputKeyMap[inmd->KeyboardMode], ch) - (u_int)InputKeyMap[inmd->KeyboardMode]) + 1; if (ch >= ZHUYIN_FIRST_TONE) /* it's a tone symbol */ { if (inmd->ZYbufLen == 0) return 0; if (inmd->ZYbuf[inmd->ZYbufLen-1] >= ZHUYIN_FIRST_TONE) { if (ch == ZHUYIN_FIRST_TONE) /* 1 tone symbol + 1 SPACE */ return 0; /* not handled */ else return 1; } IsDirectSelect = 1; } } else /* check invalid ' (two consecutive ') */ { if (inmd->EnglishPos > 0) /* already in English */ { } else if ((ch >= 'A' && ch <= 'Z') || ch == ':') /* still in Chinese mode but Capital Letter enter */ { inmd->EnglishPos = inmd->ZYbufLen+1; /* position, starting from 1 */ inmd->iahzbuf[0] = '\0'; inmd->TotalSelNum = 0; } else if (ch < 'a' || ch > 'z') /* not pinyin letter */ { static u_char *tonestr = "`-/=\\"; u_char *tone = strchr(tonestr, ch); if (tone) ch = (u_int)tone - (u_int)tonestr + '0'; if (ch == '\'' || (ch >= '0' && ch <= '5')) /* a tone symbol or ' */ { if (inmd->ZYbufLen == 0) return 0; /* not allowed for the first one */ if (strchr("012345'", inmd->ZYbuf[inmd->ZYbufLen-1])) return 1; /* ignore it? */ } } } if (inmd->KeybufLen >= MAX_ZYINPUT_BUF) /* will overflow Keybuf soon if not output or ignored */ { return 1; } inmd->Keybuf[inmd->KeybufLen++] = ch; inmd->Keybuf[inmd->KeybufLen] = '\0'; inmd->ZYbuf[inmd->ZYbufLen++] = ch; inmd->ZYbuf[inmd->ZYbufLen] = '\0'; return -1; } static int ZhuyinKeyPressed(ZhuyinModule *inmd, u_char ch, int tty_fd) { int ret; if (ch == KEYCODE_CONTROL_H || ch == KEYCODE_BACKSPACE) /* BackSpace */ { if (inmd->KeybufLen == 0) return 0; /* output ch */ if (inmd->ZYbufLen == 0) /* no additional unconverted zhuyin */ { strcpy(inmd->ZYbuf, inmd->Keybuf); inmd->ZYbufLen = inmd->KeybufLen; inmd->HZbuf[0] = '\0'; /* clear all the selected chars, reparse */ inmd->HZbufLen = 0; } else { if (IS_KB_ZHUYIN(inmd->KeyboardMode) && inmd->ZYbufLen > 1 && inmd->ZYbuf[inmd->ZYbufLen-1] == ZHUYIN_FIRST_TONE) { inmd->Keybuf[--inmd->KeybufLen] = '\0'; inmd->ZYbuf[--inmd->ZYbufLen] = '\0'; /* delete the SPACE tone symbol */ } inmd->Keybuf[--inmd->KeybufLen] = '\0'; inmd->ZYbuf[--inmd->ZYbufLen] = '\0'; /* cut one pinyin-char off */ if (inmd->KeybufLen == 0) { ResetZhuyinInput(inmd); RefreshZYInputArea(inmd); return 1; /* clear all, refresh */ } if (inmd->ZYbufLen < inmd->EnglishPos) inmd->EnglishPos = 0; if (inmd->ZYbufLen == 0) /* should clear the selection */ { inmd->iahzbuf[0] = '\0'; inmd->TotalSelNum = 0; } } } else /* a-z A-Z or other keys */ { ret = ProcessZhuyinKeys(inmd, ch, tty_fd); if (ret == 0 || ret == 1) return ret; /* -1 for continue */ } if (inmd->EnglishPos > 0) { CreateZYMsg(inmd); RefreshZYInputArea(inmd); return 1; /* handled in English mode */ } if (inmd->HZbufLen == 0) inmd->CurZYPos = 0; if (IS_KB_ZHUYIN(inmd->KeyboardMode)) { inmd->CurZYNum = inmd->CurZYPos + ParseZhuyinKeys( inmd->KeyboardMode, inmd->ZYbuf, inmd->CurZYStr + inmd->CurZYPos, inmd->CurZYKey + inmd->CurZYPos, inmd->CurZYMask + inmd->CurZYPos, MAX_ZYPHRASE_LEN - inmd->CurZYPos); } else { inmd->CurZYNum = inmd->CurZYPos + ConvertPinyinKeysToZhuyin( inmd->KeyboardMode, inmd->ZYbuf, inmd->CurZYStr + inmd->CurZYPos, inmd->CurZYKey + inmd->CurZYPos, inmd->CurZYMask + inmd->CurZYPos, inmd->SavedPYStr + inmd->CurZYPos, inmd->CurSPStr + inmd->CurZYPos, MAX_ZYPHRASE_LEN - inmd->CurZYPos); if (inmd->CurZYNum > 0) { char chtmp = inmd->SavedPYStr[inmd->CurZYNum-1][0]; if (chtmp == 'i' || chtmp == 'u' || chtmp == 'v') { inmd->Keybuf[--inmd->KeybufLen] = '\0'; inmd->ZYbuf[--inmd->ZYbufLen] = '\0'; inmd->CurZYNum--; return 1; /* ignore the key */ } } } if (inmd->CurZYNum > MAX_ZYPHRASE_LEN) { inmd->Keybuf[--inmd->KeybufLen] = '\0'; inmd->ZYbuf[--inmd->ZYbufLen] = '\0'; inmd->CurZYNum--; return 1; } FindMatchCharPhrase(inmd,inmd->CurZYKey + inmd->CurZYPos, inmd->CurZYMask + inmd->CurZYPos, inmd->CurZYNum-inmd->CurZYPos); FillForwardSelection(inmd,0); CreateZYMsg(inmd); RefreshZYInputArea(inmd); return 1; } static int ChangeDisplayPage_Filter(ZhuyinModule *inmd, unsigned char key) { if (index(cur_table->pageprevkey, key)) /* Select Backward */ { if (inmd->SelStartPos > 0) /* not first page */ { if (--CurrentPage < MAX_PAGE_INDEX) FillForwardSelection(inmd, SavedPageIndex[CurrentPage]); else FillBackwardSelection(inmd,inmd->SelStartPos-1); RefreshZYInputArea(inmd); } return 1; } else if (index(cur_table->pagenextkey, key) /* Select Forward */ || (key == ' ' && IsDirectSelect && IS_KB_ZHUYIN(inmd->KeyboardMode)) ) /* we are using Zhuyin */ { if (inmd->SelEndPos < inmd->TotalSelNum-1) /* should we go back to 0 if last page? yes! */ CurrentPage++; else { CurrentPage = 0; inmd->SelEndPos = -1; } FillForwardSelection(inmd,inmd->SelEndPos+1); RefreshZYInputArea(inmd); return 1; } return 0; /* not processed */ } int Zhuyin_HZFilter(int tty_fd, u_char ch) { ZhuyinModule *inmd = (ZhuyinModule *)cur_table->PrivateData; unsigned int SelIdx; char *SelKeyStr; SelKeyStr = ( IsDirectSelect ? cur_table->selkey : cur_table->Shift_SelKey); if ((inmd->TotalSelNum > 0) && (SelIdx = (unsigned int)index(SelKeyStr, ch))) { SelIdx -= (unsigned int)SelKeyStr; /* converted to 0-9 */ if (inmd->CandidateIndex[SelIdx] >= 0 && /* a valid candidate? */ SelectKeyPressed(inmd, SelIdx, tty_fd)) return 1; } if ( IsValidPinyinZhuyinKey( inmd->KeyboardMode, inmd->EnglishPos, inmd->KeybufLen, ch) ) { if (ZhuyinKeyPressed(inmd,ch, tty_fd)) return 1; } if (inmd->KeybufLen == 0) /* Keybuf is the input key buffer no key yet */ return 0; if (ChangeDisplayPage_Filter(inmd, ch)) return 1; /*handled */ switch(ch) { case KEYCODE_ENTER: /* Process ENTER to output original English string (pinyin only) */ if (IS_KB_PINYIN(inmd->KeyboardMode)) { OutputZYBuf(inmd, tty_fd); return 1; } else if (inmd->TotalSelNum > 0 && inmd->CandidateIndex[0] >= 0 ) { return SelectKeyPressed(inmd, 0, tty_fd); } break; case KEYCODE_ESCAPE: /*ESCAPE */ ResetZhuyinInput(inmd); RefreshZYInputArea(inmd); return 1; case KEYCODE_SPACE: /* SPACE */ if (inmd->TotalSelNum == 0) { if (inmd->ZYbufLen == 0 && inmd->HZbufLen > 0) /* output the current selected one */ { OutputHZBuf(inmd, -1, inmd->HZbufLen, tty_fd); return 1; } return 0; /* we don't handle it */ } if (!IsDirectSelect) /* follow Big5 custom, SPACE switch to DirectSelect */ { IsDirectSelect = 1; FillForwardSelection(inmd,inmd->SelStartPos); RefreshZYInputArea(inmd); return 1; } } if ( strchr(cur_table->selfirstkey, ch) && inmd->TotalSelNum > 0 && inmd->CandidateIndex[0] >= 0 ) /* do we need to check IsDirectSelct? */ { return SelectKeyPressed(inmd, 0, tty_fd); } return 0; /* other keys: we didn't handle it */ }