001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * If you wish your version of this file to be governed by only the CDDL
025: * or only the GPL Version 2, indicate your decision by adding
026: * "[Contributor] elects to include this software in this distribution
027: * under the [CDDL or GPL Version 2] license." If you do not indicate a
028: * single choice of license, a recipient has the option to distribute
029: * your version of this file under either the CDDL, the GPL Version 2 or
030: * to extend the choice of license to its licensees as provided above.
031: * However, if you add GPL Version 2 code and therefore, elected the GPL
032: * Version 2 license, then the option applies only if the new code is
033: * made subject to such option by the copyright holder.
034: *
035: * Contributor(s):
036: *
037: * Portions Copyrighted 2007 Sun Microsystems, Inc.
038: */
039:
040: package org.netbeans.modules.search;
041:
042: import java.awt.Dimension;
043: import java.awt.Graphics;
044: import java.awt.Insets;
045: import java.awt.Point;
046: import java.awt.Shape;
047: import javax.swing.JLabel;
048: import javax.swing.JRadioButton;
049: import javax.swing.SwingConstants;
050: import org.jdesktop.layout.Baseline;
051: import org.jdesktop.layout.LayoutStyle;
052:
053: /**
054: * A radio-button with an extra information displayed by the main text.
055: *
056: * @author Marian Petras
057: */
058: class ButtonWithExtraInfo extends JRadioButton {
059:
060: private String extraInfo;
061: private JLabel lblInfo;
062: private JLabel lblStart, lblEnd;
063: private boolean infoVisible;
064: private Dimension infoPrefSize;
065: private int infoBaseline = -1;
066: private int infoGap = -1;
067: private int startWidth = 0, endWidth = 0;
068:
069: public ButtonWithExtraInfo(String extraInfo) {
070: this .extraInfo = extraInfo;
071: }
072:
073: private void checkInfoLabel() {
074: if ((lblInfo == null) && (extraInfo != null)) {
075: lblInfo = new JLabel(extraInfo);
076: lblStart = new JLabel("("); //NOI18N
077: lblEnd = new JLabel(")"); //NOI18N
078:
079: final boolean enabled = isEnabled();
080: lblInfo.setEnabled(enabled);
081: lblStart.setEnabled(enabled);
082: lblEnd.setEnabled(enabled);
083: }
084: }
085:
086: private void checkInfoGap() {
087: if (infoGap == -1) {
088: checkInfoLabel();
089: infoGap = LayoutStyle.getSharedInstance().getPreferredGap(
090: this , lblInfo, LayoutStyle.RELATED,
091: SwingConstants.EAST, getParent());
092: startWidth = lblStart.getPreferredSize().width;
093: endWidth = lblEnd.getPreferredSize().width;
094: }
095: }
096:
097: private void checkInfoPrefSize() {
098: if (infoPrefSize == null) {
099: infoPrefSize = lblInfo.getPreferredSize();
100: infoBaseline = Baseline.getBaseline(lblInfo,
101: infoPrefSize.width, infoPrefSize.height);
102: }
103: }
104:
105: @Override
106: public void doLayout() {
107: super .doLayout();
108: if (extraInfo == null) {
109: return;
110: }
111:
112: infoVisible = false;
113:
114: Dimension size = getSize();
115: Dimension prefSize = getPreferredSize();
116: if (size.width > prefSize.width) {
117: int widthDelta = size.width - prefSize.width;
118: checkInfoGap();
119: if (widthDelta > (infoGap + startWidth + endWidth)) {
120: infoVisible = true;
121: Insets insets = getInsets();
122: checkInfoPrefSize();
123: assert infoBaseline != -1;
124: int infoX = prefSize.width - insets.right + infoGap;
125: int infoY = Baseline.getBaseline(this , size.width,
126: size.height)
127: - infoBaseline;
128: int infoWidth = Math.min(widthDelta - infoGap
129: - (startWidth + endWidth), infoPrefSize.width);
130: int infoHeight = infoPrefSize.height;
131: lblInfo.setBounds(infoX, infoY, infoWidth, infoHeight);
132: lblStart.setSize(startWidth, infoHeight);
133: lblEnd.setSize(endWidth, infoHeight);
134: }
135: }
136: }
137:
138: @Override
139: protected void paintComponent(Graphics g) {
140: super .paintComponent(g);
141: if (!infoVisible) {
142: return;
143: }
144:
145: Point infoLocation = lblInfo.getLocation();
146:
147: /* Paint the opening bracket: */
148: g.translate(infoLocation.x, infoLocation.y);
149: lblStart.paint(g);
150:
151: /* Paint the additional information: */
152: g.translate(startWidth, 0);
153: /*
154: * If the label's text does not fit the reserved space, the text is
155: * truncated and an ellipsis is painted at the end of the truncated
156: * text. If no text fits, just the ellipsis is painted. If the label
157: * is so narrow that even the ellipsis does not fit, the whole ellipsis
158: * is still painted, thus leaking from the label's bounds. In this case,
159: * the ellipsis may overlap the closing bracket. To prevent this
160: * overlapping, a clip region is set to the label's area such that
161: * nothing is painted over the label's bounds.
162: */
163: Shape originalClipShape = g.getClip();
164: g.setClip(0, 0, lblInfo.getWidth(), lblInfo.getHeight());
165: lblInfo.paint(g);
166: g.setClip(originalClipShape);
167:
168: /* Paint the closing bracket: */
169: g.translate(lblInfo.getWidth(), 0);
170: lblEnd.paint(g);
171:
172: g.translate(
173: -(infoLocation.x + startWidth + lblInfo.getWidth()),
174: -infoLocation.y);
175: }
176:
177: }
|