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: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: /*
043: * DefaultOutlineTreeCellRenderer.java
044: *
045: * Created on January 28, 2004, 7:49 PM
046: */
047:
048: package org.netbeans.swing.outline;
049:
050: import java.awt.Color;
051: import java.awt.Component;
052: import java.awt.Insets;
053: import javax.swing.BorderFactory;
054: import javax.swing.Icon;
055: import javax.swing.JTable;
056: import javax.swing.UIManager;
057: import javax.swing.border.Border;
058: import javax.swing.table.DefaultTableCellRenderer;
059: import javax.swing.tree.AbstractLayoutCache;
060: import javax.swing.tree.TreePath;
061:
062: /** An outline-aware TableCellRenderer which knows how to paint expansion
063: * handles and indent child nodes an appropriate amount.
064: *
065: * @author Tim Boudreau
066: */
067: public class DefaultOutlineCellRenderer extends
068: DefaultTableCellRenderer {
069: private boolean expanded = false;
070: private boolean leaf = true;
071: private boolean showHandle = true;
072: private int nestingDepth = 0;
073: private static final Border expansionBorder = new ExpansionHandleBorder();
074:
075: /** Creates a new instance of DefaultOutlineTreeCellRenderer */
076: public DefaultOutlineCellRenderer() {
077: }
078:
079: /** Overridden to combine the expansion border (whose insets determine how
080: * much a child tree node is shifted to the right relative to the ancestor
081: * root node) with whatever border is set, as a CompoundBorder. The expansion
082: * border is also responsible for drawing the expansion icon. */
083: public final void setBorder(Border b) {
084: if (b == expansionBorder) {
085: super .setBorder(b);
086: } else {
087: super .setBorder(BorderFactory.createCompoundBorder(b,
088: expansionBorder));
089: }
090: }
091:
092: private static Icon getDefaultOpenIcon() {
093: return UIManager.getIcon("Tree.openIcon"); //NOI18N
094: }
095:
096: private static Icon getDefaultClosedIcon() {
097: return UIManager.getIcon("Tree.closedIcon"); //NOI18N
098: }
099:
100: private static Icon getDefaultLeafIcon() {
101: return UIManager.getIcon("Tree.leafIcon"); //NOI18N
102: }
103:
104: private static Icon getExpandedIcon() {
105: return UIManager.getIcon("Tree.collapsedIcon"); //NOI18N
106: }
107:
108: private static Icon getCollapsedIcon() {
109: return UIManager.getIcon("Tree.expandedIcon"); //NOI18N
110: }
111:
112: static int getNestingWidth() {
113: return getExpansionHandleWidth();
114: }
115:
116: static int getExpansionHandleWidth() {
117: return getExpandedIcon().getIconWidth();
118: }
119:
120: static int getExpansionHandleHeight() {
121: return getExpandedIcon().getIconHeight();
122: }
123:
124: private void setNestingDepth(int i) {
125: nestingDepth = i;
126: }
127:
128: private void setExpanded(boolean val) {
129: expanded = val;
130: }
131:
132: private void setLeaf(boolean val) {
133: leaf = val;
134: }
135:
136: private void setShowHandle(boolean val) {
137: showHandle = val;
138: }
139:
140: private boolean isLeaf() {
141: return leaf;
142: }
143:
144: private boolean isExpanded() {
145: return expanded;
146: }
147:
148: private boolean isShowHandle() {
149: return showHandle;
150: }
151:
152: /** Set the nesting depth - the number of path elements below the root.
153: * This is set in getTableCellEditorComponent(), and retrieved by the
154: * expansion border to determine how far to the right to indent the current
155: * node. */
156: private int getNestingDepth() {
157: return nestingDepth;
158: }
159:
160: /** Get a component that can render cells in an Outline. If
161: * <code>((Outline) table).isTreeColumnIndex(column)</code> is true,
162: * it will paint as indented and with an expansion handle if the
163: * Outline's model returns false from <code>isLeaf</code> for the
164: * passed value.
165: * <p>
166: * If the column is not the tree column, its behavior is the same as
167: * DefaultTableCellRenderer.
168: */
169: public Component getTableCellRendererComponent(JTable table,
170: Object value, boolean isSelected, boolean hasFocus,
171: int row, int column) {
172:
173: Component c = (DefaultOutlineCellRenderer) super
174: .getTableCellRendererComponent(table, value,
175: isSelected, hasFocus, row, column);
176: Outline tbl = (Outline) table;
177: if (tbl.isTreeColumnIndex(column)) {
178: AbstractLayoutCache layout = tbl.getLayoutCache();
179:
180: boolean leaf = tbl.getOutlineModel().isLeaf(value);
181: setLeaf(leaf);
182: setShowHandle(true);
183: TreePath path = layout.getPathForRow(row);
184: boolean expanded = !layout.isExpanded(path);
185: setExpanded(expanded);
186: setNestingDepth(path.getPathCount() - 1);
187: RenderDataProvider rendata = tbl.getRenderDataProvider();
188: Icon icon = null;
189: if (rendata != null) {
190: String displayName = rendata.getDisplayName(value);
191: if (displayName != null) {
192: setText(displayName);
193: }
194: setToolTipText(rendata.getTooltipText(value));
195: Color bg = rendata.getBackground(value);
196: Color fg = rendata.getForeground(value);
197: if (bg != null && !isSelected) {
198: setBackground(bg);
199: } else {
200: setBackground(isSelected ? tbl
201: .getSelectionBackground() : tbl
202: .getBackground());
203: }
204: if (fg != null && !isSelected) {
205: setForeground(fg);
206: } else {
207: setForeground(isSelected ? tbl
208: .getSelectionForeground() : tbl
209: .getForeground());
210: }
211: icon = rendata.getIcon(value);
212: }
213: if (icon == null) {
214: if (!leaf) {
215: if (expanded) {
216: setIcon(getDefaultClosedIcon());
217: } else {
218: setIcon(getDefaultOpenIcon());
219: }
220: } else {
221: setIcon(getDefaultLeafIcon());
222: }
223: }
224:
225: } else {
226: setIcon(null);
227: setShowHandle(false);
228: }
229: return this ;
230: }
231:
232: private static class ExpansionHandleBorder implements Border {
233: private Insets insets = new Insets(0, 0, 0, 0);
234:
235: public Insets getBorderInsets(Component c) {
236: DefaultOutlineCellRenderer ren = (DefaultOutlineCellRenderer) c;
237: if (ren.isShowHandle()) {
238: insets.left = getExpansionHandleWidth()
239: + (ren.getNestingDepth() * getNestingWidth());
240: //Defensively adjust all the insets fields
241: insets.top = 1;
242: insets.right = 1;
243: insets.bottom = 1;
244: } else {
245: //Defensively adjust all the insets fields
246: insets.left = 1;
247: insets.top = 1;
248: insets.right = 1;
249: insets.bottom = 1;
250: }
251: return insets;
252: }
253:
254: public boolean isBorderOpaque() {
255: return false;
256: }
257:
258: public void paintBorder(Component c, java.awt.Graphics g,
259: int x, int y, int width, int height) {
260: DefaultOutlineCellRenderer ren = (DefaultOutlineCellRenderer) c;
261: if (ren.isShowHandle() && !ren.isLeaf()) {
262: Icon icon = ren.isExpanded() ? getExpandedIcon()
263: : getCollapsedIcon();
264: int iconY;
265: int iconX = ren.getNestingDepth() * getNestingWidth();
266: if (icon.getIconHeight() < height) {
267: iconY = (height / 2) - (icon.getIconHeight() / 2);
268: } else {
269: iconY = 0;
270: }
271: icon.paintIcon(c, g, iconX, iconY);
272: }
273: }
274: }
275: }
|