Java tutorial
/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 by Pentaho : http://www.pentaho.com * ******************************************************************************* * * 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. * ******************************************************************************/ package org.pentaho.di.core.gui; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.ImageObserver; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.Map; import javax.imageio.ImageIO; import org.apache.commons.io.IOUtils; import org.jfree.text.TextUtilities; import org.pentaho.di.core.Const; import org.pentaho.di.core.SwingUniversalImage; import org.pentaho.di.core.SwingUniversalImageBitmap; import org.pentaho.di.core.SwingUniversalImageSvg; import org.pentaho.di.core.exception.KettleException; import org.pentaho.di.core.svg.SvgImage; import org.pentaho.di.core.svg.SvgSupport; import org.pentaho.di.core.util.SwingSvgImageUtil; import org.pentaho.di.job.entry.JobEntryCopy; import org.pentaho.di.laf.BasePropertyHandler; import org.pentaho.di.trans.step.StepMeta; import org.pentaho.reporting.libraries.base.util.WaitingImageObserver; public class SwingDirectGC implements GCInterface { private static SwingUniversalImage imageLocked; private static SwingUniversalImage imageStepError; private static SwingUniversalImage imageEdit; private static SwingUniversalImage imageContextMenu; private static SwingUniversalImage imageTrue; private static SwingUniversalImage imageFalse; private static SwingUniversalImage imageErrorHop; private static SwingUniversalImage imageInfoHop; private static SwingUniversalImage imageHopTarget; private static SwingUniversalImage imageHopInput; private static SwingUniversalImage imageHopOutput; private static SwingUniversalImage imageArrow; private static SwingUniversalImage imageCopyHop; private static SwingUniversalImage imageLoadBalance; private static SwingUniversalImage imageCheckpoint; private static SwingUniversalImage imageDatabase; private static SwingUniversalImage imageParallelHop; private static SwingUniversalImage imageUnconditionalHop; private static SwingUniversalImage imageStart; private static SwingUniversalImage imageDummy; private static SwingUniversalImage imageBusy; private static SwingUniversalImage imageInject; private static SwingUniversalImage defaultArrow; private static SwingUniversalImage okArrow; private static SwingUniversalImage errorArrow; private static SwingUniversalImage disabledArrow; protected Color background; protected Color black; protected Color red; protected Color yellow; protected Color orange; protected Color green; protected Color blue; protected Color magenta; protected Color gray; protected Color lightGray; protected Color darkGray; protected Color lightBlue; protected Color crystal; protected Color hopDefault; protected Color hopOK; private Graphics2D gc; private int iconsize; //TODO should be changed to PropsUI usage private int small_icon_size = 16; private Map<String, SwingUniversalImage> stepImages; private Map<String, SwingUniversalImage> entryImages; private BufferedImage image; private ImageObserver observer; private Point area; private int alpha; private Font fontGraph; private Font fontNote; private Font fontSmall; private int lineWidth; private ELineStyle lineStyle; private int yOffset; private int xOffset; private boolean drawingPixelatedImages; public SwingDirectGC(ImageObserver observer, Point area, int iconsize, int xOffset, int yOffset) throws KettleException { this.image = new BufferedImage(area.x, area.y, BufferedImage.TYPE_INT_ARGB); this.gc = image.createGraphics(); this.observer = observer; this.stepImages = SwingGUIResource.getInstance().getStepImages(); this.entryImages = SwingGUIResource.getInstance().getEntryImages(); this.iconsize = iconsize; this.area = area; this.xOffset = xOffset; this.yOffset = yOffset; init(); } public SwingDirectGC(Graphics2D gc, Rectangle2D rect, int iconsize, int xOffset, int yOffset) throws KettleException { this.image = null; this.gc = gc; this.observer = null; this.stepImages = SwingGUIResource.getInstance().getStepImages(); this.entryImages = SwingGUIResource.getInstance().getEntryImages(); this.iconsize = iconsize; this.area = new Point((int) rect.getWidth(), (int) rect.getHeight()); this.xOffset = xOffset; this.yOffset = yOffset; init(); } private void init() throws KettleException { this.lineStyle = ELineStyle.SOLID; this.lineWidth = 1; this.alpha = 255; this.background = new Color(255, 255, 255); this.black = new Color(0, 0, 0); this.red = new Color(255, 0, 0); this.yellow = new Color(255, 255, 0); this.orange = new Color(255, 165, 0); this.green = new Color(0, 255, 0); this.blue = new Color(0, 0, 255); this.magenta = new Color(255, 0, 255); this.gray = new Color(128, 128, 128); this.lightGray = new Color(200, 200, 200); this.darkGray = new Color(80, 80, 80); this.lightBlue = new Color(135, 206, 250); // light sky blue this.crystal = new Color(61, 99, 128); this.hopDefault = new Color(61, 99, 128); this.hopOK = new Color(12, 178, 15); imageLocked = getImageIcon(BasePropertyHandler.getProperty("Locked_image")); imageStepError = getImageIcon(BasePropertyHandler.getProperty("StepErrorLines_image")); imageEdit = getImageIcon(BasePropertyHandler.getProperty("EditSmall_image")); imageContextMenu = getImageIcon(BasePropertyHandler.getProperty("ContextMenu_image")); imageTrue = getImageIcon(BasePropertyHandler.getProperty("True_image")); imageFalse = getImageIcon(BasePropertyHandler.getProperty("False_image")); imageErrorHop = getImageIcon(BasePropertyHandler.getProperty("ErrorHop_image")); imageInfoHop = getImageIcon(BasePropertyHandler.getProperty("InfoHop_image")); imageHopTarget = getImageIcon(BasePropertyHandler.getProperty("HopTarget_image")); imageHopInput = getImageIcon(BasePropertyHandler.getProperty("HopInput_image")); imageHopOutput = getImageIcon(BasePropertyHandler.getProperty("HopOutput_image")); imageArrow = getImageIcon(BasePropertyHandler.getProperty("ArrowIcon_image")); imageCopyHop = getImageIcon(BasePropertyHandler.getProperty("CopyHop_image")); imageLoadBalance = getImageIcon(BasePropertyHandler.getProperty("LoadBalance_image")); imageCheckpoint = getImageIcon(BasePropertyHandler.getProperty("CheckeredFlag_image")); imageDatabase = getImageIcon(BasePropertyHandler.getProperty("Database_image")); imageParallelHop = getImageIcon(BasePropertyHandler.getProperty("ParallelHop_image")); imageUnconditionalHop = getImageIcon(BasePropertyHandler.getProperty("UnconditionalHop_image")); imageStart = getImageIcon(BasePropertyHandler.getProperty("STR_image")); imageDummy = getImageIcon(BasePropertyHandler.getProperty("DUM_image")); imageBusy = getImageIcon(BasePropertyHandler.getProperty("Busy_image")); imageInject = getImageIcon(BasePropertyHandler.getProperty("Inject_image")); defaultArrow = getImageIcon(BasePropertyHandler.getProperty("defaultArrow_image")); okArrow = getImageIcon(BasePropertyHandler.getProperty("okArrow_image")); errorArrow = getImageIcon(BasePropertyHandler.getProperty("errorArrow_image")); disabledArrow = getImageIcon(BasePropertyHandler.getProperty("disabledArrow_image")); fontGraph = new Font("FreeSans", Font.PLAIN, 10); fontNote = new Font("FreeSans", Font.PLAIN, 10); fontSmall = new Font("FreeSans", Font.PLAIN, 8); gc.setFont(fontGraph); gc.setColor(background); gc.fillRect(0, 0, area.x, area.y); } private SwingUniversalImage getImageIcon(String fileName) throws KettleException { SwingUniversalImage image = null; InputStream inputStream = null; if (fileName == null) { throw new KettleException("Image icon file name can not be null"); } if (SvgSupport.isSvgEnabled() && SvgSupport.isSvgName(fileName)) { try { inputStream = new FileInputStream(fileName); } catch (FileNotFoundException ex) { // no need to fail } if (inputStream == null) { try { inputStream = new FileInputStream("/" + fileName); } catch (FileNotFoundException ex) { // no need to fail } } if (inputStream == null) { inputStream = getClass().getResourceAsStream(fileName); } if (inputStream == null) { inputStream = getClass().getResourceAsStream("/" + fileName); } if (inputStream != null) { try { SvgImage svg = SvgSupport.loadSvgImage(inputStream); image = new SwingUniversalImageSvg(svg); } catch (Exception ex) { throw new KettleException("Unable to load image from classpath : '" + fileName + "'", ex); } finally { IOUtils.closeQuietly(inputStream); } } } if (image == null) { fileName = SvgSupport.toPngName(fileName); try { inputStream = new FileInputStream(fileName); } catch (FileNotFoundException ex) { // no need to fail } if (inputStream == null) { try { inputStream = new FileInputStream("/" + fileName); } catch (FileNotFoundException ex) { // no need to fail } } if (inputStream == null) { inputStream = getClass().getResourceAsStream(fileName); } if (inputStream == null) { inputStream = getClass().getResourceAsStream("/" + fileName); } if (inputStream != null) { try { BufferedImage bitmap = ImageIO.read(inputStream); WaitingImageObserver wia = new WaitingImageObserver(bitmap); wia.waitImageLoaded(); image = new SwingUniversalImageBitmap(bitmap); } catch (Exception ex) { throw new KettleException("Unable to load image from classpath : '" + fileName + "'", ex); } finally { IOUtils.closeQuietly(inputStream); } } } if (image == null) { throw new KettleException("Unable to load image from classpath : '" + fileName + "'"); } return image; } public void dispose() { } public void drawLine(int x, int y, int x2, int y2) { gc.drawLine(x + xOffset, y + yOffset, x2 + xOffset, y2 + yOffset); } @Override public void drawImage(String location, ClassLoader classLoader, int x, int y) { SwingUniversalImage img = SwingSvgImageUtil.getUniversalImage(classLoader, location); drawImage(img, x, y, small_icon_size); } @Override public void drawImage(EImage image, int x, int y) { drawImage(image, x, y, 0.0f); } public void drawImage(EImage image, int locationX, int locationY, float magnification) { SwingUniversalImage img = getNativeImage(image); drawImage(img, locationX, locationY, small_icon_size); // gc.drawImage(img, locationX+xOffset, locationY+yOffset, observer); } public void drawImage(EImage image, int x, int y, int width, int height, float magnification) { SwingUniversalImage img = getNativeImage(image); drawImage(img, x, y, width, height); } public void drawImage(EImage image, int locationX, int locationY, float magnification, double angle) { SwingUniversalImage img = getNativeImage(image); drawImage(img, locationX, locationY, angle, small_icon_size); } private void drawImage(SwingUniversalImage img, int locationX, int locationY, int imageSize) { if (isDrawingPixelatedImages() && img.isBitmap()) { BufferedImage bi = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) bi.getGraphics(); g2.setColor(Color.WHITE); g2.fillRect(0, 0, imageSize, imageSize); g2.drawImage(img.getAsBitmapForSize(imageSize, imageSize), 0, 0, observer); g2.dispose(); for (int x = 0; x < bi.getWidth(observer); x++) { for (int y = 0; y < bi.getHeight(observer); y++) { int rgb = bi.getRGB(x, y); gc.setColor(new Color(rgb)); gc.setStroke(new BasicStroke(1.0f)); gc.drawLine(locationX + xOffset + x, locationY + yOffset + y, locationX + xOffset + x, locationY + yOffset + y); } } } else { gc.setBackground(Color.white); gc.clearRect(locationX, locationY, imageSize, imageSize); img.drawToGraphics(gc, locationX, locationY, imageSize, imageSize); } } private void drawImage(SwingUniversalImage img, int centerX, int centerY, double angle, int imageSize) { if (isDrawingPixelatedImages() && img.isBitmap()) { BufferedImage bi = img.getAsBitmapForSize(imageSize, imageSize, angle); int offx = centerX + xOffset - bi.getWidth() / 2; int offy = centerY + yOffset - bi.getHeight() / 2; for (int x = 0; x < bi.getWidth(observer); x++) { for (int y = 0; y < bi.getHeight(observer); y++) { int rgb = bi.getRGB(x, y); gc.setColor(new Color(rgb)); gc.setStroke(new BasicStroke(1.0f)); gc.drawLine(offx + x, offy + y, offx + x, offy + y); } } } else { gc.setBackground(Color.white); gc.clearRect(centerX, centerY, imageSize, imageSize); img.drawToGraphics(gc, centerX, centerY, imageSize, imageSize, angle); } } public Point getImageBounds(EImage image) { return new Point(small_icon_size, small_icon_size); } public static final SwingUniversalImage getNativeImage(EImage image) { switch (image) { case LOCK: return imageLocked; case STEP_ERROR: return imageStepError; case EDIT: return imageEdit; case CONTEXT_MENU: return imageContextMenu; case TRUE: return imageTrue; case FALSE: return imageFalse; case ERROR: return imageErrorHop; case INFO: return imageInfoHop; case TARGET: return imageHopTarget; case INPUT: return imageHopInput; case OUTPUT: return imageHopOutput; case ARROW: return imageArrow; case COPY_ROWS: return imageCopyHop; case LOAD_BALANCE: return imageLoadBalance; case CHECKPOINT: return imageCheckpoint; case DB: return imageDatabase; case PARALLEL: return imageParallelHop; case UNCONDITIONAL: return imageUnconditionalHop; case BUSY: return imageBusy; case INJECT: return imageInject; case ARROW_DEFAULT: return defaultArrow; case ARROW_OK: return okArrow; case ARROW_ERROR: return errorArrow; case ARROW_DISABLED: return disabledArrow; default: break; } return null; } public void drawPoint(int x, int y) { gc.drawLine(x + xOffset, y + yOffset, x + xOffset, y + yOffset); } public void drawPolygon(int[] polygon) { gc.drawPolygon(getSwingPolygon(polygon)); } private Polygon getSwingPolygon(int[] polygon) { int nPoints = polygon.length / 2; int[] xPoints = new int[polygon.length / 2]; int[] yPoints = new int[polygon.length / 2]; for (int i = 0; i < nPoints; i++) { xPoints[i] = polygon[2 * i + 0] + xOffset; yPoints[i] = polygon[2 * i + 1] + yOffset; } return new Polygon(xPoints, yPoints, nPoints); } public void drawPolyline(int[] polyline) { int nPoints = polyline.length / 2; int[] xPoints = new int[polyline.length / 2]; int[] yPoints = new int[polyline.length / 2]; for (int i = 0; i < nPoints; i++) { xPoints[i] = polyline[2 * i + 0] + xOffset; yPoints[i] = polyline[2 * i + 1] + yOffset; } gc.drawPolyline(xPoints, yPoints, nPoints); } public void drawRectangle(int x, int y, int width, int height) { gc.drawRect(x + xOffset, y + yOffset, width, height); } public void drawRoundRectangle(int x, int y, int width, int height, int circleWidth, int circleHeight) { gc.drawRoundRect(x + xOffset, y + yOffset, width, height, circleWidth, circleHeight); } public void drawText(String text, int x, int y) { int height = gc.getFontMetrics().getHeight(); String[] lines = text.split("\n"); for (String line : lines) { gc.drawString(line, x + xOffset, y + height + yOffset); y += height; } } public void drawText(String text, int x, int y, boolean transparent) { drawText(text, x, y); } public void fillPolygon(int[] polygon) { switchForegroundBackgroundColors(); gc.fillPolygon(getSwingPolygon(polygon)); switchForegroundBackgroundColors(); } public void fillRectangle(int x, int y, int width, int height) { switchForegroundBackgroundColors(); gc.fillRect(x + xOffset, y + yOffset, width, height); switchForegroundBackgroundColors(); } // TODO: complete code public void fillGradientRectangle(int x, int y, int width, int height, boolean vertical) { fillRectangle(x, y, width, height); } public void fillRoundRectangle(int x, int y, int width, int height, int circleWidth, int circleHeight) { switchForegroundBackgroundColors(); gc.fillRoundRect(x + xOffset, y + yOffset, width, height, circleWidth, circleHeight); switchForegroundBackgroundColors(); } public Point getDeviceBounds() { return area; } public void setAlpha(int alpha) { this.alpha = alpha; AlphaComposite alphaComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha / 255); gc.setComposite(alphaComposite); } public int getAlpha() { return alpha; } public void setBackground(EColor color) { gc.setBackground(getColor(color)); } private Color getColor(EColor color) { switch (color) { case BACKGROUND: return background; case BLACK: return black; case RED: return red; case YELLOW: return yellow; case ORANGE: return orange; case GREEN: return green; case BLUE: return blue; case MAGENTA: return magenta; case GRAY: return gray; case LIGHTGRAY: return lightGray; case DARKGRAY: return darkGray; case LIGHTBLUE: return lightBlue; case CRYSTAL: return crystal; case HOP_DEFAULT: return hopDefault; case HOP_OK: return hopOK; default: break; } return null; } public void setFont(EFont font) { switch (font) { case GRAPH: gc.setFont(fontGraph); break; case NOTE: gc.setFont(fontNote); break; case SMALL: gc.setFont(fontSmall); break; default: break; } } public void setForeground(EColor color) { gc.setColor(getColor(color)); } public void setLineStyle(ELineStyle lineStyle) { this.lineStyle = lineStyle; gc.setStroke(createStroke()); } private Stroke createStroke() { float[] dash; switch (lineStyle) { case SOLID: dash = null; break; case DOT: dash = new float[] { 5, }; break; case DASHDOT: dash = new float[] { 10, 5, 5, 5, }; break; case PARALLEL: dash = new float[] { 10, 5, 10, 5, }; break; default: throw new RuntimeException("Unhandled line style!"); } return new BasicStroke(lineWidth, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2, dash, 0); } public void setLineWidth(int width) { this.lineWidth = width; gc.setStroke(createStroke()); } public void setTransform(float translationX, float translationY, int shadowsize, float magnification) { AffineTransform transform = new AffineTransform(); transform.translate(translationX + shadowsize * magnification, translationY + shadowsize * magnification); transform.scale(magnification, magnification); gc.setTransform(transform); } public Point textExtent(String text) { String[] lines = text.split(Const.CR); int maxWidth = 0; for (String line : lines) { Rectangle2D bounds = TextUtilities.getTextBounds(line, gc, gc.getFontMetrics()); if (bounds.getWidth() > maxWidth) { maxWidth = (int) bounds.getWidth(); } } int height = gc.getFontMetrics().getHeight() * lines.length; return new Point(maxWidth, height); } public void drawStepIcon(int x, int y, StepMeta stepMeta, float magnification) { String steptype = stepMeta.getStepID(); SwingUniversalImage im = stepImages.get(steptype); if (im != null) { // Draw the icon! drawImage(im, x + xOffset, y + xOffset, iconsize); } } public void drawJobEntryIcon(int x, int y, JobEntryCopy jobEntryCopy, float magnification) { if (jobEntryCopy == null) { return; // Don't draw anything } SwingUniversalImage image = null; if (jobEntryCopy.isSpecial()) { if (jobEntryCopy.isStart()) { image = imageStart; } if (jobEntryCopy.isDummy()) { image = imageDummy; } } else { String configId = jobEntryCopy.getEntry().getPluginId(); if (configId != null) { image = entryImages.get(configId); } } if (image == null) { return; } drawImage(image, x + xOffset, y + xOffset, iconsize); // gc.drawImage(image, x+xOffset, y+yOffset, observer); } @Override public void drawJobEntryIcon(int x, int y, JobEntryCopy jobEntryCopy) { drawJobEntryIcon(x, y, jobEntryCopy, 1.0f); } @Override public void drawStepIcon(int x, int y, StepMeta stepMeta) { drawStepIcon(x, y, stepMeta, 1.0f); } public void setAntialias(boolean antiAlias) { if (antiAlias) { RenderingHints hints = new RenderingHints(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); hints.add(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY)); hints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); // hints.add(new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, // RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)); gc.setRenderingHints(hints); } } public void setBackground(int r, int g, int b) { Color color = getColor(r, g, b); gc.setBackground(color); } public void setForeground(int r, int g, int b) { Color color = getColor(r, g, b); gc.setColor(color); } private Color getColor(int r, int g, int b) { return new Color(r, g, b); } public void setFont(String fontName, int fontSize, boolean fontBold, boolean fontItalic) { int style = Font.PLAIN; if (fontBold) { style = Font.BOLD; } if (fontItalic) { style = style | Font.ITALIC; } Font font = new Font(fontName, style, fontSize); gc.setFont(font); } public Object getImage() { return image; } public void switchForegroundBackgroundColors() { Color fg = gc.getColor(); Color bg = gc.getBackground(); gc.setColor(bg); gc.setBackground(fg); } public Point getArea() { return area; } /** * @return the drawingPixelatedImages */ public boolean isDrawingPixelatedImages() { return drawingPixelatedImages; } /** * @param drawingPixelatedImages * the drawingPixelatedImages to set */ public void setDrawingPixelatedImages(boolean drawingPixelatedImages) { this.drawingPixelatedImages = drawingPixelatedImages; } @Override public void drawImage(BufferedImage image, int x, int y) { gc.drawImage(image, x, y, observer); } }