| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * 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 com.intellij.ui; |
| |
| import org.intellij.lang.annotations.JdkConstants; |
| |
| import javax.swing.border.Border; |
| import javax.swing.border.TitledBorder; |
| import java.awt.*; |
| |
| /** |
| * @author Konstantin Bulenkov |
| */ |
| public class TitledBorderWithMnemonic extends TitledBorder { |
| private final String myOriginalTitle; |
| |
| public TitledBorderWithMnemonic(String title) { |
| this(null, title, LEADING, DEFAULT_POSITION, null, null); |
| } |
| |
| public TitledBorderWithMnemonic(Border border) { |
| this(border, "", LEADING, DEFAULT_POSITION, null, null); |
| } |
| |
| public TitledBorderWithMnemonic(Border border, String title) { |
| this(border, title, LEADING, DEFAULT_POSITION, null, null); |
| } |
| |
| public TitledBorderWithMnemonic(Border border, String title, @JdkConstants.TitledBorderJustification int titleJustification, @JdkConstants.TitledBorderTitlePosition int titlePosition) { |
| this(border, title, titleJustification, titlePosition, null, null); |
| } |
| |
| public TitledBorderWithMnemonic(Border border, String title, @JdkConstants.TitledBorderJustification int titleJustification, @JdkConstants.TitledBorderTitlePosition int titlePosition, Font titleFont) { |
| this(border, title, titleJustification, titlePosition, titleFont, null); |
| } |
| |
| public TitledBorderWithMnemonic(Border border, String title, @JdkConstants.TitledBorderJustification int titleJustification, @JdkConstants.TitledBorderTitlePosition int titlePosition, Font titleFont, |
| Color titleColor) { |
| super(border, title.replaceAll("&", ""), titleJustification, titlePosition, titleFont, titleColor); |
| myOriginalTitle = title; |
| } |
| |
| public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { |
| Point textLoc = new Point(); |
| Border border = getBorder(); |
| |
| if (getTitle() == null || getTitle().length() == 0) { |
| 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)); |
| |
| FontMetrics fm = g.getFontMetrics(); |
| int fontHeight = fm.getHeight(); |
| int descent = fm.getDescent(); |
| int ascent = fm.getAscent(); |
| int diff; |
| int stringWidth = fm.stringWidth(getTitle()); |
| 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 (computeIntersection2(clipRect, x, y, textLoc.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 (computeIntersection2(clipRect, textLoc.x+stringWidth+1, y, |
| x+width-(textLoc.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 (computeIntersection2(clipRect, textLoc.x-1, textLoc.y+descent, |
| stringWidth+2, y+height-textLoc.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 (computeIntersection2(clipRect, textLoc.x-1, y, |
| stringWidth+2, textLoc.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()); |
| g.drawString(getTitle(), textLoc.x, textLoc.y); |
| |
| final int index = myOriginalTitle.indexOf('&'); |
| if (index != -1 && index != myOriginalTitle.length() - 1 && index == myOriginalTitle.lastIndexOf('&') && g instanceof Graphics2D) { |
| int x0 = fm.stringWidth(getTitle().substring(0, index)); |
| int x1 = fm.stringWidth(getTitle().substring(0, index+1)); |
| ((Graphics2D)g).setPaint(getTitleColor()); |
| g.drawLine(textLoc.x + x0 - 1, textLoc.y + 1, textLoc.x + x1 - 1, textLoc.y + 1); |
| ((Graphics2D)g).setPaint(color); |
| } |
| |
| g.setFont(font); |
| g.setColor(color); |
| } |
| |
| private static boolean computeIntersection2(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; |
| } |
| } |