/*
* SVGPickScreen.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.svg.svgpickdemo;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
import javax.microedition.m2g.SVGImage;
import javax.microedition.m2g.ScalableGraphics;
import javax.microedition.m2g.ScalableImage;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.util.MathUtilities;
import org.w3c.dom.Document;
import org.w3c.dom.svg.SVGElement;
import org.w3c.dom.svg.SVGLocatableElement;
import org.w3c.dom.svg.SVGMatrix;
import org.w3c.dom.svg.SVGRect;
/**
* SVG cursor program to select from a group of selectable shapes and display
* stats.
*
* The attribute values of the SVG elements in sample.svg were hard-coded in a
* way to display the image correctly on a 9500 device. However, one can
* programmatically adjust those values by calling setFloatTrait() on the
* SVGElement.
*/
public final class SVGPickScreen extends MainScreen {
private static final int CURSOR_X_DEFAULT = 30;
private static final int CURSOR_Y_DEFAULT = 60;
private static final int CURSOR_MOVE_FACTOR = 10;
private ScalableGraphics _scalableGraphics;
private SVGImage _image;
private Document _document;
private int _cursorX;
private int _cursorY;
private SVGElement _cursor;
private SVGElement _cursorPosition;
private SVGElement _selectableShapesGroup;
private final Vector _selectableShapes = new Vector();
private SVGElement _selectedShapeBounds;
private SVGElement _display;
private SVGElement _displayName;
private SVGElement _displayBounds;
private int _displayWidth;
private int _displayHeight;
/**
* Default constructor.
*/
public SVGPickScreen() {
setTitle("SVG Pick Demo");
try {
// Load SVG from svg
final InputStream inputStream =
getClass().getResourceAsStream("/sample.svg");
// Create SVGImage
_image = (SVGImage) ScalableImage.createImage(inputStream, null);
// Create Document
_document = _image.getDocument();
// Create ScalableGraphics Instance
_scalableGraphics = ScalableGraphics.createInstance();
_displayWidth = Display.getWidth();
_displayHeight = Display.getHeight();
// Initialize our view bindings.
intializeElements();
// Initial position of cursor point.
updateCursor(CURSOR_X_DEFAULT, CURSOR_Y_DEFAULT);
} catch (final IOException e) {
System.exit(1);
}
}
/**
* Gets and initializes any view elements.
*/
private void intializeElements() {
// Get the cursor related elements.
_cursor = (SVGElement) _document.getElementById("cursor");
_cursorPosition =
(SVGElement) _document.getElementById("cursorPosition");
// Get the display elements which we will interact with
_display = (SVGElement) _document.getElementById("display");
_displayName = (SVGElement) _document.getElementById("displayName");
_displayBounds = (SVGElement) _document.getElementById("displayBounds");
// Get the selected shape elements and get the child elements of the
// group
// adding them to the array which is being managed.
_selectedShapeBounds =
(SVGElement) _document.getElementById("selectedShapeBounds");
_selectableShapesGroup =
(SVGElement) _document.getElementById("selectableShapesGroup");
// Append all our selectable elements to the managed set.
SVGLocatableElement element =
(SVGLocatableElement) _selectableShapesGroup.getFirstChild();
_selectableShapes.addElement(element);
while ((element = (SVGLocatableElement) element.getNextSibling()) != null) {
_selectableShapes.addElement(element);
}
}
/**
* @see net.rim.device.api.ui.Screen#touchEvent(TouchEvent)
*/
protected boolean touchEvent(final TouchEvent message) {
final int touchEvent = message.getEvent();
if (touchEvent == TouchEvent.DOWN || touchEvent == TouchEvent.CLICK
|| touchEvent == TouchEvent.MOVE) {
final int dx = message.getX(1) - _cursorX;
final int dy = message.getY(1) - _cursorY;
updateCursor(dx, dy);
}
return true;
}
/**
* @see Screen#navigationMovement(int, int, int, int)
*/
protected boolean navigationMovement(final int dx, final int dy,
final int status, final int time) {
final int deltaX = CURSOR_MOVE_FACTOR * dx;
final int deltaY = CURSOR_MOVE_FACTOR * dy;
// Update our new cursor position
updateCursor(deltaX, deltaY);
return true;
}
/**
* Updates our cursor position.
*
* @param deltaX
* The change in x of the cursor position
* @param deltaY
* The change in y of the cursor position
*/
private void updateCursor(final int deltaX, final int deltaY) {
// Translate the cursor pointer.
final int translateX = clampDelta(0, _displayWidth, _cursorX, deltaX);
final int translateY = clampDelta(0, _displayHeight, _cursorY, deltaY);
final SVGMatrix transform = _cursor.getMatrixTrait("transform");
transform.mTranslate(translateX, translateY);
_cursor.setMatrixTrait("transform", transform);
// Update cursor position Text
_cursorX = MathUtilities.clamp(0, _cursorX + translateX, _displayWidth);
_cursorY =
MathUtilities.clamp(0, _cursorY + translateY, _displayHeight);
_cursorPosition.setTrait("#text", "" + _cursorX + ',' + _cursorY);
// If a shape is selected then update the display.
final SVGLocatableElement selected = findFirstSelectedShape();
if (selected != null) {
// Get the bounding box of selected object
final SVGRect rect = selected.getBBox();
// Update the display for the selected shape.
updateDisplay(true, selected.getTrait("id"), rect);
// Updated the bounding box for the selected shape.
updateBounds(true, rect);
} else {
updateDisplay(false, null, null);
updateBounds(false, null);
}
invalidate();
}
/**
* Clamps the delta value to keep (current value + delta) value to between
* the min and max bounds.
*
* Note: The minimum bound should be less than, or equal to, the maximum
* bound.
*
* @param min
* The minimum bound
* @param max
* The maximum bound
* @param current
* The current value
* @param delta
* The change in value
* @return The new delta which will keep the (current + delta) value
* inbounds
*/
private int clampDelta(final int min, final int max, final int current,
final int delta) {
final int currentAndDelta = current + delta;
if (currentAndDelta < min) {
return min - current;
} else if (currentAndDelta > max) {
return max - current;
} else {
return delta;
}
}
/**
* Searches for the first selected shape.
*
* @return The shape that is located or <code>null</code> if no shape is
* found.
*/
private SVGLocatableElement findFirstSelectedShape() {
// Scroll through shapes, determine if cursor point
// is within shape's bounding box, and update.
SVGRect box;
for (int i = 0; i < _selectableShapes.size(); i++) {
final SVGLocatableElement element =
(SVGLocatableElement) _selectableShapes.elementAt(i);
box = element.getBBox();
// Do a bounding box to point test.
if (_cursorX >= box.getX()
&& _cursorX <= box.getX() + box.getWidth()
&& _cursorY >= box.getY()
&& _cursorY <= box.getY() + box.getHeight()) {
return element;
}
}
return null;
}
/**
* Updates our display id and bounds region.
*
* @param enabled
* <code>true</code> if the display is enabled.
* <code>false</code> if disabled.
* @param id
* The id of the selected shape.
* @param bounds
* The bounds of the selected shape.
*/
private void updateDisplay(final boolean enabled, final String id,
final SVGRect bounds) {
if (enabled) {
_display.setTrait("display", "inline");
if (id != null) {
// Update the id.
_displayName.setTrait("#text", id);
}
if (bounds != null) {
// Update the bounds region.
final StringBuffer boundsText = new StringBuffer();
boundsText.append(bounds.getX());
boundsText.append(',');
boundsText.append(bounds.getY());
boundsText.append(',');
boundsText.append(bounds.getWidth());
boundsText.append(',');
boundsText.append(bounds.getHeight());
_displayBounds.setTrait("#text", boundsText.toString());
}
} else {
_display.setTrait("display", "none");
}
}
/**
* Updates the selected shape bounding box.
*
* @param enabled
* <code>true</code> if the display is enabled.
* <code>false</code> if disabled.
* @param rect
* The rectangle whose bounds should be updated
*/
private void updateBounds(final boolean enabled, final SVGRect rect) {
if (enabled) {
// Show the bounding box.
_selectedShapeBounds.setTrait("display", "inline");
if (rect != null) {
// Updates the actually selected shapes bounding box.
_selectedShapeBounds.setFloatTrait("x", rect.getX());
_selectedShapeBounds.setFloatTrait("y", rect.getY());
_selectedShapeBounds.setFloatTrait("width", rect.getWidth());
_selectedShapeBounds.setFloatTrait("height", rect.getHeight());
}
} else {
// Hide the bounding box.
_selectedShapeBounds.setTrait("display", "none");
}
}
/**
* @see Screen#paint(Graphics)
*/
protected void paint(final Graphics graphics) {
super.paint(graphics);
if (_image == null) {
return;
}
// Bind target Graphics
_scalableGraphics.bindTarget(graphics);
// Set the viewport dimensions
_image.setViewportWidth(_displayWidth);
_image.setViewportHeight(_displayHeight);
// Render the svg image/ model
_scalableGraphics.render(0, 0, _image);
// Release bindings on Graphics
_scalableGraphics.releaseTarget();
}
/**
* @see net.rim.device.api.ui.container.FullScreen#sublayout(int, int)
*/
protected void sublayout(final int width, final int height) {
if (_displayWidth != width || _displayHeight != height) {
_displayWidth = width;
_displayHeight = height;
}
super.sublayout(width, height);
}
}