|
1 /* |
|
2 * Copyright (c) 2002 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "CMIDKeyTranslator.h" |
|
20 #include <jdebug.h> |
|
21 |
|
22 // Uncomment for loads of debug output |
|
23 //#define DEBUG_KEY_TRANSLATOR |
|
24 |
|
25 #ifndef DEBUG_KEY_TRANSLATOR |
|
26 |
|
27 #ifdef DEBUG_STR |
|
28 #undef DEBUG_STR |
|
29 #define DEBUG_STR(msg, str) |
|
30 #endif // def DEBUG_STR |
|
31 |
|
32 #ifdef DEBUG_INT |
|
33 #undef DEBUG_INT |
|
34 #define DEBUG_INT(msg,val) |
|
35 #endif // def DEBUG_INT |
|
36 |
|
37 #ifdef DEBUG_INT2 |
|
38 #undef DEBUG_INT2 |
|
39 #define DEBUG_INT2(msg,val1,val2) |
|
40 #endif // def DEBUG_INT2 |
|
41 |
|
42 #endif // def DEBUG_KEY_TRANSLATOR |
|
43 |
|
44 #if defined(__WINS__) // from w32cmd.h |
|
45 // Under WINS character code is passed in as HIWORD of the scan code, |
|
46 // and will need to be removed in some situations |
|
47 #define SCANCODE(x) ((x) & 0xFFFF) |
|
48 #else |
|
49 #define SCANCODE(x) (x) |
|
50 #endif |
|
51 |
|
52 const TInt KMIDKeyEdit = -50; |
|
53 const TInt KMIDKeySelect = -5; |
|
54 |
|
55 inline TBool NonUnicodeKeyCode(TInt aKeyCode) |
|
56 { |
|
57 return (aKeyCode > CMIDKeyTranslator::KMaxUnicodeKeyCode) || (aKeyCode <= 0); |
|
58 } |
|
59 |
|
60 // |
|
61 // Record of translated keys. |
|
62 // |
|
63 |
|
64 CMIDKeyTranslator::CMIDKeyTranslator(MMIDEnv& aEnv) |
|
65 : iEnv(aEnv) |
|
66 , iKeyList(4) // granularity |
|
67 , iS60SelectionKeyCompatibility(EFalse) |
|
68 { |
|
69 } |
|
70 |
|
71 void CMIDKeyTranslator::ConstructL() |
|
72 { |
|
73 |
|
74 iS60SelectionKeyCompatibility = iEnv.MidletAttributeIsSetToVal( |
|
75 LcduiMidletAttributes::KAttribS60SelectionKeyCompatibility, |
|
76 LcduiMidletAttributeValues::KTrueValue |
|
77 ); |
|
78 |
|
79 } |
|
80 |
|
81 CMIDKeyTranslator::~CMIDKeyTranslator() |
|
82 { |
|
83 iKeyList.Reset(); |
|
84 } |
|
85 |
|
86 void CMIDKeyTranslator::SetUtils(MMIDUtils* aUtils) |
|
87 { |
|
88 iUtils = aUtils; |
|
89 } |
|
90 |
|
91 void CMIDKeyTranslator::Reset() |
|
92 { |
|
93 iKeyList.Reset(); |
|
94 } |
|
95 |
|
96 /** |
|
97 DEBUG |
|
98 */ |
|
99 TText EventCode(TEventCode aType) |
|
100 { |
|
101 switch (aType) |
|
102 { |
|
103 case EEventKeyDown: |
|
104 return L'D'; |
|
105 case EEventKey: |
|
106 return L'C'; |
|
107 case EEventKeyUp: |
|
108 return L'U'; |
|
109 default: |
|
110 return L'?'; |
|
111 } |
|
112 } |
|
113 |
|
114 TBool CMIDKeyTranslator::TranslateL(TMIDKeyEvent& aMIDPKeyEvent, const TKeyEvent& aWsEvent, TEventCode aEventCode) |
|
115 { |
|
116 #ifdef DEBUG_KEY_TRANSLATOR |
|
117 TBuf<64> buf; |
|
118 buf.Format(_L("[%C,%d,%d,%d]"), EventCode(aEventCode), SCANCODE(aWsEvent.iScanCode), aWsEvent.iCode, aWsEvent.iRepeats); |
|
119 DEBUG_STR("TKeyEvent: %S", buf); |
|
120 #endif |
|
121 ASSERT(iUtils); |
|
122 |
|
123 // check if this key should be sent to java |
|
124 if (!iUtils->IsJavaKey(SCANCODE(aWsEvent.iScanCode))) |
|
125 { |
|
126 DEBUG_INT("Non-Java scancode rejected: %d", SCANCODE(aWsEvent.iScanCode)); |
|
127 return EFalse; |
|
128 } |
|
129 |
|
130 aMIDPKeyEvent.iEvents =0; |
|
131 aMIDPKeyEvent.iKeyCode=0; |
|
132 aMIDPKeyEvent.iRepeats=0; |
|
133 |
|
134 // check if key is already pressed |
|
135 TInt index = FindKeyRecord(SCANCODE(aWsEvent.iScanCode)); |
|
136 if (index == KErrNotFound) |
|
137 { |
|
138 // new key press |
|
139 TKeyRecord record; |
|
140 record.iScanCode = SCANCODE(aWsEvent.iScanCode); |
|
141 record.iIsSpecialKey = EFalse; |
|
142 //store the value for some special key (non-unicode) |
|
143 TInt mappedCode = Map(record.iScanCode, aWsEvent.iCode); |
|
144 if (aWsEvent.iCode && |
|
145 (aWsEvent.iScanCode == EStdKeyLeftShift || |
|
146 aWsEvent.iScanCode == EStdKeyLeftFunc || |
|
147 aWsEvent.iScanCode == EStdKeySpace)) |
|
148 { |
|
149 //If iCode has some non-zero value, the key was already mapped |
|
150 //via PTIEngine. Key code value is simply copied to record. |
|
151 //This step is because in Half-QWERTY keyboard layout, some keys |
|
152 //can have additionl characters. |
|
153 //E.g. Chr key on half-QWERTY keyboard has '*'. |
|
154 record.iKeyCode = aWsEvent.iCode; |
|
155 if (mappedCode == KMIDKeyEdit) |
|
156 { |
|
157 //Some special keys (in different keyborad layouts) have |
|
158 //additional key value. So we need to store information that |
|
159 //special key was pressed. |
|
160 record.iIsSpecialKey = ETrue; |
|
161 } |
|
162 } |
|
163 else |
|
164 { |
|
165 //If iCode is 0, this means, that special key was pressed |
|
166 //(e.g Fn or Shift) and it doesn't have any additional character |
|
167 //(so PTIEngine didn't do any mapping) |
|
168 record.iKeyCode = mappedCode; |
|
169 } |
|
170 record.iState = TKeyRecord::EStateInitial; |
|
171 User::LeaveIfError(iKeyList.Append(record)); |
|
172 index = iKeyList.Count()-1; |
|
173 } |
|
174 TKeyRecord& record = iKeyList[index]; |
|
175 |
|
176 if (record.iKeyCode == 0 && aEventCode == EEventKey) |
|
177 { |
|
178 record.iKeyCode = Map(record.iScanCode, aWsEvent.iCode); |
|
179 } |
|
180 |
|
181 // transition to next state... |
|
182 record.Transition(aEventCode, aMIDPKeyEvent, aWsEvent.iRepeats, iS60SelectionKeyCompatibility); |
|
183 |
|
184 if (record.iState == TKeyRecord::EStateInitial) |
|
185 { |
|
186 iKeyList.Remove(index); |
|
187 } |
|
188 |
|
189 return aMIDPKeyEvent.iEvents; // non-zero == true post something |
|
190 |
|
191 } |
|
192 |
|
193 TInt CMIDKeyTranslator::FindKeyRecord(TInt aScanCode) const |
|
194 { |
|
195 for (TInt ii=iKeyList.Count(); ii--;) |
|
196 { |
|
197 if (iKeyList[ii].iScanCode==aScanCode) |
|
198 return ii; |
|
199 } |
|
200 return KErrNotFound; |
|
201 } |
|
202 |
|
203 |
|
204 TInt CMIDKeyTranslator::Map(TInt aScanCode, TInt aCode) |
|
205 { |
|
206 // |
|
207 // Fitler out some non-unicode keys - only rejects obviously |
|
208 // out of bounds unicode values. Any keys that should be |
|
209 // mapped to Java will be mapped in MapNonUnicodeKeyCode |
|
210 // below. |
|
211 // |
|
212 if (NonUnicodeKeyCode(aCode)) |
|
213 { |
|
214 aCode = KInvalidKeyCode; |
|
215 } |
|
216 |
|
217 // |
|
218 // At this point keyCode can be any valid EPOC key code, some of which |
|
219 // will not be UNICODE values. To comply with the MIDP spec, all non |
|
220 // UNICODE keys must be mapped to negative values. |
|
221 // |
|
222 // Any keys for which we do not have a valid MIDP keyCode, will be |
|
223 // mapped to KInvalidKeyCode. |
|
224 // |
|
225 // We do the mapping here regardless of the return value of |
|
226 // NonUnicodeKeyCode() as that function may not remove all non-unicode |
|
227 // keycodes. |
|
228 // |
|
229 // The mapping is done from the scancode. |
|
230 // |
|
231 TInt specialKey = MapNonUnicodeKeyCode(aScanCode); |
|
232 if (KInvalidKeyCode != specialKey) |
|
233 { |
|
234 DEBUG_INT2("mapping non-unicode keycode: %d to %d", aCode, specialKey); |
|
235 aCode = specialKey; |
|
236 } |
|
237 |
|
238 DEBUG_INT("keycode = %d\n", aCode); |
|
239 |
|
240 return aCode; |
|
241 } |
|
242 |
|
243 TInt CMIDKeyTranslator::MapNonUnicodeKeyCode(TInt aScanCode) |
|
244 { |
|
245 ASSERT(iUtils); |
|
246 return iUtils->MapNonUnicodeKey(aScanCode); |
|
247 } |
|
248 |
|
249 TBool CMIDKeyTranslator::PostKeyEvent(MMIDComponent& aComponent, const TMIDKeyEvent& aEvent) |
|
250 { |
|
251 ASSERT(aEvent.iKeyCode); |
|
252 TBool posted(EFalse); |
|
253 |
|
254 if (aEvent.iEvents & TMIDKeyEvent::EPressed) |
|
255 { |
|
256 PostKeyEvent(aComponent, EKeyPressed, aEvent.iKeyCode, 0); |
|
257 posted=ETrue; |
|
258 } |
|
259 |
|
260 if (aEvent.iEvents & TMIDKeyEvent::ERepeated) |
|
261 { |
|
262 PostKeyEvent(aComponent, EKeyRepeated, aEvent.iKeyCode, aEvent.iRepeats); |
|
263 posted=ETrue; |
|
264 } |
|
265 |
|
266 if (aEvent.iEvents & TMIDKeyEvent::EReleased) |
|
267 { |
|
268 PostKeyEvent(aComponent, EKeyReleased, aEvent.iKeyCode, 0); |
|
269 posted=ETrue; |
|
270 } |
|
271 |
|
272 return posted; |
|
273 } |
|
274 |
|
275 void CMIDKeyTranslator::PostKeyEvent(MMIDComponent& aComponent, TEventType aType, TInt aKeyCode, TInt aRepeats) |
|
276 { |
|
277 ASSERT(aKeyCode != KInvalidKeyCode); |
|
278 DEBUG_INT2("PostKeyEvent[%d] (%d)", aType, aKeyCode); |
|
279 (void) iEnv.PostJavaEvent( |
|
280 aComponent, |
|
281 aComponent.Type() == MMIDComponent::ECustomItem ? EItem : EDisplayable, |
|
282 aType, |
|
283 aKeyCode, |
|
284 aRepeats, |
|
285 0 |
|
286 ); |
|
287 } |
|
288 |
|
289 void TKeyRecord::Transition(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats, TBool aS60SelectionKeyCompatibility) |
|
290 { |
|
291 // transition to next state... |
|
292 switch (iState) |
|
293 { |
|
294 case EStateInitial: |
|
295 StateInitial(aEventCode, aMIDPKeyEvent, aRepeats, aS60SelectionKeyCompatibility); |
|
296 break; |
|
297 |
|
298 case EStateDown: |
|
299 StateDown(aEventCode, aMIDPKeyEvent,aRepeats); |
|
300 break; |
|
301 |
|
302 case EStateDownTranslated: |
|
303 StateDownTranslated(aEventCode, aMIDPKeyEvent,aRepeats); |
|
304 break; |
|
305 |
|
306 case EStateKeyTranslated: |
|
307 StateKeyTranslated(aEventCode, aMIDPKeyEvent,aRepeats); |
|
308 break; |
|
309 |
|
310 case EStateKeyRepeated: |
|
311 StateKeyRepeated(aEventCode, aMIDPKeyEvent,aRepeats); |
|
312 break; |
|
313 |
|
314 case EStateDownRepeated: |
|
315 StateDownRepeated(aEventCode, aMIDPKeyEvent,aRepeats); |
|
316 break; |
|
317 } |
|
318 } |
|
319 |
|
320 void TKeyRecord::StateInitial(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats, TBool aS60SelectionKeyCompatibility) |
|
321 { |
|
322 switch (aEventCode) |
|
323 { |
|
324 case EEventKeyDown: |
|
325 // When canvas is not in full-screen mode then EEventKey event for Edit and |
|
326 // Select key-presses is catched in CEikCba::OfferKeyEventL. This function |
|
327 // returns EKeyWasConsumed and CCoeControlStack doesn't send EEvent to CMIDCanvas. |
|
328 // Therefore EEvent key never reach to CMIDCanvas::OfferKeyEventL. |
|
329 // This is why we use here EEventKeyDown for sending the key-press |
|
330 // events for Edit and Select to MIDlet. |
|
331 // |
|
332 // Special keys, like Edit key, can have also additional characters. |
|
333 // If special key carries the additional character (mapped by PTIEngine) |
|
334 // and it should be sent to Java, iIsSpecialKey is true and record |
|
335 // switches to EStateDownTranslated. This will guarantee, that event |
|
336 // will be sent to MIDlet. |
|
337 if (iKeyCode == KMIDKeyEdit || |
|
338 (iKeyCode == KMIDKeySelect && aS60SelectionKeyCompatibility) || |
|
339 (iIsSpecialKey) |
|
340 ) |
|
341 { |
|
342 // have a -ve code, can post KeyPressed now |
|
343 KeyPressed(aMIDPKeyEvent); |
|
344 iState = EStateDownTranslated; |
|
345 } |
|
346 else |
|
347 { |
|
348 // must delay KeyPressed until have keyCode |
|
349 iState = EStateDown; |
|
350 } |
|
351 break; |
|
352 |
|
353 case EEventKey: |
|
354 // FEP |
|
355 KeyPressed(aMIDPKeyEvent); |
|
356 if (aRepeats > 0) |
|
357 { |
|
358 KeyRepeated(aMIDPKeyEvent,aRepeats); |
|
359 } |
|
360 KeyReleased(aMIDPKeyEvent); |
|
361 iState = EStateInitial; |
|
362 break; |
|
363 |
|
364 case EEventKeyUp: |
|
365 iState = EStateInitial; |
|
366 break; |
|
367 |
|
368 default: |
|
369 ASSERT(EFalse); |
|
370 } |
|
371 } |
|
372 |
|
373 void TKeyRecord::StateDown(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats) |
|
374 { |
|
375 // key code was previously NOT translated. |
|
376 |
|
377 switch (aEventCode) |
|
378 { |
|
379 case EEventKeyDown: |
|
380 // no-op |
|
381 DEBUG_INT("THIS SCANCODE REQUIRES MAPPING TO NEGATIVE KEYCODE: %d", iScanCode); |
|
382 break; |
|
383 |
|
384 case EEventKey: |
|
385 // now we have key code, can send key pressed |
|
386 KeyPressed(aMIDPKeyEvent); |
|
387 if (aRepeats > 0) |
|
388 { |
|
389 KeyRepeated(aMIDPKeyEvent,aRepeats); |
|
390 } |
|
391 iState = EStateDownTranslated; |
|
392 break; |
|
393 |
|
394 case EEventKeyUp: |
|
395 iState = EStateInitial; |
|
396 DEBUG_INT("THIS SCANCODE REQUIRES MAPPING TO NEGATIVE KEYCODE: %d", iScanCode); |
|
397 break; |
|
398 |
|
399 default: |
|
400 ASSERT(EFalse); |
|
401 } |
|
402 } |
|
403 |
|
404 void TKeyRecord::StateDownTranslated(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats) |
|
405 { |
|
406 |
|
407 switch (aEventCode) |
|
408 { |
|
409 case EEventKeyDown: |
|
410 KeyRepeated(aMIDPKeyEvent,1); |
|
411 iState = EStateDownRepeated; |
|
412 break; |
|
413 |
|
414 case EEventKey: |
|
415 if (aRepeats > 0) |
|
416 { |
|
417 KeyRepeated(aMIDPKeyEvent,aRepeats); |
|
418 iState= EStateKeyRepeated; |
|
419 } |
|
420 else |
|
421 { |
|
422 iState = EStateKeyTranslated; |
|
423 } |
|
424 break; |
|
425 |
|
426 case EEventKeyUp: |
|
427 KeyReleased(aMIDPKeyEvent); |
|
428 iState = EStateInitial; |
|
429 break; |
|
430 |
|
431 default: |
|
432 ASSERT(EFalse); |
|
433 } |
|
434 } |
|
435 |
|
436 void TKeyRecord::StateKeyTranslated(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats) |
|
437 { |
|
438 switch (aEventCode) |
|
439 { |
|
440 case EEventKeyDown: |
|
441 // no-op |
|
442 break; |
|
443 case EEventKey: |
|
444 KeyRepeated(aMIDPKeyEvent, aRepeats ? aRepeats : 1); |
|
445 iState=EStateKeyRepeated; |
|
446 break; |
|
447 case EEventKeyUp: |
|
448 KeyReleased(aMIDPKeyEvent); |
|
449 iState = EStateInitial; |
|
450 break; |
|
451 default: |
|
452 ASSERT(EFalse); |
|
453 } |
|
454 } |
|
455 |
|
456 void TKeyRecord::StateKeyRepeated(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats) |
|
457 { |
|
458 switch (aEventCode) |
|
459 { |
|
460 case EEventKeyDown: |
|
461 // no-op |
|
462 break; |
|
463 case EEventKey: |
|
464 KeyRepeated(aMIDPKeyEvent, aRepeats ? aRepeats : 1); |
|
465 iState=EStateKeyRepeated; |
|
466 break; |
|
467 case EEventKeyUp: |
|
468 KeyReleased(aMIDPKeyEvent); |
|
469 iState = EStateInitial; |
|
470 break; |
|
471 default: |
|
472 ASSERT(EFalse); |
|
473 } |
|
474 } |
|
475 |
|
476 void TKeyRecord::StateDownRepeated(TEventCode aEventCode, TMIDKeyEvent& aMIDPKeyEvent, TInt /*aRepeats*/) |
|
477 { |
|
478 switch (aEventCode) |
|
479 { |
|
480 case EEventKeyDown: |
|
481 KeyRepeated(aMIDPKeyEvent,1); |
|
482 break; |
|
483 case EEventKey: |
|
484 iState = EStateDownTranslated; |
|
485 break; |
|
486 case EEventKeyUp: |
|
487 KeyReleased(aMIDPKeyEvent); |
|
488 iState = EStateInitial; |
|
489 break; |
|
490 default: |
|
491 ASSERT(EFalse); |
|
492 } |
|
493 } |
|
494 |
|
495 |
|
496 void TKeyRecord::SetKeyCode(TMIDKeyEvent& aMIDPKeyEvent) |
|
497 { |
|
498 ASSERT(iKeyCode); |
|
499 if (0 == aMIDPKeyEvent.iKeyCode) |
|
500 { |
|
501 aMIDPKeyEvent.iKeyCode = iKeyCode; |
|
502 } |
|
503 ASSERT(aMIDPKeyEvent.iKeyCode == iKeyCode); |
|
504 } |
|
505 |
|
506 void TKeyRecord::KeyPressed(TMIDKeyEvent& aMIDPKeyEvent) |
|
507 { |
|
508 SetKeyCode(aMIDPKeyEvent); |
|
509 aMIDPKeyEvent.iEvents |= TMIDKeyEvent::EPressed; |
|
510 aMIDPKeyEvent.iRepeats = 0; |
|
511 } |
|
512 |
|
513 void TKeyRecord::KeyRepeated(TMIDKeyEvent& aMIDPKeyEvent, TInt aRepeats) |
|
514 { |
|
515 SetKeyCode(aMIDPKeyEvent); |
|
516 aMIDPKeyEvent.iEvents |= TMIDKeyEvent::ERepeated; |
|
517 aMIDPKeyEvent.iRepeats = aRepeats; |
|
518 } |
|
519 |
|
520 void TKeyRecord::KeyReleased(TMIDKeyEvent& aMIDPKeyEvent) |
|
521 { |
|
522 SetKeyCode(aMIDPKeyEvent); |
|
523 aMIDPKeyEvent.iEvents |= TMIDKeyEvent::EReleased; |
|
524 aMIDPKeyEvent.iRepeats = 0; |
|
525 } |
|
526 |