/* * org.openmicroscopy.shoola.util.ui.border.TitledLineBorder * *------------------------------------------------------------------------------ * Copyright (C) 2006-2007 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.ui.border; //Java imports import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.border.Border; import javax.swing.border.TitledBorder; //Third-party libraries //Application-internal dependencies /** * Extends title border but only draws the top (resp. bottom) of the border if * the title position is at the top (resp. bottom). * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * <small> * (<b>Internal version:</b> $Revision: $Date: $) * </small> * @since OME3.0 */ public class TitledLineBorder extends TitledBorder { /** The default color of the line. */ private static final Color DEFAULT_COLOR = Color.LIGHT_GRAY; /** Extra space added to the bounds of the close button. */ private static final int EDGE_GAP = 2; /** Location of the text. */ protected Point textLoc; /** The color of the line. */ private Color lineColor; /** Collection of images to add, Images correspond to button. */ private List<Image> images; /** Bounds of the images. */ private List<Rectangle> imageBounds; /** * Returns <code>true</code> if the passed rectangle intersects with * the passed parameters defining the rectangle of reference, * <code>false</code> otherwise. * * @param dest The rectangle to handle. * @param rx The x-coordinate of the top-left corner of the rectangle * of reference. * @param ry The y-coordinate of the top-left corner of the rectangle * of reference. * @param rw The width of the rectangle of reference. * @param rh The height of the rectangle of reference. * @return See above. */ private static boolean computeIntersection(Rectangle dest, int rx, int ry, int rw, int rh) { int x1 = Math.max(rx, dest.x); int x2 = Math.min(rx+rw, dest.x+dest.width); int y1 = Math.max(ry, dest.y); int y2 = Math.min(ry+rh, dest.y+dest.height); dest.x = x1; dest.y = y1; dest.width = x2-x1; dest.height = y2-y1; if (dest.width <= 0 || dest.height <= 0) return false; return true; } /** * Creates a new instance. * * @param title The title the border should display. * @param lineColor The color of the line. */ public TitledLineBorder(String title, Color lineColor) { super(title); textLoc = new Point(); setLineColor(lineColor); } /** * Creates a new instance. * * @param title The title the border should display. */ public TitledLineBorder(String title) { this(title, null); } /** * Sets the color of the line. * * @param lineColor The color of the line. */ public void setLineColor(Color lineColor) { if (lineColor == null) this.lineColor = DEFAULT_COLOR; else this.lineColor = lineColor; } /** * Sets the collections of images. * * @param images The collection of images to set. */ public void setImages(List<Image> images) { this.images = images; } /** * Returns the collection of images' bounds. * * @return See above. */ public List<Rectangle> getImagesBounds() { return imageBounds; } /** * Overridden to remove one segment of the border, either the top or * bottom part. * @see TitledBorder#paintBorder(Component, Graphics, int, int, int, int) */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { g.setColor(Color.RED); Border border = getBorder(); if (getTitle() == null || getTitle().equals("")) { if (border != null) border.paintBorder(c, g, x, y, width, height); return; } Rectangle grooveRect = new Rectangle(x+EDGE_SPACING, y+EDGE_SPACING, width-(EDGE_SPACING*2), height-(EDGE_SPACING*2)); Font font = g.getFont(); Color color = g.getColor(); Font fontc = getFont(c); if (fontc != null) g.setFont(fontc.deriveFont(Font.BOLD)); FontMetrics fm = g.getFontMetrics(g.getFont()); int fontHeight = fm.getHeight(); int descent = fm.getDescent(); int ascent = fm.getAscent(); int diff; int stringWidth = fm.stringWidth(getTitle()); if (getTitle().equals("")) stringWidth = 0; Insets insets; if (border != null) insets = border.getBorderInsets(c); else insets = new Insets(0, 0, 0, 0); int titlePos = getTitlePosition(); switch (titlePos) { case ABOVE_TOP: diff = ascent+descent+Math.max(EDGE_SPACING, TEXT_SPACING*2)- EDGE_SPACING; grooveRect.y += diff; grooveRect.height -= diff; textLoc.y = grooveRect.y-descent-TEXT_SPACING; break; case TOP: case DEFAULT_POSITION: diff = Math.max(0, ((ascent/2)+TEXT_SPACING)-EDGE_SPACING); grooveRect.y += diff; grooveRect.height -= diff; textLoc.y = grooveRect.y-descent+(insets.top+ascent+descent)/2; break; case BELOW_TOP: textLoc.y = grooveRect.y+insets.top+ascent+TEXT_SPACING; break; case ABOVE_BOTTOM: textLoc.y = grooveRect.y+grooveRect.height- (insets.bottom+descent+TEXT_SPACING); break; case BOTTOM: grooveRect.height -= fontHeight/2; textLoc.y = grooveRect.y+grooveRect.height-descent + (ascent+descent-insets.bottom)/2; break; case BELOW_BOTTOM: grooveRect.height -= fontHeight; textLoc.y = grooveRect.y+grooveRect.height+ascent+TEXT_SPACING; break; } int justification = getTitleJustification(); if (c.getComponentOrientation().isLeftToRight()) { if (justification == LEADING || justification == DEFAULT_JUSTIFICATION) justification = LEFT; else if (justification == TRAILING) justification = RIGHT; } else { if (justification == LEADING || justification == DEFAULT_JUSTIFICATION) justification = RIGHT; else if (justification == TRAILING) justification = LEFT; } switch (justification) { case LEFT: textLoc.x = grooveRect.x+TEXT_INSET_H+insets.left; break; case RIGHT: textLoc.x = (grooveRect.x+grooveRect.width)- (stringWidth+TEXT_INSET_H+insets.right); break; case CENTER: textLoc.x = grooveRect.x +((grooveRect.width-stringWidth)/2); break; } // If title is positioned in middle of border AND its fontsize // is greater than the border's thickness, we'll need to paint // the border in sections to leave space for the component's background // to show through the title. // if (border != null) { if (((titlePos == TOP || titlePos == DEFAULT_POSITION) && (grooveRect.y > textLoc.y-ascent)) || (titlePos == BOTTOM && (grooveRect.y+grooveRect.height < textLoc.y+descent))) { Rectangle clipRect = new Rectangle(); // save original clip Rectangle saveClip = g.getClipBounds(); // paint strip left of text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, x, y, textLoc.x-1-x, height)) { g.setClip(clipRect); g.setColor(lineColor); g.drawLine(grooveRect.x, grooveRect.y, grooveRect.x+grooveRect.width, grooveRect.y); g.setColor(color); } // paint strip right of text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, textLoc.x+stringWidth+1, y, x+width-(textLoc.x+stringWidth+1), height)) { g.setClip(clipRect); g.setColor(lineColor); g.drawLine(grooveRect.x, grooveRect.y, grooveRect.x+grooveRect.width, grooveRect.y); g.setColor(color); } if (titlePos == TOP || titlePos == DEFAULT_POSITION) { // paint strip below text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, textLoc.x-1, textLoc.y+descent, stringWidth+2, y+height-textLoc.y-descent)) { g.setClip(clipRect); g.setColor(lineColor); g.drawLine(grooveRect.x, grooveRect.y, grooveRect.x+grooveRect.width, grooveRect.y); g.setColor(color); } } else { // titlePos == BOTTOM // paint strip above text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, textLoc.x-1, y, stringWidth+2, textLoc.y - ascent - y)) { g.setClip(clipRect); g.setColor(lineColor); g.drawLine(grooveRect.x, grooveRect.y+grooveRect.height, grooveRect.x+grooveRect.width, grooveRect.y+grooveRect.height); g.setColor(color); } } // restore clip g.setClip(saveClip); } else { border.paintBorder(c, g, grooveRect.x, grooveRect.y, grooveRect.width, grooveRect.height); } } g.setColor(getTitleColor()); g.drawString(getTitle(), textLoc.x, textLoc.y); g.setFont(font); g.setColor(color); if (images == null || images.size() == 0) return; Iterator i = images.iterator(); Rectangle r; Image img; x = textLoc.x+fm.stringWidth(getTitle())+2; y = textLoc.y; int w, h; imageBounds = new ArrayList<Rectangle>(images.size()); while (i.hasNext()) { r = new Rectangle(); img = (Image) i.next(); w = img.getWidth(null); h = img.getHeight(null); r.setBounds(x, y-3*h/4, w, h); g.drawImage(img, x, y-3*h/4, w, h, null); x += w; x += EDGE_GAP; imageBounds.add(r); } } }