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; } }