| /******************************************************************************* |
| * Copyright (c) 2012, 2020 Original authors and others. |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Original authors and others - initial API and implementation |
| * Loris Securo <lorissek@gmail.com> - Bug 499701, 500800 |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.painter.cell; |
| |
| import org.eclipse.nebula.widgets.nattable.style.BorderStyle; |
| import org.eclipse.nebula.widgets.nattable.style.BorderStyle.BorderModeEnum; |
| import org.eclipse.nebula.widgets.nattable.style.BorderStyle.LineStyleEnum; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.graphics.Color; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.GC; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.ImageData; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Display; |
| |
| /** |
| * This class contains utility methods for drawing graphics |
| * |
| * @see <a href= |
| * "http://java-gui.info/Apress-The.Definitive.Guide.to.SWT.and.JFace/8886final/LiB0095.html">GC |
| * snippets</a> |
| */ |
| public final class GraphicsUtils { |
| |
| private GraphicsUtils() { |
| // private default constructor for helper class |
| } |
| |
| /** |
| * Draws text vertically (rotates plus or minus 90 degrees). Uses the |
| * current font, color, and background. |
| * <dl> |
| * <dt><b>Styles: </b></dt> |
| * <dd>UP, DOWN</dd> |
| * </dl> |
| * |
| * @param string |
| * the text to draw |
| * @param x |
| * the x coordinate of the top left corner of the drawing |
| * rectangle |
| * @param y |
| * the y coordinate of the top left corner of the drawing |
| * rectangle |
| * @param gc |
| * the GC on which to draw the text |
| * @param style |
| * the style (SWT.UP or SWT.DOWN) |
| * <p> |
| * Note: Only one of the style UP or DOWN may be specified. |
| * </p> |
| */ |
| public static void drawVerticalText(String string, int x, int y, GC gc, int style) { |
| drawVerticalText(string, x, y, false, false, true, gc, style); |
| } |
| |
| /** |
| * Draws text vertically (rotates plus or minus 90 degrees). Uses the |
| * current font, color, and background. |
| * <dl> |
| * <dt><b>Styles: </b></dt> |
| * <dd>UP, DOWN</dd> |
| * </dl> |
| * |
| * @param string |
| * the text to draw |
| * @param x |
| * the x coordinate of the top left corner of the drawing |
| * rectangle |
| * @param y |
| * the y coordinate of the top left corner of the drawing |
| * rectangle |
| * @param underline |
| * set to <code>true</code> to render the text underlined |
| * @param strikethrough |
| * set to <code>true</code> to render the text strikethrough |
| * @param paintBackground |
| * set to <code>false</code> to render the background |
| * transparent. Needed for example to render the background with |
| * an image or gradient with another painter so the text drawn |
| * here should have no background. |
| * @param gc |
| * the GC on which to draw the text |
| * @param style |
| * the style (SWT.UP or SWT.DOWN) |
| * <p> |
| * Note: Only one of the style UP or DOWN may be specified. |
| * </p> |
| */ |
| public static void drawVerticalText(String string, int x, int y, |
| boolean underline, boolean strikethrough, boolean paintBackground, |
| GC gc, int style) { |
| // Get the current display |
| Display display = Display.getCurrent(); |
| if (display == null) |
| SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); |
| |
| // Determine string's dimensions |
| Point pt = gc.textExtent(string.trim()); |
| |
| // Create an image the same size as the string |
| Image stringImage = new Image(display, pt.x, pt.y); |
| |
| // Create a GC so we can draw the image |
| GC stringGc = new GC(stringImage); |
| |
| // Set attributes from the original GC to the new GC |
| stringGc.setAntialias(gc.getAntialias()); |
| stringGc.setTextAntialias(gc.getTextAntialias()); |
| stringGc.setForeground(gc.getForeground()); |
| stringGc.setBackground(gc.getBackground()); |
| stringGc.setFont(gc.getFont()); |
| |
| // Fill the image with the specified background color |
| // to avoid white spaces if the text does not fill the |
| // whole image (e.g. on new lines) |
| stringGc.fillRectangle(0, 0, pt.x, pt.y); |
| |
| // Draw the text onto the image |
| stringGc.drawText(string, 0, 0); |
| |
| // draw underline and/or strikethrough |
| if (underline || strikethrough) { |
| // check and draw underline and strikethrough separately so it is |
| // possible to combine both |
| if (underline) { |
| // y = start y of text + font height |
| // - half of the font descent so the underline is between the |
| // baseline and the bottom |
| int underlineY = pt.y |
| - (stringGc.getFontMetrics().getDescent() / 2); |
| stringGc.drawLine(0, underlineY, pt.x, underlineY); |
| } |
| |
| if (strikethrough) { |
| // y = start y of text + half of font height + ascent so lower |
| // case characters are |
| // also strikethrough |
| int strikeY = (pt.y / 2) |
| + (stringGc.getFontMetrics().getLeading() / 2); |
| stringGc.drawLine(0, strikeY, pt.x, strikeY); |
| } |
| } |
| |
| // Draw the image vertically onto the original GC |
| drawVerticalImage(stringImage, x, y, paintBackground, gc, style); |
| |
| // Dispose the new GC |
| stringGc.dispose(); |
| |
| // Dispose the image |
| stringImage.dispose(); |
| } |
| |
| /** |
| * Draws an image vertically (rotates plus or minus 90 degrees) |
| * <dl> |
| * <dt><b>Styles: </b></dt> |
| * <dd>UP, DOWN</dd> |
| * </dl> |
| * |
| * @param image |
| * the image to draw |
| * @param x |
| * the x coordinate of the top left corner of the drawing |
| * rectangle |
| * @param y |
| * the y coordinate of the top left corner of the drawing |
| * rectangle |
| * @param gc |
| * the GC on which to draw the image |
| * @param style |
| * the style (SWT.UP or SWT.DOWN) |
| * <p> |
| * Note: Only one of the style UP or DOWN may be specified. |
| * </p> |
| */ |
| public static void drawVerticalImage(Image image, int x, int y, GC gc, |
| int style) { |
| drawVerticalImage(image, x, y, true, gc, style); |
| } |
| |
| /** |
| * Draws an image vertically (rotates plus or minus 90 degrees) |
| * <dl> |
| * <dt><b>Styles: </b></dt> |
| * <dd>UP, DOWN</dd> |
| * </dl> |
| * |
| * @param image |
| * the image to draw |
| * @param x |
| * the x coordinate of the top left corner of the drawing |
| * rectangle |
| * @param y |
| * the y coordinate of the top left corner of the drawing |
| * rectangle |
| * @param paintBackground |
| * set to <code>false</code> to render the background |
| * transparent. Needed for example to render the background with |
| * an image or gradient with another painter so the text drawn |
| * here should have no background. |
| * @param gc |
| * the GC on which to draw the image |
| * @param style |
| * the style (SWT.UP or SWT.DOWN) |
| * <p> |
| * Note: Only one of the style UP or DOWN may be specified. |
| * </p> |
| */ |
| public static void drawVerticalImage(Image image, int x, int y, |
| boolean paintBackground, GC gc, int style) { |
| // Get the current display |
| Display display = Display.getCurrent(); |
| if (display == null) |
| SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); |
| |
| // Use the image's data to create a rotated image's data |
| ImageData sd = image.getImageData(); |
| ImageData dd = new ImageData(sd.height, sd.width, sd.depth, sd.palette); |
| dd.transparentPixel = sd.transparentPixel; |
| |
| // set the defined backgroundcolor to be transparent |
| if (!paintBackground) { |
| dd.transparentPixel = sd.palette.getPixel(gc.getBackground() |
| .getRGB()); |
| } |
| |
| // Determine which way to rotate, depending on up or down |
| boolean up = (style & SWT.UP) == SWT.UP; |
| |
| // Run through the horizontal pixels |
| for (int sx = 0; sx < sd.width; sx++) { |
| // Run through the vertical pixels |
| for (int sy = 0; sy < sd.height; sy++) { |
| // Determine where to move pixel to in destination image data |
| int dx = up ? sy : sd.height - sy - 1; |
| int dy = up ? sd.width - sx - 1 : sx; |
| // Swap the x, y source data to y, x in the destination |
| dd.setPixel(dx, dy, sd.getPixel(sx, sy)); |
| } |
| } |
| |
| // Create the vertical image |
| Image vertical = new Image(display, dd); |
| |
| // Draw the vertical image onto the original GC |
| gc.drawImage(vertical, x, y); |
| |
| // Dispose the vertical image |
| vertical.dispose(); |
| } |
| |
| /** |
| * Creates an image containing the specified text, rotated either plus or |
| * minus 90 degrees. |
| * <dl> |
| * <dt><b>Styles: </b></dt> |
| * <dd>UP, DOWN</dd> |
| * </dl> |
| * |
| * @param text |
| * the text to rotate |
| * @param font |
| * the font to use |
| * @param foreground |
| * the color for the text |
| * @param background |
| * the background color |
| * @param style |
| * direction to rotate (up or down) |
| * @return Image |
| * <p> |
| * Note: Only one of the style UP or DOWN may be specified. |
| * </p> |
| */ |
| public static Image createRotatedText(String text, Font font, |
| Color foreground, Color background, int style) { |
| // Get the current display |
| Display display = Display.getCurrent(); |
| if (display == null) |
| SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); |
| |
| // Create a GC to calculate font's dimensions |
| GC gc = new GC(display); |
| gc.setFont(font); |
| |
| // Determine string's dimensions |
| Point pt = gc.textExtent(text); |
| |
| // Dispose that gc |
| gc.dispose(); |
| |
| // Create an image the same size as the string |
| Image stringImage = new Image(display, pt.x, pt.y); |
| // Create a gc for the image |
| gc = new GC(stringImage); |
| gc.setFont(font); |
| gc.setForeground(foreground); |
| gc.setBackground(background); |
| |
| // Draw the text onto the image |
| gc.drawText(text, 0, 0); |
| |
| // Draw the image vertically onto the original GC |
| Image image = createRotatedImage(stringImage, style); |
| |
| // Dispose the new GC |
| gc.dispose(); |
| |
| // Dispose the horizontal image |
| stringImage.dispose(); |
| |
| // Return the rotated image |
| return image; |
| } |
| |
| /** |
| * Creates a rotated image (plus or minus 90 degrees) |
| * <dl> |
| * <dt><b>Styles: </b></dt> |
| * <dd>UP, DOWN</dd> |
| * </dl> |
| * |
| * @param image |
| * the image to rotate |
| * @param style |
| * direction to rotate (up or down) |
| * @return Image |
| * <p> |
| * Note: Only one of the style UP or DOWN may be specified. |
| * </p> |
| */ |
| public static Image createRotatedImage(Image image, int style) { |
| // Get the current display |
| Display display = Display.getCurrent(); |
| if (display == null) |
| SWT.error(SWT.ERROR_THREAD_INVALID_ACCESS); |
| |
| // Use the image's data to create a rotated image's data |
| ImageData sd = image.getImageData(); |
| ImageData dd = new ImageData(sd.height, sd.width, sd.depth, sd.palette); |
| |
| // Determine which way to rotate, depending on up or down |
| boolean up = (style & SWT.UP) == SWT.UP; |
| |
| // Run through the horizontal pixels |
| for (int sx = 0; sx < sd.width; sx++) { |
| // Run through the vertical pixels |
| for (int sy = 0; sy < sd.height; sy++) { |
| // Determine where to move pixel to in destination image data |
| int dx = up ? sy : sd.height - sy - 1; |
| int dy = up ? sd.width - sx - 1 : sx; |
| |
| // Swap the x, y source data to y, x in the destination |
| dd.setPixel(dx, dy, sd.getPixel(sx, sy)); |
| } |
| } |
| |
| // Create the vertical image |
| return new Image(display, dd); |
| } |
| |
| /** |
| * Draws a horizontal line starting at (x, y) and having the given width. |
| * <p> |
| * Unlike {@link GC#drawLine}, this method guarantees that the line will |
| * always start at the given coordinates and will always have the given |
| * width. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param width |
| * the width of the line to draw |
| * |
| * @see #drawLineHorizontalBorderTop(GC, int, int, int) |
| * @see #drawLineHorizontalBorderBottom(GC, int, int, int) |
| * @see #drawLineHorizontal(GC, int, int, int, boolean, boolean) |
| * @since 1.5 |
| */ |
| public static void drawLineHorizontal(GC gc, int x, int y, int width) { |
| |
| if (width == 0) { |
| return; |
| } |
| |
| int x2; |
| |
| // create coordinate for destination point |
| if (width > 0) { |
| x2 = x + width - 1; |
| } else { |
| x2 = x + width + 1; |
| } |
| |
| // fix position and length |
| if (x2 < x) { |
| x++; |
| } else { |
| int lineWidth = gc.getLineWidth(); |
| if (lineWidth != 1) { |
| x2++; |
| } |
| } |
| |
| gc.drawLine(x, y, x2, y); |
| } |
| |
| /** |
| * The difference between this method and |
| * {@link #drawLineHorizontal(GC, int, int, int)} is that the line could be |
| * extended to draw left and/or right corners (e.g. the corners of a |
| * rectangle). |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param width |
| * the width of the line to draw |
| * @param drawLeftCorner |
| * to draw the left corner |
| * @param drawRightCorner |
| * to draw the right corner |
| * |
| * @see #drawLineHorizontal(GC, int, int, int) |
| * @since 1.5 |
| */ |
| public static void drawLineHorizontal(GC gc, int x, int y, int width, boolean drawLeftCorner, boolean drawRightCorner) { |
| |
| if (width == 0) { |
| return; |
| } |
| |
| // adjust the position and length in order to create the corners |
| int lineWidth = gc.getLineWidth(); |
| if (width > 0) { |
| if (drawLeftCorner) { |
| x = x - (lineWidth / 2); |
| width = width + (lineWidth / 2); |
| } |
| if (drawRightCorner) { |
| width = width + ((lineWidth - 1) / 2); |
| } |
| } else { |
| if (drawRightCorner) { |
| x = x + ((lineWidth - 1) / 2); |
| width = width - ((lineWidth - 1) / 2); |
| } |
| if (drawLeftCorner) { |
| width = width - (lineWidth / 2); |
| } |
| } |
| |
| drawLineHorizontal(gc, x, y, width); |
| } |
| |
| /** |
| * Draws a vertical line starting at (x, y) and having the given height. |
| * <p> |
| * Unlike {@link GC#drawLine}, this method guarantees that the line will |
| * always start at the given coordinates and will always have the given |
| * height. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param height |
| * the height of the line to draw |
| * |
| * @see #drawLineVerticalBorderLeft(GC, int, int, int) |
| * @see #drawLineVerticalBorderRight(GC, int, int, int) |
| * @see #drawLineVertical(GC, int, int, int, boolean, boolean) |
| * @since 1.5 |
| */ |
| public static void drawLineVertical(GC gc, int x, int y, int height) { |
| |
| if (height == 0) { |
| return; |
| } |
| |
| int y2; |
| |
| // create coordinate for destination point |
| if (height > 0) { |
| y2 = y + height - 1; |
| } else { |
| y2 = y + height + 1; |
| } |
| |
| // fix position and length |
| if (y2 < y) { |
| y++; |
| } else { |
| int lineWidth = gc.getLineWidth(); |
| if (lineWidth != 1) { |
| y2++; |
| } |
| } |
| |
| gc.drawLine(x, y, x, y2); |
| } |
| |
| /** |
| * The difference between this method and |
| * {@link #drawLineVertical(GC, int, int, int)} is that the line could be |
| * extended to draw top and/or bottom corners (e.g. the corners of a |
| * rectangle). |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param height |
| * the height of the line to draw |
| * @param drawTopCorner |
| * to draw the top corner |
| * @param drawBottomCorner |
| * to draw the bottom corner |
| * |
| * @see #drawLineVertical(GC, int, int, int) |
| * @since 1.5 |
| */ |
| public static void drawLineVertical(GC gc, int x, int y, int height, boolean drawTopCorner, boolean drawBottomCorner) { |
| |
| if (height == 0) { |
| return; |
| } |
| |
| // adjust the position and length in order to create the corners |
| int lineWidth = gc.getLineWidth(); |
| if (height > 0) { |
| if (drawTopCorner) { |
| y = y - (lineWidth / 2); |
| height = height + (lineWidth / 2); |
| } |
| if (drawBottomCorner) { |
| height = height + ((lineWidth - 1) / 2); |
| } |
| } else { |
| if (drawBottomCorner) { |
| y = y + ((lineWidth - 1) / 2); |
| height = height - ((lineWidth - 1) / 2); |
| } |
| if (drawTopCorner) { |
| height = height - (lineWidth / 2); |
| } |
| } |
| |
| drawLineVertical(gc, x, y, height); |
| } |
| |
| /** |
| * Draws a horizontal line starting at (x, y) and having the given width. |
| * The increased thickness resulting from {@link GC#getLineWidth} will be |
| * strictly drawn below the line. |
| * <p> |
| * Unlike {@link GC#drawLine}, this method guarantees that the line will |
| * always start at the given coordinates and will always have the given |
| * width. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param width |
| * the width of the line to draw |
| * |
| * @see #drawLineHorizontal(GC, int, int, int) |
| * @see #drawLineHorizontalBorderTop(GC, int, int, int) |
| * @see #drawLineHorizontalBorderBottom(GC, int, int, int, boolean, boolean) |
| * @since 1.5 |
| */ |
| public static void drawLineHorizontalBorderBottom(GC gc, int x, int y, int width) { |
| |
| if (width == 0) { |
| return; |
| } |
| |
| // adjust the line to make it have the border at bottom |
| int lineWidth = gc.getLineWidth(); |
| y = y + (lineWidth / 2); |
| |
| drawLineHorizontal(gc, x, y, width); |
| } |
| |
| /** |
| * The difference between this method and |
| * {@link #drawLineHorizontalBorderBottom(GC, int, int, int)} is that the |
| * line could be extended to draw left and/or right corners (e.g. the |
| * corners of a rectangle). |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param width |
| * the width of the line to draw |
| * @param drawLeftCorner |
| * to draw the left corner |
| * @param drawRightCorner |
| * to draw the right corner |
| * |
| * @see #drawLineHorizontalBorderBottom(GC, int, int, int) |
| * @since 1.5 |
| */ |
| public static void drawLineHorizontalBorderBottom(GC gc, int x, int y, int width, boolean drawLeftCorner, boolean drawRightCorner) { |
| |
| if (width == 0) { |
| return; |
| } |
| |
| // adjust the position and length in order to create the corners |
| int lineWidth = gc.getLineWidth(); |
| if (width > 0) { |
| if (drawLeftCorner) { |
| x = x - (lineWidth - 1); |
| width = width + (lineWidth - 1); |
| } |
| if (drawRightCorner) { |
| width = width + (lineWidth - 1); |
| } |
| } else { |
| if (drawRightCorner) { |
| x = x + (lineWidth - 1); |
| width = width - (lineWidth - 1); |
| } |
| if (drawLeftCorner) { |
| width = width - (lineWidth - 1); |
| } |
| } |
| |
| drawLineHorizontalBorderBottom(gc, x, y, width); |
| } |
| |
| /** |
| * Draws a horizontal line starting at (x, y) and having the given width. |
| * The increased thickness resulting from {@link GC#getLineWidth} will be |
| * strictly drawn above the line. |
| * <p> |
| * Unlike {@link GC#drawLine}, this method guarantees that the line will |
| * always start at the given coordinates and will always have the given |
| * width. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param width |
| * the width of the line to draw |
| * |
| * @see #drawLineHorizontal(GC, int, int, int) |
| * @see #drawLineHorizontalBorderBottom(GC, int, int, int) |
| * @see #drawLineHorizontalBorderTop(GC, int, int, int, boolean, boolean) |
| * @since 1.5 |
| */ |
| public static void drawLineHorizontalBorderTop(GC gc, int x, int y, int width) { |
| |
| if (width == 0) { |
| return; |
| } |
| |
| // adjust the line to make it have the border at top |
| int lineWidth = gc.getLineWidth(); |
| y = y - ((lineWidth - 1) / 2); |
| |
| drawLineHorizontal(gc, x, y, width); |
| } |
| |
| /** |
| * The difference between this method and |
| * {@link #drawLineHorizontalBorderTop(GC, int, int, int)} is that the line |
| * could be extended to draw left and/or right corners (e.g. the corners of |
| * a rectangle). |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param width |
| * the width of the line to draw |
| * @param drawLeftCorner |
| * to draw the left corner |
| * @param drawRightCorner |
| * to draw the right corner |
| * |
| * @see #drawLineHorizontalBorderTop(GC, int, int, int) |
| * @since 1.5 |
| */ |
| public static void drawLineHorizontalBorderTop(GC gc, int x, int y, int width, boolean drawLeftCorner, boolean drawRightCorner) { |
| |
| if (width == 0) { |
| return; |
| } |
| |
| // adjust the position and length in order to create the corners |
| int lineWidth = gc.getLineWidth(); |
| if (width > 0) { |
| if (drawLeftCorner) { |
| x = x - (lineWidth - 1); |
| width = width + (lineWidth - 1); |
| } |
| if (drawRightCorner) { |
| width = width + (lineWidth - 1); |
| } |
| } else { |
| if (drawRightCorner) { |
| x = x + (lineWidth - 1); |
| width = width - (lineWidth - 1); |
| } |
| if (drawLeftCorner) { |
| width = width - (lineWidth - 1); |
| } |
| } |
| |
| drawLineHorizontalBorderTop(gc, x, y, width); |
| } |
| |
| /** |
| * Draws a vertical line starting at (x, y) and having the given height. The |
| * increased thickness resulting from {@link GC#getLineWidth} will be |
| * strictly drawn to the right of the line. |
| * <p> |
| * Unlike {@link GC#drawLine}, this method guarantees that the line will |
| * always start at the given coordinates and will always have the given |
| * height. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param height |
| * the height of the line to draw |
| * |
| * @see #drawLineVertical(GC, int, int, int) |
| * @see #drawLineVerticalBorderLeft(GC, int, int, int) |
| * @see #drawLineVerticalBorderRight(GC, int, int, int, boolean, boolean) |
| * @since 1.5 |
| */ |
| public static void drawLineVerticalBorderRight(GC gc, int x, int y, int height) { |
| |
| if (height == 0) { |
| return; |
| } |
| |
| // adjust the line to make it have the border at right |
| int lineWidth = gc.getLineWidth(); |
| x = x + (lineWidth / 2); |
| |
| drawLineVertical(gc, x, y, height); |
| } |
| |
| /** |
| * The difference between this method and |
| * {@link #drawLineVerticalBorderRight(GC, int, int, int)} is that the line |
| * could be extended to draw top and/or bottom corners (e.g. the corners of |
| * a rectangle). |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param height |
| * the height of the line to draw |
| * @param drawTopCorner |
| * to draw the top corner |
| * @param drawBottomCorner |
| * to draw the bottom corner |
| * |
| * @see #drawLineVerticalBorderRight(GC, int, int, int) |
| * @since 1.5 |
| */ |
| public static void drawLineVerticalBorderRight(GC gc, int x, int y, int height, boolean drawTopCorner, boolean drawBottomCorner) { |
| |
| if (height == 0) { |
| return; |
| } |
| |
| // adjust the position and length in order to create the corners |
| int lineWidth = gc.getLineWidth(); |
| if (height > 0) { |
| if (drawTopCorner) { |
| y = y - (lineWidth - 1); |
| height = height + (lineWidth - 1); |
| } |
| if (drawBottomCorner) { |
| height = height + (lineWidth - 1); |
| } |
| } else { |
| if (drawBottomCorner) { |
| y = y + (lineWidth - 1); |
| height = height - (lineWidth - 1); |
| } |
| if (drawTopCorner) { |
| height = height - (lineWidth - 1); |
| } |
| } |
| |
| drawLineVerticalBorderRight(gc, x, y, height); |
| } |
| |
| /** |
| * Draws a vertical line starting at (x, y) and having the given height. The |
| * increased thickness resulting from {@link GC#getLineWidth} will be |
| * strictly drawn to the left of the line. |
| * <p> |
| * Unlike {@link GC#drawLine}, this method guarantees that the line will |
| * always start at the given coordinates and will always have the given |
| * height. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param height |
| * the height of the line to draw |
| * |
| * @see #drawLineVertical(GC, int, int, int) |
| * @see #drawLineVerticalBorderRight(GC, int, int, int) |
| * @see #drawLineVerticalBorderLeft(GC, int, int, int, boolean, boolean) |
| * @since 1.5 |
| */ |
| public static void drawLineVerticalBorderLeft(GC gc, int x, int y, int height) { |
| |
| if (height == 0) { |
| return; |
| } |
| |
| // adjust the line to make it have the border at left |
| int lineWidth = gc.getLineWidth(); |
| x = x - ((lineWidth - 1) / 2); |
| |
| drawLineVertical(gc, x, y, height); |
| } |
| |
| /** |
| * The difference between this method and |
| * {@link #drawLineVerticalBorderLeft(GC, int, int, int)} is that the line |
| * could be extended to draw top and/or bottom corners (e.g. the corners of |
| * a rectangle). |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param x |
| * the starting point's x coordinate |
| * @param y |
| * the starting point's y coordinate |
| * @param height |
| * the height of the line to draw |
| * @param drawTopCorner |
| * to draw the top corner |
| * @param drawBottomCorner |
| * to draw the bottom corner |
| * |
| * @see #drawLineVerticalBorderLeft(GC, int, int, int) |
| * @since 1.5 |
| */ |
| public static void drawLineVerticalBorderLeft(GC gc, int x, int y, int height, boolean drawTopCorner, boolean drawBottomCorner) { |
| |
| if (height == 0) { |
| return; |
| } |
| |
| // adjust the position and length in order to create the corners |
| int lineWidth = gc.getLineWidth(); |
| if (height > 0) { |
| if (drawTopCorner) { |
| y = y - (lineWidth - 1); |
| height = height + (lineWidth - 1); |
| } |
| if (drawBottomCorner) { |
| height = height + (lineWidth - 1); |
| } |
| } else { |
| if (drawBottomCorner) { |
| y = y + (lineWidth - 1); |
| height = height - (lineWidth - 1); |
| } |
| if (drawTopCorner) { |
| height = height - (lineWidth - 1); |
| } |
| } |
| |
| drawLineVerticalBorderLeft(gc, x, y, height); |
| } |
| |
| /** |
| * Draws a filled rectangle. |
| * <p> |
| * Unlike {@link GC#fillRectangle(Rectangle)}, this method guarantees that |
| * the rectangle will always start at the given coordinates even in case of |
| * negative width/height. |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param rect |
| * the rectangle to draw |
| * @since 1.5 |
| */ |
| public static void fillRectangle(GC gc, Rectangle rect) { |
| |
| Rectangle rectangle = new Rectangle(rect.x, rect.y, rect.width, rect.height); |
| |
| // even with negative lengths, the rectangle should start from (x, y) |
| if (rectangle.width < 0) { |
| rectangle.x++; |
| } |
| if (rectangle.height < 0) { |
| rectangle.y++; |
| } |
| |
| gc.fillRectangle(rectangle); |
| } |
| |
| /** |
| * Draws a rectangle with the given border style. |
| * <p> |
| * Unlike {@link GC#drawRectangle(Rectangle)}: |
| * <ul> |
| * <li>the width and height of the resulting rectangle will be always |
| * exactly {@code rect.width} and {@code rect.height} |
| * <li>the rectangle will always start at the given coordinates even in case |
| * of negative width/height |
| * </ul> |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param rect |
| * the rectangle to draw |
| * @param borderStyle |
| * the border style of the rectangle |
| * @since 1.5 |
| */ |
| public static void drawRectangle(GC gc, Rectangle rect, BorderStyle borderStyle) { |
| |
| int originalLineStyle = gc.getLineStyle(); |
| int originalLineWidth = gc.getLineWidth(); |
| Color originalForeground = gc.getForeground(); |
| |
| gc.setLineStyle(LineStyleEnum.toSWT(borderStyle.getLineStyle())); |
| gc.setLineWidth(borderStyle.getThickness()); |
| gc.setForeground(borderStyle.getColor()); |
| |
| switch (borderStyle.getBorderMode()) { |
| case CENTERED: |
| drawRectangle(gc, rect); |
| break; |
| case INTERNAL: |
| drawRectangleBorderInternal(gc, rect); |
| break; |
| case EXTERNAL: |
| drawRectangleBorderExternal(gc, rect); |
| break; |
| } |
| |
| gc.setLineStyle(originalLineStyle); |
| gc.setLineWidth(originalLineWidth); |
| gc.setForeground(originalForeground); |
| } |
| |
| /** |
| * Draws a rectangle. |
| * <p> |
| * Unlike {@link GC#drawRectangle(Rectangle)}: |
| * <ul> |
| * <li>the width and height of the resulting rectangle will be always |
| * exactly {@code rect.width} and {@code rect.height} |
| * <li>the rectangle will always start at the given coordinates even in case |
| * of negative width/height |
| * </ul> |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param rect |
| * the rectangle to draw |
| * |
| * @see #drawRectangleBorderInternal |
| * @see #drawRectangleBorderExternal |
| * @since 1.5 |
| */ |
| public static void drawRectangle(GC gc, Rectangle rect) { |
| |
| if (rect.width == 0 || rect.height == 0) { |
| return; |
| } |
| |
| Rectangle rectangle = new Rectangle(rect.x, rect.y, rect.width, rect.height); |
| |
| int absWidth = Math.abs(rectangle.width); |
| int absHeight = Math.abs(rectangle.height); |
| |
| // it's a point but it can grow with the border width |
| if (absWidth == 1 && absHeight == 1) { |
| |
| int lineWidth = gc.getLineWidth(); |
| |
| rectangle.x = rectangle.x - (lineWidth / 2); |
| rectangle.y = rectangle.y - (lineWidth / 2); |
| rectangle.width = rectangle.width + lineWidth - 1; |
| rectangle.height = rectangle.height + lineWidth - 1; |
| |
| Color originalBackground = gc.getBackground(); |
| gc.setBackground(gc.getForeground()); |
| fillRectangle(gc, rectangle); |
| gc.setBackground(originalBackground); |
| |
| return; |
| } |
| |
| // it's a line |
| if (absWidth == 1) { |
| drawLineVertical(gc, rectangle.x, rectangle.y, rectangle.height); |
| return; |
| } |
| if (absHeight == 1) { |
| drawLineHorizontal(gc, rectangle.x, rectangle.y, rectangle.width); |
| return; |
| } |
| |
| // even with negative lengths, the rectangle should start from (x, y) |
| if (rectangle.width < 0) { |
| rectangle.x++; |
| } |
| if (rectangle.height < 0) { |
| rectangle.y++; |
| } |
| |
| // fix the lengths |
| rectangle.width--; |
| rectangle.height--; |
| |
| gc.drawRectangle(rectangle); |
| } |
| |
| /** |
| * Draws a rectangle. The increased border thickness resulting from |
| * {@link GC#getLineWidth} will be strictly drawn inside of the rectangle. |
| * <p> |
| * Unlike {@link GC#drawRectangle(Rectangle)}: |
| * <ul> |
| * <li>the width and height of the resulting rectangle will be always |
| * exactly {@code rect.width} and {@code rect.height} |
| * <li>the rectangle will always start at the given coordinates even in case |
| * of negative width/height |
| * </ul> |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param rect |
| * the rectangle to draw |
| * @see #drawRectangle(GC, Rectangle) |
| * @see #drawRectangleBorderExternal |
| * @since 1.5 |
| */ |
| public static void drawRectangleBorderInternal(GC gc, Rectangle rect) { |
| |
| if (rect.width == 0 || rect.height == 0) { |
| return; |
| } |
| |
| Rectangle rectangle = new Rectangle(rect.x, rect.y, rect.width, rect.height); |
| |
| int lineWidth = gc.getLineWidth(); |
| |
| // check if it's a line |
| int absWidth = Math.abs(rectangle.width); |
| if (absWidth == 1) { |
| // we're supposed to draw inside the rectangle and since it's a line |
| // we don't want it to grow outside, so we force the line width at 1 |
| if (lineWidth != 1) { |
| gc.setLineWidth(1); |
| } |
| drawLineVertical(gc, rectangle.x, rectangle.y, rectangle.height); |
| if (lineWidth != 1) { |
| gc.setLineWidth(lineWidth); |
| } |
| return; |
| } |
| int absHeight = Math.abs(rectangle.height); |
| if (absHeight == 1) { |
| // we're supposed to draw inside the rectangle and since it's a line |
| // we don't want it to grow outside, so we force the line width at 1 |
| if (lineWidth != 1) { |
| gc.setLineWidth(1); |
| } |
| drawLineHorizontal(gc, rectangle.x, rectangle.y, rectangle.width); |
| if (lineWidth != 1) { |
| gc.setLineWidth(lineWidth); |
| } |
| return; |
| } |
| |
| // if the border is big enough to cover the whole rectangle interior we |
| // simply draw a fill rectangle |
| int minSide = Math.min(absWidth, absHeight); |
| if (minSide <= (lineWidth * 2)) { |
| Color originalBackground = gc.getBackground(); |
| gc.setBackground(gc.getForeground()); |
| fillRectangle(gc, rectangle); |
| gc.setBackground(originalBackground); |
| return; |
| } |
| |
| // adjust the rectangle to make it internal |
| if (rectangle.width > 0) { |
| rectangle.x = rectangle.x + (lineWidth / 2); |
| rectangle.width = rectangle.width - (lineWidth - 1); |
| } else { |
| rectangle.x = rectangle.x - ((lineWidth - 1) / 2); |
| rectangle.width = rectangle.width + (lineWidth - 1); |
| } |
| |
| if (rectangle.height > 0) { |
| rectangle.y = rectangle.y + (lineWidth / 2); |
| rectangle.height = rectangle.height - (lineWidth - 1); |
| } else { |
| rectangle.y = rectangle.y - ((lineWidth - 1) / 2); |
| rectangle.height = rectangle.height + (lineWidth - 1); |
| } |
| |
| drawRectangle(gc, rectangle); |
| } |
| |
| /** |
| * Draws a rectangle. The increased border thickness resulting from |
| * {@link GC#getLineWidth} will be strictly drawn outside of the rectangle. |
| * <p> |
| * Unlike {@link GC#drawRectangle(Rectangle)}: |
| * <ul> |
| * <li>the width and height of the resulting rectangle will be always |
| * exactly {@code rect.width} and {@code rect.height} |
| * <li>the rectangle will always start at the given coordinates even in case |
| * of negative width/height |
| * </ul> |
| * |
| * @param gc |
| * the GC to use to draw |
| * @param rect |
| * the rectangle to draw |
| * |
| * @see #drawRectangle(GC, Rectangle) |
| * @see #drawRectangleBorderInternal |
| * @since 1.5 |
| */ |
| public static void drawRectangleBorderExternal(GC gc, Rectangle rect) { |
| |
| if (rect.width == 0 || rect.height == 0) { |
| return; |
| } |
| |
| Rectangle rectangle = new Rectangle(rect.x, rect.y, rect.width, rect.height); |
| |
| int lineWidth = gc.getLineWidth(); |
| |
| // adjust the rectangle to make it external |
| if (rectangle.width > 0) { |
| rectangle.x = rectangle.x - ((lineWidth - 1) / 2); |
| rectangle.width = rectangle.width + (lineWidth - 1); |
| } else { |
| rectangle.x = rectangle.x + (lineWidth / 2); |
| rectangle.width = rectangle.width - (lineWidth - 1); |
| } |
| |
| if (rectangle.height > 0) { |
| rectangle.y = rectangle.y - ((lineWidth - 1) / 2); |
| rectangle.height = rectangle.height + (lineWidth - 1); |
| } else { |
| rectangle.y = rectangle.y + (lineWidth / 2); |
| rectangle.height = rectangle.height - (lineWidth - 1); |
| } |
| |
| drawRectangle(gc, rectangle); |
| } |
| |
| /** |
| * Returns the external bounds of the rectangle that would be obtained by |
| * painting <code>rectangle</code> with <code>borderStyle</code> using one |
| * of the <code>GraphicsUtils.drawRectangle...</code> methods. |
| * |
| * @param rectangle |
| * the rectangle to consider |
| * @param borderStyle |
| * the border style to consider |
| * @return the external bounds of what would be the resulting rectangle |
| * |
| * @since 1.5 |
| */ |
| public static Rectangle getResultingExternalBounds(Rectangle rectangle, BorderStyle borderStyle) { |
| if (rectangle == null || borderStyle == null) { |
| return null; |
| } |
| |
| Rectangle resultingRectangle = new Rectangle(rectangle.x, rectangle.y, rectangle.width, rectangle.height); |
| |
| int borderWidth = borderStyle.getThickness(); |
| BorderModeEnum borderMode = borderStyle.getBorderMode(); |
| |
| switch (borderMode) { |
| case CENTERED: |
| if (resultingRectangle.width > 0) { |
| resultingRectangle.x = resultingRectangle.x - (borderWidth / 2); |
| resultingRectangle.width = resultingRectangle.width + (borderWidth - 1); |
| } else { |
| resultingRectangle.x = resultingRectangle.x + ((borderWidth - 1) / 2); |
| resultingRectangle.width = resultingRectangle.width - (borderWidth - 1); |
| } |
| |
| if (resultingRectangle.height > 0) { |
| resultingRectangle.y = resultingRectangle.y - (borderWidth / 2); |
| resultingRectangle.height = resultingRectangle.height + (borderWidth - 1); |
| } else { |
| resultingRectangle.y = resultingRectangle.y + ((borderWidth - 1) / 2); |
| resultingRectangle.height = resultingRectangle.height - (borderWidth - 1); |
| } |
| break; |
| case EXTERNAL: |
| if (resultingRectangle.width > 0) { |
| resultingRectangle.x = resultingRectangle.x - (borderWidth - 1); |
| resultingRectangle.width = resultingRectangle.width + ((borderWidth - 1) * 2); |
| } else { |
| resultingRectangle.x = resultingRectangle.x + (borderWidth - 1); |
| resultingRectangle.width = resultingRectangle.width - ((borderWidth - 1) * 2); |
| } |
| |
| if (resultingRectangle.height > 0) { |
| resultingRectangle.y = resultingRectangle.y - (borderWidth - 1); |
| resultingRectangle.height = resultingRectangle.height + ((borderWidth - 1) * 2); |
| } else { |
| resultingRectangle.y = resultingRectangle.y + (borderWidth - 1); |
| resultingRectangle.height = resultingRectangle.height - ((borderWidth - 1) * 2); |
| } |
| break; |
| case INTERNAL: |
| // the external bounds remain the same |
| break; |
| } |
| |
| return resultingRectangle; |
| } |
| } |