package com.rapidftr.controls;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Display;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.system.KeypadListener;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.ContextMenu;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.component.Dialog;
/**
* A custom field that allows for scrolling of an image that is larger than the field can display at one time.
*/
public class ScrollableImageField extends BitmapField {
private Bitmap theImage; //The image to display.
private int preferredHeight; //The height the field request.
private int preferredWidth; //The width the field requests.
private int xCoord = 0; //The x coordinate for the top left corner of the image.
private int yCoord = 0; //The y coordinate for the top left corner of the image.
private static final int[] COLOURS = {Color.WHITE, Color.GRAY, Color.GRAY, Color.WHITE}; //Colours used to draw the scroll arrows.
private static final int TRIANGLE_SIZE = 6; //The scroll arrow size.
private static final int TRIANGLE_OFFSET = 1; //The scroll arrow offset from the edge of the focus bar.
private MenuItem helpMenu; //The help MenuItem for this field.
private int imageYCord;
public ScrollableImageField(Bitmap theImage, int imageYCord) {
super(theImage, BitmapField.FOCUSABLE);
this.imageYCord = imageYCord;
this.theImage = theImage;
calculateSize();
//Initialize the help MenuItem.
helpMenu = new MenuItem("Image Help", 40, 40) {
public void run() {
Dialog.alert("Scroll the trackwheel to view the image. Scroll to the end of the image or hold down Alt while scrolling to leave the field.");
}
};
}
public ScrollableImageField(EncodedImage theImage) {
this(theImage.getBitmap(), 0);
}
//Calculates the preferred width and height of the field and determines if the image should scroll.
private void calculateSize() {
//Set the preferred height to the image size or screen height if the image is larger than the screen height.
int viewPortHeight = Display.getHeight() - imageYCord;
int viewPortWidth = Display.getWidth();
if (theImage.getHeight() > viewPortHeight) {
preferredHeight = viewPortHeight;
} else {
preferredHeight = theImage.getHeight();
}
//Set the preferred width to the image size or screen width if the image is larger than the screen width.
if (theImage.getWidth() > viewPortWidth) {
preferredWidth = viewPortWidth;
} else {
preferredWidth = theImage.getWidth();
}
}
//Override setBitmap to perform new size calculations.
public void setBitmap(Bitmap bitmap) {
super.setBitmap(bitmap);
//Reset the image location to display the top left of the image.
xCoord = 0;
yCoord = 0;
theImage = bitmap;
calculateSize();
}
//Override setImage to perform new size calculations.
public void setImage(EncodedImage image) {
theImage = image.getBitmap();
//Reset the image location to display the top left of the image.
xCoord = 0;
yCoord = 0;
this.setBitmap(theImage);
calculateSize();
}
public int getPreferredHeight() {
return preferredHeight;
}
public int getPreferredWidth() {
return preferredWidth;
}
protected void paint(Graphics graphics) {
//Get the actual field size from its manager.
XYRect rect = this.getManager().getContentRect();
//Draw the Bitmap, taking up the full size of the BitmapField.
//Start drawing the bitmap at the current coordinates.
graphics.drawBitmap(0, 0, rect.width, rect.height, theImage, xCoord, yCoord);
}
protected void drawFocus(Graphics graphics, boolean on) {
if (!on)
return;
//Get the field size that is available from its manager.
XYRect rect = this.getManager().getContentRect();
graphics.setColor(Color.BLUE);
int width = rect.width;
int height = rect.height;
//The manager may allocate more space than we need.
//Make sure our dimensions are not greater than the image's.
if (width > theImage.getWidth()) {
width = theImage.getWidth();
}
if (height > theImage.getHeight()) {
height = theImage.getHeight();
}
//Determine if the image is larger than the displayed area..
if (width < theImage.getWidth() || height < theImage.getHeight()) {
//It is, draw scrolling indicators.
//All point paths start at the inner tip of the triangle and return back to it.
int centerWidth = width / 2;
int centerHeight = height / 2;
//Draw the top triangle.
int[] xPts = {centerWidth, centerWidth + TRIANGLE_SIZE, centerWidth - TRIANGLE_SIZE, centerWidth};
int[] yPts = {TRIANGLE_OFFSET, TRIANGLE_OFFSET + TRIANGLE_SIZE, TRIANGLE_OFFSET + TRIANGLE_SIZE, TRIANGLE_OFFSET};
graphics.drawShadedFilledPath(xPts, yPts, null, COLOURS, null);
//Draw the bottom arrow.
yPts[0] = height - TRIANGLE_OFFSET;
yPts[1] = height - TRIANGLE_SIZE;
yPts[2] = height - TRIANGLE_SIZE;
yPts[3] = height - TRIANGLE_OFFSET;
graphics.drawShadedFilledPath(xPts, yPts, null, COLOURS, null);
//Draw the left arrow.
xPts[0] = TRIANGLE_OFFSET;
xPts[1] = TRIANGLE_OFFSET + TRIANGLE_SIZE;
xPts[2] = TRIANGLE_OFFSET + TRIANGLE_SIZE;
xPts[3] = TRIANGLE_OFFSET;
yPts[0] = centerHeight;
yPts[1] = centerHeight + TRIANGLE_SIZE;
yPts[2] = centerHeight - TRIANGLE_SIZE;
yPts[3] = centerHeight;
graphics.drawShadedFilledPath(xPts, yPts, null, COLOURS, null);
//Draw the right arrow.
xPts[0] = width - TRIANGLE_OFFSET;
xPts[1] = width - TRIANGLE_SIZE;
xPts[2] = width - TRIANGLE_SIZE;
xPts[3] = width - TRIANGLE_OFFSET;
graphics.drawShadedFilledPath(xPts, yPts, null, COLOURS, null);
}
}
//Override navigationMovement to provide image scrolling.
protected boolean navigationMovement(int dx, int dy, int status, int time) {
//Alt + scroll bypasses scrolling within the field.
if ((status & KeypadListener.STATUS_ALT) != 0) {
return super.navigationMovement(dx, dy, status, time);
}
if (scrollImage(dx * 5, dy * 5)) {
//The image was scrolled and the movement consumed.
return true;
} else {
//Scrolling is not required.
return super.navigationMovement(dx, dy, status, time);
}
}
//Determines if scrolling is required and handles the positioning of the image
//when scrolling occurs. Returns true if the image was scrolled.
private boolean scrollImage(int xScroll, int yScroll) {
//Get the actual field size from its manager.
XYRect rect = this.getManager().getContentRect();
//Determine if the image is larger than the field.
if (rect.width < theImage.getWidth() || rect.height < theImage.getHeight()) {
//Image is larger than the field. Enable scrolling support.
xCoord += xScroll;
yCoord += yScroll;
//If the user has scrolled to the end of the image, use the default
//navigationMovement to allow focus to scroll off the field.
if (xCoord < 0) {
xCoord = 0;
}
if (yCoord < 0) {
yCoord = 0;
}
if (((theImage.getWidth() - xCoord) < rect.width) ||
((theImage.getHeight() - yCoord) < rect.height)) {
//Ensure the coordinates do not go lower than 0.
//Ensure that we don't scroll beyond the image size (causing white space to be drawn).
if ((theImage.getWidth() - xCoord) < rect.width) {
xCoord = theImage.getWidth() - rect.width;
}
if ((theImage.getHeight() - yCoord) < rect.height) {
yCoord = theImage.getHeight() - rect.height;
}
if (xCoord < 0) {
xCoord = 0;
}
if (yCoord < 0) {
yCoord = 0;
}
//Scrolling is not required.
return false;
} else {
//The image was scrolled and the movement consumed.
//Redraw the bitmap.
this.invalidate();
return true;
}
} else {
//Scrolling is not required.
return false;
}
}
//Override makeContextMenu to add the field's help MenuItem.
protected void makeContextMenu(ContextMenu contextMenu) {
contextMenu.addItem(helpMenu);
}
}