package ie.dcu.swt;
import ie.dcu.swt.event.*;
import ie.dcu.util.FileUtils;
import java.io.*;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.swt.*;
import org.eclipse.swt.dnd.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
/**
* Miscellaneous SWT Utility functions.
*
* @author Kevin McGuinness
*/
public class SwtUtils {
/**
* Out-code TOP.
*/
public static final int TOP = java.awt.Rectangle.OUT_TOP;
/**
* Out-code LEFT.
*/
public static final int LEFT = java.awt.Rectangle.OUT_LEFT;
/**
* Out-code BOTTOM.
*/
public static final int BOTTOM = java.awt.Rectangle.OUT_BOTTOM;
/**
* Out-code RIGHT.
*/
public static final int RIGHT = java.awt.Rectangle.OUT_RIGHT;
/**
* Clone a rectangle.
*/
public static Rectangle clone(Rectangle r) {
return new Rectangle(r.x, r.y, r.width, r.height);
}
/**
* Clone a point.
*/
public static Point clone(Point p) {
return new Point(p.x, p.y);
}
/**
* Scale a rectangle.
*
* @param r
* The rectangle.
* @param scale
* The scale.
* @return A scaled rectangle.
*/
public static Rectangle scale(Rectangle r, float scale) {
return new Rectangle(
(int) Math.floor(r.x * scale),
(int) Math.floor(r.y * scale),
(int) Math.floor(r.width * scale),
(int) Math.floor(r.height * scale)
);
}
/**
* Convert a SWT rectangle to an AWT rectangle.
*
* @param r
* An SWT Rectangle.
* @return An AWT Rectangle.
*/
public static java.awt.Rectangle convert(Rectangle r) {
return new java.awt.Rectangle(r.x, r.y, r.width, r.height);
}
/**
* Determines where the specified coordinates lie with respect to this
* Rectangle. This method computes a binary OR of the
* appropriate mask values indicating, for each side of this
* Rectangle, whether or not the specified coordinates are on
* the same side of the edge as the rest of this Rectangle.
*
* @param r
* A rectangle.
* @param p
* A point.
* @return the logical OR of all appropriate out codes.
* @see #LEFT
* @see #TOP
* @see #RIGHT
* @see #BOTTOM
*/
public static int outcode(Rectangle r, Point p) {
return convert(r).outcode(p.x, p.y);
}
/**
* Determines where the specified coordinates lie with respect to this
* Rectangle. This method computes a binary OR of the
* appropriate mask values indicating, for each side of this
* Rectangle, whether or not the specified coordinates are on
* the same side of the edge as the rest of this Rectangle.
*
* @param r
* A rectangle.
* @param x
* the specified x coordinate
* @param y
* the specified y coordinate
* @return the logical OR of all appropriate out codes.
*
* @see #LEFT
* @see #TOP
* @see #RIGHT
* @see #BOTTOM
*/
public static int outcode(Rectangle r, float x, float y){
return convert(r).outcode(x, y);
}
// Point class for Liang-Barsky algorithm
private static class Pt {
float x, y;
Pt(float x, float y) {
this.x = x; this.y = y;
}
}
// Rectangle class for Liang-Barsky algorithm
private static class Rect {
float ymin, ymax, xmin, xmax;
Rect(float x, float y, float w, float h) {
// max needs -1 otherwise its outside the rectangle!
ymin = y; ymax = y + h - 1;
xmin = x; xmax = x + w - 1;
}
boolean contains(Pt p) {
return p.x >= xmin && p.x <= xmax && p.y >= ymin && p.y <= ymax;
}
}
// Liang-Barsky clip test method
private static boolean clipt(float y, float x, Pt p) {
if (y > 0) {
float t = x / y;
if (t > p.y) {
return false;
} else if (t > p.x) {
p.x = t;
}
} else if (y < 0) {
float t = x / y;
if (t < p.x) {
return false;
} else if (t < p.y) {
p.y = t;
}
} else if (x > 0) {
return false;
}
return true;
}
// Liang-barsky line clipping algorithm
private static boolean liangBarskyClip(Rect r, Pt p, Pt q) {
float dx = q.x - p.x;
float dy = q.y - p.y;
if (dx == 0 && dy == 0 && r.contains(p)) {
return true;
}
Pt t = new Pt(0, 1);
if (clipt(dx, r.xmin - p.x, t)) {
if (clipt(-dx, p.x - r.xmax, t)) {
if (clipt(dy, r.ymin - p.y, t)) {
if (clipt(-dy, p.y - r.ymax, t)) {
if (t.y < 1) {
q.x = p.x + t.y * dx;
q.y = p.y + t.y * dy;
}
if (t.x > 0) {
p.x += t.x * dx;
p.y += t.x * dy;
}
return true;
}
}
}
}
return false;
}
/**
* Clip a line using the Liang-Barsky line clipping algorithm.
*
* @param r
* The clipping rectangle.
* @param p
* First point on the line segment. If the point lies outside the
* clipping rectangle it will be clipped to the nearest point on the
* line inside the clipping rectangle on exit.
* @param q
* Second point on the line segment. If the point lies outside the
* clipping rectangle it will be clipped to the nearest point on the
* line inside the clipping rectangle on exit.
*
* @return true if the line has a visible segment inside the
* clipping rectangle, false if it is completely
* outside the clipping window.
*/
public static boolean clip(Rectangle r, Point p, Point q) {
Rect r1 = new Rect(r.x, r.y, r.width, r.height);
Pt p1 = new Pt(p.x, p.y);
Pt p2 = new Pt(q.x, q.y);
boolean result = liangBarskyClip(r1, p1, p2);
p.x = (int) p1.x;
p.y = (int) p1.y;
q.x = (int) p2.x;
q.y = (int) p2.y;
return result;
}
/**
* Clone the given SWT image.
*
* @param image
* An SWT Image.
* @return A newly created SWT image that is a copy of the given image.
*/
public static Image clone(Image image) {
return new Image(Display.getCurrent(), image, SWT.IMAGE_COPY);
}
/**
* Apply the given SWT system color as the foreground color in the given
* graphics context.
*
* @param gc
* The graphics context.
* @param swtColor
* An SWT system color.
*/
public static void setForeground(GC gc, int swtColor) {
gc.setForeground(Display.getCurrent().getSystemColor(swtColor));
}
/**
* Apply the given SWT system color as the background color in the given
* graphics context.
*
* @param gc
* The graphics context.
* @param swtColor
* An SWT system color.
*/
public static void setBackground(GC gc, int swtColor) {
gc.setBackground(Display.getCurrent().getSystemColor(swtColor));
}
/**
* Returns a standard RGB palette.
*
* @return An RGB palette.
*/
public static PaletteData getRgbPalette() {
return new PaletteData(0xff, 0xff00, 0xff0000);
}
/**
* Create an RGB image with a transparent component, and fill the image with
* the transparent value.
*
* @param width
* The image width.
* @param height
* The image height.
* @return A newly created Image object.
*/
public static Image createTransparentImage(int width, int height) {
PaletteData palette = getRgbPalette();
// Use a bit depth of 32 to allow transparent pixel outside RGB space
ImageData data = new ImageData(width, height, 32, palette);
// Create a transparent pixel outside the RGB color space
int transparent = palette.getPixel(new RGB(255,255,255)) + 2;
data.transparentPixel = transparent;
// Create a row of transparent pixels
int [] pixels = new int[data.width];
for (int i = 0; i < pixels.length; i++) {
pixels[i] = transparent;
}
// Copy pixels to entire image
for (int y = 0; y < data.height; y++) {
data.setPixels(0, y, pixels.length, pixels, 0);
}
// Create and return the image
return new Image(Display.getCurrent(), data);
}
/**
* Create a standard image for the current display with the given width and
* height.
*
* @param w
* The image width.
* @param h
* The image height.
* @return A newly created Image object.
*/
public static Image createImage(int w, int h) {
return new Image(Display.getCurrent(), w, h);
}
/**
* Create a new image with for the current display with the size given by
* the width and height of the specified bounds object.
*
* @param bounds
* The image bounds.
* @return A newly created Image object.
*/
public static Image createImage(Rectangle bounds) {
return createImage(bounds.width, bounds.height);
}
/**
* Scale the given image to fit inside the specified Rectangle.
*
* @param image
* An image
* @param rect
* The rectangle to fit to
* @param maintainAspectRatio
* if true the aspect ratio of the image is
* maintained.
* @return A newly created scaled image.
*/
public static Image scaleImageToFit(Image image,
Rectangle rect, boolean maintainAspectRatio)
{
return scaleImageToFit(image, rect.width, rect.height,
maintainAspectRatio);
}
/**
* Scale the given image to fit inside the specified height and width.
*
* @param image
* An image
* @param width
* The width
* @param height
* The height
* @param maintainAspectRatio
* if true the aspect ratio of the image is
* maintained.
* @return A newly created scaled image.
*/
public static Image scaleImageToFit(Image image,
int width, int height, boolean maintainAspectRatio)
{
ImageData data = image.getImageData();
data = scaleImageDataToFit(data, width, height, maintainAspectRatio);
return new Image(image.getDevice(), data);
}
/**
* Scale the given ImageData to fit inside the specified Rectangle.
*
* @param data
* A non-null ImageData object
* @param rect
* The rectangle to fit to
* @param maintainAspectRatio
* if true the aspect ratio of the image is
* maintained.
* @return A scaled ImageData object.
*/
public static ImageData scaleImageDataToFit(ImageData data,
Rectangle rect, boolean maintainAspectRatio)
{
return scaleImageDataToFit(data, rect.width, rect.height,
maintainAspectRatio);
}
/**
* Scale the given ImageData to fit inside the specified height and width.
*
* @param data
* A non-null ImageData object
* @param width
* The width to fit to
* @param height
* The height to fit to
* @param maintainAspectRatio
* if true the aspect ratio of the image is
* maintained.
* @return A scaled ImageData object.
*/
public static ImageData scaleImageDataToFit(ImageData data,
int width, int height, boolean maintainAspectRatio)
{
if (width <= 0) {
throw new IllegalArgumentException("width <= 0");
}
if (height <= 0) {
throw new IllegalArgumentException("height <= 0");
}
if (data == null) {
throw new IllegalArgumentException("data == null");
}
if (!maintainAspectRatio) {
// Direct scale
data = data.scaledTo(width, height);
} else {
// Compute scale
double sx = width / (double) data.width;
double sy = height / (double) data.height;
double scale = Math.min(sx, sy);
// Compute new dimensions
int newWidth = (int) (data.width * scale);
int newHeight = (int) (data.height * scale);
// Clamp dimensions
newWidth = Math.max(Math.min(newWidth, width), 1);
newHeight = Math.max(Math.min(newHeight, height), 1);
// Scale
data = data.scaledTo(newWidth, newHeight);
}
return data;
}
/**
* Load an image for the current display from the given file.
*
* @param file
* A file.
* @return A newly created image.
* @throws IOException
* If an error occurs loading the image.
*/
public static Image loadImage(File file)
throws IOException
{
try {
return new Image(Display.getCurrent(), file.getAbsolutePath());
} catch (SWTException e) {
// Translate to checked exception!
throw new IOException("SWTException: " + e.getMessage());
}
}
/**
* Load an image for the current display from the given InputStream.
*
* @param in
* An input stream.
* @return A newly created image.
* @throws IOException
* If an error occurs loading the image.
*/
public static Image loadImage(InputStream in)
throws IOException
{
try {
return new Image(Display.getCurrent(), in);
} catch (SWTException e) {
// Translate to checked exception!
throw new IOException("SWTException: " + e.getMessage());
}
}
/**
* Load the ImageData object from the given file.
*
* @param file
* A file.
* @return A newly created image.
* @throws IOException
* If an error occurs loading the image.
*/
public static ImageData loadImageData(File file)
throws IOException
{
try {
return new ImageData(file.getAbsolutePath());
} catch (SWTException e) {
// Translate to checked exception!
throw new IOException("SWTException: " + e.getMessage());
}
}
/**
* Load the ImageData object from the given InputStream.
*
* @param in
* An input stream.
* @return A newly created image.
* @throws IOException
* If an error occurs loading the image.
*/
public static ImageData loadImageData(InputStream in)
throws IOException
{
try {
return new ImageData(in);
} catch (SWTException e) {
// Translate to checked exception!
throw new IOException("SWTException: " + e.getMessage());
}
}
/**
* Add a label to the given ToolBar. The label will be aligned to the
* vertical center of the ToolBar.
*
* @param bar
* A ToolBar.
* @param text
* The label text.
*/
public static void addLabel(ToolBar bar, String text) {
Composite box = new Composite(bar, SWT.NONE);
box.setLayout(new GridLayout());
// Create label
Label label = new Label(box, SWT.NONE);
label.setText(text);
// Layout in center of toolbar
GridData data = new GridData();
data.grabExcessVerticalSpace = true;
data.verticalAlignment = SWT.CENTER;
label.setLayoutData(data);
// Set item size
Point sz = box.computeSize(SWT.DEFAULT, SWT.DEFAULT);
ToolItem item = new ToolItem(bar, SWT.SEPARATOR);
item.setWidth(sz.x);
// Set control
item.setControl(box);
}
public static void addLabelToComposite(Composite parent, String text) {
Composite box = new Composite(parent, SWT.NONE);
box.setLayout(new GridLayout());
// Create label
Label label = new Label(box, SWT.NONE);
label.setText(text);
GridData data = new GridData();
data.grabExcessVerticalSpace = true;
data.grabExcessHorizontalSpace = true;
//data.heightHint = heightInt;
label.setLayoutData(data);
}
public static Text addTextFieldToComposite(Composite parent) {
Composite box = new Composite(parent, SWT.NONE);
box.setLayout(new GridLayout(SWT.FILL,false));
// Create Single line TextField
Text text = new Text(box, SWT.BORDER | SWT.SINGLE);
GridData data = new GridData();
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
data.widthHint = 140;
text.setLayoutData(data);
return text;
}
public static Text addTextAreaToComposite(Composite parent) {
Composite box = new Composite(parent, SWT.NONE);
box.setLayout(new GridLayout());
// Create TextArea
Text text = new Text(box, SWT.BORDER | SWT.MULTI);
GridData data = new GridData();
data.grabExcessHorizontalSpace = true;
data.grabExcessVerticalSpace = true;
data.heightHint = 150;
data.widthHint = 150;
text.setLayoutData(data);
return text;
}
/**
* Add a able to the given ToolBar.
*
* @param bar
* A ToolBar.
* @param text
* The label text.
*/
public static Table addTable(Composite parent) {
// Create table
Table table = new Table(parent,SWT.NONE);
return table;
}
/**
* Add a separator to the given ToolBar.
*
* @param bar
* A ToolBar.
*/
public static void addSeparator(ToolBar bar) {
new ToolItem(bar, SWT.SEPARATOR);
}
/**
* Add a Combo box to the given ToolBar.
*
* @param bar
* A ToolBar.
* @param width
* The horizontal width to make the ToolBar.
* @param style
* The Combo box style.
*/
public static Combo addCombo(ToolBar bar, int width, int style) {
ToolItem item = new ToolItem(bar, SWT.SEPARATOR);
Combo combo = new Combo(bar, style);
item.setWidth(width);
item.setControl(combo);
return combo;
}
public static Combo addComboToComposite(Composite parent, int width, int style) {
Composite box = new Composite(parent, SWT.NONE);
box.setLayout(new GridLayout(SWT.FILL,false));
ToolBar bar = new ToolBar(box,SWT.NONE);
// Create label
Combo combo = addCombo(bar,width,SWT.NONE);
GridData data = new GridData();
data.verticalAlignment = SWT.CENTER;
data.grabExcessHorizontalSpace = true;
combo.setLayoutData(data);
return combo;
}
/**
* Add a button to the given ToolBar.
*
* @param bar
* A ToolBar.
* @param width
* The horizontal width to make the ToolBar.
*
*/
public static Button addButton(ToolBar bar, int width, String text) {
ToolItem item = new ToolItem(bar, SWT.SEPARATOR);
Button button = new Button(bar, SWT.PUSH);
button.setText(text);
item.setWidth(width);
item.setControl(button);
return button;
}
public static Button addButtonToComposite(Composite composite, int width, String text) {
ToolBar bar = new ToolBar(composite, SWT.RIGHT | SWT.FLAT);
ToolItem item = new ToolItem(bar, SWT.SEPARATOR);
Button button = new Button(bar, SWT.PUSH);
button.setText(text);
item.setWidth(width);
item.setControl(button);
return button;
}
/**
* Center the given shell on the Display.
*
* @param shell
* A shell.
*/
public static void center(Shell shell) {
Point p = shell.getSize();
Rectangle area = shell.getDisplay().getClientArea();
int x = area.width / 2 - p.x / 2 + area.x;
int y = area.height / 2 - p.y / 2 + area.y;
shell.setLocation(x, y);
}
/**
* Center the given shell with respect to the given parent shell.
*
* @param parent
* A shell.
* @param shell
* The shell to center.
*/
public static void center(Shell parent, Shell shell) {
Point p = shell.getSize();
Rectangle area = parent.getBounds();
int x = area.width / 2 - p.x / 2 + area.x;
int y = area.height / 2 - p.y / 2 + area.y;
shell.setLocation(x, y);
}
/**
* Returns the SWT image format constant for the given by examining
* the file extension of the file.
*
* @param file
* A file.
* @return The SWT image format constant.
*/
public static int getImageFormat(File file) {
String ext = FileUtils.getExtension(file);
if (ext != null) {
if (ext.equals("bmp")) {
return SWT.IMAGE_BMP;
} else if (ext.equals("jpg") || ext.equals("jpeg")) {
return SWT.IMAGE_JPEG;
} else if (ext.equals("png")) {
return SWT.IMAGE_PNG;
} else if (ext.equals("gif")) {
return SWT.IMAGE_GIF;
} else if (ext.equals("ico")) {
return SWT.IMAGE_ICO;
}
}
return -1;
}
/**
* Save the given SWT image to the given file. The format will be determined
* from the file extension.
*
* @param im
* An image object.
* @param file
* The destination file.
* @throws IOException
* If there is an error saving the file or the format cannot be
* determined from the filename.
*/
public static void saveImage(Image im, File file)
throws IOException {
saveImage(im.getImageData(), file);
}
/**
* Save the given SWT ImageData to the given file. The format will be
* determined from the file extension.
*
* @param im
* An ImageData object.
* @param file
* The destination file.
* @throws IOException
* If there is an error saving the file or the format cannot be
* determined from the filename.
*/
public static void saveImage(ImageData im, File file)
throws IOException {
// Check if file is a directory
if (file.isDirectory()) {
throw new FileNotFoundException(String.format("%s is a directory", file));
}
// Check if we can write to file
if (file.exists()) {
if (!file.canWrite()) {
throw new IOException(String.format("Cannot write to %s", file));
}
}
// Get image format
int format = getImageFormat(file);
// Check for unrecognized format
if (format < 0) {
String ext = FileUtils.getExtension(file);
throw new IOException(String.format("Unknown format \"%s\"", ext));
}
// Create image loader
ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { im };
try {
// Save image
loader.save(file.getAbsolutePath(), format);
} catch (SWTException e) {
// Change unchecked exception to checked one
throw new IOException("SWT Exception: " + e.getMessage());
}
// Done
return;
}
/**
* Save the given SWT image to the given OutputStream.
*
* @param im
* An image object.
* @param out
* The destination output stream.
* @param format
* The SWT format constant.
* @throws IOException
* If there is an error saving the to the output stream.
*/
public static void saveImage(Image im, OutputStream out, int format)
throws IOException
{
saveImage(im.getImageData(), out, format);
}
/**
* Save the given SWT ImageData object to the given OutputStream.
*
* @param im
* An ImageData object.
* @param out
* The destination output stream.
* @param format
* The SWT format constant.
* @throws IOException
* If there is an error saving the to the output stream.
*/
public static void saveImage(ImageData im, OutputStream out, int format)
throws IOException
{
ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { im };
try {
// Save image
loader.save(out, format);
} catch (SWTException e) {
// Change unchecked exception to checked one
throw new IOException("SWT Exception: " + e.getMessage());
}
}
/**
* Create a horizontal separator for the given parent.
*
* @param parent
* A composite.
* @return The created separator control.
*/
public static Control createSeparator(Composite parent) {
return new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL);
}
/**
* Turn a SWT control into a file drop target.
*
* @param control
* An SWT control (not null).
* @param listener
* A file drop listener (not null).
*/
public static void createFileDropTarget(
final Control control,
final FileDropListener listener)
{
// Create target
DropTarget target = new DropTarget(
control, DND.DROP_DEFAULT | DND.DROP_MOVE
);
// Create transfer
final FileTransfer transfer = FileTransfer.getInstance();
target.setTransfer(new Transfer[] { transfer });
// Add listener
target.addDropListener(new DropTargetAdapter() {
public void drop(final DropTargetEvent event) {
if (transfer.isSupportedType(event.currentDataType)) {
String[] files = (String[]) event.data;
// Send event
listener.drop(new FileDropEvent(control, files));
}
}
});
return;
}
/**
* Compress and RGB object into a ARGB integer.
*
* @param rgb
* RGB object.
* @return An ARGB integer.
*/
public static int rgb2int(RGB rgb) {
return (rgb.red & 0xff) << 16 |
(rgb.green & 0xff) << 8 |
(rgb.blue & 0xff);
}
/**
* De-compress an ARGB integer into an RGB object.
*
* @param v
* an ARGB integer.
* @return An RGB object.
*/
public static RGB int2rgb(int v) {
return new RGB((v >> 16) & 0xff, (v >> 8) & 0xff, v & 0xff);
}
public static Label spacer(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
return label;
}
}