/** * author: Marcel Genzmehr * 07.12.2011 */ package org.docear.plugin.core.ui.components; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import javax.swing.Action; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.border.Border; import javax.swing.border.TitledBorder; public class JCheckboxBorder extends TitledBorder { private static final long serialVersionUID = 1L; private Point textLocation = new Point(); private JCheckBox checkbox = new JCheckBox(); /*********************************************************************************** * CONSTRUCTORS **********************************************************************************/ public JCheckboxBorder(String title) { this(null, title, LEADING, TOP, null, null); } /** * @param border * @param title */ public JCheckboxBorder(Border border, String title) { this(border, title, LEADING, TOP, null, null); } public JCheckboxBorder(Border border) { this(border, "", LEADING, TOP, null, null); } public JCheckboxBorder(Border border, String title, int titleJustification, int titlePosition) { this(border, title, titleJustification, titlePosition, null, null); } public JCheckboxBorder(Border border, String title, int titleJustification, int titlePosition, Font titleFont) { this(border, title, titleJustification, titlePosition, titleFont, null); } public JCheckboxBorder(Border border, String title, int titleJustification, int titlePosition, Font titleFont, Color titleColor) { super(border, title, titleJustification, titlePosition, titleFont, titleColor); checkbox.setText(title); checkbox.setEnabled(true); checkbox.setVisible(true); } /*********************************************************************************** * METHODS **********************************************************************************/ public void setCheckboxAction(Action action) { checkbox.setAction(action); } public boolean isCheckboxSelected() { return checkbox.isSelected(); } public void setCheckboxSelected(boolean selected) { checkbox.setSelected(selected); } public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { 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(); g.setFont(getFont(c)); JComponent jc = (c instanceof JComponent) ? (JComponent) c : null; FontMetrics fm = getFontMetrics(jc, g, g.getFont()); int fontHeight = fm.getHeight(); int descent = fm.getDescent(); int ascent = fm.getAscent(); int stringWidth = checkbox.getUI().getPreferredSize(checkbox).width; int diff; 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; textLocation.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; textLocation.y = (grooveRect.y - descent) + (insets.top + ascent + descent) / 2; break; case BELOW_TOP: textLocation.y = grooveRect.y + insets.top + ascent + TEXT_SPACING; break; case ABOVE_BOTTOM: textLocation.y = (grooveRect.y + grooveRect.height) - (insets.bottom + descent + TEXT_SPACING); break; case BOTTOM: grooveRect.height -= fontHeight / 2; textLocation.y = ((grooveRect.y + grooveRect.height) - descent) + ((ascent + descent) - insets.bottom) / 2; break; case BELOW_BOTTOM: grooveRect.height -= fontHeight; textLocation.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: textLocation.x = grooveRect.x + TEXT_INSET_H + insets.left; break; case RIGHT: textLocation.x = (grooveRect.x + grooveRect.width) - (stringWidth + TEXT_INSET_H + insets.right); break; case CENTER: textLocation.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. // Rectangle clipRect = new Rectangle(); if (border != null) { if (((titlePos == TOP || titlePos == DEFAULT_POSITION) && (grooveRect.y > textLocation.y - ascent)) || (titlePos == BOTTOM && (grooveRect.y + grooveRect.height < textLocation.y + descent))) { // save original clip Rectangle saveClip = g.getClipBounds(); // paint strip left of text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, x, y, textLocation.x - 1 - x, height)) { g.setClip(clipRect); border.paintBorder(c, g, grooveRect.x, grooveRect.y, grooveRect.width, grooveRect.height); } // paint strip right of text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, textLocation.x + stringWidth + 1, y, x + width - (textLocation.x + stringWidth + 1), height)) { g.setClip(clipRect); border.paintBorder(c, g, grooveRect.x, grooveRect.y, grooveRect.width, grooveRect.height); } if (titlePos == TOP || titlePos == DEFAULT_POSITION) { // paint strip below text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, textLocation.x - 1, textLocation.y + descent, stringWidth + 2, y + height - textLocation.y - descent)) { g.setClip(clipRect); border.paintBorder(c, g, grooveRect.x, grooveRect.y, grooveRect.width, grooveRect.height); } } else { // titlePos == BOTTOM // paint strip above text clipRect.setBounds(saveClip); if (computeIntersection(clipRect, textLocation.x - 1, y, stringWidth + 2, textLocation.y - ascent - y)) { g.setClip(clipRect); border.paintBorder(c, g, grooveRect.x, grooveRect.y, grooveRect.width, grooveRect.height); } } // restore clip g.setClip(saveClip); } else { border.paintBorder(c, g, grooveRect.x, grooveRect.y, grooveRect.width, grooveRect.height); } } g.setColor(getTitleColor()); checkbox.setBounds(c.getX()+textLocation.x+100, c.getY()+textLocation.y, stringWidth, fontHeight); checkbox.getUI().paint(g, checkbox); g.setFont(font); g.setColor(color); } 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; } @SuppressWarnings("deprecation") public FontMetrics getFontMetrics(JComponent c, Graphics g, Font font) { if (c != null) { // Note: We assume that we're using the FontMetrics // from the widget to layout out text, otherwise we can get // mismatches when printing. return c.getFontMetrics(font); } return Toolkit.getDefaultToolkit().getFontMetrics(font); } /*********************************************************************************** * REQUIRED METHODS FOR INTERFACES **********************************************************************************/ }