|
1 /* |
|
2 * Copyright (c) 2009, 2010 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 package javax.microedition.lcdui; |
|
18 |
|
19 import java.util.Enumeration; |
|
20 import java.util.Vector; |
|
21 import java.util.Timer; |
|
22 import java.util.TimerTask; |
|
23 import javax.microedition.lcdui.game.GameCanvas; |
|
24 import javax.microedition.lcdui.EventDispatcher.LCDUIEvent; |
|
25 import org.eclipse.ercp.swt.mobile.MobileShell; |
|
26 import org.eclipse.swt.SWT; |
|
27 import org.eclipse.swt.events.*; |
|
28 import org.eclipse.swt.widgets.*; |
|
29 import org.eclipse.swt.widgets.Display; |
|
30 import org.eclipse.swt.graphics.*; |
|
31 import org.eclipse.swt.internal.qt.graphics.WindowSurface; |
|
32 import org.eclipse.swt.internal.extension.MobileShellExtension; |
|
33 import org.eclipse.swt.internal.qt.SymbianWindowVisibilityListener; |
|
34 |
|
35 |
|
36 /** |
|
37 * The abstract <code>Canvas</code> class is designed to handle low-level |
|
38 * graphical operations as well as low-level key and pointer events. Canvas is |
|
39 * derived from <code>Displayable</code> so the application can switch between |
|
40 * different <code>Canvases</code> together with other |
|
41 * <code>Displayables</code>. |
|
42 * <p> |
|
43 * <b>Canvas</b> the abstract method <code>paint(Graphics g)</code> designed |
|
44 * to be implemented by the developer. |
|
45 */ |
|
46 public abstract class Canvas extends Displayable |
|
47 { |
|
48 |
|
49 /** |
|
50 * Constant for <code>UP</code> game action. |
|
51 */ |
|
52 public static final int UP = 1; |
|
53 |
|
54 /** |
|
55 * Constant for <code>DOWN</code> game action. |
|
56 */ |
|
57 public static final int DOWN = 6; |
|
58 |
|
59 /** |
|
60 * Constant for <code>LEFT</code> game action. |
|
61 */ |
|
62 public static final int LEFT = 2; |
|
63 |
|
64 /** |
|
65 * Constant for <code>RIGHT</code> game action. |
|
66 */ |
|
67 public static final int RIGHT = 5; |
|
68 |
|
69 /** |
|
70 * Constant for <code>FIRE</code> game action. |
|
71 */ |
|
72 public static final int FIRE = 8; |
|
73 |
|
74 /** |
|
75 * Constant for general "<code>A</code>" game action. |
|
76 */ |
|
77 public static final int GAME_A = 9; |
|
78 |
|
79 /** |
|
80 * Constant for general "<code>B</code>" game action. |
|
81 */ |
|
82 public static final int GAME_B = 10; |
|
83 |
|
84 /** |
|
85 * Constant for general "<code>C</code>" game action. |
|
86 */ |
|
87 public static final int GAME_C = 11; |
|
88 |
|
89 /** |
|
90 * Constant for general "<code>D</code>" game action. |
|
91 */ |
|
92 public static final int GAME_D = 12; |
|
93 |
|
94 /** |
|
95 * Key code for <code>0</code> key. |
|
96 */ |
|
97 public static final int KEY_NUM0 = 48; |
|
98 |
|
99 /** |
|
100 * Key code for <code>1</code> key. |
|
101 */ |
|
102 public static final int KEY_NUM1 = 49; |
|
103 |
|
104 /** |
|
105 * Key code for <code>2</code> key. |
|
106 */ |
|
107 public static final int KEY_NUM2 = 50; |
|
108 |
|
109 /** |
|
110 * Key code for <code>3</code> key. |
|
111 */ |
|
112 public static final int KEY_NUM3 = 51; |
|
113 |
|
114 /** |
|
115 * Key code for <code>4</code> key. |
|
116 */ |
|
117 public static final int KEY_NUM4 = 52; |
|
118 |
|
119 /** |
|
120 * Key code for <code>5</code> key. |
|
121 */ |
|
122 public static final int KEY_NUM5 = 53; |
|
123 |
|
124 /** |
|
125 * Key code for <code>6</code> key. |
|
126 */ |
|
127 public static final int KEY_NUM6 = 54; |
|
128 |
|
129 /** |
|
130 * Key code for <code>7</code> key. |
|
131 */ |
|
132 public static final int KEY_NUM7 = 55; |
|
133 |
|
134 /** |
|
135 * Key code for <code>8</code> key. |
|
136 */ |
|
137 public static final int KEY_NUM8 = 56; |
|
138 |
|
139 /** |
|
140 * Key code for <code>9</code> key. |
|
141 */ |
|
142 public static final int KEY_NUM9 = 57; |
|
143 |
|
144 /** |
|
145 * Key code for <code>*</code> key. |
|
146 */ |
|
147 public static final int KEY_STAR = 42; |
|
148 |
|
149 /** |
|
150 * Key code for <code>#</code> key. |
|
151 */ |
|
152 public static final int KEY_POUND = 35; |
|
153 |
|
154 |
|
155 private static final int GAME_CANVAS = 1; |
|
156 private static final int NO_BACKGROUND = 1 << 1; |
|
157 private static final int FULLSCREEN_MODE = 1 << 2; |
|
158 |
|
159 private static final int DISABLE_TAPDETECTION = 1 << 3; |
|
160 private static final int SUPPRESS_GAMEKEYS = 1 << 4; |
|
161 private static final int SUPPRESS_DRAGEVENT = 1 << 5; |
|
162 private static final int CLEANUP_NEEDED = 1 << 6; |
|
163 private static final int REPAINT_PENDING = 1 << 7; |
|
164 private static final int SELECTIONKEY_COMPATIBILITY = 1 << 8; |
|
165 |
|
166 private static final int CURRENTLY_VISIBLE = 1 << 9; |
|
167 |
|
168 |
|
169 // Listeners for various events. |
|
170 private org.eclipse.swt.events.PaintListener paintListener = |
|
171 new CanvasShellPaintListener(); |
|
172 |
|
173 private CanvasShellMouseListener mouseListener = |
|
174 new CanvasShellMouseListener(); |
|
175 |
|
176 private CanvasShellVisibilityListener shellVisibilityListener = |
|
177 new CanvasShellVisibilityListener(); |
|
178 |
|
179 // Canvas Graphics object passed to paint(Graphics g) |
|
180 private Graphics canvasGraphics; |
|
181 |
|
182 // Graphics object for transferring return values |
|
183 // from UI thread |
|
184 private Graphics tempGraphics; |
|
185 |
|
186 // Graphics command buffer for this instance |
|
187 Buffer graphicsBuffer; |
|
188 |
|
189 //On Screen Keypad |
|
190 //private Composite keypadComposite; |
|
191 private CanvasKeypad onScreenkeypad; |
|
192 private static CanvasKeypad sharedKeypad; |
|
193 |
|
194 // Vector of flags that a certain key was pressed but was not released. |
|
195 // Used to implement keyRepeated since eSWT does not support |
|
196 // key repeat events. |
|
197 private Vector keysPressed; |
|
198 private int gameKeyState; |
|
199 |
|
200 private static int objectCount; |
|
201 private static Shell sharedShell; |
|
202 private Shell mShell; |
|
203 private Composite canvasComp; |
|
204 private Label tickerLabel; |
|
205 |
|
206 private int mode; |
|
207 private Object modeLock; |
|
208 private Object cleanupLock; |
|
209 private Object repaintLock; |
|
210 private Object flushLock; |
|
211 |
|
212 private Timer timer = new Timer(); |
|
213 private CanvasTimerTask timerTask; |
|
214 |
|
215 private static final int DEFAULT_TIMEOUT = 500; |
|
216 private static final int DEFAULT_TWIPS = 200; |
|
217 |
|
218 private int pointerDownX; |
|
219 private int pointerDownY; |
|
220 private int twips; |
|
221 private int timeout; |
|
222 |
|
223 private int repaintX1; |
|
224 private int repaintY1; |
|
225 private int repaintX2; |
|
226 private int repaintY2; |
|
227 |
|
228 |
|
229 /** |
|
230 * Constructs <code>Canvas</code> object. |
|
231 */ |
|
232 public Canvas() |
|
233 { |
|
234 super(null); |
|
235 synchronized(this) |
|
236 { |
|
237 objectCount++; |
|
238 } |
|
239 |
|
240 modeLock = new Object(); |
|
241 repaintLock = new Object(); |
|
242 cleanupLock = new Object(); |
|
243 flushLock = new Object(); |
|
244 setMode(GAME_CANVAS, this instanceof GameCanvas); |
|
245 construct(); |
|
246 keysPressed = new Vector(); |
|
247 } |
|
248 |
|
249 /** |
|
250 * Disposes this instance |
|
251 * Called when finalizer is destroying this instance. |
|
252 */ |
|
253 void dispose() |
|
254 { |
|
255 ESWTUIThreadRunner.update(getClass().getName(), -1); |
|
256 ESWTUIThreadRunner.safeSyncExec(new Runnable() |
|
257 { |
|
258 public void run() |
|
259 { |
|
260 if(graphicsBuffer != null) |
|
261 { |
|
262 graphicsBuffer.dispose(); |
|
263 graphicsBuffer = null; |
|
264 } |
|
265 |
|
266 synchronized(this) |
|
267 { |
|
268 objectCount--; |
|
269 |
|
270 if((objectCount == 0) || isMode(GAME_CANVAS)) |
|
271 { |
|
272 mShell.dispose(); |
|
273 sharedShell = null; |
|
274 sharedKeypad = null; |
|
275 } |
|
276 else |
|
277 { |
|
278 Ticker ticker = getTicker(); |
|
279 if (ticker != null) |
|
280 { |
|
281 ticker.removeLabel(tickerLabel); |
|
282 } |
|
283 if(tickerLabel != null) |
|
284 { |
|
285 tickerLabel.dispose(); |
|
286 } |
|
287 |
|
288 canvasComp.dispose(); |
|
289 } |
|
290 } |
|
291 } |
|
292 }); |
|
293 } |
|
294 |
|
295 /* (non-Javadoc) |
|
296 * @see Displayable#eswtConstructShell(int) |
|
297 */ |
|
298 Shell eswtConstructShell(int style) |
|
299 { |
|
300 if(isMode(GAME_CANVAS)) |
|
301 { |
|
302 mShell = super.eswtConstructShell(style); |
|
303 } |
|
304 else |
|
305 { |
|
306 if(sharedShell == null) |
|
307 { |
|
308 sharedShell = super.eswtConstructShell(style); |
|
309 } |
|
310 mShell = sharedShell; |
|
311 } |
|
312 |
|
313 // Give the Shell the maximized size already before it becomes visible |
|
314 // so that it will return the correct size. |
|
315 mShell.setBounds(org.eclipse.swt.widgets.Display.getCurrent().getClientArea()); |
|
316 |
|
317 // Make the Shell maximized. On Symbian it's automatically maximized |
|
318 // so this has no effect but on other platforms explicit maximizing |
|
319 // might be needed. |
|
320 mShell.setMaximized(true); |
|
321 |
|
322 return mShell; |
|
323 } |
|
324 |
|
325 /** |
|
326 * Sets ticker. If ticker is added already to other canvas(es), |
|
327 * it continues running from position where it was. Otherwise |
|
328 * it will start running from beginning when this method returns. |
|
329 * |
|
330 * @param newTicker New ticker. If null, current ticker is removed. |
|
331 */ |
|
332 public void setTicker(Ticker newTicker) |
|
333 { |
|
334 super.setTicker(newTicker); |
|
335 |
|
336 ESWTUIThreadRunner.syncExec(new Runnable() |
|
337 { |
|
338 public void run() |
|
339 { |
|
340 tickerLabel.setVisible(isMode(CURRENTLY_VISIBLE)); |
|
341 } |
|
342 }); |
|
343 } |
|
344 |
|
345 /** |
|
346 * Creates singleton Label instance used by Ticker. |
|
347 * Creates tickerLabel on shell and sets the visibility of the same. |
|
348 */ |
|
349 Label getTickerLabel() |
|
350 { |
|
351 tickerLabel = super.getTickerLabel(); |
|
352 |
|
353 if (!isMode(CURRENTLY_VISIBLE)) |
|
354 { |
|
355 ESWTUIThreadRunner.syncExec(new Runnable() |
|
356 { |
|
357 public void run() |
|
358 { |
|
359 tickerLabel.setVisible(false); |
|
360 } |
|
361 }); |
|
362 } |
|
363 |
|
364 return tickerLabel; |
|
365 } |
|
366 |
|
367 /* (non-Javadoc) |
|
368 * @see Displayable#eswtConstructContent(int) |
|
369 */ |
|
370 Composite eswtConstructContent(int style) |
|
371 { |
|
372 // Get JAD attribute |
|
373 setMode(NO_BACKGROUND, !JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_UI_ENHANCEMENT, |
|
374 JadAttributeUtil.VALUE_CANVAS_HAS_BACKGROUND)); |
|
375 if(isMode(NO_BACKGROUND)) |
|
376 { |
|
377 style |= SWT.NO_BACKGROUND; |
|
378 } |
|
379 |
|
380 // Get JAD attribute for S60 Selection Key Compatibility |
|
381 setMode(SELECTIONKEY_COMPATIBILITY, JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_S60_SELECTION_KEY_COMPATIBILITY, |
|
382 JadAttributeUtil.VALUE_TRUE)); |
|
383 |
|
384 // Get JAD attribute for MIDlet Tap Detection |
|
385 String tapAttr = JadAttributeUtil.getValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_TAP_DETECTION_OPTIONS); |
|
386 if(tapAttr != null) |
|
387 { |
|
388 if(tapAttr.indexOf(',') == -1) |
|
389 { |
|
390 setDefaultTapValues(); |
|
391 } |
|
392 else |
|
393 { |
|
394 String twipString = tapAttr.substring(0, tapAttr.indexOf(',')).trim(); |
|
395 String timeoutString = tapAttr.substring(tapAttr.indexOf(',') + 1, tapAttr.length()).trim(); |
|
396 |
|
397 try |
|
398 { |
|
399 twips = Integer.parseInt(twipString); |
|
400 timeout = Integer.parseInt(timeoutString); |
|
401 |
|
402 // Check for Negative Values |
|
403 if((twips < 0) && (timeout < 0)) |
|
404 { |
|
405 setDefaultTapValues(); |
|
406 } |
|
407 |
|
408 if((twips == 0) && (timeout == 0)) |
|
409 { |
|
410 setMode(DISABLE_TAPDETECTION, true); |
|
411 } |
|
412 |
|
413 // if any one of the value is zero, set defaults |
|
414 if(!((twips != 0) && (timeout != 0))) |
|
415 { |
|
416 setDefaultTapValues(); |
|
417 } |
|
418 } |
|
419 catch(NumberFormatException e) |
|
420 { |
|
421 // Alpha Numeric Values of Timeouts and Timeout |
|
422 setDefaultTapValues(); |
|
423 } |
|
424 } |
|
425 } |
|
426 else |
|
427 { |
|
428 setDefaultTapValues(); |
|
429 } |
|
430 |
|
431 canvasComp = super.eswtConstructContent(style); |
|
432 canvasComp.setVisible(false); |
|
433 |
|
434 createOnScreenKeypad(); |
|
435 return canvasComp; |
|
436 } |
|
437 |
|
438 void eswtInitGraphics() { |
|
439 // create graphics buffer |
|
440 graphicsBuffer = Buffer.createInstance(this, canvasComp); |
|
441 } |
|
442 |
|
443 /** |
|
444 * Creates OSK(OnScreenKeypad), shared Keypad will be created for Canvas, |
|
445 * seperate OSK will be created for each GameCanvas. |
|
446 */ |
|
447 CanvasKeypad createOnScreenKeypad() |
|
448 { |
|
449 // Read the on screen keypad settings from the jad attribute |
|
450 String oskAttr = JadAttributeUtil |
|
451 .getValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_ON_SCREEN_KEYPAD); |
|
452 if(oskAttr != null |
|
453 && (!oskAttr.equalsIgnoreCase(JadAttributeUtil.VALUE_NO)) |
|
454 && ((oskAttr |
|
455 .equalsIgnoreCase(JadAttributeUtil.VALUE_GAMEACTIONS)) || (oskAttr |
|
456 .equalsIgnoreCase(JadAttributeUtil.VALUE_NAVIGATIONKEYS)))) |
|
457 { |
|
458 // On screen keypad is required, On devices without keyboard it can |
|
459 // be either navigation keys or navigation and game keys |
|
460 |
|
461 if(isMode(GAME_CANVAS)) |
|
462 { |
|
463 onScreenkeypad = new CanvasKeypad(this, oskAttr); |
|
464 return onScreenkeypad; |
|
465 } |
|
466 |
|
467 if(sharedKeypad == null) |
|
468 { |
|
469 sharedKeypad = new CanvasKeypad(this, oskAttr); |
|
470 } |
|
471 onScreenkeypad = sharedKeypad; |
|
472 return onScreenkeypad; |
|
473 } |
|
474 |
|
475 return null; |
|
476 } |
|
477 |
|
478 Rectangle eswtLayoutShellContent() |
|
479 { |
|
480 Rectangle shellArea = mShell.getClientArea(); |
|
481 int oskHeight = (onScreenkeypad != null ? onScreenkeypad.getHeight() : 0); |
|
482 int tickerHeight = (tickerLabel != null ? tickerLabel.getBounds().height : 0); |
|
483 |
|
484 canvasComp.setBounds(0, tickerHeight, |
|
485 shellArea.width, shellArea.height - tickerHeight - oskHeight); |
|
486 |
|
487 canvasComp.setFocus(); |
|
488 return canvasComp.getClientArea(); |
|
489 } |
|
490 |
|
491 /* (non-Javadoc) |
|
492 * @see Displayable#eswtHandleShowCurrentEvent() |
|
493 */ |
|
494 void eswtHandleShowCurrentEvent() |
|
495 { |
|
496 setMode(CURRENTLY_VISIBLE, true); |
|
497 eswtSetTitle(); |
|
498 ((MobileShell) mShell).setFullScreenMode(isMode(FULLSCREEN_MODE)); |
|
499 if(onScreenkeypad != null) |
|
500 { |
|
501 if(!isMode(GAME_CANVAS)) |
|
502 { |
|
503 onScreenkeypad.setCurrentCanvas(this); |
|
504 } |
|
505 onScreenkeypad.setFullScreenMode(isMode(FULLSCREEN_MODE)); |
|
506 } |
|
507 canvasComp.setVisible(true); |
|
508 if(tickerLabel != null) |
|
509 { |
|
510 tickerLabel.setVisible(!isMode(FULLSCREEN_MODE)); |
|
511 } |
|
512 addCommands(); |
|
513 super.eswtHandleShowCurrentEvent(); |
|
514 getContentComp().addPaintListener(paintListener); |
|
515 getContentComp().addMouseListener(mouseListener); |
|
516 getContentComp().addMouseMoveListener(mouseListener); |
|
517 ((MobileShellExtension)getShell()).addSymbianWindowVisibilityListener(shellVisibilityListener); |
|
518 } |
|
519 |
|
520 /* (non-Javadoc) |
|
521 * @see Displayable#eswtHandleHideCurrentEvent() |
|
522 */ |
|
523 void eswtHandleHideCurrentEvent() |
|
524 { |
|
525 setMode(CURRENTLY_VISIBLE, false); |
|
526 canvasComp.setVisible(false); |
|
527 if(tickerLabel != null) |
|
528 { |
|
529 tickerLabel.setVisible(false); |
|
530 } |
|
531 removeCommands(); |
|
532 super.eswtHandleHideCurrentEvent(); |
|
533 getContentComp().removePaintListener(paintListener); |
|
534 getContentComp().removeMouseListener(mouseListener); |
|
535 getContentComp().removeMouseMoveListener(mouseListener); |
|
536 ((MobileShellExtension)getShell()).removeSymbianWindowVisibilityListener(shellVisibilityListener); |
|
537 } |
|
538 |
|
539 /** |
|
540 * eSWT callback to add a Command. |
|
541 */ |
|
542 void eswtAddCommand(Command cmd) { |
|
543 if (isMode(CURRENTLY_VISIBLE)) { |
|
544 cmd.eswtAddESWTCommand(mShell, false); |
|
545 } |
|
546 if (eswtIsShown()) { |
|
547 cmd.eswtAddCommandSelectionListener(mShell, getCommandListener()); |
|
548 } |
|
549 } |
|
550 |
|
551 /** |
|
552 * Adds the commands to this Canvas. |
|
553 * Adds all the commands of displayable to the shell. |
|
554 */ |
|
555 void addCommands() |
|
556 { |
|
557 Command cmd = null; |
|
558 for (Enumeration e = getCommands().elements(); e.hasMoreElements();) |
|
559 { |
|
560 cmd = (Command) e.nextElement(); |
|
561 final Command finalCommand = cmd; |
|
562 finalCommand.eswtAddESWTCommand(mShell, false); |
|
563 } |
|
564 } |
|
565 |
|
566 /** |
|
567 * Removes the commands from this Canvas. |
|
568 * Removes all the commands of displayable from the shell. |
|
569 */ |
|
570 void removeCommands() |
|
571 { |
|
572 Command cmd = null; |
|
573 for (Enumeration e = getCommands().elements(); e.hasMoreElements();) |
|
574 { |
|
575 cmd = (Command) e.nextElement(); |
|
576 final Command finalCommand = cmd; |
|
577 finalCommand.eswtRemoveESWTCommand(mShell); |
|
578 } |
|
579 } |
|
580 |
|
581 /** |
|
582 * Issues the request to repaint the whole Canvas. |
|
583 */ |
|
584 public void repaint() |
|
585 { |
|
586 repaint(0, 0, getWidth(), getHeight()); |
|
587 } |
|
588 |
|
589 /** |
|
590 * Issues the request to repaint the specified area of the Canvas. The |
|
591 * request is processed asynchronously. |
|
592 * |
|
593 * @param x - left bound of the rectangle to redraw. |
|
594 * @param y - top bound of the rectangle to redraw. |
|
595 * @param width - width of the rectangle to redraw. |
|
596 * @param height - height of the rectangle to redraw. |
|
597 */ |
|
598 public void repaint(int x, int y, int width, int height) |
|
599 { |
|
600 // Paint callback event is posted without any invalid area info. |
|
601 // Invalid area info is kept in the member variables. Only one event |
|
602 // per Canvas is added to the queue. If there are more repaint() calls |
|
603 // before the already posted event has been served those are merged |
|
604 // to the invalid area in the member variables without posting a new |
|
605 // event. |
|
606 synchronized(repaintLock) |
|
607 { |
|
608 if(invalidate(x, y, width, height)) |
|
609 { |
|
610 // Note that repaintPending doesn't mean that there's a repaint |
|
611 // event in the queue. It means that the invalid area is going |
|
612 // to be repainted and there's no need to add a new event. |
|
613 // It's possible that the repaint event has already been |
|
614 // removed from the queue but repaintPending is still true. In |
|
615 // that case it's currently being processed and we can still |
|
616 // add to the invalid area. |
|
617 if(!isMode(REPAINT_PENDING)) |
|
618 { |
|
619 EventDispatcher eventDispatcher = EventDispatcher.instance(); |
|
620 LCDUIEvent event = eventDispatcher.newEvent( |
|
621 LCDUIEvent.CANVAS_PAINT_MIDLET_REQUEST, this); |
|
622 event.widget = getContentComp(); |
|
623 eventDispatcher.postEvent(event); |
|
624 setMode(REPAINT_PENDING, true); |
|
625 } |
|
626 } |
|
627 } |
|
628 } |
|
629 |
|
630 /** |
|
631 * Switches the Canvas between full screen and non-full screen modes. |
|
632 * |
|
633 * @param mode - true switches the Canvas to the full-screen mode. |
|
634 */ |
|
635 public void setFullScreenMode(boolean aMode) |
|
636 { |
|
637 setMode(FULLSCREEN_MODE, aMode); |
|
638 ESWTUIThreadRunner.syncExec(new Runnable() |
|
639 { |
|
640 public void run() |
|
641 { |
|
642 if(tickerLabel != null) |
|
643 { |
|
644 if(isMode(FULLSCREEN_MODE)) |
|
645 { |
|
646 tickerLabel.setBounds(Integer.MIN_VALUE, 0, 0, 0); |
|
647 } |
|
648 else |
|
649 { |
|
650 tickerLabel.pack(); |
|
651 tickerLabel.setLocation(Integer.MIN_VALUE, 0); |
|
652 } |
|
653 |
|
654 if(isMode(CURRENTLY_VISIBLE)) |
|
655 { |
|
656 tickerLabel.setVisible(!isMode(FULLSCREEN_MODE)); |
|
657 } |
|
658 } |
|
659 |
|
660 if(isMode(CURRENTLY_VISIBLE)) |
|
661 { |
|
662 ((MobileShell)mShell).setFullScreenMode(isMode(FULLSCREEN_MODE)); |
|
663 //set the CanvasKeypad to the required mode |
|
664 if(onScreenkeypad != null) |
|
665 { |
|
666 onScreenkeypad.setFullScreenMode(isMode(FULLSCREEN_MODE)); |
|
667 } |
|
668 } |
|
669 } |
|
670 }); |
|
671 } |
|
672 |
|
673 /** |
|
674 * Processes all the issued paint requests. |
|
675 */ |
|
676 public final void serviceRepaints() |
|
677 { |
|
678 EventDispatcher.instance().serviceRepaints(this); |
|
679 } |
|
680 |
|
681 /** |
|
682 * Checks if the Canvas supports pointer dragging events. |
|
683 * |
|
684 * @return true if the Canvas supports pointer dragging, false otherwise. |
|
685 */ |
|
686 public boolean hasPointerMotionEvents() |
|
687 { |
|
688 return true; |
|
689 } |
|
690 |
|
691 /** |
|
692 * Checks if the Canvas supports pointer events. |
|
693 * |
|
694 * @return true if the Canvas supports pointer events, false otherwise. |
|
695 */ |
|
696 public boolean hasPointerEvents() |
|
697 { |
|
698 return true; |
|
699 } |
|
700 |
|
701 /** |
|
702 * Is the Canvas double buffered. |
|
703 * |
|
704 * @return true if the Canvas is double buffered, false otherwise. |
|
705 */ |
|
706 public boolean isDoubleBuffered() |
|
707 { |
|
708 return true; |
|
709 } |
|
710 |
|
711 /** |
|
712 * Queries if the Canvas supports key repeat events. |
|
713 * |
|
714 * @return true if Canvas supports key repeat events, false otherwise. |
|
715 */ |
|
716 public boolean hasRepeatEvents() |
|
717 { |
|
718 return true; |
|
719 } |
|
720 |
|
721 /** |
|
722 * Returns game action for a specified key code. |
|
723 * |
|
724 * @param keyCode Key code to map to game action. |
|
725 * @return Game action for a a specified key code or |
|
726 * IllegalArgumentException. |
|
727 */ |
|
728 public int getGameAction(int keyCode) |
|
729 { |
|
730 return KeyTable.getGameAction(keyCode); |
|
731 } |
|
732 |
|
733 /** |
|
734 * Returns the key code specific for a certain game action. |
|
735 * |
|
736 * @param gameAction - game action to be mapped to the key code. |
|
737 * @return Key code that is mapped to the specified game action. |
|
738 */ |
|
739 public int getKeyCode(int gameAction) |
|
740 { |
|
741 return KeyTable.getKeyCode(gameAction); |
|
742 } |
|
743 |
|
744 /** |
|
745 * Returns the key name specific for a certain key code. |
|
746 * |
|
747 * @param keyCode - key name to get the name of. |
|
748 * @return String that contains textual name of the key specified by the key |
|
749 * code. |
|
750 */ |
|
751 public String getKeyName(int keyCode) |
|
752 { |
|
753 return KeyTable.getKeyName(keyCode); |
|
754 } |
|
755 |
|
756 /** |
|
757 * Callback to be implemented by the application to render the |
|
758 * <code>Canvas</code>. The clip region of <code>Graphics</code> object |
|
759 * specifies the area that needs to be updated. All the region has to be |
|
760 * updated in <code>paint()</code> method. |
|
761 * <p> |
|
762 * Application should not take into account the source where the |
|
763 * <code>paint()</code> request came from. |
|
764 * <p> |
|
765 * Any interaction with Graphics object outside of <code>paint()</code> |
|
766 * method is undefined. |
|
767 * <p> |
|
768 * The application should never call <code>paint()</code> explicitly, it is |
|
769 * called by the framework. |
|
770 * |
|
771 * @param g - the <code>Graphics</code> object to be used for graphical |
|
772 * operations on the <code>Canvas.</code> |
|
773 */ |
|
774 protected abstract void paint(Graphics g); |
|
775 |
|
776 /** |
|
777 * Callback to signal a key press. |
|
778 * |
|
779 * @param keyCode - key code of the key pressed. |
|
780 */ |
|
781 protected void keyPressed(int keyCode) |
|
782 { |
|
783 } |
|
784 |
|
785 /** |
|
786 * Callback to signal a key release. |
|
787 * |
|
788 * @param keyCode - key code of the key released. |
|
789 */ |
|
790 protected void keyReleased(int keyCode) |
|
791 { |
|
792 } |
|
793 |
|
794 /** |
|
795 * Callback to signal a key repeat. Happens when the key is pressed down for |
|
796 * a long time. |
|
797 * |
|
798 * @param keyCode - key code of the key repeated. |
|
799 */ |
|
800 protected void keyRepeated(int keyCode) |
|
801 { |
|
802 } |
|
803 |
|
804 /** |
|
805 * Callback to signal pointer press event. |
|
806 * |
|
807 * @param x - X-coordinate of the tap point. |
|
808 * @param y - Y-coordinate of the tap point. |
|
809 */ |
|
810 protected void pointerPressed(int x, int y) |
|
811 { |
|
812 } |
|
813 |
|
814 /** |
|
815 * Callback to signal pointer release event. |
|
816 * |
|
817 * @param x - X-coordinate of the pointer release point. |
|
818 * @param y - Y-coordinate of the pointer release point. |
|
819 */ |
|
820 protected void pointerReleased(int x, int y) |
|
821 { |
|
822 } |
|
823 |
|
824 /** |
|
825 * Callback to signal pointer drag event. |
|
826 * |
|
827 * @param x - X-coordinate of the pointer drag point. |
|
828 * @param y - Y-coordinate of the pointer drag point. |
|
829 */ |
|
830 protected void pointerDragged(int x, int y) |
|
831 { |
|
832 } |
|
833 |
|
834 /** |
|
835 * Callback similar to the one in <code>Displayable</code>. Additional |
|
836 * case for <code>sizeChanged</code> to be called in <code>Canvas</code> |
|
837 * is switching between full-screen mode by calling |
|
838 * <code>setFullScreenMode()</code>. |
|
839 */ |
|
840 protected void sizeChanged(int w, int h) |
|
841 { |
|
842 } |
|
843 |
|
844 /** |
|
845 * showNotify() is the callback called before the Canvas is shown on the |
|
846 * screen. |
|
847 */ |
|
848 protected void showNotify() |
|
849 { |
|
850 } |
|
851 |
|
852 /** |
|
853 * hideNotify() is the callback called after the Canvas is removed from the |
|
854 * screen. |
|
855 */ |
|
856 protected void hideNotify() |
|
857 { |
|
858 } |
|
859 |
|
860 /** |
|
861 * Init GameCanvas frame buffer. |
|
862 * |
|
863 * @param supressKeys suppress game keys |
|
864 * @return frame buffer |
|
865 */ |
|
866 final void initGameCanvas(boolean suppressKeys) |
|
867 { |
|
868 setMode(SUPPRESS_GAMEKEYS, suppressKeys); |
|
869 } |
|
870 |
|
871 /** |
|
872 * Gets composite that contains Canvas content. |
|
873 * |
|
874 * @return Composite. |
|
875 */ |
|
876 Composite getContentComp() |
|
877 { |
|
878 return canvasComp; |
|
879 } |
|
880 |
|
881 /** |
|
882 * Get game canvas frame buffer graphics. |
|
883 */ |
|
884 final Graphics getGameBufferGraphics() |
|
885 { |
|
886 tempGraphics = null; |
|
887 ESWTUIThreadRunner.safeSyncExec(new Runnable() |
|
888 { |
|
889 public void run() |
|
890 { |
|
891 tempGraphics = graphicsBuffer.getGraphics(); |
|
892 } |
|
893 }); |
|
894 return tempGraphics; |
|
895 } |
|
896 |
|
897 CanvasKeypad getCanvasKeypad() |
|
898 { |
|
899 return onScreenkeypad; |
|
900 } |
|
901 |
|
902 boolean IsFullScreenMode() |
|
903 { |
|
904 return isMode(FULLSCREEN_MODE); |
|
905 } |
|
906 |
|
907 /** |
|
908 * Return game key states for GameCanvas. |
|
909 * |
|
910 * @return game key states. |
|
911 */ |
|
912 final int getGameKeyStates() |
|
913 { |
|
914 int ret = gameKeyState; |
|
915 gameKeyState = 0; |
|
916 return ret; |
|
917 } |
|
918 |
|
919 void renderGraphics(final Graphics g) |
|
920 { |
|
921 // Implementation missing. GameCanvas.paint() should by default render |
|
922 // the the off-screen buffer at (0,0). Rendering of the buffer must be |
|
923 // subject to the clip region and origin translation of the Graphics |
|
924 // object. |
|
925 } |
|
926 |
|
927 /** |
|
928 * Flushes the frameBuffer to the display. |
|
929 * |
|
930 * @param x |
|
931 * @param y |
|
932 * @param width |
|
933 * @param height |
|
934 */ |
|
935 void flushGameBuffer(final int x, final int y, final int width, |
|
936 final int height) |
|
937 { |
|
938 // This is serialized with the |
|
939 // paint callback processing |
|
940 synchronized(flushLock) |
|
941 { |
|
942 synchronized(graphicsBuffer) |
|
943 { |
|
944 ESWTUIThreadRunner.safeSyncExec(new Runnable() |
|
945 { |
|
946 public void run() |
|
947 { |
|
948 graphicsBuffer.sync(); |
|
949 graphicsBuffer.blitToDisplay(null, getContentComp()); |
|
950 } |
|
951 }); |
|
952 } |
|
953 } |
|
954 } |
|
955 |
|
956 /** |
|
957 * Called by ShellListener when shell gets activated. |
|
958 */ |
|
959 void handleShellActivatedEvent() |
|
960 { |
|
961 super.handleShellActivatedEvent(); |
|
962 |
|
963 // reset the game key state |
|
964 gameKeyState = 0; |
|
965 |
|
966 synchronized(cleanupLock) |
|
967 { |
|
968 setMode(CLEANUP_NEEDED, true); |
|
969 } |
|
970 |
|
971 LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_SHOWNOTIFY, this); |
|
972 EventDispatcher.instance().postEvent(event); |
|
973 } |
|
974 |
|
975 /** |
|
976 * Called by ShellListener when shell gets de-activated. |
|
977 */ |
|
978 void handleShellDeActivatedEvent() |
|
979 { |
|
980 super.handleShellDeActivatedEvent(); |
|
981 LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_HIDENOTIFY, this); |
|
982 EventDispatcher.instance().postEvent(event); |
|
983 } |
|
984 |
|
985 /* |
|
986 * UI thread calls |
|
987 */ |
|
988 void eswtHandleResizeEvent(int width, int height) |
|
989 { |
|
990 super.eswtHandleResizeEvent(width, height); |
|
991 // update new bounds to graphicsBuffer |
|
992 // this call must not be synchronized as we |
|
993 // cannot use locking in UI thread |
|
994 graphicsBuffer.setControlBounds(getContentComp()); |
|
995 synchronized(cleanupLock) |
|
996 { |
|
997 setMode(CLEANUP_NEEDED, true); |
|
998 } |
|
999 } |
|
1000 |
|
1001 /* |
|
1002 * UI thread calls |
|
1003 */ |
|
1004 void eswtHandleEvent(Event e) |
|
1005 { |
|
1006 super.eswtHandleEvent(e); |
|
1007 if(e.type == SWT.KeyDown) |
|
1008 { |
|
1009 doKeyPressed(e.keyCode); |
|
1010 } |
|
1011 else if(e.type == SWT.KeyUp) |
|
1012 { |
|
1013 doKeyReleased(e.keyCode); |
|
1014 } |
|
1015 } |
|
1016 |
|
1017 private void setMode(final int aMode, boolean value) |
|
1018 { |
|
1019 synchronized(modeLock) |
|
1020 { |
|
1021 if(value) |
|
1022 { |
|
1023 mode |= aMode; |
|
1024 } |
|
1025 else |
|
1026 { |
|
1027 mode &= ~aMode; |
|
1028 } |
|
1029 } |
|
1030 } |
|
1031 |
|
1032 private boolean isMode(final int aMode) |
|
1033 { |
|
1034 return ((mode & aMode) != 0); |
|
1035 } |
|
1036 |
|
1037 /* |
|
1038 * UI thread calls. Paint listener of the eSWT widget. |
|
1039 */ |
|
1040 class CanvasShellPaintListener implements PaintListener |
|
1041 { |
|
1042 public void paintControl(PaintEvent pe) |
|
1043 { |
|
1044 // Check if we got here from buffer flush |
|
1045 if(graphicsBuffer.isPaintingActive()) |
|
1046 { |
|
1047 graphicsBuffer.blitToDisplay(pe.gc.getGCData().internalGc, null); |
|
1048 } |
|
1049 else |
|
1050 { |
|
1051 // Native toolkit is requesting an update of an area that has |
|
1052 // become invalid. Can't do anything here because the contents |
|
1053 // need to be queried from the MIDlet in another thread by |
|
1054 // a paint callback. For this a paint callback event is posted. |
|
1055 // For a moment the native toolkit thinks that the area has |
|
1056 // been validated when in truth it will be painted later after |
|
1057 // the paint callback has been executed. |
|
1058 EventDispatcher eventDispatcher = EventDispatcher.instance(); |
|
1059 LCDUIEvent event = eventDispatcher.newEvent( |
|
1060 LCDUIEvent.CANVAS_PAINT_NATIVE_REQUEST, |
|
1061 javax.microedition.lcdui.Canvas.this); |
|
1062 event.x = pe.x; |
|
1063 event.y = pe.y; |
|
1064 event.width = pe.width; |
|
1065 event.height = pe.height; |
|
1066 event.widget = pe.widget; |
|
1067 eventDispatcher.postEvent(event); |
|
1068 } |
|
1069 } |
|
1070 } |
|
1071 |
|
1072 /* |
|
1073 * Dispatcher thread or the serviceRepaints()-thread calls. |
|
1074 */ |
|
1075 final void doCallback(LCDUIEvent event) |
|
1076 { |
|
1077 switch(event.type) |
|
1078 { |
|
1079 case LCDUIEvent.CANVAS_PAINT_MIDLET_REQUEST: // fall through |
|
1080 case LCDUIEvent.CANVAS_PAINT_NATIVE_REQUEST: |
|
1081 doPaintCallback(event); |
|
1082 break; |
|
1083 case LCDUIEvent.CANVAS_HIDENOTIFY: |
|
1084 hideNotify(); |
|
1085 break; |
|
1086 case LCDUIEvent.CANVAS_KEYPRESSED: |
|
1087 keyPressed(event.keyCode); |
|
1088 break; |
|
1089 case LCDUIEvent.CANVAS_KEYREPEATED: |
|
1090 keyRepeated(event.keyCode); |
|
1091 break; |
|
1092 case LCDUIEvent.CANVAS_KEYRELEASED: |
|
1093 keyReleased(event.keyCode); |
|
1094 break; |
|
1095 case LCDUIEvent.CANVAS_POINTERDRAGGED: |
|
1096 pointerDragged(event.x, event.y); |
|
1097 break; |
|
1098 case LCDUIEvent.CANVAS_POINTERPRESSED: |
|
1099 pointerPressed(event.x, event.y); |
|
1100 break; |
|
1101 case LCDUIEvent.CANVAS_POINTERRELEASED: |
|
1102 pointerReleased(event.x, event.y); |
|
1103 break; |
|
1104 case LCDUIEvent.CANVAS_SHOWNOTIFY: |
|
1105 showNotify(); |
|
1106 break; |
|
1107 default: |
|
1108 super.doCallback(event); |
|
1109 break; |
|
1110 } |
|
1111 } |
|
1112 |
|
1113 /* |
|
1114 * Dispatcher thread or the serviceRepaints()-thread calls. |
|
1115 */ |
|
1116 private final void doPaintCallback(final LCDUIEvent event) |
|
1117 { |
|
1118 synchronized(flushLock) |
|
1119 { |
|
1120 // It's possible that this Canvas is sent to background |
|
1121 // right after the visibility is checked here, however |
|
1122 // it is okay as in such case we just do one extra paint |
|
1123 // callback. The visibility change cannot be synchronized with |
|
1124 // this method, since it would expose implementation to deadlock |
|
1125 if(!isMode(CURRENTLY_VISIBLE)) |
|
1126 { |
|
1127 return; |
|
1128 } |
|
1129 |
|
1130 // Decide the area going to be painted by the callback. |
|
1131 final int redrawNowX; |
|
1132 final int redrawNowY; |
|
1133 final int redrawNowW; |
|
1134 final int redrawNowH; |
|
1135 // Before this thread obtains the repaintLock any repaint() calls |
|
1136 // will still be adding to the invalid area that is going to be |
|
1137 // painted by this callback. |
|
1138 synchronized(repaintLock) |
|
1139 { |
|
1140 if(event.type == LCDUIEvent.CANVAS_PAINT_NATIVE_REQUEST) |
|
1141 { |
|
1142 // Merge with possibly existing repaint() requests |
|
1143 invalidate(event.x, event.y, event.width, event.height); |
|
1144 } |
|
1145 else |
|
1146 { |
|
1147 // Need to add a new event to the queue in subsequent repaint() |
|
1148 // calls. |
|
1149 setMode(REPAINT_PENDING, false); |
|
1150 } |
|
1151 |
|
1152 // Store the current area to be painted |
|
1153 redrawNowX = repaintX1; |
|
1154 redrawNowY = repaintY1; |
|
1155 redrawNowW = repaintX2-repaintX1; |
|
1156 redrawNowH = repaintY2-repaintY1; |
|
1157 |
|
1158 // After releasing the lock the repaint() calls will start with |
|
1159 // new invalid area. |
|
1160 repaintX1 = repaintX2 = repaintY1 = repaintY2 = 0; |
|
1161 |
|
1162 // Don't do the callback if there's nothing to paint |
|
1163 if(!((redrawNowW > 0) && (redrawNowH > 0))) |
|
1164 { |
|
1165 return; |
|
1166 } |
|
1167 } |
|
1168 |
|
1169 // Create instance of Graphics if not created yet |
|
1170 if(canvasGraphics == null) |
|
1171 { |
|
1172 canvasGraphics = graphicsBuffer.getGraphics(); |
|
1173 canvasGraphics.setSyncStrategy(Graphics.SYNC_LEAVE_SURFACE_SESSION_OPEN); |
|
1174 } |
|
1175 |
|
1176 // Clean the background if dirty, buffer the operations. |
|
1177 synchronized(cleanupLock) |
|
1178 { |
|
1179 if(isMode(CLEANUP_NEEDED) && isMode(NO_BACKGROUND)) |
|
1180 { |
|
1181 // UI thread can change the contentArea object reference at |
|
1182 // any time. Store the object reference locally to ensure it |
|
1183 // points to the same rectangle all the time. |
|
1184 Rectangle contentArea = getContentArea(); |
|
1185 |
|
1186 canvasGraphics.setClip(contentArea.x, contentArea.y, |
|
1187 contentArea.width, contentArea.height); |
|
1188 canvasGraphics.cleanBackground(contentArea); |
|
1189 setMode(CLEANUP_NEEDED, false); |
|
1190 } |
|
1191 } |
|
1192 |
|
1193 // Clip must define the invalid area |
|
1194 canvasGraphics.reset(); |
|
1195 canvasGraphics.setClip(redrawNowX, redrawNowY, redrawNowW, redrawNowH); |
|
1196 |
|
1197 // The callback |
|
1198 paint(canvasGraphics); |
|
1199 |
|
1200 // Blit frame to display |
|
1201 synchronized(graphicsBuffer) |
|
1202 { |
|
1203 ESWTUIThreadRunner.safeSyncExec(new Runnable() |
|
1204 { |
|
1205 public void run() |
|
1206 { |
|
1207 if(event.widget.isDisposed()) |
|
1208 { |
|
1209 return; |
|
1210 } |
|
1211 graphicsBuffer.sync(); |
|
1212 graphicsBuffer.blitToDisplay(null, event.widget); |
|
1213 } |
|
1214 }); |
|
1215 } |
|
1216 } |
|
1217 } |
|
1218 |
|
1219 /* |
|
1220 * UI thread calls to flush the command buffer of a graphics context. |
|
1221 */ |
|
1222 private final void doBufferFlush(PaintEvent event, Graphics graphics) |
|
1223 { |
|
1224 // event.gc.getGCData().internalGc.render(graphics.getCommandBuffer()); |
|
1225 } |
|
1226 |
|
1227 /* |
|
1228 * UI thread calls. |
|
1229 */ |
|
1230 void doKeyPressed(int keyCode) |
|
1231 { |
|
1232 Logger.method(this, "doKeyPressed", String.valueOf(keyCode)); |
|
1233 boolean sendCallback = false; |
|
1234 |
|
1235 if(!(updateGameKeyState(keyCode, true) && isMode(SUPPRESS_GAMEKEYS))) |
|
1236 { |
|
1237 if(isMode(SELECTIONKEY_COMPATIBILITY) && (keyCode == -5)) |
|
1238 { |
|
1239 if(isMode(FULLSCREEN_MODE)) |
|
1240 { |
|
1241 if(!((getNumCommands() > 0) && hasCommandListener())) |
|
1242 { |
|
1243 sendCallback = true; |
|
1244 } |
|
1245 else |
|
1246 { |
|
1247 sendCallback = false; |
|
1248 } |
|
1249 } |
|
1250 else |
|
1251 { |
|
1252 sendCallback = true; |
|
1253 } |
|
1254 } |
|
1255 else if((!isMode(SELECTIONKEY_COMPATIBILITY)) && (keyCode == -5)) |
|
1256 { |
|
1257 sendCallback = false; |
|
1258 } |
|
1259 else |
|
1260 { |
|
1261 sendCallback = true; |
|
1262 } |
|
1263 } |
|
1264 else |
|
1265 { |
|
1266 sendCallback = false; |
|
1267 } |
|
1268 |
|
1269 if(sendCallback == true) |
|
1270 { |
|
1271 LCDUIEvent event; |
|
1272 |
|
1273 if(keysPressed.contains(new Integer(keyCode))) |
|
1274 { |
|
1275 event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_KEYREPEATED, this); |
|
1276 } |
|
1277 else |
|
1278 { |
|
1279 event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_KEYPRESSED, this); |
|
1280 } |
|
1281 event.keyCode = keyCode; |
|
1282 EventDispatcher.instance().postEvent(event); |
|
1283 |
|
1284 } |
|
1285 } |
|
1286 |
|
1287 /* |
|
1288 * UI thread calls. |
|
1289 */ |
|
1290 void doKeyReleased(int keyCode) |
|
1291 { |
|
1292 Logger.method(this, "doKeyReleased", String.valueOf(keyCode)); |
|
1293 boolean sendCallback = false; |
|
1294 if(!(updateGameKeyState(keyCode, true) && isMode(SUPPRESS_GAMEKEYS))) |
|
1295 { |
|
1296 if(isMode(SELECTIONKEY_COMPATIBILITY) && (keyCode == -5)) |
|
1297 { |
|
1298 if(isMode(FULLSCREEN_MODE)) |
|
1299 { |
|
1300 if(!((getNumCommands() > 0) && hasCommandListener())) |
|
1301 { |
|
1302 sendCallback = true; |
|
1303 } |
|
1304 else |
|
1305 { |
|
1306 sendCallback = false; |
|
1307 } |
|
1308 } |
|
1309 else |
|
1310 { |
|
1311 sendCallback = true; |
|
1312 } |
|
1313 } |
|
1314 else if((!isMode(SELECTIONKEY_COMPATIBILITY)) && (keyCode == -5)) |
|
1315 { |
|
1316 sendCallback = false; |
|
1317 } |
|
1318 else |
|
1319 { |
|
1320 sendCallback = true; |
|
1321 } |
|
1322 } |
|
1323 else |
|
1324 { |
|
1325 sendCallback = false; |
|
1326 } |
|
1327 |
|
1328 if(sendCallback == true) |
|
1329 { |
|
1330 |
|
1331 LCDUIEvent event = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_KEYRELEASED, this); |
|
1332 event.keyCode = keyCode; |
|
1333 EventDispatcher.instance().postEvent(event); |
|
1334 |
|
1335 } |
|
1336 } |
|
1337 |
|
1338 /** |
|
1339 * Updates game key states and returns if the key was a game key. |
|
1340 */ |
|
1341 private boolean updateGameKeyState(int keyCode, boolean addKeyState) |
|
1342 { |
|
1343 // Ignore key repeat events |
|
1344 if(ESWTUIThreadRunner.getKeyRepeatCount() > 1) |
|
1345 { |
|
1346 return true; |
|
1347 } |
|
1348 try |
|
1349 { |
|
1350 int gameAction = KeyTable.getGameAction(keyCode); |
|
1351 if(addKeyState) |
|
1352 { |
|
1353 // set bitfield |
|
1354 gameKeyState |= (1 << gameAction); |
|
1355 } |
|
1356 return true; |
|
1357 } |
|
1358 catch(IllegalArgumentException iae) |
|
1359 { |
|
1360 return false; |
|
1361 } |
|
1362 } |
|
1363 |
|
1364 private void setDefaultTapValues() |
|
1365 { |
|
1366 twips = DEFAULT_TWIPS; |
|
1367 timeout = DEFAULT_TIMEOUT; |
|
1368 } |
|
1369 |
|
1370 class CanvasShellMouseListener implements |
|
1371 org.eclipse.swt.events.MouseListener, |
|
1372 org.eclipse.swt.events.MouseMoveListener |
|
1373 { |
|
1374 |
|
1375 public void mouseDoubleClick(MouseEvent arg0) |
|
1376 { |
|
1377 } |
|
1378 |
|
1379 public void mouseDown(MouseEvent event) |
|
1380 { |
|
1381 LCDUIEvent e = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_POINTERPRESSED, |
|
1382 javax.microedition.lcdui.Canvas.this); |
|
1383 e.x = event.x; |
|
1384 e.y = event.y; |
|
1385 EventDispatcher.instance().postEvent(e); |
|
1386 |
|
1387 if(!isMode(DISABLE_TAPDETECTION)) |
|
1388 { |
|
1389 // Supress Drag events |
|
1390 setMode(SUPPRESS_DRAGEVENT, true); |
|
1391 |
|
1392 pointerDownX = event.x; |
|
1393 pointerDownY = event.y; |
|
1394 |
|
1395 // Create and Schedule Timer |
|
1396 timerTask = new CanvasTimerTask(); |
|
1397 timer.schedule(timerTask, timeout); |
|
1398 } |
|
1399 } |
|
1400 |
|
1401 public void mouseUp(MouseEvent event) |
|
1402 { |
|
1403 int pointerUpX = event.x; |
|
1404 int pointerUpY = event.y; |
|
1405 |
|
1406 if(!isMode(DISABLE_TAPDETECTION)) |
|
1407 { |
|
1408 if(timerTask != null) |
|
1409 { |
|
1410 timerTask.cancel(); |
|
1411 timerTask = null; |
|
1412 } |
|
1413 |
|
1414 // If Timer not expired and Mouseup is withing rectangle assign |
|
1415 // PointercDown to Pinter Up |
|
1416 if(isMode(SUPPRESS_DRAGEVENT) && checkWithinRect(event.x, event.y)) |
|
1417 { |
|
1418 pointerUpX = pointerDownX; |
|
1419 pointerUpY = pointerDownY; |
|
1420 setMode(SUPPRESS_DRAGEVENT, false); |
|
1421 } |
|
1422 } |
|
1423 |
|
1424 LCDUIEvent e = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_POINTERRELEASED, |
|
1425 javax.microedition.lcdui.Canvas.this); |
|
1426 e.x = pointerUpX; |
|
1427 e.y = pointerUpY; |
|
1428 EventDispatcher.instance().postEvent(e); |
|
1429 } |
|
1430 |
|
1431 public void mouseMove(MouseEvent event) |
|
1432 { |
|
1433 // Check for timeout expiration and if PointerUp falls outside the rectangle |
|
1434 if(isMode(DISABLE_TAPDETECTION) || (!isMode(SUPPRESS_DRAGEVENT)) || !checkWithinRect(event.x, event.y)) |
|
1435 { |
|
1436 LCDUIEvent e = EventDispatcher.instance().newEvent(LCDUIEvent.CANVAS_POINTERDRAGGED, |
|
1437 javax.microedition.lcdui.Canvas.this); |
|
1438 e.x = event.x; |
|
1439 e.y = event.y; |
|
1440 EventDispatcher.instance().postEvent(e); |
|
1441 } |
|
1442 } |
|
1443 |
|
1444 boolean checkWithinRect(int x, int y) |
|
1445 { |
|
1446 // Get pixel per inch |
|
1447 Point P = Display.getCurrent().getDPI(); |
|
1448 |
|
1449 float xPxielwidth = (twips * P.x) / 1440; |
|
1450 float yPixelHeight = (twips * P.y) / 1440; |
|
1451 |
|
1452 int RightX = pointerDownX + (int) xPxielwidth; |
|
1453 |
|
1454 // If the rectange width falls outside the canvas area |
|
1455 if(RightX > getWidth()) |
|
1456 { |
|
1457 RightX = getWidth(); |
|
1458 } |
|
1459 |
|
1460 int LeftX = pointerDownX - (int) xPxielwidth; |
|
1461 |
|
1462 // If the rectange width falls outside the canvas area |
|
1463 if(LeftX < 0) |
|
1464 LeftX = 0; |
|
1465 |
|
1466 int TopY = pointerDownY - (int) yPixelHeight; |
|
1467 |
|
1468 // If the rectange height falls outside the canvas area |
|
1469 if(TopY < 0) |
|
1470 TopY = 0; |
|
1471 |
|
1472 int DownY = pointerDownY + (int) yPixelHeight; |
|
1473 |
|
1474 // If the rectange heightfalls outside the canvas area. |
|
1475 if(DownY > getHeight()) |
|
1476 DownY = getHeight(); |
|
1477 |
|
1478 // Find the PointerUp is within rectange |
|
1479 if((x >= LeftX) && (x <= RightX)) |
|
1480 { |
|
1481 if((y >= TopY) && (y <= DownY)) |
|
1482 { |
|
1483 return true; |
|
1484 } |
|
1485 } |
|
1486 |
|
1487 return false; |
|
1488 } |
|
1489 } |
|
1490 |
|
1491 private boolean invalidate(int x, int y, int width, int height) |
|
1492 { |
|
1493 // Regularize bounds |
|
1494 final int x1 = x; |
|
1495 final int y1 = y; |
|
1496 final int x2 = x + width; |
|
1497 final int y2 = y + height; |
|
1498 |
|
1499 // Union the current and new damaged rects |
|
1500 final boolean valid = ((repaintX2 - repaintX1) <= 0) && ((repaintY2 - repaintY1) <= 0); |
|
1501 if(!valid) |
|
1502 { |
|
1503 repaintX1 = Math.min(repaintX1, x1); |
|
1504 repaintY1 = Math.min(repaintY1, y1); |
|
1505 repaintX2 = Math.max(repaintX2, x2); |
|
1506 repaintY2 = Math.max(repaintY2, y2); |
|
1507 } |
|
1508 else |
|
1509 { |
|
1510 repaintX1 = x1; |
|
1511 repaintY1 = y1; |
|
1512 repaintX2 = x2; |
|
1513 repaintY2 = y2; |
|
1514 } |
|
1515 |
|
1516 // UI thread can change the the contentArea object reference at |
|
1517 // any time. Store the object reference locally to ensure it |
|
1518 // points to the same rectangle all the time. |
|
1519 Rectangle contentArea = getContentArea(); |
|
1520 if(contentArea == null) return valid; |
|
1521 final int w = contentArea.width; |
|
1522 final int h = contentArea.height; |
|
1523 |
|
1524 // Clip to bounds |
|
1525 repaintX1 = repaintX1 > 0 ? repaintX1 : 0; |
|
1526 repaintY1 = repaintY1 > 0 ? repaintY1 : 0; |
|
1527 repaintX2 = repaintX2 < w ? repaintX2 : w; |
|
1528 repaintY2 = repaintY2 < h ? repaintY2 : h; |
|
1529 |
|
1530 return valid; |
|
1531 } |
|
1532 |
|
1533 class CanvasTimerTask extends TimerTask |
|
1534 { |
|
1535 |
|
1536 public void run() |
|
1537 { |
|
1538 setMode(SUPPRESS_DRAGEVENT, false); |
|
1539 } |
|
1540 } |
|
1541 |
|
1542 class CanvasShellVisibilityListener implements SymbianWindowVisibilityListener |
|
1543 { |
|
1544 public void handleSymbianWindowVisibilityChange(Widget widget, boolean visible) { |
|
1545 if (javax.microedition.lcdui.Canvas.this.getShell() == widget && graphicsBuffer != null) |
|
1546 { |
|
1547 WindowSurface surface = graphicsBuffer.getWindowSurface(); |
|
1548 if (surface != null) |
|
1549 surface.handleSymbianWindowVisibilityChange(visible); |
|
1550 } |
|
1551 } |
|
1552 } |
|
1553 } |