/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.explorer.view; import java.awt.*; import java.beans.*; import java.awt.event.MouseEvent; import javax.swing.*; import javax.swing.event.*; import javax.swing.border.*; import org.openide.explorer.propertysheet.*; import org.openide.nodes.Node; import org.openide.nodes.Node.Property; /** Table view of node properties. Table header displays property display names and each cell * contains (<code>PropertyPanel</code>) for displaying/editting of properties. Each property * row belongs to one node. * * @author Jan Rojcek */ class TableSheet extends JScrollPane { /** table */ transient protected JTable table; /** model */ transient private NodeTableModel tableModel; /** Create table view with default table model. */ public TableSheet() { tableModel = new NodeTableModel(); initializeView(); } /** Create table view with users table model. */ public TableSheet(NodeTableModel tableModel) { this.tableModel = tableModel; initializeView(); } private void initializeView() { table = createTable(); initializeTable(); setViewportView(table); // do not care about focus setRequestFocusEnabled (false); table.getAccessibleContext().setAccessibleName( org.openide.util.NbBundle.getBundle(TableSheet.class).getString("ACS_TableSheet")); table.getAccessibleContext().setAccessibleDescription( org.openide.util.NbBundle.getBundle(TableSheet.class).getString("ACSD_TableSheet")); } /** Set rows. * @param props rows */ public void setNodes(Node[] nodes) { tableModel.setNodes(nodes); } /** Set columns. * @param nodes columns */ public void setProperties(Property[] props) { tableModel.setProperties(props); } /** Sets resize mode of table. * * @param mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF, * JTable.AUTO_RESIZE_NEXT_COLUMN, * JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS, * JTable.AUTO_RESIZE_LAST_COLUMN, * JTable.AUTO_RESIZE_ALL_COLUMNS</pre> */ public final void setAutoResizeMode(int mode) { table.setAutoResizeMode(mode); } /** Sets resize mode of table. * * @param mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF, * JTable.AUTO_RESIZE_NEXT_COLUMN, * JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS, * JTable.AUTO_RESIZE_LAST_COLUMN, * JTable.AUTO_RESIZE_ALL_COLUMNS</pre> */ public final int getAutoResizeMode() { return table.getAutoResizeMode(); } /** Sets preferred width of table column * @param index column index * @param width preferred column width */ public final void setColumnPreferredWidth(int index, int width) { table.getColumnModel().getColumn(index).setPreferredWidth(width); table.setPreferredScrollableViewportSize(table.getPreferredSize()); } /** Gets preferred width of table column * @param index column index * @param width preferred column width */ public final int getColumnPreferredWidth(int index) { return table.getColumnModel().getColumn(index).getPreferredWidth(); } /** Allows to subclasses provide its own table. * @param tm node table model * @return table which will be placed into scroll pane */ JTable createTable() { return new JTable(); } /** Allows to subclasses initialize table * @param t */ private void initializeTable() { table.setModel(tableModel); TableSheetCell tableCell = new TableSheetCell(tableModel); table.setDefaultRenderer(Node.Property.class, tableCell); table.setDefaultEditor(Node.Property.class, tableCell); table.getTableHeader().setDefaultRenderer(tableCell); table.setShowGrid(false); table.setIntercellSpacing(new Dimension(0, 0)); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); table.getTableHeader().setReorderingAllowed(false); table.setBackground(UIManager.getColor("Panel.background")); // NOI18N table.setSelectionBackground(UIManager.getColor("Panel.background")); // NOI18N } /** Synchronized table view with other view (TreeView, ListView). Two views (scroll panes) * have only one vertical scroll bar. Right view is allways table view, left view could be any * scroll pane. Use ControlledTableView.compoundScrollPane() to get compound view. */ static class ControlledTableView extends TableSheet { /** Scroll pane which controls vertical scroll bar */ JScrollPane controllingView; /** Table like header of controlling view */ Component header; /** Compound scroll pane used in MouseDragHandler */ JPanel compoundScrollPane; /** Creates controlled scroll pane with <code>contView</code> on the left, table view * on the right */ ControlledTableView(JScrollPane contrView) { super(); this.controllingView = contrView; initializeView(); } /** Creates controlled scroll pane with <code>contView</code> on the left, table view * on the right. */ ControlledTableView(JScrollPane contrView, NodeTableModel ntm) { super(ntm); this.controllingView = contrView; initializeView(); } /** Validate root is outer scroll pane. */ public boolean isValidateRoot() { return false; } /** initialize view */ private void initializeView() { // adjustment of controlling view Component comp = controllingView.getViewport().getView(); controllingView.setViewportView(comp); getViewport().setBackground(UIManager.getColor("Table.background")); // NOI18N // both views share one vertical scrollbar setVerticalScrollBar(controllingView.getVerticalScrollBar()); ScrollPaneLayout spl = new EnablingScrollPaneLayout(controllingView); setLayout(spl); spl.syncWithScrollPane(this); spl = new EnablingScrollPaneLayout(this); controllingView.setLayout(spl); spl.syncWithScrollPane(controllingView); table.setBorder(null); // table like header header = new JTable().getTableHeader().getDefaultRenderer() .getTableCellRendererComponent(null, " ", false, false, 0, 0); // NOI18N MouseInputListener mouseHandler = new MouseDragHandler(); header.addMouseListener(mouseHandler); header.addMouseMotionListener(mouseHandler); } /** Overriden to return table with controlled height * @param tm table model * @return table */ JTable createTable() { return new ATable(); } private class ATable extends JTable { public ATable() { // fix for JTable bug - JTable consumes Esc key which is wrong // after JDK bug 4624483 is fixed this workaround can // be removed getActionMap().put("cancel", new OurCancelEditingAction()); // NOI18N } public Dimension getPreferredScrollableViewportSize() { Dimension pref = super.getPreferredScrollableViewportSize(); if (this.getAutoResizeMode() != JTable.AUTO_RESIZE_OFF && getParent() != null) { Insets insets = getParent().getInsets(); Dimension size = getParent().getSize(); pref.height = size.height - insets.top - insets.bottom; } return pref; } private boolean trytorevalidate = true; /** Try to revalidate once again because we want table to have * width that it asked for. */ public void setBounds(int x, int y, int width, int height) { super.setBounds(x, y, width, height); if (this.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF) { return; } if (trytorevalidate && width != getPreferredScrollableViewportSize().width) { trytorevalidate = false; compoundScrollPane.validate(); trytorevalidate = true; } } private class OurCancelEditingAction extends AbstractAction { OurCancelEditingAction() {} public void actionPerformed(java.awt.event.ActionEvent e) { JTable table = (JTable)e.getSource(); table.removeEditor(); } public boolean isEnabled() { return ATable.this.isEditing(); } } } JTable getTable() { return table; } /** Overriden because I can't set border to null by calling setBorder(null). * @param border */ public void setBorder(Border border) { super.setBorder(null); } /** Is used to synchronize table row height with left view. */ void setRowHeight(int h) { table.setRowHeight(h); getVerticalScrollBar().setUnitIncrement(h); } /** Sets text of table like header above left scroll pane. */ void setHeaderText(String text) { if (header instanceof JLabel) { ((JLabel)header).setText(text); } } /** Sets preferred size of left scroll pane. */ void setControllingViewWidth(int width) { controllingView.setPreferredSize(new Dimension(width, 0)); } /** Gets preferred size of left scroll pane. */ int getControllingViewWidth() { return controllingView.getPreferredSize().width; } /* public void setPreferredSize(Dimension prefSize) { table.setPreferredScrollableViewportSize(prefSize); } */ /** Returns component which contains two synchronized scroll panes. Above left one is * placed table like header. */ JComponent compoundScrollPane() { JPanel leftPanel = new JPanel(new BorderLayout()); leftPanel.add(header, BorderLayout.NORTH); leftPanel.add(controllingView, BorderLayout.CENTER); compoundScrollPane = new CompoundScrollPane(); compoundScrollPane.setLayout(new BorderLayout()); compoundScrollPane.add(leftPanel, BorderLayout.CENTER); compoundScrollPane.add(this, BorderLayout.EAST); return compoundScrollPane; } private class MouseDragHandler extends MouseInputAdapter { MouseDragHandler() {} boolean dragging = false; int lastMouseX; public void mousePressed(MouseEvent e) { Point p = e.getPoint(); dragging = canResize(p); lastMouseX = p.x; } private void setCursor(Cursor c) { if (header.getCursor() != c) { header.setCursor(c); } } private boolean canResize(Point mousePoint) { return mousePoint.x >= header.getSize().width - 3; } public void mouseMoved(MouseEvent e) { if (canResize(e.getPoint()) || dragging) { setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); } else { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } public void mouseDragged(MouseEvent e) { int mouseX = e.getX(); int deltaX = lastMouseX - mouseX; if (deltaX == 0) { return; } if (dragging) { Dimension size = table.getPreferredScrollableViewportSize(); int parentWidth = compoundScrollPane.getWidth(); int tableMinWidth = table.getMinimumSize().width; int newWidth; if (size.width + deltaX > parentWidth - 20) { newWidth = parentWidth - 20; } else if (size.width + deltaX < tableMinWidth){ newWidth = tableMinWidth; } else { newWidth = size.width + deltaX; } table.setPreferredScrollableViewportSize(new Dimension(newWidth, size.height)); lastMouseX = lastMouseX - (newWidth - size.width); table.revalidate(); table.repaint(); } else { lastMouseX = mouseX; } } public void mouseReleased(MouseEvent e) { dragging = false; } }; } /** Scrollable (better say not scrollable) pane. Used as container for * left (controlling) and rigth (controlled) scroll panes. */ private static class CompoundScrollPane extends JPanel implements Scrollable { CompoundScrollPane() {} public boolean getScrollableTracksViewportWidth() { return true; } public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } public boolean getScrollableTracksViewportHeight() { return true; } public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } } private static String getString(String key) { return org.openide.util.NbBundle.getBundle(TableSheet.class).getString(key); } /** Makes visible horizontal scroll bar of dependent scrollpane. Enables/disables * horizontal scrollbar of parent scrollpane. */ private static final class EnablingScrollPaneLayout extends ScrollPaneLayout { JScrollPane dependentScrollPane; public EnablingScrollPaneLayout(JScrollPane scrollPane) { dependentScrollPane = scrollPane; } public void layoutContainer(Container parent) { super.layoutContainer(parent); Component view = (viewport != null) ? viewport.getView() : null; Dimension viewPrefSize = (view != null) ? view.getPreferredSize() : new Dimension(0,0); Dimension extentSize = (viewport != null) ? viewport.toViewCoordinates(viewport.getSize()) : new Dimension(0,0); boolean viewTracksViewportWidth = (view instanceof Scrollable) && ((Scrollable)view).getScrollableTracksViewportWidth(); boolean hsbNeeded = !viewTracksViewportWidth && viewPrefSize.width > extentSize.width; // enable horizontal scrollbar only if it is needed if (hsb != null) { hsb.setEnabled(hsbNeeded); } // make dependent horizontal scrollbar visible by setting scrollbar policy JScrollPane scrollPane = (JScrollPane)parent; if (scrollPane.getHorizontalScrollBarPolicy() != JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) { int newPolicy = hsbNeeded ? JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS : JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED; if (newPolicy != dependentScrollPane.getHorizontalScrollBarPolicy()) { dependentScrollPane.setHorizontalScrollBarPolicy(newPolicy); dependentScrollPane.getViewport().invalidate(); } } } } }