/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package javax.microedition.lcdui;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.extension.CanvasExtension;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.EventDispatcher.LCDUIEvent;
import org.eclipse.swt.widgets.Display;
/**
* Responsible for layouting CustomItem.
*/
class CustomItemLayouter extends ItemLayouter
{
/**
* Key name for paint listener.
*/
private static final String PAINT_LISTENER = "PaintListener";
/**
* Key name for mouse listener.
*/
private static final String MOUSE_LISTENER = "MouseListener";
boolean noBackground;
private Timer timer = new Timer();
private CustomItemTimerTask timerTask;
private static final int DEFAULT_TIMEOUT = 500;
private static final int DEFAULT_TWIPS = 200;
private int pointerDownX;
private int pointerDownY;
private int twips;
private int timeout;
private boolean disableTapDetection;
private boolean suppressDragEvent;
private boolean selectionKeyCompatibility;
/**
* Constructor.
*
* @param aFormLayouter FormLayouter used for layouting.
*/
public CustomItemLayouter(FormLayouter aFormLayouter)
{
super(aFormLayouter);
noBackground = JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_UI_ENHANCEMENT,
JadAttributeUtil.VALUE_CANVAS_HAS_BACKGROUND);
selectionKeyCompatibility = JadAttributeUtil.isValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_S60_SELECTION_KEY_COMPATIBILITY,
JadAttributeUtil.VALUE_TRUE);
String tapAttr = JadAttributeUtil.getValue(JadAttributeUtil.ATTRIB_NOKIA_MIDLET_TAP_DETECTION_OPTIONS);
if(tapAttr != null)
{
if(tapAttr.indexOf(',') == -1)
{
setDefaultTapValues();
}
else
{
String twipString = tapAttr.substring(0, tapAttr.indexOf(',')).trim();
String timeoutString = tapAttr.substring(tapAttr.indexOf(',') + 1, tapAttr.length()).trim();
try
{
twips = Integer.parseInt(twipString);
timeout = Integer.parseInt(timeoutString);
// If any one of the value is zero or Negative, set defaults.
// If both are zero disable the tapdetection.
if((twips == 0) && (timeout == 0))
{
disableTapDetection = true;
}
else if((twips <= 0) || (timeout <= 0))
{
setDefaultTapValues();
}
}
catch(NumberFormatException e)
{
// Alpha Numeric Values of Twips and Timeout
setDefaultTapValues();
}
}
}
else
{
setDefaultTapValues();
}
}
private void setDefaultTapValues()
{
twips = DEFAULT_TWIPS;
timeout = DEFAULT_TIMEOUT;
}
/**
* Creates the eSWT control.
*
* @param parent where to create.
* @param item on which it is based. Must be CustomItem.
* @return Control.
*/
Control eswtGetControl(Composite parent, Item item)
{
Control ret = new CanvasExtension(parent,
(noBackground ? SWT.NO_BACKGROUND : SWT.NONE));
return ret;
}
/**
* Set the size of the layouted Control.
*/
void eswtResizeControl(Item item, Control control, int width, int height)
{
super.eswtResizeControl(item, control, width, height);
((CustomItem)item).internalHandleSizeChanged(width, height);
}
/**
* Add listeners to Layouter specific control.
*/
void eswtAddSpecificListeners(Item item, Control control)
{
super.eswtAddSpecificListeners(item, control);
CanvasExtension canvas = (CanvasExtension) control;
CIPaintListener pl = new CIPaintListener((CustomItem) item);
canvas.addPaintListener(pl);
canvas.setData(PAINT_LISTENER, pl);
CIMouseListener ml = new CIMouseListener((CustomItem) item);
canvas.addMouseListener(ml);
canvas.addMouseMoveListener(ml);
canvas.setData(MOUSE_LISTENER, ml);
}
/**
* Remove listeners from Layouter specific control.
*/
void eswtRemoveSpecificListeners(Item item, Control control)
{
super.eswtRemoveSpecificListeners(item, control);
CanvasExtension canvas = (CanvasExtension) control;
CIPaintListener pl = (CIPaintListener) canvas.getData(PAINT_LISTENER);
if(pl != null)
{
canvas.removePaintListener(pl);
canvas.setData(PAINT_LISTENER, null);
}
CIMouseListener ml = (CIMouseListener) canvas.getData(MOUSE_LISTENER);
if(ml != null)
{
canvas.removeMouseListener(ml);
canvas.removeMouseMoveListener(ml);
canvas.setData(MOUSE_LISTENER, null);
}
}
/**
* Returns if this eSWT control is Layouter specific.
*/
boolean eswtIsSpecificControl(Item item, Control control)
{
return (control instanceof CanvasExtension);
}
/**
* Updates the values of CustomItem.
*/
void eswtUpdateItem(Item item, Control control, int reason, Object param)
{
if(reason == CustomItem.UPDATE_REASON_REPAINT)
{
Rectangle rect = (Rectangle) param;
((CustomItem)item).updateItem(rect, control);
}
}
/**
* Gets Canvas direction based on SWT arrows.
*/
int getCanvasDirection(int key)
{
int ret = 0;
switch(key)
{
case SWT.ARROW_DOWN:
ret = Canvas.DOWN;
break;
case SWT.ARROW_UP:
ret = Canvas.UP;
break;
case SWT.ARROW_LEFT:
ret = Canvas.LEFT;
break;
case SWT.ARROW_RIGHT:
ret = Canvas.RIGHT;
break;
default:
}
return ret;
}
/**
* Gets the specified visRect parameter needed for traverse.
*/
int[] getVisRect(Control control)
{
final int[] visRect = new int[4];
if(control != null)
{
Point size = control.getSize();
visRect[0] = 0;
visRect[1] = 0;
visRect[2] = size.x;
visRect[3] = size.y;
}
return visRect;
}
/**
* Returns whether the key event was consumed by CustomItem or not.
*
* @param item CustomItem.
* @param key key code.
*/
boolean eswtOfferKeyPressed(Item item, int key)
{
CustomItem customItem = (CustomItem) item;
if(!((selectionKeyCompatibility == true) && (key == -5)))
{
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent e = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_KEYPRESSED, formLayouter.getForm());
e.item = customItem;
e.keyCode = key;
eventDispatcher.postEvent(e);
}
boolean consumed = true;
int direction = getCanvasDirection(key);
if(direction > 0)
{
Control control = eswtGetFirstControl(item);
Control ctrl = eswtFindSpecificControl(item, control);
int[] visRect = getVisRect(ctrl);
// Offer event for inner traversal
consumed = customItem.traverse(direction,
formLayouter.getFormWidth(), formLayouter.getFormHeight(), visRect);
if(consumed)
{
// if inner focus is on - scroll to inner focus
Point loc = new Point(0, 0);
formLayouter.getControlPositionOnComposite(ctrl, loc);
formLayouter.eswtScrolltoRegion(loc.y + visRect[1],
loc.y + visRect[1] + visRect[3], key);
}
control.redraw();
}
return consumed;
}
/* (non-Javadoc)
* @see ItemLayouter#eswtOfferKeyReleased(Item, int)
*/
boolean eswtOfferKeyReleased(Item item, int key)
{
CustomItem customItem = (CustomItem) item;
if(!((selectionKeyCompatibility == true) && (key == -5)))
{
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent e = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_KEYRELEASED, formLayouter.getForm());
e.item = customItem;
e.keyCode = key;
eventDispatcher.postEvent(e);
}
if(getCanvasDirection(key) > 0)
{
return true;
}
return false;
}
/* (non-Javadoc)
* @see ItemLayouter#eswtFocusGained(Item, int)
*/
void eswtFocusGained(Item item, int swtDir)
{
super.eswtFocusGained(item, swtDir);
CustomItem customItem = (CustomItem) item;
Control control = eswtGetFirstControl(item);
if(control != null)
{
Control ctrl = eswtFindSpecificControl(item, control);
int[] visRect = getVisRect(ctrl);
if(customItem.traverse(getCanvasDirection(swtDir),
formLayouter.getFormWidth(), formLayouter.getFormHeight(), visRect))
{
// if inner focus is on - scroll to inner focus
Point loc = new Point(0, 0);
formLayouter.getControlPositionOnComposite(ctrl, loc);
formLayouter.eswtScrolltoRegion(loc.y + visRect[1],
loc.y + visRect[1] + visRect[3], swtDir);
}
control.redraw();
}
}
/* (non-Javadoc)
* @see ItemLayouter#eswtFocusLost(Item)
*/
void eswtFocusLost(Item item)
{
super.eswtFocusLost(item);
CustomItem customItem = (CustomItem) item;
customItem.traverseOut();
}
/* (non-Javadoc)
* @see temLayouter#eswtHandleShow(Item)
*/
void eswtHandleShow(Item item)
{
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent event = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_SHOWNOTIFY, formLayouter.getForm());
event.item = item;
eventDispatcher.postEvent(event);
CustomItem customItem = (CustomItem) item;
customItem.repaint();
}
/* (non-Javadoc)
* @see ItemLayouter#eswtHandleHide(Item)
*/
void eswtHandleHide(Item item)
{
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent event = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_HIDENOTIFY, formLayouter.getForm());
event.item = item;
eventDispatcher.postEvent(event);
}
/**
* Returns the minimum area needed to display a CustomItem.
*
* @param customitem CustomItem object.
* @return Minimum area needed to display CustomItem.
*/
static Point calculateMinimumBounds(final CustomItem customitem)
{
Point minSize = new Point(customitem.getMinContentWidth(),
customitem.getMinContentHeight());
applyMinMargins(customitem, minSize);
return minSize;
}
/**
* Returns the preferred area needed to display a CustomItem.
*
* @param item CustomItem object.
* @return preferred area needed to display CustomItem.
*/
static Point calculatePreferredBounds(final Item item)
{
CustomItem customitem = (CustomItem) item;
Point prefSize = new Point(
customitem.getPrefContentWidth(item.getLockedPreferredHeight()),
customitem.getPrefContentHeight(item.getLockedPreferredWidth()));
applyPrefMargins(item, prefSize);
return prefSize;
}
/**
* Paint listener.
*/
class CIPaintListener implements PaintListener
{
private CustomItem customItem;
CIPaintListener(CustomItem customItem)
{
this.customItem = customItem;
}
public void paintControl(PaintEvent pe)
{
// graphicsBuffer may be null when control
// is brought to foreground the first time
if(customItem.graphicsBuffer != null)
{
// If we have initiated a synchronous paint event
// draw the buffer to display here
if(customItem.graphicsBuffer.isPaintingActive())
{
customItem.graphicsBuffer.blitToDisplay(pe.gc.getGCData().internalGc, null);
}
}
else
{
// Native toolkit is requesting an update of an area that has
// become invalid. Can't do anything here because the contents
// need to be queried from the MIDlet in another thread by
// a paint callback. For this a paint callback event is posted.
// For a moment the native toolkit thinks that the area has
// been validated when in truth it will be painted later after
// the paint callback has been executed.
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent event = eventDispatcher.newEvent(
LCDUIEvent.CUSTOMITEM_PAINT_NATIVE_REQUEST, formLayouter.getForm());
event.x = pe.x;
event.y = pe.y;
event.width = pe.width;
event.height = pe.height;
event.widget = pe.widget;
event.item = customItem;
eventDispatcher.postEvent(event);
}
}
}
/**
* Mouse listener.
*/
class CIMouseListener implements MouseListener, MouseMoveListener
{
private CustomItem customItem;
CIMouseListener(CustomItem customItem)
{
this.customItem = customItem;
}
public void mouseDown(MouseEvent event)
{
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent e = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_POINTERPRESSED, formLayouter.getForm());
e.item = customItem;
e.x = event.x;
e.y = event.y;
eventDispatcher.postEvent(e);
if(!disableTapDetection)
{
// Supress Drag events
suppressDragEvent = true;
pointerDownX = event.x;
pointerDownY = event.y;
// Create and Schedule Timer
timerTask = new CustomItemTimerTask();
timer.schedule(timerTask, timeout);
}
}
public void mouseUp(MouseEvent event)
{
int pointerUpX = event.x;
int pointerUpY = event.y;
if(!disableTapDetection)
{
timerTask.cancel();
timerTask = null;
// If Timer not expired and Mouseup is withing rectangle assign
// PointercDown to Pinter Up
if(suppressDragEvent && checkWithinRect(event.x, event.y))
{
pointerUpX = pointerDownX;
pointerUpY = pointerDownY;
suppressDragEvent = false;
}
}
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent e = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_POINTERRELEASED, formLayouter.getForm());
e.item = customItem;
e.x = pointerUpX;
e.y = pointerUpY;
eventDispatcher.postEvent(e);
}
public void mouseMove(MouseEvent event)
{
// Check for timeout expiry and PointerUp falls outside rectangle
if(disableTapDetection || (!suppressDragEvent) || !checkWithinRect(event.x, event.y))
{
EventDispatcher eventDispatcher = EventDispatcher.instance();
LCDUIEvent e = eventDispatcher.newEvent(LCDUIEvent.CUSTOMITEM_POINTERDRAGGED, formLayouter.getForm());
e.item = customItem;
e.x = event.x;
e.y = event.y;
eventDispatcher.postEvent(e);
}
}
public void mouseDoubleClick(MouseEvent event)
{
}
boolean checkWithinRect(int x, int y)
{
// Get pixel per inch
Point P = Display.getCurrent().getDPI();
float xPxielwidth = (twips * P.x) / 1440;
float yPixelHeight = (twips * P.y) / 1440;
int RightX = pointerDownX + (int) xPxielwidth;
// If the rectange width falls outside the custom area
if(RightX > customItem.getContentWidth())
{
RightX = customItem.getContentWidth();
}
int LeftX = pointerDownX - (int) xPxielwidth;
// If the rectange width falls outside the custom area
if(LeftX < 0)
{
LeftX = 0;
}
int TopY = pointerDownY - (int) yPixelHeight;
// If the rectange height falls outside the custom area
if(TopY < 0)
{
TopY = 0;
}
int DownY = pointerDownY + (int) yPixelHeight;
// If the rectange heightfalls outside the custom area.
if(DownY > customItem.getContentHeight())
{
DownY = customItem.getContentHeight();
}
// Find the PointerUp is withing rectange
if((x >= LeftX) && (x <= RightX))
{
if((y >= TopY) && (y <= DownY))
{
return true;
}
}
return false;
}
}
class CustomItemTimerTask extends TimerTask
{
public void run()
{
suppressDragEvent = false;
}
}
}