1 /**
2 * chewingio.c
3 *
4 * Copyright (c) 1999, 2000, 2001
5 * Lu-chuan Kung and Kang-pen Chen.
6 * All rights reserved.
7 *
8 * Copyright (c) 2004, 2005, 2006
9 * libchewing Core Team. See ChangeLog for details.
10 *
11 * See the file "COPYING" for information on usage and redistribution
12 * of this file.
13 */
14
15 /**
16 * @file chewingio.c
17 * @brief Implement basic I/O routines for Chewing manipulation.
18 */
19
20 #include <string.h>
21 #include <ctype.h>
22
23 #include "chewing-utf8-util.h"
24 #include "chewingio.h"
25 #include "global.h"
26 #include "zuin.h"
27 #include "chewingutil.h"
28 #include "userphrase.h"
29 #include "dict.h"
30 #include "char.h"
31 #include "hash.h"
32 #include "private.h"
33
34 #ifdef ENABLE_DEBUG
35 #include <stdio.h>
36 #include <assert.h>
37 #define FAILSAFE_OUTPUT "/tmp/chewing-debug.out"
38 FILE *fp_g;
39 #endif
40
41 extern int chewing_lifetime;
42
43 void (*TerminateServices[ TerminateServicesNUM ])() = {
44 NULL
45 };
46 static int countTerminateService = 0;
47 static int bTerminateCompleted = 0;
48
49 static char *kb_type_str[] = {
50 "KB_DEFAULT",
51 "KB_HSU",
52 "KB_IBM",
53 "KB_GIN_YIEH",
54 "KB_ET",
55 "KB_ET26",
56 "KB_DVORAK",
57 "KB_DVORAK_HSU",
58 "KB_HANYU_PINYIN"
59 };
60
chewing_KBStr2Num(char str[])61 CHEWING_API int chewing_KBStr2Num( char str[] )
62 {
63 int i;
64
65 for ( i = 0; i < KB_TYPE_NUM; i++) {
66 if ( ! strcmp( str, kb_type_str[ i ] ) )
67 return i;
68 }
69 return KB_DEFAULT;
70 }
71
72 #ifdef ENABLE_DEBUG
TerminateDebug()73 static void TerminateDebug()
74 {
75 DEBUG_OUT( "DEBUG: logging service is about to terminate.\n" );
76 if ( fp_g ) {
77 fclose( fp_g );
78 }
79 }
80 #endif
81
addTerminateService(void (* callback)())82 int addTerminateService( void (*callback)() )
83 {
84 if ( callback ) {
85 int i;
86 for ( i = 0; i < countTerminateService; ++i ) {
87 /* Avoid redundant function pointer */
88 if ( TerminateServices[ i ] == callback )
89 return 1;
90 }
91 TerminateServices[ countTerminateService++ ] = callback;
92 return 0;
93 }
94 return 1;
95 }
96
chewing_new()97 CHEWING_API ChewingContext *chewing_new()
98 {
99 ChewingContext *ctx;
100
101 ChewingData *internal_data = ALC( ChewingData, 1 );
102 ChewingOutput *internal_output = ALC( ChewingOutput, 1 );
103 ctx = ALC( ChewingContext, 1 );
104 ctx->data = internal_data;
105 ctx->output = internal_output;
106 ctx->cand_no = 0;
107
108 /* handle configuration */
109 chewing_Reset( ctx );
110
111 return ctx;
112 }
113
chewing_Init(const char * dataPath,const char * hashPath)114 CHEWING_API int chewing_Init(
115 const char *dataPath,
116 const char *hashPath )
117 {
118 /* initialize Tree, Char, and Dict */
119 /* FIXME: check the validation of dataPath */
120 ReadTree( dataPath );
121 InitChar( dataPath );
122 InitDict( dataPath );
123
124 /* initial Hash */
125 /* FIXME: check the validation of hashPath */
126 ReadHash( hashPath );
127
128 #ifdef ENABLE_DEBUG
129 {
130 char *dbg_path;
131 int failsafe = 1;
132 dbg_path = getenv( "CHEWING_DEBUG" );
133 if ( dbg_path ) {
134 fp_g = fopen( dbg_path, "w+" );
135 if ( fp_g )
136 failsafe = 0;
137 }
138 if ( failsafe == 1 ) {
139 dbg_path = FAILSAFE_OUTPUT;
140 fp_g = fopen( dbg_path, "w+" );
141 if ( ! fp_g ) {
142 fprintf( stderr,
143 "Failed to record debug message in file.\n"
144 "--> Output to stderr\n" );
145 }
146 }
147 /* register debug service */
148 if ( fp_g )
149 addTerminateService( TerminateDebug );
150 }
151 #endif
152 return 0;
153 }
154
chewing_Reset(ChewingContext * ctx)155 CHEWING_API int chewing_Reset( ChewingContext *ctx )
156 {
157 ChewingData *pgdata = ctx->data;
158
159 /* zuinData */
160 memset( &( pgdata->zuinData ), 0, sizeof( ZuinData ) );
161
162
163 /* choiceInfo */
164 memset( &( pgdata->choiceInfo ), 0, sizeof( ChoiceInfo ) );
165
166 pgdata->chiSymbolCursor = 0;
167 pgdata->chiSymbolBufLen = 0;
168 pgdata->nPhoneSeq = 0;
169 pgdata->cursor = 0;
170 memset( pgdata->bUserArrCnnct, 0, sizeof( int ) * ( MAX_PHONE_SEQ_LEN + 1 ) );
171 memset( pgdata->bUserArrBrkpt, 0, sizeof( int ) * ( MAX_PHONE_SEQ_LEN + 1 ) );
172 pgdata->bChiSym = CHINESE_MODE;
173 pgdata->bFullShape = HALFSHAPE_MODE;
174 pgdata->bSelect = 0;
175 pgdata->nSelect = 0;
176 pgdata->PointStart = -1;
177 pgdata->PointEnd = 0;
178 pgdata->phrOut.nNumCut = 0;
179 return 0;
180 }
181
chewing_set_KBType(ChewingContext * ctx,int kbtype)182 CHEWING_API int chewing_set_KBType( ChewingContext *ctx, int kbtype )
183 {
184 ctx->data->zuinData.kbtype = kbtype;
185 return 0;
186 }
187
chewing_Terminate()188 CHEWING_API void chewing_Terminate()
189 {
190 int i;
191
192 /* No terminating services are registered. */
193 if ( bTerminateCompleted || countTerminateService == 0 )
194 return;
195
196 for ( i = 0; i < countTerminateService; i++ ) {
197 if ( TerminateServices[ i ] ) {
198 #ifdef ENABLE_DEBUG
199 /* Can't output to debug file because it's about to close */
200 fprintf( stderr,
201 EMPHASIZE( "Terminating service #%d / %d" ) ".\n",
202 i, countTerminateService );
203 #endif
204 (*TerminateServices[ i ])();
205 }
206 }
207
208 /* XXX: should check if the services are really completed. */
209 bTerminateCompleted = 1;
210 return;
211 }
212
chewing_free(ChewingContext * ctx)213 CHEWING_API void chewing_free( ChewingContext *ctx )
214 {
215 if ( ctx->data )
216 free( ctx->data);
217 if ( ctx->output )
218 free( ctx->output);
219 if ( ctx )
220 free( ctx );
221 return;
222 }
223
chewing_Configure(ChewingContext * ctx,ChewingConfigData * pcd)224 CHEWING_API int chewing_Configure( ChewingContext *ctx, ChewingConfigData *pcd )
225 {
226 ChewingData *pgdata = ctx->data;
227
228 pgdata->config.selectAreaLen = pcd->selectAreaLen;
229 pgdata->config.maxChiSymbolLen = pcd->maxChiSymbolLen;
230 memcpy(
231 pgdata->config.selKey,
232 pcd->selKey,
233 sizeof( pcd->selKey[ 0 ] ) * MAX_SELKEY );
234 pgdata->config.bAddPhraseForward = pcd->bAddPhraseForward;
235 pgdata->config.bSpaceAsSelection = pcd->bSpaceAsSelection;
236 pgdata->config.bEscCleanAllBuf = pcd->bEscCleanAllBuf;
237
238 /* Failback to default value */
239 if ( (pgdata->config.bAddPhraseForward != 0) && (pgdata->config.bAddPhraseForward != 1) )
240 pgdata->config.bAddPhraseForward = 0;
241 if ( (pgdata->config.bSpaceAsSelection != 0) && (pgdata->config.bSpaceAsSelection != 1) )
242 pgdata->config.bSpaceAsSelection = 1;
243 if ( (pgdata->config.bEscCleanAllBuf != 0) && (pgdata->config.bEscCleanAllBuf != 1) )
244 pgdata->config.bEscCleanAllBuf = 0;
245
246 return 0;
247 }
248
chewing_set_ChiEngMode(ChewingContext * ctx,int mode)249 CHEWING_API void chewing_set_ChiEngMode( ChewingContext *ctx, int mode )
250 {
251 ChewingData *pgdata = ctx->data;
252
253 if ( pgdata->bFirstKey == 0 ) {
254 pgdata->bChiSym = mode;
255 pgdata->bCaseChange = ( mode == CHINESE_MODE ? 0 : 1 );
256 pgdata->bFirstKey = 1;
257 }
258 }
259
chewing_get_ChiEngMode(ChewingContext * ctx)260 CHEWING_API int chewing_get_ChiEngMode( ChewingContext *ctx )
261 {
262 return ctx->data->bChiSym;
263 }
264
chewing_set_ShapeMode(ChewingContext * ctx,int mode)265 CHEWING_API void chewing_set_ShapeMode( ChewingContext *ctx, int mode )
266 {
267 ctx->data->bFullShape = (mode == FULLSHAPE_MODE ? 1 : 0);
268 }
269
chewing_get_ShapeMode(ChewingContext * ctx)270 CHEWING_API int chewing_get_ShapeMode( ChewingContext *ctx )
271 {
272 return ctx->data->bFullShape;
273 }
274
CheckAndResetRange(ChewingData * pgdata)275 static void CheckAndResetRange( ChewingData *pgdata )
276 {
277 if ( pgdata->PointStart > -1 ) {
278 pgdata->PointStart = -1;
279 pgdata->PointEnd = 0;
280 }
281 }
282
DoSelect(ChewingData * pgdata,int num)283 static int DoSelect( ChewingData *pgdata, int num )
284 {
285 if ( num >= 0 ) {
286 num += pgdata->choiceInfo.pageNo * pgdata->choiceInfo.nChoicePerPage;
287 /* Note: if num is larger than the total, there will be big troubles. */
288 if ( num < pgdata->choiceInfo.nTotalChoice ) {
289 if ( pgdata->choiceInfo.isSymbol ) {
290 SymbolChoice( pgdata, num );
291 } else {
292 /* change the select interval & selectStr & nSelect */
293 AddSelect( pgdata, num );
294 /* second, call choice module */
295 ChoiceSelect( pgdata, num );
296 /* automatically shift the cursor to next phrase */
297 if ( pgdata->bAutoShiftCur != 0 &&
298 /* if cursor at end of string, do not shift the cursor. */
299 pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen ) {
300 int len = pgdata->availInfo.avail[
301 pgdata->availInfo.currentAvail ].len;
302 pgdata->chiSymbolCursor += len;
303 pgdata->cursor += len;
304 }
305 }
306 return 1;
307 }
308 }
309 return 0;
310 }
311
chewing_handle_Space(ChewingContext * ctx)312 CHEWING_API int chewing_handle_Space( ChewingContext *ctx )
313 {
314 ChewingData *pgdata = ctx->data;
315 ChewingOutput *pgo = ctx->output;
316 int keystrokeRtn = KEYSTROKE_ABSORB;
317 int toSelect = 0;
318 int rtn, key_buf_cursor;
319 int bQuickCommit = 0;
320
321 /* check if Old Chewing style */
322 if ( ! pgdata->config.bSpaceAsSelection ) {
323 return chewing_handle_Default( ctx, ' ' );
324 }
325
326 CheckAndResetRange( pgdata );
327
328 if ( pgdata->bSelect ) {
329 if ( pgdata->choiceInfo.pageNo < ( pgdata->choiceInfo.nPage - 1 ) ) {
330 return chewing_handle_Right( ctx );
331 }
332 }
333
334 if ( ! ChewingIsEntering( pgdata ) ) {
335 if ( pgdata->bFullShape ) {
336 rtn = FullShapeSymbolInput( ' ', pgdata );
337 }
338 else {
339 rtn = SymbolInput( ' ', pgdata );
340 }
341 pgo->commitStr[ 0 ] = pgdata->chiSymbolBuf[ 0 ];
342 pgo->nCommitStr = 1;
343 pgdata->chiSymbolBufLen = 0;
344 pgdata->chiSymbolCursor = 0;
345 keystrokeRtn = KEYSTROKE_COMMIT;
346 }
347 else if ( pgdata->bChiSym != CHINESE_MODE ) {
348 /* see if buffer contains nothing */
349 if ( pgdata->chiSymbolBufLen == 0 ) {
350 bQuickCommit = 1;
351 }
352
353 if ( pgdata->bFullShape ) {
354 rtn = FullShapeSymbolInput( ' ', pgdata );
355 }
356 else {
357 rtn = SymbolInput( ' ', pgdata );
358 }
359
360 keystrokeRtn = KEYSTROKE_ABSORB;
361 if ( rtn == SYMBOL_KEY_ERROR ) {
362 keystrokeRtn = KEYSTROKE_IGNORE;
363 /*
364 * If the key is not a printable symbol,
365 * then it's wrongto commit it.
366 */
367 bQuickCommit = 0;
368 }
369 else {
370 keystrokeRtn = KEYSTROKE_ABSORB;
371 }
372
373 if ( ! bQuickCommit ) {
374 CallPhrasing( pgdata );
375 if( ReleaseChiSymbolBuf( pgdata, pgo ) != 0 )
376 keystrokeRtn = KEYSTROKE_COMMIT;
377 }
378 /* Quick commit */
379 else {
380 DEBUG_OUT(
381 "\t\tQuick commit buf[0]=%c\n",
382 pgdata->chiSymbolBuf[ 0 ].s[ 0 ] );
383 pgo->commitStr[ 0 ] = pgdata->chiSymbolBuf[ 0 ];
384 pgo->nCommitStr = 1;
385 pgdata->chiSymbolBufLen = 0;
386 pgdata->chiSymbolCursor = 0;
387 keystrokeRtn = KEYSTROKE_COMMIT;
388 }
389 }
390 else {
391 rtn = ZuinPhoInput( &( pgdata->zuinData ), ' ' );
392 switch ( rtn ) {
393 case ZUIN_ABSORB:
394 keystrokeRtn = KEYSTROKE_ABSORB;
395 break;
396 case ZUIN_COMMIT:
397 AddChi( pgdata->zuinData.phone, pgdata );
398 CallPhrasing( pgdata );
399 break;
400 case ZUIN_NO_WORD:
401 keystrokeRtn = KEYSTROKE_BELL | KEYSTROKE_ABSORB;
402 break;
403 case ZUIN_KEY_ERROR:
404 case ZUIN_IGNORE:
405 key_buf_cursor = pgdata->chiSymbolCursor;
406 if ( pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen )
407 key_buf_cursor--;
408
409 /* see if to select */
410 if ( ChewingIsChiAt( key_buf_cursor, pgdata ) )
411 toSelect = 1;
412
413 if ( toSelect ) {
414 if ( ! pgdata->bSelect )
415 ChoiceFirstAvail( pgdata );
416 else
417 ChoiceNextAvail( pgdata );
418 }
419 else if ( pgdata->symbolKeyBuf[ key_buf_cursor ] ) {
420 /* Open Symbol Choice List */
421 if( ! pgdata->choiceInfo.isSymbol )
422 OpenSymbolChoice( pgdata );
423 }
424 break;
425 }
426 }
427
428 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
429 return 0;
430 }
431
chewing_handle_Esc(ChewingContext * ctx)432 CHEWING_API int chewing_handle_Esc( ChewingContext *ctx )
433 {
434 ChewingData *pgdata = ctx->data;
435 ChewingOutput *pgo = ctx->output;
436 int keystrokeRtn = KEYSTROKE_ABSORB;
437
438 CheckAndResetRange( pgdata );
439
440 if ( ! ChewingIsEntering( pgdata ) ) {
441 keystrokeRtn = KEYSTROKE_IGNORE;
442 } else if ( pgdata->bSelect ) {
443 if ( pgdata->choiceInfo.isSymbol == 1 ) {
444 ChewingKillChar(
445 pgdata,
446 pgdata->cursor - 1,
447 pgdata->chiSymbolCursor - 1,
448 DECREASE_CURSOR );
449 }
450 ChoiceEndChoice( pgdata );
451 } else if ( ZuinIsEntering( &( pgdata->zuinData ) ) ) {
452 ZuinRemoveAll( &( pgdata->zuinData ) );
453 } else if( pgdata->config.bEscCleanAllBuf ) {
454 CleanAllBuf( pgdata );
455 }
456
457 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
458 return 0;
459 }
460
chewing_handle_Enter(ChewingContext * ctx)461 CHEWING_API int chewing_handle_Enter( ChewingContext *ctx )
462 {
463 ChewingData *pgdata = ctx->data;
464 ChewingOutput *pgo = ctx->output;
465 int nCommitStr = pgdata->chiSymbolBufLen;
466 int keystrokeRtn = KEYSTROKE_ABSORB;
467
468 if ( ! ChewingIsEntering( pgdata ) ) {
469 keystrokeRtn = KEYSTROKE_IGNORE;
470 } else if ( pgdata->bSelect ) {
471 keystrokeRtn = KEYSTROKE_ABSORB | KEYSTROKE_BELL;
472 } else if ( pgdata->PointStart > -1 ) {
473 int buf = pgdata->cursor;
474 int key;
475 if ( pgdata->PointEnd > 0 ) {
476 if ( ! pgdata->config.bAddPhraseForward ) {
477 pgdata->cursor = pgdata->PointStart;
478 key = '0' + pgdata->PointEnd;
479 } else {
480 pgdata->cursor = pgdata->PointStart + pgdata->PointEnd;
481 key = '0' + pgdata->PointEnd;
482 }
483 chewing_handle_CtrlNum( ctx, key );
484 pgdata->cursor = buf;
485 } else if ( pgdata->PointEnd < 0 ) {
486 if ( pgdata->config.bAddPhraseForward )
487 pgdata->cursor = buf - pgdata->PointEnd;
488 key = '0' - pgdata->PointEnd;
489 chewing_handle_CtrlNum( ctx, key );
490 pgdata->cursor = buf;
491 }
492 pgdata->PointStart = -1;
493 pgdata->PointEnd = 0;
494 } else {
495 keystrokeRtn = KEYSTROKE_COMMIT;
496 WriteChiSymbolToBuf( pgo->commitStr, nCommitStr, pgdata );
497 AutoLearnPhrase( pgdata );
498 CleanAllBuf( pgdata );
499 pgo->nCommitStr = nCommitStr;
500 }
501
502 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
503 return 0;
504 }
505
chewing_handle_Del(ChewingContext * ctx)506 CHEWING_API int chewing_handle_Del( ChewingContext *ctx )
507 {
508 ChewingData *pgdata = ctx->data;
509 ChewingOutput *pgo = ctx->output;
510 int keystrokeRtn = KEYSTROKE_ABSORB;
511
512 CheckAndResetRange( pgdata );
513
514 if ( ! ChewingIsEntering( pgdata ) ) {
515 keystrokeRtn = KEYSTROKE_IGNORE;
516 }
517
518 if ( ! pgdata->bSelect ) {
519 if (
520 ! ZuinIsEntering( &( pgdata->zuinData ) ) &&
521 pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen ) {
522 ChewingKillChar(
523 pgdata,
524 pgdata->cursor,
525 pgdata->chiSymbolCursor,
526 NONDECREASE_CURSOR );
527 }
528 CallPhrasing( pgdata );
529 }
530 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
531 return 0;
532 }
533
chewing_handle_Backspace(ChewingContext * ctx)534 CHEWING_API int chewing_handle_Backspace( ChewingContext *ctx )
535 {
536 ChewingData *pgdata = ctx->data;
537 ChewingOutput *pgo = ctx->output;
538 int keystrokeRtn = KEYSTROKE_ABSORB;
539
540 CheckAndResetRange( pgdata );
541
542 if ( ! ChewingIsEntering( pgdata ) ) {
543 keystrokeRtn = KEYSTROKE_IGNORE;
544 }
545
546 if ( ! pgdata->bSelect ) {
547 if ( ZuinIsEntering( &( pgdata->zuinData ) ) ) {
548 ZuinRemoveLast( &( pgdata->zuinData ) );
549 }
550 else if ( pgdata->chiSymbolCursor > 0 ) {
551 ChewingKillChar(
552 pgdata,
553 pgdata->cursor - 1,
554 pgdata->chiSymbolCursor - 1,
555 DECREASE_CURSOR );
556 }
557 CallPhrasing( pgdata );
558 }
559 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
560
561 return 0;
562 }
563
chewing_handle_Up(ChewingContext * ctx)564 CHEWING_API int chewing_handle_Up( ChewingContext *ctx )
565 {
566 ChewingData *pgdata = ctx->data;
567 ChewingOutput *pgo = ctx->output;
568 int keystrokeRtn = KEYSTROKE_ABSORB;
569
570 CheckAndResetRange( pgdata );
571
572 if ( ! ChewingIsEntering( pgdata ) ) {
573 keystrokeRtn = KEYSTROKE_IGNORE;
574 }
575
576 if ( pgdata->bSelect )
577 ChoicePrevAvail( pgdata );
578
579 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
580 return 0;
581 }
582
chewing_handle_Down(ChewingContext * ctx)583 CHEWING_API int chewing_handle_Down( ChewingContext *ctx )
584 {
585 ChewingData *pgdata = ctx->data;
586 ChewingOutput *pgo = ctx->output;
587 int toSelect = 0;
588 int keystrokeRtn = KEYSTROKE_ABSORB;
589 int key_buf_cursor;
590
591 CheckAndResetRange( pgdata );
592
593 if ( ! ChewingIsEntering( pgdata ) ) {
594 keystrokeRtn = KEYSTROKE_IGNORE;
595 }
596
597 key_buf_cursor = pgdata->chiSymbolCursor;
598 if ( pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen )
599 key_buf_cursor--;
600
601 /* see if to select */
602 if ( ChewingIsChiAt( key_buf_cursor, pgdata ) )
603 toSelect = 1;
604
605 if ( toSelect ) {
606 if( ! pgdata->bSelect ) {
607 ChoiceFirstAvail( pgdata );
608 } else {
609 ChoiceNextAvail( pgdata );
610 }
611 }
612 else if ( pgdata->symbolKeyBuf[ key_buf_cursor ] ) {
613 /* Open Symbol Choice List */
614 if ( ! pgdata->choiceInfo.isSymbol )
615 OpenSymbolChoice( pgdata );
616 }
617
618 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
619 return 0;
620 }
621
622 /* Add phrase in Hanin Style */
chewing_handle_ShiftLeft(ChewingContext * ctx)623 CHEWING_API int chewing_handle_ShiftLeft( ChewingContext *ctx )
624 {
625 ChewingData *pgdata = ctx->data;
626 ChewingOutput *pgo = ctx->output;
627 int keystrokeRtn = KEYSTROKE_ABSORB;
628
629 if ( ! ChewingIsEntering( pgdata ) ) {
630 keystrokeRtn = KEYSTROKE_IGNORE;
631 }
632 if ( ! pgdata->bSelect ) {
633 /* PointEnd locates (-9, +9) */
634 if (
635 ! ZuinIsEntering( &( pgdata->zuinData ) ) &&
636 pgdata->chiSymbolCursor > 0 &&
637 pgdata->PointEnd > -9 ) {
638 pgdata->chiSymbolCursor--;
639 if ( pgdata->PointStart == -1 )
640 pgdata->PointStart = pgdata->cursor;
641 if (
642 pgdata->cursor > 0 &&
643 ChewingIsChiAt( pgdata->chiSymbolCursor, pgdata ) ) {
644 pgdata->cursor--;
645 pgdata->PointEnd--;
646 }
647 if ( pgdata->PointEnd == 0 )
648 pgdata->PointStart = -1;
649 }
650 }
651
652 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
653 return 0;
654 }
655
chewing_handle_Left(ChewingContext * ctx)656 CHEWING_API int chewing_handle_Left( ChewingContext *ctx )
657 {
658 ChewingData *pgdata = ctx->data;
659 ChewingOutput *pgo = ctx->output;
660 int keystrokeRtn = KEYSTROKE_ABSORB;
661
662 if ( ! ChewingIsEntering( pgdata ) ) {
663 keystrokeRtn = KEYSTROKE_IGNORE;
664 }
665
666 if ( pgdata->bSelect ) {
667 if ( pgdata->choiceInfo.pageNo > 0 )
668 pgdata->choiceInfo.pageNo--;
669 else
670 pgdata->choiceInfo.pageNo = pgdata->choiceInfo.nPage - 1;
671 }
672 else {
673 if (
674 ! ZuinIsEntering( &( pgdata->zuinData ) ) &&
675 pgdata->chiSymbolCursor > 0 ) {
676 CheckAndResetRange( pgdata );
677 pgdata->chiSymbolCursor--;
678 if (
679 pgdata->cursor > 0 &&
680 ChewingIsChiAt( pgdata->chiSymbolCursor, pgdata ) )
681 pgdata->cursor--;
682 }
683 }
684 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
685 return 0;
686 }
687
688 /* Add phrase in Hanin Style */
chewing_handle_ShiftRight(ChewingContext * ctx)689 CHEWING_API int chewing_handle_ShiftRight( ChewingContext *ctx )
690 {
691 ChewingData *pgdata = ctx->data;
692 ChewingOutput *pgo = ctx->output;
693 int keystrokeRtn = KEYSTROKE_ABSORB;
694
695 if ( ! ChewingIsEntering( pgdata ) ) {
696 keystrokeRtn = KEYSTROKE_IGNORE;
697 }
698
699 if ( ! pgdata->bSelect ) {
700 /* PointEnd locates (-9, +9) */
701 if (
702 ! ZuinIsEntering( &( pgdata->zuinData ) ) &&
703 pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen &&
704 pgdata->PointEnd < 9 ) {
705 if ( pgdata->PointStart == -1 )
706 pgdata->PointStart = pgdata->cursor;
707 if (
708 pgdata->cursor < pgdata->nPhoneSeq &&
709 ChewingIsChiAt( pgdata->chiSymbolCursor, pgdata ) ) {
710 pgdata->cursor++;
711 pgdata->PointEnd++;
712 }
713 pgdata->chiSymbolCursor++;
714 if ( pgdata->PointEnd == 0 )
715 pgdata->PointStart = -1;
716 }
717 }
718
719 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
720 return 0;
721 }
722
chewing_handle_Right(ChewingContext * ctx)723 CHEWING_API int chewing_handle_Right( ChewingContext *ctx )
724 {
725 ChewingData *pgdata = ctx->data;
726 ChewingOutput *pgo = ctx->output;
727 int keystrokeRtn = KEYSTROKE_ABSORB;
728
729 if ( ! ChewingIsEntering( pgdata ) ) {
730 keystrokeRtn = KEYSTROKE_IGNORE;
731 }
732
733 if ( pgdata->bSelect ) {
734 if ( pgdata->choiceInfo.pageNo < pgdata->choiceInfo.nPage - 1)
735 pgdata->choiceInfo.pageNo++;
736 else
737 pgdata->choiceInfo.pageNo = 0;
738 }
739 else {
740 if (
741 ! ZuinIsEntering( &( pgdata->zuinData ) ) &&
742 pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen ) {
743 CheckAndResetRange( pgdata );
744 if (
745 pgdata->cursor < pgdata->nPhoneSeq &&
746 ChewingIsChiAt( pgdata->chiSymbolCursor, pgdata ) )
747 pgdata->cursor++;
748 pgdata->chiSymbolCursor++;
749 }
750 }
751
752 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
753 return 0;
754 }
755
chewing_handle_Tab(ChewingContext * ctx)756 CHEWING_API int chewing_handle_Tab( ChewingContext *ctx )
757 {
758 ChewingData *pgdata = ctx->data;
759 ChewingOutput *pgo = ctx->output;
760 int keystrokeRtn = KEYSTROKE_ABSORB;
761
762 CheckAndResetRange( pgdata );
763
764 if ( ! ChewingIsEntering( pgdata ) ) {
765 keystrokeRtn = KEYSTROKE_IGNORE;
766 }
767
768
769 if ( ! pgdata->bSelect ) {
770 if ( pgdata->chiSymbolCursor == pgdata->chiSymbolBufLen ) {
771 pgdata->phrOut.nNumCut++;
772 }
773 else if ( ChewingIsChiAt( pgdata->chiSymbolCursor - 1, pgdata ) ) {
774 if ( IsPreferIntervalConnted( pgdata->cursor, pgdata) ) {
775 pgdata->bUserArrBrkpt[ pgdata->cursor ] = 1;
776 pgdata->bUserArrCnnct[ pgdata->cursor ] = 0;
777 }
778 else {
779 pgdata->bUserArrBrkpt[ pgdata->cursor ] = 0;
780 pgdata->bUserArrCnnct[ pgdata->cursor ] = 1;
781 }
782 }
783 CallPhrasing( pgdata );
784 }
785 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
786 return 0;
787 }
788
chewing_handle_DblTab(ChewingContext * ctx)789 CHEWING_API int chewing_handle_DblTab( ChewingContext *ctx )
790 {
791 ChewingData *pgdata = ctx->data;
792 ChewingOutput *pgo = ctx->output;
793 int keystrokeRtn = KEYSTROKE_ABSORB;
794
795 CheckAndResetRange( pgdata );
796
797 if ( ! ChewingIsEntering( pgdata ) ) {
798 keystrokeRtn = KEYSTROKE_IGNORE;
799 }
800
801 if ( ! pgdata->bSelect ) {
802 pgdata->bUserArrBrkpt[ pgdata->cursor ] = 0;
803 pgdata->bUserArrCnnct[ pgdata->cursor ] = 0;
804 }
805 CallPhrasing( pgdata );
806
807 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
808 return 0;
809 }
810
811
chewing_handle_Capslock(ChewingContext * ctx)812 CHEWING_API int chewing_handle_Capslock( ChewingContext *ctx )
813 {
814 ChewingData *pgdata = ctx->data;
815 ChewingOutput *pgo = ctx->output;
816
817 pgdata->bChiSym = 1 - pgdata->bChiSym;
818 pgdata->bCaseChange = ( pgdata->bChiSym == CHINESE_MODE ? 0 : 1 );
819 MakeOutputWithRtn( pgo, pgdata, KEYSTROKE_ABSORB );
820 return 0;
821 }
822
chewing_handle_Home(ChewingContext * ctx)823 CHEWING_API int chewing_handle_Home( ChewingContext *ctx )
824 {
825 ChewingData *pgdata = ctx->data;
826 ChewingOutput *pgo = ctx->output;
827 int keystrokeRtn = KEYSTROKE_ABSORB;
828
829 CheckAndResetRange( pgdata );
830
831 if ( ! ChewingIsEntering( pgdata ) ) {
832 keystrokeRtn = KEYSTROKE_IGNORE;
833 }
834 else if ( ! pgdata->bSelect ) {
835 pgdata->chiSymbolCursor = 0;
836 pgdata->cursor = 0;
837 }
838 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
839 return 0;
840 }
841
chewing_handle_End(ChewingContext * ctx)842 CHEWING_API int chewing_handle_End( ChewingContext *ctx )
843 {
844 ChewingData *pgdata = ctx->data;
845 ChewingOutput *pgo = ctx->output;
846 int keystrokeRtn = KEYSTROKE_ABSORB;
847
848 CheckAndResetRange( pgdata );
849
850 if ( ! ChewingIsEntering( pgdata ) ) {
851 keystrokeRtn = KEYSTROKE_IGNORE;
852 }
853 else if ( ! pgdata->bSelect ) {
854 pgdata->chiSymbolCursor = pgdata->chiSymbolBufLen;
855 pgdata->cursor = pgdata->nPhoneSeq;
856 }
857 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
858 return 0;
859 }
860
861 /* Dvorak <-> Qwerty keyboard layout converter */
dvorak_convert(int key)862 static int dvorak_convert( int key )
863 {
864 char dkey[] = {
865 '\'','\"',',','<','.','>','p','P','y','Y','f','F','g','G',
866 'c','C','r','R','l','L','/','?','=','+','\\','|',
867 'a','A','o','O','e','E','u','U','i','I','d','D','h','H',
868 't','T','n','N','s','S','-','_',
869 ';',':','q','Q','j','J','k','K','x','X','b','B','m','M',
870 'w','W','v','V','z','Z'};
871 char qkey[] = {
872 'Q','q','w','W','e','E','r','R','t','T','y','Y','u','U',
873 'i','I','o','O','p','P','[','{',']','}','\\','|',
874 'a','A','s','S','d','D','f','F','g','G','h','H','j','J',
875 'k','K','l','L',';',':','\'','\"',
876 'z','Z','x','X','c','C','v','V','b','B','n','N','m','M',
877 ',','<','.','>','/','?'};
878 int i = 0, Total = 67;
879
880 for ( i = 0; i < Total; i++ ) {
881 if ( key == qkey[ i ] ) {
882 key = dkey[ i ];
883 return key;
884 }
885 }
886 return key;
887 }
888
chewing_handle_Default(ChewingContext * ctx,int key)889 CHEWING_API int chewing_handle_Default( ChewingContext *ctx, int key )
890 {
891 ChewingData *pgdata = ctx->data;
892 ChewingOutput *pgo = ctx->output;
893 int rtn, num;
894 int keystrokeRtn = KEYSTROKE_ABSORB;
895 int bQuickCommit = 0;
896
897 /* Update lifetime */
898 chewing_lifetime++;
899
900 /* Skip the special key */
901 if ( key & 0xFF00 ) {
902 keystrokeRtn = KEYSTROKE_IGNORE;
903 goto End_KeyDefault;
904 }
905
906 /* We ignore non-printable input */
907 if ( ! isprint( key ) )
908 goto End_KeyDefault;
909
910 CheckAndResetRange( pgdata );
911
912 DEBUG_CHECKPOINT();
913 DEBUG_OUT( " key=%d\n", key );
914
915 /* Dvorak Hsu */
916 if ( pgdata->zuinData.kbtype == KB_DVORAK_HSU ) {
917 key = dvorak_convert( key );
918 }
919
920 /* selecting */
921 if ( pgdata->bSelect ) {
922 if ( key == ' ' )
923 return chewing_handle_Right( ctx );
924 /* num starts from 0 */
925 num = CountSelKeyNum( key, pgdata );
926 if ( num >= 0 ) {
927 DoSelect( pgdata, num );
928 goto End_KeyDefault;
929 }
930
931 /* Otherwise, use 'j' and 'k' for paging in selection mode */
932 DEBUG_OUT(
933 "\t\tchecking paging key, got '%c'\n",
934 key );
935 /*
936 switch ( key ) {
937 case 'j':
938 case 'J':
939 if ( pgdata->chiSymbolCursor > 0 ) {
940 if ( ! ChewingIsEntering( pgdata ) ) {
941 keystrokeRtn = KEYSTROKE_IGNORE;
942 }
943 CheckAndResetRange( pgdata );
944 pgdata->chiSymbolCursor--;
945 if ( (pgdata->cursor > 0) &&
946 ChewingIsChiAt(
947 pgdata->chiSymbolCursor,
948 pgdata ) ) {
949 pgdata->cursor--;
950 }
951 ChoiceFirstAvail( pgdata );
952 }
953 goto End_Paging;
954 case 'k':
955 case 'K':
956 if ( pgdata->chiSymbolCursor < pgdata->chiSymbolBufLen ) {
957 if ( ! ChewingIsEntering( pgdata ) ) {
958 keystrokeRtn = KEYSTROKE_IGNORE;
959 }
960 CheckAndResetRange( pgdata );
961 if ( (pgdata->cursor < pgdata->nPhoneSeq) &&
962 ChewingIsChiAt(
963 pgdata->chiSymbolCursor,
964 pgdata ) ) {
965 pgdata->cursor++;
966 }
967 pgdata->chiSymbolCursor++;
968 ChoiceFirstAvail( pgdata );
969 }
970 goto End_Paging;
971 default:
972 break;
973 } */
974 }
975 /* editing */
976 else {
977 if ( pgdata->bChiSym == CHINESE_MODE ) {
978 rtn = ZuinPhoInput( &( pgdata->zuinData ), key );
979 DEBUG_OUT(
980 "\t\tchinese mode key, "
981 "ZuinPhoInput return value = %d\n",
982 rtn );
983 DEBUG_FLUSH;
984
985 if ( rtn == ZUIN_KEY_ERROR )
986 rtn = SpecialSymbolInput( key, pgdata );
987 switch ( rtn ) {
988 case ZUIN_ABSORB:
989 keystrokeRtn = KEYSTROKE_ABSORB;
990 break;
991 case ZUIN_COMMIT:
992 AddChi( pgdata->zuinData.phone, pgdata );
993 break;
994 case ZUIN_NO_WORD:
995 keystrokeRtn = KEYSTROKE_BELL | KEYSTROKE_ABSORB;
996 break;
997 case ZUIN_KEY_ERROR:
998 case ZUIN_IGNORE:
999 DEBUG_OUT(
1000 "\t\tbefore isupper(key),key=%d\n",
1001 key );
1002 /* change upper case into lower case */
1003 if ( isupper( key ) )
1004 key = tolower( key );
1005
1006 DEBUG_OUT(
1007 "\t\tafter isupper(key),key=%d\n",
1008 key );
1009
1010 /* see if buffer contains nothing */
1011 if ( pgdata->chiSymbolBufLen == 0 ) {
1012 bQuickCommit = 1;
1013 }
1014
1015 if ( pgdata->bFullShape ) {
1016 rtn = FullShapeSymbolInput( key, pgdata );
1017 }
1018 else {
1019 rtn = SymbolInput( key, pgdata );
1020 }
1021 if ( pgdata->bFullShape ) {
1022 rtn = FullShapeSymbolInput( key, pgdata );
1023 }
1024 else {
1025 rtn = SymbolInput( key, pgdata );
1026 }
1027 if ( rtn == SYMBOL_KEY_ERROR ) {
1028 keystrokeRtn = KEYSTROKE_IGNORE;
1029 /*
1030 * If the key is not a printable symbol,
1031 * then it's wrongto commit it.
1032 */
1033 bQuickCommit = 0;
1034 } else
1035 keystrokeRtn = KEYSTROKE_ABSORB;
1036
1037 break;
1038
1039 }
1040 }
1041 /* English mode */
1042 else {
1043 /* see if buffer contains nothing */
1044 if ( pgdata->chiSymbolBufLen == 0 ) {
1045 bQuickCommit = 1;
1046 }
1047 if ( pgdata->bFullShape ) {
1048 rtn = FullShapeSymbolInput( key, pgdata );
1049 }
1050 else {
1051 rtn = SymbolInput( key, pgdata );
1052 }
1053
1054 if ( rtn == SYMBOL_KEY_ERROR ) {
1055 keystrokeRtn = KEYSTROKE_IGNORE;
1056 bQuickCommit = 0;
1057 }
1058 }
1059 if ( ! bQuickCommit ) {
1060 CallPhrasing( pgdata );
1061 if( ReleaseChiSymbolBuf( pgdata, pgo ) != 0 )
1062 keystrokeRtn = KEYSTROKE_COMMIT;
1063 }
1064 /* Quick commit */
1065 else {
1066 DEBUG_OUT(
1067 "\t\tQuick commit buf[0]=%c\n",
1068 pgdata->chiSymbolBuf[ 0 ].s[ 0 ] );
1069 pgo->commitStr[ 0 ] = pgdata->chiSymbolBuf[ 0 ];
1070 pgo->nCommitStr = 1;
1071 pgdata->chiSymbolBufLen = 0;
1072 pgdata->chiSymbolCursor = 0;
1073 keystrokeRtn = KEYSTROKE_COMMIT;
1074 }
1075 }
1076
1077 if ( pgdata->phrOut.nNumCut > 0 ) {
1078 int i;
1079 for ( i = 0; i < pgdata->phrOut.nDispInterval; i++ ) {
1080 pgdata->bUserArrBrkpt[ pgdata->phrOut.dispInterval[ i ].from ] = 1;
1081 pgdata->bUserArrBrkpt[ pgdata->phrOut.dispInterval[ i ].to ] = 1;
1082 }
1083 pgdata->phrOut.nNumCut = 0;
1084 }
1085
1086 End_KeyDefault:
1087 CallPhrasing( pgdata );
1088 End_Paging:
1089 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
1090 return 0;
1091 }
1092
chewing_handle_CtrlNum(ChewingContext * ctx,int key)1093 CHEWING_API int chewing_handle_CtrlNum( ChewingContext *ctx, int key )
1094 {
1095 ChewingData *pgdata = ctx->data;
1096 ChewingOutput *pgo = ctx->output;
1097 int keystrokeRtn = KEYSTROKE_ABSORB;
1098 int newPhraseLen;
1099 int i;
1100 uint16 addPhoneSeq[ MAX_PHONE_SEQ_LEN ];
1101 char addWordSeq[ MAX_PHONE_SEQ_LEN * MAX_UTF8_SIZE + 1 ];
1102 int phraseState;
1103
1104 CheckAndResetRange( pgdata );
1105
1106 CallPhrasing( pgdata );
1107 newPhraseLen = key - '0';
1108
1109 if ( ( key == '0' || key == '1') && ! pgdata->bSelect ) {
1110 pgdata->bSelect = 1;
1111 HaninSymbolInput(
1112 &( pgdata->choiceInfo ),
1113 &( pgdata->availInfo ),
1114 pgdata->phoneSeq,
1115 pgdata->config.selectAreaLen );
1116 SemiSymbolInput(pgdata );
1117 CallPhrasing( pgdata );
1118 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
1119 return 0;
1120 }
1121
1122 if ( ! pgdata->config.bAddPhraseForward ) {
1123 if (
1124 newPhraseLen >= 1 &&
1125 pgdata->cursor + newPhraseLen - 1 <= pgdata->nPhoneSeq ) {
1126 if ( NoSymbolBetween(
1127 pgdata,
1128 pgdata->cursor,
1129 pgdata->cursor + newPhraseLen - 1 ) ) {
1130 /* Manually add phrase to the user phrase database. */
1131 memcpy( addPhoneSeq,
1132 &pgdata->phoneSeq[ pgdata->cursor ],
1133 sizeof( uint16 ) * newPhraseLen );
1134 addPhoneSeq[ newPhraseLen ] = 0;
1135 ueStrNCpy( addWordSeq,
1136 ueStrSeek( (char *) &pgdata->phrOut.chiBuf,
1137 pgdata->cursor ),
1138 newPhraseLen, 1);
1139
1140
1141 phraseState = UserUpdatePhrase( addPhoneSeq, addWordSeq );
1142 SetUpdatePhraseMsg(
1143 pgdata,
1144 addWordSeq,
1145 newPhraseLen,
1146 phraseState );
1147
1148 /* Clear the breakpoint between the New Phrase */
1149 for ( i = 1; i < newPhraseLen; i++ )
1150 pgdata->bUserArrBrkpt[ pgdata->cursor + i ] = 0;
1151 }
1152 }
1153 }
1154 else {
1155 if (
1156 newPhraseLen >= 1 &&
1157 pgdata->cursor - newPhraseLen >= 0 ) {
1158 if ( NoSymbolBetween( pgdata,
1159 pgdata->cursor,
1160 pgdata->cursor - newPhraseLen ) ) {
1161 /* Manually add phrase to the user phrase database. */
1162 memcpy( addPhoneSeq,
1163 &pgdata->phoneSeq[ pgdata->cursor - newPhraseLen ],
1164 sizeof( uint16 ) * newPhraseLen );
1165 addPhoneSeq[ newPhraseLen ] = 0;
1166 ueStrNCpy( addWordSeq,
1167 ueStrSeek( (char *) &pgdata->phrOut.chiBuf,
1168 pgdata->cursor - newPhraseLen ),
1169 newPhraseLen, 1);
1170
1171 phraseState = UserUpdatePhrase( addPhoneSeq, addWordSeq );
1172 SetUpdatePhraseMsg(
1173 pgdata,
1174 addWordSeq,
1175 newPhraseLen,
1176 phraseState );
1177
1178 /* Clear the breakpoint between the New Phrase */
1179 for ( i = 1; i < newPhraseLen; i++ )
1180 pgdata->bUserArrBrkpt[ pgdata->cursor - newPhraseLen + i ] = 0;
1181 }
1182 }
1183 }
1184 CallPhrasing( pgdata );
1185 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
1186 MakeOutputAddMsgAndCleanInterval( pgo, pgdata );
1187 return 0;
1188 }
1189
chewing_handle_CtrlOption(ChewingContext * ctx,int key)1190 CHEWING_API int chewing_handle_CtrlOption( ChewingContext *ctx, int key )
1191 {
1192 ChewingData *pgdata = ctx->data;
1193 ChewingOutput *pgo = ctx->output;
1194 int rtn;
1195 int keystrokeRtn = KEYSTROKE_ABSORB;
1196
1197 if ( ! pgdata->bSelect ) {
1198 CheckAndResetRange( pgdata );
1199 rtn = SpecialEtenSymbolInput( key, pgdata );
1200 }
1201 CallPhrasing( pgdata );
1202 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
1203 return 0;
1204 }
1205
chewing_handle_ShiftSpace(ChewingContext * ctx)1206 CHEWING_API int chewing_handle_ShiftSpace( ChewingContext *ctx )
1207 {
1208 ChewingData *pgdata = ctx->data;
1209 ChewingOutput *pgo = ctx->output;
1210 int rtn;
1211 int keystrokeRtn = KEYSTROKE_ABSORB;
1212
1213 if ( ! pgdata->bSelect ) {
1214 CheckAndResetRange( pgdata );
1215 rtn = FullShapeSymbolInput( ' ', pgdata );
1216 }
1217 CallPhrasing( pgdata );
1218 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
1219 return 0;
1220 }
1221
chewing_handle_Numlock(ChewingContext * ctx,int key)1222 CHEWING_API int chewing_handle_Numlock( ChewingContext *ctx, int key )
1223 {
1224 ChewingData *pgdata = ctx->data;
1225 ChewingOutput *pgo = ctx->output;
1226 int rtn, QuickCommit = 0;
1227 int keystrokeRtn = KEYSTROKE_ABSORB;
1228
1229 if ( ! pgdata->bSelect ) {
1230 /* If we're not selecting words, we should send out numeric
1231 * characters at once.
1232 */
1233 if ( pgdata->chiSymbolBufLen == 0 ) {
1234 QuickCommit = 1;
1235 }
1236 rtn = SymbolInput( key, pgdata );
1237 /* copied from chewing_handle_Default */
1238 if ( rtn == SYMBOL_KEY_ERROR ) {
1239 keystrokeRtn = KEYSTROKE_IGNORE ;
1240 } else if ( QuickCommit ) {
1241 pgo->commitStr[ 0 ] = pgdata->chiSymbolBuf[ 0 ];
1242 pgo->nCommitStr = 1;
1243 pgdata->chiSymbolBufLen = 0;
1244 pgdata->chiSymbolCursor = 0;
1245 keystrokeRtn = KEYSTROKE_COMMIT;
1246 }
1247 else /* Not quick commit */
1248 {
1249 CallPhrasing( pgdata );
1250 if( ReleaseChiSymbolBuf( pgdata, pgo ) != 0 )
1251 keystrokeRtn = KEYSTROKE_COMMIT;
1252 }
1253 } else {
1254 /* Otherwise, if we are selecting words, we use numeric keys
1255 * as selkey
1256 * and submit the words.
1257 */
1258 int num = -1;
1259 if ( key > '0' && key < '9' )
1260 num = key - '1';
1261 else if ( key == '0' )
1262 num = 9;
1263 DoSelect( pgdata, num );
1264 }
1265 CallPhrasing( pgdata );
1266 MakeOutputWithRtn( pgo, pgdata, keystrokeRtn );
1267 return 0;
1268 }
1269
1270 /* Local Variables: */
1271 /* c-indentation-style: linux */
1272 /* indent-tabs-mode: t */
1273 /* End: */
1274