/*! ****************************************************************************** * * 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 org.pentaho.di.base.BaseHopMeta; import org.pentaho.di.base.BaseMeta; import org.pentaho.di.core.Const; import org.pentaho.di.core.NotePadMeta; import org.pentaho.di.core.gui.AreaOwner.AreaType; import org.pentaho.di.core.gui.PrimitiveGCInterface.EColor; import org.pentaho.di.core.gui.PrimitiveGCInterface.EImage; import org.pentaho.di.core.gui.PrimitiveGCInterface.ELineStyle; import org.pentaho.di.core.util.Utils; import org.pentaho.di.trans.step.errorhandling.StreamIcon; import java.util.List; public abstract class BasePainter<Hop extends BaseHopMeta, Part extends BaseMeta> { public final double theta = Math.toRadians( 11 ); // arrowhead sharpness public static final int MINI_ICON_SIZE = 16; public static final int MINI_ICON_MARGIN = 5; public static final int MINI_ICON_TRIANGLE_BASE = 20; public static final int MINI_ICON_DISTANCE = 4; public static final int MINI_ICON_SKEW = 0; public static final int CONTENT_MENU_INDENT = 4; public static final int CORNER_RADIUS_5 = 10; public static final int CORNER_RADIUS_4 = 8; public static final int CORNER_RADIUS_3 = 6; public static final int CORNER_RADIUS_2 = 4; public static final float FACTOR_1_TO_1 = 1.0f; protected Point area; protected ScrollBarInterface hori, vert; protected List<AreaOwner> areaOwners; protected Point offset; protected Point drop_candidate; protected int iconsize; protected int gridSize; protected Rectangle selrect; protected int linewidth; protected float magnification; protected float translationX; protected float translationY; protected boolean shadow; protected Object subject; protected GCInterface gc; protected int shadowSize; private String noteFontName; private int noteFontHeight; protected Hop candidate; public BasePainter( GCInterface gc, Object subject, Point area, ScrollBarInterface hori, ScrollBarInterface vert, Point drop_candidate, Rectangle selrect, List<AreaOwner> areaOwners, int iconsize, int linewidth, int gridsize, int shadowSize, boolean antiAliasing, String noteFontName, int noteFontHeight ) { this.gc = gc; this.subject = subject; this.area = area; this.hori = hori; this.vert = vert; this.selrect = selrect; this.drop_candidate = drop_candidate; this.areaOwners = areaOwners; areaOwners.clear(); // clear it before we start filling it up again. // props = PropsUI.getInstance(); this.iconsize = iconsize; this.linewidth = linewidth; this.gridSize = gridsize; this.shadowSize = shadowSize; this.shadow = shadowSize > 0; this.magnification = 1.0f; gc.setAntialias( antiAliasing ); this.noteFontName = noteFontName; this.noteFontHeight = noteFontHeight; } public static EImage getStreamIconImage( StreamIcon streamIcon ) { switch ( streamIcon ) { case TRUE: return EImage.TRUE; case FALSE: return EImage.FALSE; case ERROR: return EImage.ERROR; case INFO: return EImage.INFO; case TARGET: return EImage.TARGET; case INPUT: return EImage.INPUT; case OUTPUT: return EImage.OUTPUT; default: return EImage.ARROW; } } protected void drawNote( NotePadMeta notePadMeta ) { if ( notePadMeta.isSelected() ) { gc.setLineWidth( 2 ); } else { gc.setLineWidth( 1 ); } Point ext; if ( Utils.isEmpty( notePadMeta.getNote() ) ) { ext = new Point( 10, 10 ); // Empty note } else { gc.setFont( Const.NVL( notePadMeta.getFontName(), noteFontName ), notePadMeta.getFontSize() == -1 ? noteFontHeight : notePadMeta.getFontSize(), notePadMeta.isFontBold(), notePadMeta.isFontItalic() ); ext = gc.textExtent( notePadMeta.getNote() ); } Point p = new Point( ext.x, ext.y ); Point loc = notePadMeta.getLocation(); Point note = real2screen( loc.x, loc.y ); int margin = Const.NOTE_MARGIN; p.x += 2 * margin; p.y += 2 * margin; int width = notePadMeta.width; int height = notePadMeta.height; if ( p.x > width ) { width = p.x; } if ( p.y > height ) { height = p.y; } int[] noteshape = new int[] { note.x, note.y, // Top left note.x + width + 2 * margin, note.y, // Top right note.x + width + 2 * margin, note.y + height, // bottom right 1 note.x + width, note.y + height + 2 * margin, // bottom right 2 note.x + width, note.y + height, // bottom right 3 note.x + width + 2 * margin, note.y + height, // bottom right 1 note.x + width, note.y + height + 2 * margin, // bottom right 2 note.x, note.y + height + 2 * margin // bottom left }; // Draw shadow around note? if ( notePadMeta.isDrawShadow() ) { int s = shadowSize; int[] shadowa = new int[] { note.x + s, note.y + s, // Top left note.x + width + 2 * margin + s, note.y + s, // Top right note.x + width + 2 * margin + s, note.y + height + s, // bottom right 1 note.x + width + s, note.y + height + 2 * margin + s, // bottom right 2 note.x + s, note.y + height + 2 * margin + s // bottom left }; gc.setBackground( EColor.LIGHTGRAY ); gc.fillPolygon( shadowa ); } gc.setBackground( notePadMeta.getBackGroundColorRed(), notePadMeta.getBackGroundColorGreen(), notePadMeta .getBackGroundColorBlue() ); gc.setForeground( notePadMeta.getBorderColorRed(), notePadMeta.getBorderColorGreen(), notePadMeta .getBorderColorBlue() ); gc.fillPolygon( noteshape ); gc.drawPolygon( noteshape ); if ( !Utils.isEmpty( notePadMeta.getNote() ) ) { gc.setForeground( notePadMeta.getFontColorRed(), notePadMeta.getFontColorGreen(), notePadMeta .getFontColorBlue() ); gc.drawText( notePadMeta.getNote(), note.x + margin, note.y + margin, true ); } notePadMeta.width = width; // Save for the "mouse" later on... notePadMeta.height = height; if ( notePadMeta.isSelected() ) { gc.setLineWidth( 1 ); } else { gc.setLineWidth( 2 ); } // Add to the list of areas... // if ( !shadow ) { areaOwners.add( new AreaOwner( AreaType.NOTE, note.x, note.y, width, height, offset, subject, notePadMeta ) ); } } protected int translateTo1To1( int value ) { return Math.round( value / magnification ); } protected int translateToCurrentScale( int value ) { return Math.round( value * magnification ); } protected Point real2screen( int x, int y ) { Point screen = new Point( x + offset.x, y + offset.y ); return screen; } protected Point getThumb( Point area, Point transMax ) { Point resizedMax = magnifyPoint( transMax ); Point thumb = new Point( 0, 0 ); if ( resizedMax.x <= area.x ) { thumb.x = 100; } else { thumb.x = (int) Math.floor( 100d * area.x / resizedMax.x ); } if ( resizedMax.y <= area.y ) { thumb.y = 100; } else { thumb.y = (int) Math.floor( 100d * area.y / resizedMax.y ); } return thumb; } protected Point magnifyPoint( Point p ) { return new Point( Math.round( p.x * magnification ), Math.round( p.y * magnification ) ); } protected Point getOffset( Point thumb, Point area ) { Point p = new Point( 0, 0 ); if ( hori == null || vert == null ) { return p; } Point sel = new Point( hori.getSelection(), vert.getSelection() ); if ( thumb.x == 0 || thumb.y == 0 ) { return p; } p.x = Math.round( -sel.x * area.x / thumb.x / magnification ); p.y = Math.round( -sel.y * area.y / thumb.y / magnification ); return p; } protected void drawRect( Rectangle rect ) { if ( rect == null ) { return; } gc.setLineStyle( ELineStyle.DASHDOT ); gc.setLineWidth( linewidth ); gc.setForeground( EColor.GRAY ); // PDI-2619: SWT on Windows doesn't cater for negative rect.width/height so handle here. Point s = real2screen( rect.x, rect.y ); if ( rect.width < 0 ) { s.x = s.x + rect.width; } if ( rect.height < 0 ) { s.y = s.y + rect.height; } gc.drawRectangle( s.x, s.y, Math.abs( rect.width ), Math.abs( rect.height ) ); gc.setLineStyle( ELineStyle.SOLID ); } protected void drawGrid() { Point bounds = gc.getDeviceBounds(); for ( int x = 0; x < bounds.x; x += gridSize ) { for ( int y = 0; y < bounds.y; y += gridSize ) { gc.drawPoint( x + ( offset.x % gridSize ), y + ( offset.y % gridSize ) ); } } } protected int calcArrowLength() { return 19 + ( linewidth - 1 ) * 5; // arrowhead length; } /** * @return the magnification */ public float getMagnification() { return magnification; } /** * @param magnification * the magnification to set */ public void setMagnification( float magnification ) { this.magnification = magnification; } public Point getArea() { return area; } public void setArea( Point area ) { this.area = area; } public ScrollBarInterface getHori() { return hori; } public void setHori( ScrollBarInterface hori ) { this.hori = hori; } public ScrollBarInterface getVert() { return vert; } public void setVert( ScrollBarInterface vert ) { this.vert = vert; } public List<AreaOwner> getAreaOwners() { return areaOwners; } public void setAreaOwners( List<AreaOwner> areaOwners ) { this.areaOwners = areaOwners; } public Point getOffset() { return offset; } public void setOffset( Point offset ) { this.offset = offset; } public Point getDrop_candidate() { return drop_candidate; } public void setDrop_candidate( Point drop_candidate ) { this.drop_candidate = drop_candidate; } public int getIconsize() { return iconsize; } public void setIconsize( int iconsize ) { this.iconsize = iconsize; } public int getGridSize() { return gridSize; } public void setGridSize( int gridSize ) { this.gridSize = gridSize; } public Rectangle getSelrect() { return selrect; } public void setSelrect( Rectangle selrect ) { this.selrect = selrect; } public int getLinewidth() { return linewidth; } public void setLinewidth( int linewidth ) { this.linewidth = linewidth; } public float getTranslationX() { return translationX; } public void setTranslationX( float translationX ) { this.translationX = translationX; } public float getTranslationY() { return translationY; } public void setTranslationY( float translationY ) { this.translationY = translationY; } public boolean isShadow() { return shadow; } public void setShadow( boolean shadow ) { this.shadow = shadow; } public Object getSubject() { return subject; } public void setSubject( Object subject ) { this.subject = subject; } public GCInterface getGc() { return gc; } public void setGc( GCInterface gc ) { this.gc = gc; } public int getShadowSize() { return shadowSize; } public void setShadowSize( int shadowSize ) { this.shadowSize = shadowSize; } public String getNoteFontName() { return noteFontName; } public void setNoteFontName( String noteFontName ) { this.noteFontName = noteFontName; } public int getNoteFontHeight() { return noteFontHeight; } public void setNoteFontHeight( int noteFontHeight ) { this.noteFontHeight = noteFontHeight; } public double getTheta() { return theta; } public Hop getCandidate() { return candidate; } public void setCandidate( Hop candidate ) { this.candidate = candidate; } protected int[] getLine( Part fs, Part ts ) { if ( fs == null || ts == null ) { return null; } Point from = fs.getLocation(); Point to = ts.getLocation(); int x1 = from.x + iconsize / 2; int y1 = from.y + iconsize / 2; int x2 = to.x + iconsize / 2; int y2 = to.y + iconsize / 2; return new int[] { x1, y1, x2, y2 }; } protected void drawArrow( EImage arrow, int[] line, Hop hop, Object startObject, Object endObject ) { Point screen_from = real2screen( line[0], line[1] ); Point screen_to = real2screen( line[2], line[3] ); drawArrow( arrow, screen_from.x, screen_from.y, screen_to.x, screen_to.y, theta, calcArrowLength(), -1, hop, startObject, endObject ); } protected abstract void drawArrow( EImage arrow, int x1, int y1, int x2, int y2, double theta, int size, double factor, Hop jobHop, Object startObject, Object endObject ); }