/* * Copyright 2007 - 2017 the original author or authors. * * 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 net.sf.jailer.ui.graphical_view; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.awt.geom.RoundRectangle2D; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.swing.ImageIcon; import net.sf.jailer.datamodel.Association; import net.sf.jailer.datamodel.Column; import net.sf.jailer.datamodel.DataModel; import net.sf.jailer.datamodel.Table; import net.sf.jailer.ui.UIUtil; import prefuse.Constants; import prefuse.render.AbstractShapeRenderer; import prefuse.render.ImageFactory; import prefuse.render.LabelRenderer; import prefuse.util.ColorLib; import prefuse.util.FontLib; import prefuse.util.GraphicsLib; import prefuse.util.StringLib; import prefuse.visual.VisualItem; /** * Renders {@link Table}s. * <br> * Copy of {@link LabelRenderer}, added support for multi-image rendering. * * @author Ralf Wisser */ public class TableRenderer extends AbstractShapeRenderer { protected ImageFactory m_images = null; protected String m_delim = "\n"; protected String m_labelName = "label"; protected String m_imageName = null; protected int m_xAlign = Constants.CENTER; protected int m_yAlign = Constants.CENTER; protected int m_hTextAlign = Constants.LEFT; protected int m_vTextAlign = Constants.TOP; protected int m_hImageAlign = Constants.CENTER; protected int m_vImageAlign = Constants.TOP; protected int m_imagePos = Constants.LEFT; protected int m_horizBorder = 2; protected int m_vertBorder = 0; protected int m_imageMargin = 2; protected int m_arcWidth = 0; protected int m_arcHeight = 0; protected int m_maxTextWidth = -1; /** Transform used to scale and position images */ AffineTransform m_transform = new AffineTransform(); /** The holder for the currently computed bounding box */ protected RectangularShape m_bbox = new Rectangle2D.Double(); protected Point2D m_pt = new Point2D.Double(); // temp point protected Font m_font; // temp font holder protected Font m_font_nic; // temp font holder protected Font m_font2; // temp font holder protected String m_text; // label text protected Dimension m_textDim = new Dimension(); // text width / height protected Dimension m_headerDim = new Dimension(); // text width / height of header private int m_color; private int NOT_IN_CLOSURE_COLOR = ColorLib.rgb(170, 50, 50); private int IN_CLOSURE_COLOR = ColorLib.rgb(0, 0, 0); // ------------------------------------------------------------------------ /** * Rounds the corners of the bounding rectangle in which the text * string is rendered. This will only be seen if either the stroke * or fill color is non-transparent. * @param arcWidth the width of the curved corner * @param arcHeight the height of the curved corner */ public void setRoundedCorner(int arcWidth, int arcHeight) { if ( (arcWidth == 0 || arcHeight == 0) && !(m_bbox instanceof Rectangle2D) ) { m_bbox = new Rectangle2D.Double(); } else { if ( !(m_bbox instanceof RoundRectangle2D) ) m_bbox = new RoundRectangle2D.Double(); ((RoundRectangle2D)m_bbox) .setRoundRect(0,0,10,10,arcWidth,arcHeight); m_arcWidth = arcWidth; m_arcHeight = arcHeight; } } /** * Get the field name to use for text labels. * @return the data field for text labels, or null for no text */ public String getTextField() { return m_labelName; } /** * Set the field name to use for text labels. * @param textField the data field for text labels, or null for no text */ public void setTextField(String textField) { m_labelName = textField; } /** * Sets the maximum width that should be allowed of the text label. * A value of -1 specifies no limit (this is the default). * @param maxWidth the maximum width of the text or -1 for no limit */ public void setMaxTextWidth(int maxWidth) { m_maxTextWidth = maxWidth; } /** * Caches texts. */ private Map<String, String> textCache = new HashMap<String, String>(); private long textCacheVersion = -1; /** * Returns the text to draw. Subclasses can override this class to * perform custom text selection. * @param item the item to represent as a <code>String</code> * @return a <code>String</code> to draw */ protected String getText(VisualItem item) { if (textCacheVersion != model.version) { textCacheVersion = model.version; textCache.clear(); } m_color = IN_CLOSURE_COLOR; if (item.canGetString(m_labelName) ) { String tableName = item.getString(m_labelName); Table table = model.getTable(tableName); if (table != null) { tableName = model.getDisplayName(table); } if (table != null && !graphicalDataModelView.modelEditor.getCurrentSubjectClosure().contains(table)) { m_color = NOT_IN_CLOSURE_COLOR; } if (table != null && graphicalDataModelView.showDetails(table)) { if (textCache.containsKey(table.getName())) { return textCache.get(table.getName()); } StringBuilder sb = new StringBuilder(tableName + " \n-\n"); for (Column c: table.getColumns()) { if (c.getFilter() != null) { sb.append("!"); } for (Column pk: table.primaryKey.getColumns()) { if (pk.name.equals(c.name)) { sb.append("+"); break; } } String sql = c.toSQL(null); sb.append(c.name).append(" \t").append(sql.substring(c.name.length()).trim()).append(" \n"); } textCache.put(table.getName(), sb.toString()); return sb.toString(); } return tableName + " "; } return " "; } // ------------------------------------------------------------------------ // Image Handling /** * Get the data field for image locations. The value stored * in the data field should be a URL, a file within the current classpath, * a file on the filesystem, or null for no image. * @return the data field for image locations, or null for no images */ public String getImageField() { return m_imageName; } /** * Set the data field for image locations. The value stored * in the data field should be a URL, a file within the current classpath, * a file on the filesystem, or null for no image. If the * <code>imageField</code> parameter is null, no images at all will be * drawn. * @param imageField the data field for image locations, or null for * no images */ public void setImageField(String imageField) { if ( imageField != null ) m_images = new ImageFactory(); m_imageName = imageField; } /** * Sets the maximum image dimensions, used to control scaling of loaded * images. This scaling is enforced immediately upon loading of the image. * @param width the maximum width of images (-1 for no limit) * @param height the maximum height of images (-1 for no limit) */ public void setMaxImageDimensions(int width, int height) { if ( m_images == null ) m_images = new ImageFactory(); m_images.setMaxImageDimensions(width, height); } /** * Returns a location string for the image to draw. Subclasses can override * this class to perform custom image selection beyond looking up the value * from a data field. * @param item the item for which to select an image to draw * @return the location string for the image to use, or null for no image */ protected String getImageLocation(VisualItem item) { return item.canGetString(m_imageName) ? item.getString(m_imageName) : null; } // ------------------------------------------------------------------------ // Rendering private String computeTextDimensions(VisualItem item, String text, double size) { // put item font in temp member variable m_font = item.getFont(); // scale the font as needed if ( size != 1 ) { m_font = FontLib.getFont(m_font.getName(), m_font.getStyle(), size*m_font.getSize()); } m_font2 = FontLib.getFont(m_font.getName(), m_font.getStyle(), size*m_font.getSize() * 0.8); m_font_nic = FontLib.getFont(m_font.getName(), m_font.getStyle() | Font.ITALIC, size*m_font.getSize()); FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(m_font); FontMetrics fm2 = DEFAULT_GRAPHICS.getFontMetrics(m_font2); StringBuffer str = null; // compute the number of lines and the maximum width int nlines = 1, w = 0, start = 0, end = text.indexOf(m_delim); if (text.endsWith("\n")) { --nlines; } m_textDim.width = 0; m_headerDim.width = 0; String line; boolean f = true; for ( ; end >= 0; ++nlines ) { w = (f? fm : fm2).stringWidth(line=text.substring(start,end)); f = false; // abbreviate line as needed if ( m_maxTextWidth > -1 && w > m_maxTextWidth ) { if ( str == null ) str = new StringBuffer(text.substring(0,start)); str.append(StringLib.abbreviate(line, fm, m_maxTextWidth)); str.append(m_delim); w = m_maxTextWidth; } else if ( str != null ) { str.append(line).append(m_delim); } // update maximum width and substring indices m_textDim.width = Math.max(m_textDim.width, w); start = end+1; end = text.indexOf(m_delim, start); } w = (f? fm : fm2).stringWidth(line=text.substring(start)); // abbreviate line as needed if ( m_maxTextWidth > -1 && w > m_maxTextWidth ) { if ( str == null ) str = new StringBuffer(text.substring(0,start)); str.append(StringLib.abbreviate(line, fm, m_maxTextWidth)); w = m_maxTextWidth; } else if ( str != null ) { str.append(line); } // update maximum width m_textDim.width = Math.max(m_textDim.width, w); // compute the text height m_textDim.height = fm.getHeight() + fm2.getHeight() * (nlines - 1); m_headerDim.width = m_textDim.width; m_headerDim.height = fm.getHeight(); return str==null ? text : str.toString(); } /** * @see prefuse.render.AbstractShapeRenderer#getRawShape(prefuse.visual.VisualItem) */ protected Shape getRawShape(VisualItem item) { m_text = getText(item); Image[] img = getImage(item); double size = item.getSize(); // get text dimensions int tw=0, th=0; if ( m_text != null ) { m_text = computeTextDimensions(item, m_text, size); th = m_textDim.height; tw = m_textDim.width; } // get image dimensions double iw=0, ih=0; for (Image i: img) { if (i == null) { continue; } ih = i.getHeight(null) * imgScale(i); iw += i.getWidth(null) * imgScale(i) + 2; } // get bounding box dimensions double w=0, h=0; switch ( m_imagePos ) { case Constants.LEFT: case Constants.RIGHT: w = tw + size*(iw +2*m_horizBorder + (tw>0 && iw>0 ? m_imageMargin : 0)); h = Math.max(th, size*ih) + size*2*m_vertBorder; break; case Constants.TOP: case Constants.BOTTOM: w = Math.max(tw, size*iw) + size*2*m_horizBorder; h = th + size*(ih + 2*m_vertBorder + (th>0 && ih>0 ? m_imageMargin : 0)); break; default: throw new IllegalStateException( "Unrecognized image alignment setting."); } // get the top-left point, using the current alignment settings getAlignedPoint(m_pt, item, w, h, m_xAlign, m_yAlign); if ( m_bbox instanceof RoundRectangle2D ) { RoundRectangle2D rr = (RoundRectangle2D)m_bbox; rr.setRoundRect(m_pt.getX(), m_pt.getY(), w, h, size*m_arcWidth, size*m_arcHeight); } else { m_bbox.setFrame(m_pt.getX(), m_pt.getY(), w, h); } return m_bbox; } /** * Helper method, which calculates the top-left co-ordinate of an item * given the item's alignment. */ protected static void getAlignedPoint(Point2D p, VisualItem item, double w, double h, int xAlign, int yAlign) { double x = item.getX(), y = item.getY(); if ( Double.isNaN(x) || Double.isInfinite(x) ) x = 0; // safety check if ( Double.isNaN(y) || Double.isInfinite(y) ) y = 0; // safety check if ( xAlign == Constants.CENTER ) { x = x-(w/2); } else if ( xAlign == Constants.RIGHT ) { x = x-w; } if ( yAlign == Constants.CENTER ) { y = y-(h/2); } else if ( yAlign == Constants.BOTTOM ) { y = y-h; } p.setLocation(x,y); } /** * @see prefuse.render.Renderer#render(java.awt.Graphics2D, prefuse.visual.VisualItem) */ public void render(Graphics2D g, VisualItem item) { item.setTextColor(IN_CLOSURE_COLOR); RectangularShape shape = (RectangularShape)getShape(item); if ( shape == null ) return; // fill the shape, if requested int type = getRenderType(item); if ( type==RENDER_TYPE_FILL || type==RENDER_TYPE_DRAW_AND_FILL ) { boolean isSelected = false; String tableName = item.getString("label"); Table table = model.getTable(tableName); if (!graphicalDataModelView.inImageExport) { if (graphicalDataModelView.selectedAssociation != null) { isSelected = graphicalDataModelView.selectedAssociation.destination.equals(table); } else { isSelected = graphicalDataModelView.root != null && graphicalDataModelView.root.equals(table); } } if (isSelected) { item.setStrokeColor(ColorLib.rgb(0, 0, 0)); } else { item.setStrokeColor(ColorLib.rgba(0, 0, 0, 0)); } int fillColor = item.getFillColor(); if (graphicalDataModelView.tablesOnPath.contains(tableName)) { fillColor = ColorLib.rgba(0.3f, 0.9f, 1.0f, 0.30f); } paint(g, item, fillColor, shape, new BasicStroke(isSelected? 1 : 0), isSelected? RENDER_TYPE_DRAW_AND_FILL : RENDER_TYPE_FILL); } // now render the image and text String text = m_text; Image[] img = getImage(item); if ( text == null && img == null ) return; double size = item.getSize(); boolean useInt = 1.5 > Math.max(g.getTransform().getScaleX(), g.getTransform().getScaleY()); double x = shape.getMinX() + size*m_horizBorder; double y = shape.getMinY() + size*m_vertBorder; // render image for (Image i: img) { if (i == null) { continue; } double w = size * i.getWidth(null) * imgScale(i); double h = size * i.getHeight(null) * imgScale(i); double ix=x, iy=y; // determine one co-ordinate based on the image position switch ( m_imagePos ) { case Constants.LEFT: x += w + size*m_imageMargin; break; case Constants.RIGHT: ix = shape.getMaxX() - size*m_horizBorder - w; break; case Constants.TOP: y += h + size*m_imageMargin; break; case Constants.BOTTOM: iy = shape.getMaxY() - size*m_vertBorder - h; break; default: throw new IllegalStateException( "Unrecognized image alignment setting."); } // determine the other coordinate based on image alignment switch ( m_imagePos ) { case Constants.LEFT: case Constants.RIGHT: // need to set image y-coordinate switch ( m_vImageAlign ) { case Constants.TOP: break; case Constants.BOTTOM: iy = shape.getMaxY() - size*m_vertBorder - h; break; case Constants.CENTER: iy = shape.getCenterY() - h/2; break; } break; case Constants.TOP: case Constants.BOTTOM: // need to set image x-coordinate switch ( m_hImageAlign ) { case Constants.LEFT: break; case Constants.RIGHT: ix = shape.getMaxX() - size*m_horizBorder - w; break; case Constants.CENTER: ix = shape.getCenterX() - w/2; break; } break; } m_transform.setTransform(size * imgScale(i),0,0,size * imgScale(i),ix,iy); g.drawImage(i, m_transform, null); } // render text int textColor = m_color; // item.getTextColor(); if ( text != null && ColorLib.alpha(textColor) > 0 ) { g.setPaint(ColorLib.getColor(textColor)); if (m_color == NOT_IN_CLOSURE_COLOR) { g.setFont(m_font_nic); } else { g.setFont(m_font); } FontMetrics fm = DEFAULT_GRAPHICS.getFontMetrics(m_font); // compute available width double tw; switch ( m_imagePos ) { case Constants.TOP: case Constants.BOTTOM: tw = shape.getWidth() - 2*size*m_horizBorder; break; default: tw = m_textDim.width; } // compute available height double th; switch ( m_imagePos ) { case Constants.LEFT: case Constants.RIGHT: th = shape.getHeight() - 2*size*m_vertBorder; break; default: th = m_textDim.height; } // compute starting y-coordinate y += fm.getAscent(); switch ( m_vTextAlign ) { case Constants.TOP: break; case Constants.BOTTOM: y += th - m_textDim.height; break; case Constants.CENTER: y += (th - m_textDim.height)/2; } // render each line of text int lh = fm.getHeight(); // the line height boolean f = true; int start = 0, end = text.indexOf(m_delim); for ( ; end >= 0; y += lh ) { g.setPaint(ColorLib.getColor(textColor)); String line = text.substring(start, end); String a, b; int tab = line.indexOf('\t'); if (tab < 0) { a = line; b = null; } else { a = line.substring(0, tab); b = line.substring(tab + 1); if (a.startsWith("!")) { if (filterImage != null) { m_transform.setTransform(size * imgScale(filterImage) * 0.6, 0, 0, size * imgScale(filterImage) * 0.6, x - filterImage.getWidth(null) * imgScale(filterImage) * 0.9, y - lh * 0.75); g.drawImage(filterImage, m_transform, null); } a = a.substring(1); } if (a.startsWith("+")) { g.setPaint(Color.RED); a = a.substring(1); } } if ("-".equals(line)) { g.drawLine((int) x, (int) y - lh/2 + 1, (int) x + (int) tw, (int) y - lh/2 + 1); } else { drawString(g, fm, a, useInt, x, y, tw, Constants.LEFT); if (b != null) { g.setPaint(Color.GRAY); drawString(g, fm, b, useInt, x, y, tw, Constants.RIGHT); } } start = end+1; end = text.indexOf(m_delim, start); if (f) { g.setFont(m_font2); fm = DEFAULT_GRAPHICS.getFontMetrics(m_font2); lh = fm.getHeight(); } textColor = IN_CLOSURE_COLOR; f = false; } drawString(g, fm, text.substring(start), useInt, x, y, tw, Constants.LEFT); } // draw border if (type==RENDER_TYPE_DRAW || type==RENDER_TYPE_DRAW_AND_FILL) { GraphicsLib.paint(g,item,shape,getStroke(item),RENDER_TYPE_DRAW); } } private double imgScale(Image image) { if (image == null) { return 1; } return m_headerDim.height / (double) image.getHeight(null) * ((image == collapsedImage || image == collapsedRedImage)? 1.0 : 1.2); } private final void drawString(Graphics2D g, FontMetrics fm, String text, boolean useInt, double x, double y, double w, int hTextAlign) { if (text.length() == 0) { return; } // compute the x-coordinate double tx; switch (hTextAlign ) { case Constants.LEFT: tx = x; break; case Constants.RIGHT: tx = x + w - fm.stringWidth(text); break; case Constants.CENTER: tx = x + (w - fm.stringWidth(text)) / 2; break; default: throw new IllegalStateException( "Unrecognized text alignment setting."); } // use integer precision unless zoomed-in // results in more stable drawing if ( useInt ) { g.drawString(text, (int)tx, (int)y); } else { g.drawString(text, (float)tx, (float)y); } } /** * Returns the image factory used by this renderer. * @return the image factory */ public ImageFactory getImageFactory() { if ( m_images == null ) m_images = new ImageFactory(); return m_images; } /** * Sets the image factory used by this renderer. * @param ifact the image factory */ public void setImageFactory(ImageFactory ifact) { m_images = ifact; } // ------------------------------------------------------------------------ /** * Get the horizontal text alignment within the layout. One of * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or * {@link prefuse.Constants#CENTER}. The default is centered text. * @return the horizontal text alignment */ public int getHorizontalTextAlignment() { return m_hTextAlign; } /** * Set the horizontal text alignment within the layout. One of * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or * {@link prefuse.Constants#CENTER}. The default is centered text. * @param halign the desired horizontal text alignment */ public void setHorizontalTextAlignment(int halign) { if ( halign != Constants.LEFT && halign != Constants.RIGHT && halign != Constants.CENTER ) throw new IllegalArgumentException( "Illegal horizontal text alignment value."); m_hTextAlign = halign; } /** * Get the vertical text alignment within the layout. One of * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or * {@link prefuse.Constants#CENTER}. The default is centered text. * @return the vertical text alignment */ public int getVerticalTextAlignment() { return m_vTextAlign; } /** * Set the vertical text alignment within the layout. One of * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or * {@link prefuse.Constants#CENTER}. The default is centered text. * @param valign the desired vertical text alignment */ public void setVerticalTextAlignment(int valign) { if ( valign != Constants.TOP && valign != Constants.BOTTOM && valign != Constants.CENTER ) throw new IllegalArgumentException( "Illegal vertical text alignment value."); m_vTextAlign = valign; } /** * Get the horizontal image alignment within the layout. One of * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or * {@link prefuse.Constants#CENTER}. The default is a centered image. * @return the horizontal image alignment */ public int getHorizontalImageAlignment() { return m_hImageAlign; } /** * Set the horizontal image alignment within the layout. One of * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or * {@link prefuse.Constants#CENTER}. The default is a centered image. * @param halign the desired horizontal image alignment */ public void setHorizontalImageAlignment(int halign) { if ( halign != Constants.LEFT && halign != Constants.RIGHT && halign != Constants.CENTER ) throw new IllegalArgumentException( "Illegal horizontal text alignment value."); m_hImageAlign = halign; } /** * Get the vertical image alignment within the layout. One of * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or * {@link prefuse.Constants#CENTER}. The default is a centered image. * @return the vertical image alignment */ public int getVerticalImageAlignment() { return m_vImageAlign; } /** * Set the vertical image alignment within the layout. One of * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or * {@link prefuse.Constants#CENTER}. The default is a centered image. * @param valign the desired vertical image alignment */ public void setVerticalImageAlignment(int valign) { if ( valign != Constants.TOP && valign != Constants.BOTTOM && valign != Constants.CENTER ) throw new IllegalArgumentException( "Illegal vertical text alignment value."); m_vImageAlign = valign; } /** * Get the image position, determining where the image is placed with * respect to the text. One of {@link Constants#LEFT}, * {@link Constants#RIGHT}, {@link Constants#TOP}, or * {@link Constants#BOTTOM}. The default is left. * @return the image position */ public int getImagePosition() { return m_imagePos; } /** * Set the image position, determining where the image is placed with * respect to the text. One of {@link Constants#LEFT}, * {@link Constants#RIGHT}, {@link Constants#TOP}, or * {@link Constants#BOTTOM}. The default is left. * @param pos the desired image position */ public void setImagePosition(int pos) { if ( pos != Constants.TOP && pos != Constants.BOTTOM && pos != Constants.LEFT && pos != Constants.RIGHT && pos != Constants.CENTER ) throw new IllegalArgumentException( "Illegal image position value."); m_imagePos = pos; } // ------------------------------------------------------------------------ /** * Get the horizontal alignment of this node with respect to its * x, y coordinates. * @return the horizontal alignment, one of * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or * {@link prefuse.Constants#CENTER}. */ public int getHorizontalAlignment() { return m_xAlign; } /** * Get the vertical alignment of this node with respect to its * x, y coordinates. * @return the vertical alignment, one of * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or * {@link prefuse.Constants#CENTER}. */ public int getVerticalAlignment() { return m_yAlign; } /** * Set the horizontal alignment of this node with respect to its * x, y coordinates. * @param align the horizontal alignment, one of * {@link prefuse.Constants#LEFT}, {@link prefuse.Constants#RIGHT}, or * {@link prefuse.Constants#CENTER}. */ public void setHorizontalAlignment(int align) { m_xAlign = align; } /** * Set the vertical alignment of this node with respect to its * x, y coordinates. * @param align the vertical alignment, one of * {@link prefuse.Constants#TOP}, {@link prefuse.Constants#BOTTOM}, or * {@link prefuse.Constants#CENTER}. */ public void setVerticalAlignment(int align) { m_yAlign = align; } /** * Returns the amount of padding in pixels between the content * and the border of this item along the horizontal dimension. * @return the horizontal padding */ public int getHorizontalPadding() { return m_horizBorder; } /** * Sets the amount of padding in pixels between the content * and the border of this item along the horizontal dimension. * @param xpad the horizontal padding to set */ public void setHorizontalPadding(int xpad) { m_horizBorder = xpad; } /** * Returns the amount of padding in pixels between the content * and the border of this item along the vertical dimension. * @return the vertical padding */ public int getVerticalPadding() { return m_vertBorder; } /** * Sets the amount of padding in pixels between the content * and the border of this item along the vertical dimension. * @param ypad the vertical padding */ public void setVerticalPadding(int ypad) { m_vertBorder = ypad; } /** * Get the padding, in pixels, between an image and text. * @return the padding between an image and text */ public int getImageTextPadding() { return m_imageMargin; } /** * Set the padding, in pixels, between an image and text. * @param pad the padding to use between an image and text */ public void setImageTextPadding(int pad) { m_imageMargin = pad; } private final DataModel model; private final GraphicalDataModelView graphicalDataModelView; /** * Constructor. * * @param model * @param graphicalDataModelView */ public TableRenderer(DataModel model, GraphicalDataModelView graphicalDataModelView) { this.model = model; this.graphicalDataModelView = graphicalDataModelView; } private Set<Table> filteredTables = new HashSet<Table>(); private long dmVersionOfFilteredTables = -1; /** * Get the image to include in the label for the given VisualItem. * * @param item * the item to get an image for * @return the image for the item, or null for no image */ protected Image[] getImage(VisualItem item) { Image[] img = new Image[6]; int i = 0; Table table = model.getTable(item .getString("label")); if (table != null) { if (!graphicalDataModelView.expandedTables.contains(table)) { img[i++] = getCollapsedImage(table); } if (table.equals(graphicalDataModelView.modelEditor.getSubject()) || graphicalDataModelView.modelEditor.isAdditionalSubject(table)) { img[i++] = subjectImage; } if (table.isExcludedFromDeletion()) { img[i++] = excludeFromDeletionImage; } if (table.getUpsert()) { img[i++] = upsertImage; } if (model.version != dmVersionOfFilteredTables) { filteredTables.clear(); for (Table t: model.getTables()) { for (Column column: t.getColumns()) { if (column.getFilter() != null) { filteredTables.add(t); break; } } } dmVersionOfFilteredTables = model.version; } if (filteredTables.contains(table)) { img[i++] = filterImage; } } return img; } private Map<String, Image> collapsedImages = new HashMap<String, Image>(); private Image getCollapsedImage(Table table) { if (collapsedImages.containsKey(table.getName())) { return collapsedImages.get(table.getName()); } Set<String> destNames = new HashSet<String>(); for (Association a: table.associations) { destNames.add(a.destination.getName()); } Image image = destNames.size() > 20? collapsedRedImage : collapsedImage; collapsedImages.put(table.getName(), image); return image; } /** * Get tool tip text for a table. * * @param table the table * @return tool tip text */ public String getToolTip(Table table) { String tt = ""; if (table.isExcludedFromDeletion()) { tt += "Excluded from Deletion. "; } if (table.getUpsert()) { tt += "Upsert Rows/Merge. "; } return tt + table.getName() + " (" + table.primaryKey.toSQL(null, false) + ")"; } /** * Render a shape associated with a VisualItem into a graphics context. This * method uses the {@link java.awt.Graphics} interface methods when it can, * as opposed to the {@link java.awt.Graphics2D} methods such as * {@link java.awt.Graphics2D#draw(java.awt.Shape)} and * {@link java.awt.Graphics2D#fill(java.awt.Shape)}, resulting in a * significant performance increase on the Windows platform, particularly * for rectangle and line drawing calls. * @param g the graphics context to render to * @param item the item being represented by the shape, this instance is * used to get the correct color values for the drawing * @param shape the shape to render * @param stroke the stroke type to use for drawing the object. * @param type the rendering type indicating if the shape should be drawn, * filled, or both. One of * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_DRAW}, * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_FILL}, * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_DRAW_AND_FILL}, or * {@link prefuse.render.AbstractShapeRenderer#RENDER_TYPE_NONE}. */ private static void paint(Graphics2D g, VisualItem item, int fillColorI, Shape shape, BasicStroke stroke, int type) { // if render type is NONE, then there is nothing to do if ( type == AbstractShapeRenderer.RENDER_TYPE_NONE ) return; Color fillColor = ColorLib.getColor(fillColorI); // set up colors Color strokeColor = ColorLib.getColor(item.getStrokeColor()); boolean sdraw = (type == AbstractShapeRenderer.RENDER_TYPE_DRAW || type == AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL) && strokeColor.getAlpha() != 0; boolean fdraw = (type == AbstractShapeRenderer.RENDER_TYPE_FILL || type == AbstractShapeRenderer.RENDER_TYPE_DRAW_AND_FILL) && fillColor.getAlpha() != 0; if ( !(sdraw || fdraw) ) return; Stroke origStroke = null; if ( sdraw ) { origStroke = g.getStroke(); g.setStroke(stroke); } int x, y, w, h, aw, ah; double xx, yy, ww, hh; // see if an optimized (non-shape) rendering call is available for us // these can speed things up significantly on the windows JRE // it is stupid we have to do this, but we do what we must // if we are zoomed in, we have no choice but to use // full precision rendering methods. AffineTransform at = g.getTransform(); double scale = Math.max(at.getScaleX(), at.getScaleY()); if ( scale > 1.5 ) { if (fdraw) { g.setPaint(fillColor); g.fill(shape); } if (sdraw) { g.setPaint(strokeColor); g.draw(shape); } } else if ( shape instanceof RectangularShape ) { RectangularShape r = (RectangularShape)shape; xx = r.getX(); ww = r.getWidth(); yy = r.getY(); hh = r.getHeight(); x = (int)xx; y = (int)yy; w = (int)(ww+xx-x); h = (int)(hh+yy-y); if ( shape instanceof Rectangle2D ) { if (fdraw) { g.setPaint(fillColor); g.fillRect(x, y, w, h); } if (sdraw) { g.setPaint(strokeColor); g.drawRect(x, y, w, h); } } else if ( shape instanceof RoundRectangle2D ) { RoundRectangle2D rr = (RoundRectangle2D)shape; aw = (int)rr.getArcWidth(); ah = (int)rr.getArcHeight(); if (fdraw) { g.setPaint(fillColor); g.fillRoundRect(x, y, w, h, aw, ah); } if (sdraw) { g.setPaint(strokeColor); g.drawRoundRect(x, y, w, h, aw, ah); } } else if ( shape instanceof Ellipse2D ) { if (fdraw) { g.setPaint(fillColor); g.fillOval(x, y, w, h); } if (sdraw) { g.setPaint(strokeColor); g.drawOval(x, y, w, h); } } else { if (fdraw) { g.setPaint(fillColor); g.fill(shape); } if (sdraw) { g.setPaint(strokeColor); g.draw(shape); } } } else if ( shape instanceof Line2D ) { if (sdraw) { Line2D l = (Line2D)shape; x = (int)(l.getX1()+0.5); y = (int)(l.getY1()+0.5); w = (int)(l.getX2()+0.5); h = (int)(l.getY2()+0.5); g.setPaint(strokeColor); g.drawLine(x, y, w, h); } } else { if (fdraw) { g.setPaint(fillColor); g.fill(shape); } if (sdraw) { g.setPaint(strokeColor); g.draw(shape); } } if ( sdraw ) { g.setStroke(origStroke); } } // images private Image excludeFromDeletionImage = null; private Image collapsedImage = null; private Image collapsedRedImage = null; private Image upsertImage = null; private Image subjectImage = null; private Image filterImage = null; { String dir = "/net/sf/jailer/ui/resource"; // load images try { excludeFromDeletionImage = new ImageIcon(getClass().getResource(dir + "/database-lock.png")).getImage(); } catch (Exception e) { e.printStackTrace(); } try { collapsedImage = new ImageIcon(getClass().getResource(dir + "/collapsed.png")).getImage(); } catch (Exception e) { e.printStackTrace(); } try { collapsedRedImage = new ImageIcon(getClass().getResource(dir + "/collapsedred.png")).getImage(); } catch (Exception e) { e.printStackTrace(); } try { upsertImage = new ImageIcon(getClass().getResource(dir + "/upsert.png")).getImage(); } catch (Exception e) { e.printStackTrace(); } try { subjectImage = new ImageIcon(getClass().getResource(dir + "/subject.png")).getImage(); } catch (Exception e) { e.printStackTrace(); } try { filterImage = new ImageIcon(getClass().getResource(dir + "/filter.png")).getImage(); } catch (Exception e) { e.printStackTrace(); } } }