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