0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.uml.ui.swing.treetable;
0043:
0044: import javax.swing.*;
0045: import javax.swing.border.Border;
0046: import javax.swing.event.ListSelectionEvent;
0047: import javax.swing.event.ListSelectionListener;
0048: import javax.swing.event.TreeExpansionEvent;
0049: import javax.swing.event.TreeExpansionListener;
0050: import javax.swing.event.TreeWillExpandListener;
0051: import javax.swing.tree.*;
0052: import javax.swing.table.*;
0053:
0054: import org.netbeans.modules.uml.core.support.umlutils.IPropertyDefinition;
0055: import org.netbeans.modules.uml.core.support.umlutils.IPropertyElement;
0056: import org.netbeans.modules.uml.ui.swing.propertyeditor.PropertyEditorResources;
0057:
0058: import java.awt.Color;
0059: import java.awt.Component;
0060: import java.awt.Dimension;
0061: import java.awt.Graphics;
0062: import java.awt.Rectangle;
0063: import java.awt.event.ActionEvent;
0064: import java.awt.event.ActionListener;
0065: import java.awt.event.InputEvent;
0066: import java.awt.event.MouseEvent;
0067: import java.awt.event.MouseListener;
0068: import java.util.EventObject;
0069:
0070: /**
0071: * This example shows how to create a simple JTreeTable component,
0072: * by using a JTree as a renderer (and editor) for the cells in a
0073: * particular column in the JTable.
0074: *
0075: * @version 1.2 10/27/98
0076: *
0077: * @author Philip Milne
0078: * @author Scott Violet
0079: */
0080: public class JTreeTable extends JTable implements ActionListener {
0081: /** A subclass of JTree. */
0082: protected TreeTableCellRenderer tree;
0083:
0084: public JTreeTable(TreeTableModel treeTableModel) {
0085: // Create the tree. It will be used as a renderer and editor.
0086: this (treeTableModel, null);
0087: }
0088:
0089: public JTreeTable(TreeTableModel treeTableModel,
0090: TreeTableCellRenderer renderer) {
0091: super ();
0092:
0093: // Create the tree. It will be used as a renderer and editor.
0094: if (renderer != null) {
0095: tree = renderer;
0096: } else {
0097: tree = new TreeTableCellRenderer(treeTableModel);
0098: }
0099:
0100: tree.getSelectionModel().setSelectionMode(
0101: TreeSelectionModel.SINGLE_TREE_SELECTION);
0102: tree.setShowsRootHandles(true);
0103:
0104: // Install a tableModel representing the visible rows in the tree.
0105: super .setModel(new TreeTableModelAdapter(treeTableModel, tree));
0106:
0107: // Force the JTable and JTree to share their row selection models.
0108: ListToTreeSelectionModelWrapper selectionWrapper = new ListToTreeSelectionModelWrapper();
0109: tree.setSelectionModel(selectionWrapper);
0110: setSelectionModel(selectionWrapper.getListSelectionModel());
0111:
0112: // Install the tree editor renderer and editor.
0113: setDefaultRenderer(TreeTableModel.class, tree);
0114: setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor(
0115: tree));
0116:
0117: // No grid.
0118: setShowGrid(false);
0119:
0120: //setCellSelectionEnabled(true);
0121: // No intercell spacing
0122: setIntercellSpacing(new Dimension(0, 0));
0123:
0124: // And update the height of the trees row to match that of
0125: // the table.
0126: // if (tree.getRowHeight() < 1) {
0127: // Metal looks better like this.
0128: // setRowHeight(18);
0129: setRowHeight(getFont().getSize() + 5);
0130: // }
0131:
0132: this .setShowVerticalLines(true);
0133: this .doLayout();
0134:
0135: }
0136:
0137: public void actionPerformed(ActionEvent e) {
0138: JMenuItem source = (JMenuItem) (e.getSource());
0139: String srcText = source.getText();
0140: if (srcText.equals(PropertyEditorResources
0141: .getString("PropertyEditor.Create_Menu"))) {
0142: //PropertyEditor.instance().onPopupCreate();
0143: }
0144: }
0145:
0146: /**
0147: * Overridden to invoke repaint for the particular location if
0148: * the column contains the tree. This is done as the tree editor does
0149: * not fill the bounds of the cell, we need the renderer to paint
0150: * the tree in the background, and then draw the editor over it.
0151: */
0152: public boolean editCellAt(int row, int column, EventObject e) {
0153: boolean retValue = super .editCellAt(row, column, e);
0154: if (retValue && getColumnClass(column) == TreeTableModel.class) {
0155: repaint(getCellRect(row, column, false));
0156: }
0157: if (column == 1) {
0158: retValue = false;
0159: }
0160: return retValue;
0161: }
0162:
0163: /**
0164: * Overridden to message super and forward the method to the tree.
0165: * Since the tree is not actually in the component hieachy it will
0166: * never receive this unless we forward it in this manner.
0167: */
0168: public void updateUI() {
0169: super .updateUI();
0170: if (tree != null) {
0171: tree.updateUI();
0172: }
0173: setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor(
0174: tree));
0175: // Use the tree's default foreground and background colors in the
0176: // table.
0177: LookAndFeel.installColorsAndFont(this , "Tree.background",
0178: "Tree.foreground", "Tree.font");
0179: }
0180:
0181: /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
0182: * paint the editor. The UI currently uses different techniques to
0183: * paint the renderers and editors and overriding setBounds() below
0184: * is not the right thing to do for an editor. Returning -1 for the
0185: * editing row in this case, ensures the editor is never painted.
0186: */
0187: public int getEditingRow() {
0188: return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1
0189: : editingRow;
0190: }
0191:
0192: /**
0193: * Returns the actual row that is editing as <code>getEditingRow</code>
0194: * will always return -1.
0195: */
0196: private int realEditingRow() {
0197: return editingRow;
0198: }
0199:
0200: /**
0201: * This is overridden to invoke super's implementation, and then,
0202: * if the receiver is editing a Tree column, the editor's bounds is
0203: * reset. The reason we have to do this is because JTable doesn't
0204: * think the table is being edited, as <code>getEditingRow</code> returns
0205: * -1, and therefore doesn't automatically resize the editor for us.
0206: */
0207: public void sizeColumnsToFit(int resizingColumn) {
0208: super .sizeColumnsToFit(resizingColumn);
0209: if (getEditingColumn() != -1
0210: && getColumnClass(editingColumn) == TreeTableModel.class) {
0211: Rectangle cellRect = getCellRect(realEditingRow(),
0212: getEditingColumn(), false);
0213: Component component = getEditorComponent();
0214: component.setBounds(cellRect);
0215: component.validate();
0216: }
0217: }
0218:
0219: /**
0220: * Overridden to pass the new rowHeight to the tree.
0221: */
0222: public void setRowHeight(int rowHeight) {
0223: super .setRowHeight(rowHeight);
0224: if (tree != null && tree.getRowHeight() != rowHeight) {
0225: tree.setRowHeight(getRowHeight());
0226: }
0227: }
0228:
0229: /**
0230: * Returns the tree that is being shared between the model.
0231: */
0232: public TreeTableCellRenderer getTree() {
0233: return tree;
0234: }
0235:
0236: // /**
0237: // * A TreeCellRenderer that displays a JTree.
0238: // */
0239: // public class TreeTableCellRenderer extends JTree implements
0240: // TableCellRenderer {
0241: // /** Last table/tree row asked to renderer. */
0242: // protected int visibleRow;
0243: //
0244: // public TreeTableCellRenderer(TreeModel model) {
0245: // super(model);
0246: // //this.putClientProperty("JTree.lineStyle", "Angled");
0247: // //this.putClientProperty("JTree.lineStyle", "Horizontal");
0248: // setBorder(BorderFactory.createLineBorder(Color.BLACK));
0249: // setShowsRootHandles(true);
0250: //
0251: //
0252: // /*this.addTreeSelectionListener( new TreeSelectionListener() {
0253: //
0254: // public void valueChanged(TreeSelectionEvent e)
0255: // {
0256: // // TODO Auto-generated method stub
0257: // ETSystem.out.println("Value changed for tree selection");
0258: // }
0259: // }
0260: // );*/
0261: //
0262: //// this.addTreeExpansionListener( new TreeExpansionListener() {
0263: ////
0264: //// public void treeExpanded(TreeExpansionEvent event)
0265: //// {
0266: //// // TODO Auto-generated method stub
0267: //// ETSystem.out.println("Tree expanded");
0268: ////
0269: //// }
0270: ////
0271: //// public void treeCollapsed(TreeExpansionEvent event)
0272: //// {
0273: //// // TODO Auto-generated method stub
0274: //// ETSystem.out.println("Tree collapsed");
0275: ////
0276: //// }
0277: //// }
0278: //// );
0279: // }
0280: //
0281: // /**
0282: // * updateUI is overridden to set the colors of the Tree's renderer
0283: // * to match that of the table.
0284: // */
0285: // public void updateUI() {
0286: // super.updateUI();
0287: // // Make the tree's cell renderer use the table's cell selection
0288: // // colors.
0289: // TreeCellRenderer tcr = getCellRenderer();
0290: // if (tcr instanceof DefaultTreeCellRenderer) {
0291: // DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr);
0292: // // For 1.1 uncomment this, 1.2 has a bug that will cause an
0293: // // exception to be thrown if the border selection color is
0294: // // null.
0295: // //dtcr.setBorderSelectionColor(null);
0296: // dtcr.setTextSelectionColor(UIManager.getColor
0297: // ("Table.selectionForeground"));
0298: // dtcr.setBackgroundSelectionColor(UIManager.getColor
0299: // ("Table.selectionBackground"));
0300: // }
0301: // }
0302: //
0303: // /**
0304: // * Sets the row height of the tree, and forwards the row height to
0305: // * the table.
0306: // */
0307: // public void setRowHeight(int rowHeight) {
0308: // if (rowHeight > 0) {
0309: // super.setRowHeight(rowHeight);
0310: // if (JTreeTable.this != null &&
0311: // JTreeTable.this.getRowHeight() != rowHeight) {
0312: // JTreeTable.this.setRowHeight(getRowHeight());
0313: // }
0314: // }
0315: // }
0316: //
0317: // /**
0318: // * This is overridden to set the height to match that of the JTable.
0319: // */
0320: // public void setBounds(int x, int y, int w, int h) {
0321: // super.setBounds(x, 0, w, JTreeTable.this.getHeight());
0322: // }
0323: //
0324: // /**
0325: // * Sublcassed to translate the graphics such that the last visible
0326: // * row will be drawn at 0,0.
0327: // */
0328: // public void paint(Graphics g) {
0329: // g.translate(0, -visibleRow * getRowHeight());
0330: //
0331: // super.paint(g);
0332: // }
0333: //
0334: // /**
0335: // * TreeCellRenderer method. Overridden to update the visible row.
0336: // */
0337: // public Component getTableCellRendererComponent(JTable table,
0338: // Object value,
0339: // boolean isSelected,
0340: // boolean hasFocus,
0341: // int row, int column) {
0342: // if (value != null && column == 1)
0343: // {
0344: // ETSystem.out.println(value.getClass() + " " + value);
0345: // }
0346: //
0347: //
0348: // if(isSelected)
0349: // {
0350: // setBackground(table.getSelectionBackground());
0351: //
0352: // }
0353: // else
0354: // setBackground(table.getBackground());
0355: //
0356: // visibleRow = row;
0357: // return this;
0358: // }
0359: // }
0360:
0361: // /**
0362: // * TreeTableCellEditor implementation. Component returned is the
0363: // * JTree.
0364: // */
0365: // public class TreeTableCellEditor extends AbstractCellEditor implements
0366: // TableCellEditor {
0367: // public Component getTableCellEditorComponent(JTable table,
0368: // Object value,
0369: // boolean isSelected,
0370: // int r, int c)
0371: // {
0372: // Component retObj = null;//tree;
0373: // TreePath path = getTree().getSelectionPath();
0374: // if (path != null)
0375: // {
0376: // Object obj = path.getLastPathComponent();
0377: // if (obj instanceof DefaultMutableTreeNode)
0378: // {
0379: // DefaultMutableTreeNode node = (DefaultMutableTreeNode)obj;
0380: // IPropertyElement ele = (IPropertyElement)node.getUserObject();
0381: // IPropertyDefinition def = ele.getPropertyDefinition();
0382: // long mult = def.getMultiplicity();
0383: // String values = def.getValidValues();
0384: // if (values != null)
0385: // {
0386: // retObj = new JComboBox();
0387: // }
0388: // ETSystem.out.println("in getTableCellRen... = " + mult + values);
0389: // }
0390: // }
0391: // return retObj;
0392: // }
0393: //
0394: // /**
0395: // * Overridden to return false, and if the event is a mouse event
0396: // * it is forwarded to the tree.<p>
0397: // * The behavior for this is debatable, and should really be offered
0398: // * as a property. By returning false, all keyboard actions are
0399: // * implemented in terms of the table. By returning true, the
0400: // * tree would get a chance to do something with the keyboard
0401: // * events. For the most part this is ok. But for certain keys,
0402: // * such as left/right, the tree will expand/collapse where as
0403: // * the table focus should really move to a different column. Page
0404: // * up/down should also be implemented in terms of the table.
0405: // * By returning false this also has the added benefit that clicking
0406: // * outside of the bounds of the tree node, but still in the tree
0407: // * column will select the row, whereas if this returned true
0408: // * that wouldn't be the case.
0409: // * <p>By returning false we are also enforcing the policy that
0410: // * the tree will never be editable (at least by a key sequence).
0411: // */
0412: // public boolean isCellEditable(EventObject e) {
0413: // if (e instanceof MouseEvent) {
0414: // for (int counter = getColumnCount() - 1; counter >= 0;
0415: // counter--) {
0416: // if (getColumnClass(counter) == TreeTableModel.class) {
0417: // MouseEvent me = (MouseEvent)e;
0418: // MouseEvent newME = new MouseEvent(tree, me.getID(),
0419: // me.getWhen(), me.getModifiers(),
0420: // me.getX() - getCellRect(0, counter, true).x,
0421: // me.getY(), me.getClickCount(),
0422: // me.isPopupTrigger());
0423: // tree.dispatchEvent(newME);
0424: // break;
0425: // }
0426: // }
0427: // }
0428: // return false;
0429: // }
0430: //
0431: // /* (non-Javadoc)
0432: // * @see javax.swing.CellEditor#getCellEditorValue()
0433: // */
0434: // public Object getCellEditorValue()
0435: // {
0436: // // TODO Auto-generated method stub
0437: // return null;
0438: // }
0439: // }
0440:
0441: // /**
0442: // * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
0443: // * to listen for changes in the ListSelectionModel it maintains. Once
0444: // * a change in the ListSelectionModel happens, the paths are updated
0445: // * in the DefaultTreeSelectionModel.
0446: // */
0447: // class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel {
0448: // /** Set to true when we are updating the ListSelectionModel. */
0449: // protected boolean updatingListSelectionModel;
0450: //
0451: // public ListToTreeSelectionModelWrapper() {
0452: // super();
0453: // getListSelectionModel().addListSelectionListener
0454: // (createListSelectionListener());
0455: // }
0456: //
0457: // /**
0458: // * Returns the list selection model. ListToTreeSelectionModelWrapper
0459: // * listens for changes to this model and updates the selected paths
0460: // * accordingly.
0461: // */
0462: // ListSelectionModel getListSelectionModel() {
0463: // return listSelectionModel;
0464: // }
0465: //
0466: // /**
0467: // * This is overridden to set <code>updatingListSelectionModel</code>
0468: // * and message super. This is the only place DefaultTreeSelectionModel
0469: // * alters the ListSelectionModel.
0470: // */
0471: // public void resetRowSelection() {
0472: // if(!updatingListSelectionModel) {
0473: // updatingListSelectionModel = true;
0474: // try {
0475: // super.resetRowSelection();
0476: // }
0477: // finally {
0478: // updatingListSelectionModel = false;
0479: // }
0480: // }
0481: // // Notice how we don't message super if
0482: // // updatingListSelectionModel is true. If
0483: // // updatingListSelectionModel is true, it implies the
0484: // // ListSelectionModel has already been updated and the
0485: // // paths are the only thing that needs to be updated.
0486: // }
0487: //
0488: // /**
0489: // * Creates and returns an instance of ListSelectionHandler.
0490: // */
0491: // protected ListSelectionListener createListSelectionListener() {
0492: // return new ListSelectionHandler();
0493: // }
0494: //
0495: // /**
0496: // * If <code>updatingListSelectionModel</code> is false, this will
0497: // * reset the selected paths from the selected rows in the list
0498: // * selection model.
0499: // */
0500: // protected void updateSelectedPathsFromSelectedRows() {
0501: // if(!updatingListSelectionModel) {
0502: // updatingListSelectionModel = true;
0503: // try {
0504: // // This is way expensive, ListSelectionModel needs an
0505: // // enumerator for iterating.
0506: // int min = listSelectionModel.getMinSelectionIndex();
0507: // int max = listSelectionModel.getMaxSelectionIndex();
0508: //
0509: // clearSelection();
0510: // if(min != -1 && max != -1) {
0511: // for(int counter = min; counter <= max; counter++) {
0512: // if(listSelectionModel.isSelectedIndex(counter)) {
0513: // TreePath selPath = tree.getPathForRow
0514: // (counter);
0515: //
0516: // if(selPath != null) {
0517: // addSelectionPath(selPath);
0518: // }
0519: // }
0520: // }
0521: // }
0522: // }
0523: // finally {
0524: // updatingListSelectionModel = false;
0525: // }
0526: // }
0527: // }
0528: //
0529: // /**
0530: // * Class responsible for calling updateSelectedPathsFromSelectedRows
0531: // * when the selection of the list changse.
0532: // */
0533: // class ListSelectionHandler implements ListSelectionListener {
0534: // public void valueChanged(ListSelectionEvent e) {
0535: // updateSelectedPathsFromSelectedRows();
0536: // }
0537: // }
0538: // }
0539:
0540: // class PopupListener extends MouseAdapter {
0541: // public void mousePressed(MouseEvent e) {
0542: // maybeShowPopup(e);
0543: // }
0544: //
0545: // public void mouseReleased(MouseEvent e) {
0546: // maybeShowPopup(e);
0547: // }
0548: //
0549: // private void maybeShowPopup(MouseEvent e) {
0550: // //getSelectionModel()
0551: //
0552: // TreePath path = getTree().getPathForLocation(e.getX(), e.getY());
0553: // if (path != null)
0554: // {
0555: // Object obj = path.getLastPathComponent();
0556: // ETSystem.out.println(obj.getClass());
0557: // DefaultMutableTreeNode node = (DefaultMutableTreeNode)obj;
0558: // IPropertyElement ele = (IPropertyElement)node.getUserObject();
0559: // ETSystem.out.println("Mouse Clicked on " + ele.getName());
0560: // }
0561: // if (e.isPopupTrigger()) {
0562: // popup.show(e.getComponent(),
0563: // e.getX(), e.getY());
0564: // }
0565: // }
0566: // }
0567:
0568: // public class RightAlignRenderer extends Component implements TableCellRenderer {
0569: // /** Last table/tree row asked to renderer. */
0570: // protected int visibleRow;
0571: //
0572: // public RightAlignRenderer() {
0573: // super();
0574: // setAlignmentX(JLabel.RIGHT_ALIGNMENT);
0575: // }
0576: //
0577: // /**
0578: // * TreeCellRenderer method. Overridden to update the visible row.
0579: // */
0580: // public Component getTableCellRendererComponent(JTable table,
0581: // Object value,
0582: // boolean isSelected,
0583: // boolean hasFocus,
0584: // int row, int column) {
0585: // if(isSelected)
0586: // setBackground(table.getSelectionBackground());
0587: // else
0588: // setBackground(table.getBackground());
0589: //
0590: // //visibleRow = row;
0591: // return this;
0592: // }
0593: // }
0594: //
0595: // private class IndicatorRenderer extends DefaultTableCellRenderer {
0596: //
0597: // public IndicatorRenderer() {
0598: // super();
0599: // //setHorizontalAlignment(JLabel.RIGHT);
0600: //
0601: // this.putClientProperty("JTree.lineStyle", "Horizontal");
0602: // }
0603: //
0604: //
0605: // /**
0606: // * Returns this.
0607: // */
0608: // public Component getTableCellRendererComponent(JTable table,
0609: // Object value, boolean isSelected, boolean hasFocus,
0610: // int row, int column) {
0611: // super.getTableCellRendererComponent(table, value, isSelected,
0612: // hasFocus, row, column);
0613: //
0614: // TreePath path = getTree().getSelectionPath();
0615: // if (path != null)
0616: // {
0617: // Object obj = path.getLastPathComponent();
0618: // if (obj instanceof DefaultMutableTreeNode)
0619: // {
0620: // DefaultMutableTreeNode node = (DefaultMutableTreeNode)obj;
0621: // IPropertyElement ele = (IPropertyElement)node.getUserObject();
0622: // IPropertyDefinition def = ele.getPropertyDefinition();
0623: // long mult = def.getMultiplicity();
0624: // String values = def.getValidValues();
0625: // if (values != null)
0626: // {
0627: // //getColumnModel().
0628: // }
0629: // ETSystem.out.println("in getTableCellRen... = " + mult + values);
0630: // }
0631: // }
0632: // if(isSelected)
0633: // {
0634: // setBackground(table.getSelectionBackground());
0635: // }
0636: // else
0637: // setBackground(table.getBackground());
0638: //
0639: // return this;
0640: // }
0641: //
0642: // }
0643:
0644: public void expandNode(JDefaultMutableTreeNode node, boolean expand) {
0645: if (node != null) {
0646: int row = node.getRow();
0647: tree.expandNode(row, expand);
0648: }
0649: }
0650:
0651: public class TreeTableCellRenderer extends JTree implements
0652: TableCellRenderer {
0653:
0654: /**
0655: *
0656: */
0657: public TreeTableCellRenderer() {
0658: super ();
0659: }
0660:
0661: /** Last table/tree row asked to renderer. */
0662: protected int visibleRow;
0663:
0664: /** Border to draw around the tree, if this is non-null, it will
0665: * be painted. */
0666: protected Border highlightBorder;
0667:
0668: public TreeTableCellRenderer(TreeModel model) {
0669: super (model);
0670: //setBorder(BorderFactory.createLineBorder(Color.BLACK));
0671: this .putClientProperty("JTree.lineStyle", "Angled");
0672: //setShowsRootHandles(true);
0673: setExpandsSelectedPaths(true);
0674: setScrollsOnExpand(true);
0675:
0676: }
0677:
0678: public void addMouseListener(MouseListener l) {
0679: super .addMouseListener(l);
0680: }
0681:
0682: /**
0683: * updateUI is overridden to set the colors of the Tree's renderer
0684: * to match that of the table.
0685: */
0686: public void updateUI() {
0687: super .updateUI();
0688: // Make the tree's cell renderer use the table's cell selection
0689: // colors.
0690: TreeCellRenderer tcr = getCellRenderer();
0691: if (tcr instanceof DefaultTreeCellRenderer) {
0692: DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr);
0693: // For 1.1 uncomment this, 1.2 has a bug that will cause an
0694: // exception to be thrown if the border selection color is
0695: // null.
0696: //dtcr.setBorderSelectionColor(null);
0697: dtcr.setTextSelectionColor(UIManager
0698: .getColor("Table.selectionForeground"));
0699: dtcr.setBackgroundSelectionColor(UIManager
0700: .getColor("Table.selectionBackground"));
0701: }
0702: }
0703:
0704: /**
0705: * Sets the row height of the tree, and forwards the row height to
0706: * the table.
0707: */
0708: public void setRowHeight(int rowHeight) {
0709: if (rowHeight > 0) {
0710: super .setRowHeight(rowHeight);
0711: if (JTreeTable.this != null
0712: && JTreeTable.this .getRowHeight() != rowHeight) {
0713: JTreeTable.this .setRowHeight(getRowHeight());
0714: }
0715: }
0716: }
0717:
0718: /**
0719: * This is overridden to set the height to match that of the JTable.
0720: */
0721: public void setBounds(int x, int y, int w, int h) {
0722: super .setBounds(x, 0, w, JTreeTable.this .getHeight());
0723: }
0724:
0725: /**
0726: * Sublcassed to translate the graphics such that the last visible
0727: * row will be drawn at 0,0.
0728: */
0729: public void paint(Graphics g) {
0730: g.translate(0, -visibleRow * getRowHeight());
0731:
0732: super .paint(g);
0733: // Draw the Table border if we have focus.
0734: if (highlightBorder != null) {
0735: highlightBorder.paintBorder(this , g, 0, visibleRow
0736: * getRowHeight(), getWidth(), getRowHeight());
0737: }
0738: }
0739:
0740: /**
0741: * TreeCellRenderer method. Overridden to update the visible row.
0742: */
0743: public Component getTableCellRendererComponent(JTable table,
0744: Object value, boolean isSelected, boolean hasFocus,
0745: int row, int column) {
0746: this .putClientProperty("JTree.lineStyle", "Angled");
0747: //setShowsRootHandles(true);
0748: //setSelectionRow(row);
0749:
0750: Color background;
0751: Color foreground;
0752:
0753: if (isSelected) {
0754: background = table.getSelectionBackground();
0755: foreground = table.getSelectionForeground();
0756: } else {
0757: background = table.getBackground();
0758: foreground = table.getForeground();
0759: }
0760: highlightBorder = null;
0761: if (realEditingRow() == row && getEditingColumn() == column) {
0762: background = UIManager
0763: .getColor("Table.focusCellBackground");
0764: foreground = UIManager
0765: .getColor("Table.focusCellForeground");
0766: } else if (hasFocus) {
0767: highlightBorder = UIManager
0768: .getBorder("Table.focusCellHighlightBorder");
0769: if (isCellEditable(row, column)) {
0770: background = UIManager
0771: .getColor("Table.focusCellBackground");
0772: foreground = UIManager
0773: .getColor("Table.focusCellForeground");
0774: }
0775: }
0776:
0777: TreeCellRenderer tcr = getCellRenderer();
0778: if (tcr instanceof DefaultTreeCellRenderer) {
0779: DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer) tcr);
0780:
0781: //we do not want to show icons for the nodes in property editor.
0782: dtcr.setLeafIcon(null);
0783: dtcr.setOpenIcon(null);
0784: dtcr.setClosedIcon(null);
0785:
0786: if (isSelected) {
0787: dtcr.setTextSelectionColor(foreground);
0788: dtcr.setBackgroundSelectionColor(background);
0789: } else {
0790: dtcr.setTextNonSelectionColor(foreground);
0791: dtcr.setBackgroundNonSelectionColor(background);
0792: }
0793:
0794: if (column == 1) {
0795: TreePath path = getPathForRow(row);
0796: Object tempNode = path.getLastPathComponent();
0797: if (tempNode != null
0798: && tempNode instanceof JDefaultMutableTreeNode) {
0799: JDefaultMutableTreeNode node = (JDefaultMutableTreeNode) tempNode;
0800: Object obj = node.getUserObject();
0801: if (obj != null
0802: && obj instanceof IPropertyElement) {
0803: IPropertyElement ele = (IPropertyElement) obj;
0804: if (ele != null) {
0805: IPropertyDefinition pDef = ele
0806: .getPropertyDefinition();
0807: long mult = pDef.getMultiplicity();
0808: if (mult > 1) {
0809: //ETSystem.out.println("Setting bold for " + pDef.getName());
0810: java.awt.Font f = dtcr.getFont();
0811: setFont(f
0812: .deriveFont(java.awt.Font.BOLD));
0813:
0814: } else {
0815: //ETSystem.out.println("Setting plain for " + pDef.getName());
0816: java.awt.Font f = dtcr.getFont();
0817: setFont(f
0818: .deriveFont(java.awt.Font.PLAIN));
0819: }
0820: }
0821: }
0822:
0823: boolean isRootNode = node.isRoot();
0824: if (isRootNode) {
0825: Color color = new Color(192, 192, 192);
0826: setBackground(color);
0827: dtcr.setBackgroundNonSelectionColor(color);
0828: dtcr.setBackgroundSelectionColor(color);
0829: } else {
0830: setBackground(Color.WHITE);
0831: }
0832: }
0833: }
0834: }
0835:
0836: visibleRow = row;
0837: return this ;
0838: }
0839:
0840: public void expandNode(int row, boolean val) {
0841: TreePath path = getPathForRow(row);
0842: setExpandedState(path, val);
0843:
0844: }
0845:
0846: }
0847:
0848: public class TreeTableDefaultCellEditor extends DefaultCellEditor {
0849: public TreeTableDefaultCellEditor() {
0850: super (new TreeTableTextField());
0851: }
0852:
0853: /**
0854: * Overridden to determine an offset that tree would place the
0855: * editor at. The offset is determined from the
0856: * <code>getRowBounds</code> JTree method, and additionally
0857: * from the icon DefaultTreeCellRenderer will use.
0858: * <p>The offset is then set on the TreeTableTextField component
0859: * created in the constructor, and returned.
0860: */
0861: public Component getTableCellEditorComponent(JTable table,
0862: Object value, boolean isSelected, int r, int c) {
0863: Component component = super .getTableCellEditorComponent(
0864: table, value, isSelected, r, c);
0865: JTree t = getTree();
0866: boolean rv = t.isRootVisible();
0867: int offsetRow = rv ? r : r - 1;
0868: Rectangle bounds = t.getRowBounds(offsetRow);
0869: int offset = bounds.x;
0870: TreeCellRenderer tcr = t.getCellRenderer();
0871: if (tcr instanceof DefaultTreeCellRenderer) {
0872: Object node = t.getPathForRow(offsetRow)
0873: .getLastPathComponent();
0874: Icon icon = null;
0875: // if (t.getModel().isLeaf(node))
0876: // icon = ((DefaultTreeCellRenderer)tcr).getLeafIcon();
0877: // else if (tree.isExpanded(offsetRow))
0878: // icon = ((DefaultTreeCellRenderer)tcr).getOpenIcon();
0879: // else
0880: // icon = ((DefaultTreeCellRenderer)tcr).getClosedIcon();
0881: if (icon != null) {
0882: offset += ((DefaultTreeCellRenderer) tcr)
0883: .getIconTextGap()
0884: + icon.getIconWidth();
0885: }
0886: }
0887: ((TreeTableTextField) getComponent()).offset = offset;
0888: return component;
0889: }
0890:
0891: /**
0892: * This is overridden to forward the event to the tree. This will
0893: * return true if the click count >= 3, or the event is null.
0894: */
0895: public boolean isCellEditable(EventObject e) {
0896: if (e instanceof MouseEvent) {
0897: MouseEvent me = (MouseEvent) e;
0898: // If the modifiers are not 0 (or the left mouse button),
0899: // tree may try and toggle the selection, and table
0900: // will then try and toggle, resulting in the
0901: // selection remaining the same. To avoid this, we
0902: // only dispatch when the modifiers are 0 (or the left mouse
0903: // button).
0904: if (me.getModifiers() == 0
0905: || me.getModifiers() == InputEvent.BUTTON1_MASK) {
0906: for (int counter = getColumnCount() - 1; counter >= 0; counter--) {
0907: if (getColumnClass(counter) == TreeTableModel.class) {
0908: MouseEvent newME = new MouseEvent(
0909: JTreeTable.this .tree, me.getID(),
0910: me.getWhen(), me.getModifiers(), me
0911: .getX()
0912: - getCellRect(0, counter,
0913: true).x, me.getY(),
0914: me.getClickCount(), me
0915: .isPopupTrigger());
0916: JTreeTable.this .tree.dispatchEvent(newME);
0917: break;
0918: }
0919: }
0920: }
0921: return false;
0922: }
0923: return false;
0924: }
0925: }
0926:
0927: static class TreeTableTextField extends JTextField {
0928: public int offset;
0929:
0930: public void reshape(int x, int y, int w, int h) {
0931: int newX = Math.max(x, offset);
0932: super .reshape(newX, y, w - (newX - x), h);
0933: }
0934: }
0935:
0936: /**
0937: * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
0938: * to listen for changes in the ListSelectionModel it maintains. Once
0939: * a change in the ListSelectionModel happens, the paths are updated
0940: * in the DefaultTreeSelectionModel.
0941: */
0942: class ListToTreeSelectionModelWrapper extends
0943: DefaultTreeSelectionModel {
0944: /** Set to true when we are updating the ListSelectionModel. */
0945: protected boolean updatingListSelectionModel;
0946:
0947: public ListToTreeSelectionModelWrapper() {
0948: super ();
0949: getListSelectionModel().addListSelectionListener(
0950: createListSelectionListener());
0951: }
0952:
0953: /**
0954: * Returns the list selection model. ListToTreeSelectionModelWrapper
0955: * listens for changes to this model and updates the selected paths
0956: * accordingly.
0957: */
0958: ListSelectionModel getListSelectionModel() {
0959: return listSelectionModel;
0960: }
0961:
0962: /**
0963: * This is overridden to set <code>updatingListSelectionModel</code>
0964: * and message super. This is the only place DefaultTreeSelectionModel
0965: * alters the ListSelectionModel.
0966: */
0967: public void resetRowSelection() {
0968: if (!updatingListSelectionModel) {
0969: updatingListSelectionModel = true;
0970: try {
0971: super .resetRowSelection();
0972: } finally {
0973: updatingListSelectionModel = false;
0974: }
0975: }
0976: // Notice how we don't message super if
0977: // updatingListSelectionModel is true. If
0978: // updatingListSelectionModel is true, it implies the
0979: // ListSelectionModel has already been updated and the
0980: // paths are the only thing that needs to be updated.
0981: }
0982:
0983: /**
0984: * Creates and returns an instance of ListSelectionHandler.
0985: */
0986: protected ListSelectionListener createListSelectionListener() {
0987: return new ListSelectionHandler();
0988: }
0989:
0990: /**
0991: * If <code>updatingListSelectionModel</code> is false, this will
0992: * reset the selected paths from the selected rows in the list
0993: * selection model.
0994: */
0995: protected void updateSelectedPathsFromSelectedRows() {
0996: if (!updatingListSelectionModel) {
0997: updatingListSelectionModel = true;
0998: try {
0999: // This is way expensive, ListSelectionModel needs an
1000: // enumerator for iterating.
1001: int min = listSelectionModel.getMinSelectionIndex();
1002: int max = listSelectionModel.getMaxSelectionIndex();
1003:
1004: clearSelection();
1005: if (min != -1 && max != -1) {
1006: for (int counter = min; counter <= max; counter++) {
1007: if (listSelectionModel
1008: .isSelectedIndex(counter)) {
1009: TreePath selPath = tree
1010: .getPathForRow(counter);
1011:
1012: if (selPath != null) {
1013: addSelectionPath(selPath);
1014: }
1015: }
1016: }
1017: }
1018: } finally {
1019: updatingListSelectionModel = false;
1020: }
1021: }
1022: }
1023:
1024: /**
1025: * Class responsible for calling updateSelectedPathsFromSelectedRows
1026: * when the selection of the list changse.
1027: */
1028: class ListSelectionHandler implements ListSelectionListener {
1029: public void valueChanged(ListSelectionEvent e) {
1030: updateSelectedPathsFromSelectedRows();
1031: }
1032: }
1033: }
1034:
1035: }
|