/*
* 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();
}
}
}
}
}