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-2006 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: package org.openide.explorer.view;
042:
043: import org.openide.explorer.propertysheet.*;
044: import org.openide.nodes.Node;
045: import org.openide.nodes.Node.Property;
046:
047: import java.awt.*;
048: import java.awt.event.MouseEvent;
049:
050: import java.beans.*;
051:
052: import javax.swing.*;
053: import javax.swing.border.*;
054: import javax.swing.event.*;
055:
056: /** Table view of node properties. Table header displays property display names and each cell
057: * contains (<code>PropertyPanel</code>) for displaying/editting of properties. Each property
058: * row belongs to one node.
059: *
060: * @author Jan Rojcek
061: */
062: class TableSheet extends JScrollPane {
063: /** table */
064: transient protected JTable table;
065:
066: /** model */
067: transient private NodeTableModel tableModel;
068:
069: transient private TableSheetCell tableCell;
070:
071: /** Create table view with default table model.
072: */
073: public TableSheet() {
074: tableModel = new NodeTableModel();
075: initializeView();
076: }
077:
078: /** Create table view with users table model.
079: */
080: public TableSheet(NodeTableModel tableModel) {
081: this .tableModel = tableModel;
082: initializeView();
083: }
084:
085: private void initializeView() {
086: table = createTable();
087: initializeTable();
088:
089: setViewportView(table);
090:
091: // do not care about focus
092: setRequestFocusEnabled(false);
093:
094: table.getAccessibleContext().setAccessibleName(
095: org.openide.util.NbBundle.getBundle(TableSheet.class)
096: .getString("ACS_TableSheet"));
097: table.getAccessibleContext().setAccessibleDescription(
098: org.openide.util.NbBundle.getBundle(TableSheet.class)
099: .getString("ACSD_TableSheet"));
100: }
101:
102: /** Set rows.
103: * @param props rows
104: */
105: public void setNodes(Node[] nodes) {
106: tableModel.setNodes(nodes);
107: }
108:
109: /** Set columns.
110: * @param nodes columns
111: */
112: public void setProperties(Property[] props) {
113: tableModel.setProperties(props);
114: }
115:
116: /** Sets resize mode of table.
117: *
118: * @param mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF,
119: * JTable.AUTO_RESIZE_NEXT_COLUMN,
120: * JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
121: * JTable.AUTO_RESIZE_LAST_COLUMN,
122: * JTable.AUTO_RESIZE_ALL_COLUMNS</pre>
123: */
124: public final void setAutoResizeMode(int mode) {
125: table.setAutoResizeMode(mode);
126: }
127:
128: /** Sets resize mode of table.
129: *
130: * @param mode - One of 5 legal values: <pre>JTable.AUTO_RESIZE_OFF,
131: * JTable.AUTO_RESIZE_NEXT_COLUMN,
132: * JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS,
133: * JTable.AUTO_RESIZE_LAST_COLUMN,
134: * JTable.AUTO_RESIZE_ALL_COLUMNS</pre>
135: */
136: public final int getAutoResizeMode() {
137: return table.getAutoResizeMode();
138: }
139:
140: /** Sets preferred width of table column
141: * @param index column index
142: * @param width preferred column width
143: */
144: public final void setColumnPreferredWidth(int index, int width) {
145: table.getColumnModel().getColumn(index)
146: .setPreferredWidth(width);
147:
148: table.setPreferredScrollableViewportSize(table
149: .getPreferredSize());
150: }
151:
152: /** Gets preferred width of table column
153: * @param index column index
154: * @param width preferred column width
155: */
156: public final int getColumnPreferredWidth(int index) {
157: return table.getColumnModel().getColumn(index)
158: .getPreferredWidth();
159: }
160:
161: /** Allows to subclasses provide its own table.
162: * @param tm node table model
163: * @return table which will be placed into scroll pane
164: */
165: JTable createTable() {
166: return new JTable();
167: }
168:
169: /** Allows to subclasses initialize table
170: * @param t
171: */
172: private void initializeTable() {
173: table.setModel(tableModel);
174:
175: tableCell = new TableSheetCell(tableModel);
176: table.setDefaultRenderer(Node.Property.class, tableCell);
177: table.setDefaultEditor(Node.Property.class, tableCell);
178: table.getTableHeader().setDefaultRenderer(tableCell);
179:
180: table.setShowGrid(false);
181: table.setIntercellSpacing(new Dimension(0, 0));
182:
183: table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
184: table.getTableHeader().setReorderingAllowed(false);
185:
186: if (UIManager.getColor("Panel.background") != null) { // NOI18N
187: table.setBackground(UIManager.getColor("Panel.background")); // NOI18N
188: table.setSelectionBackground(UIManager
189: .getColor("Panel.background")); // NOI18N
190: }
191: }
192:
193: private static String getString(String key) {
194: return org.openide.util.NbBundle.getBundle(TableSheet.class)
195: .getString(key);
196: }
197:
198: @Override
199: public void updateUI() {
200: super .updateUI();
201: if (null != tableCell)
202: tableCell.updateUI();
203: }
204:
205: /** Synchronized table view with other view (TreeView, ListView). Two views (scroll panes)
206: * have only one vertical scroll bar. Right view is allways table view, left view could be any
207: * scroll pane. Use ControlledTableView.compoundScrollPane() to get compound view.
208: */
209: static class ControlledTableView extends TableSheet {
210: /** Scroll pane which controls vertical scroll bar */
211: JScrollPane controllingView;
212:
213: /** Table like header of controlling view */
214: Component header;
215:
216: /** Compound scroll pane used in MouseDragHandler */
217: JPanel compoundScrollPane;
218:
219: /** Creates controlled scroll pane with <code>contView</code> on the left, table view
220: * on the right
221: */
222: ControlledTableView(JScrollPane contrView) {
223: super ();
224: this .controllingView = contrView;
225: initializeView();
226: }
227:
228: /** Creates controlled scroll pane with <code>contView</code> on the left, table view
229: * on the right.
230: */
231: ControlledTableView(JScrollPane contrView, NodeTableModel ntm) {
232: super (ntm);
233: this .controllingView = contrView;
234: initializeView();
235: }
236:
237: /** Validate root is outer scroll pane. */
238: public boolean isValidateRoot() {
239: return false;
240: }
241:
242: /** initialize view
243: */
244: private void initializeView() {
245: // adjustment of controlling view
246: Component comp = controllingView.getViewport().getView();
247: controllingView.setViewportView(comp);
248:
249: if (UIManager.getColor("Table.background") != null) { // NOI18N
250: getViewport().setBackground(
251: UIManager.getColor("Table.background")); // NOI18N
252: }
253:
254: // both views share one vertical scrollbar
255: setVerticalScrollBar(controllingView.getVerticalScrollBar());
256:
257: ScrollPaneLayout spl = new EnablingScrollPaneLayout(
258: controllingView);
259: setLayout(spl);
260: spl.syncWithScrollPane(this );
261:
262: spl = new EnablingScrollPaneLayout(this );
263: controllingView.setLayout(spl);
264: spl.syncWithScrollPane(controllingView);
265:
266: table.setBorder(null);
267:
268: // table like header
269: header = new JTable().getTableHeader().getDefaultRenderer()
270: .getTableCellRendererComponent(null, " ", false,
271: false, 0, 0); // NOI18N
272:
273: MouseInputListener mouseHandler = new MouseDragHandler();
274: header.addMouseListener(mouseHandler);
275: header.addMouseMotionListener(mouseHandler);
276: }
277:
278: /** Overriden to return table with controlled height
279: * @param tm table model
280: * @return table
281: */
282: JTable createTable() {
283: return new ATable();
284: }
285:
286: JTable getTable() {
287: return table;
288: }
289:
290: /** Overriden because I can't set border to null by calling setBorder(null).
291: * @param border
292: */
293: public void setBorder(Border border) {
294: super .setBorder(null);
295: }
296:
297: /** Is used to synchronize table row height with left view.
298: */
299: void setRowHeight(int h) {
300: table.setRowHeight(h);
301: getVerticalScrollBar().setUnitIncrement(h);
302: }
303:
304: /** Sets text of table like header above left scroll pane.
305: */
306: void setHeaderText(String text) {
307: if (header instanceof JLabel) {
308: ((JLabel) header).setText(text);
309: }
310: }
311:
312: /** Sets preferred size of left scroll pane.
313: */
314: void setControllingViewWidth(int width) {
315: controllingView.setPreferredSize(new Dimension(width, 0));
316: }
317:
318: /** Gets preferred size of left scroll pane.
319: */
320: int getControllingViewWidth() {
321: return controllingView.getPreferredSize().width;
322: }
323:
324: /*
325: public void setPreferredSize(Dimension prefSize) {
326: table.setPreferredScrollableViewportSize(prefSize);
327: }
328: */
329:
330: /** Returns component which contains two synchronized scroll panes. Above left one is
331: * placed table like header.
332: */
333: JComponent compoundScrollPane() {
334: JPanel leftPanel = new JPanel(new BorderLayout());
335: leftPanel.add(header, BorderLayout.NORTH);
336: leftPanel.add(controllingView, BorderLayout.CENTER);
337:
338: compoundScrollPane = new CompoundScrollPane();
339: compoundScrollPane.setLayout(new BorderLayout());
340: compoundScrollPane.add(leftPanel, BorderLayout.CENTER);
341: compoundScrollPane.add(this , BorderLayout.EAST);
342:
343: return compoundScrollPane;
344: }
345:
346: private class ATable extends JTable {
347: private boolean trytorevalidate = true;
348:
349: public ATable() {
350: // fix for JTable bug - JTable consumes Esc key which is wrong
351: // after JDK bug 4624483 is fixed this workaround can
352: // be removed
353: getActionMap().put("cancel",
354: new OurCancelEditingAction()); // NOI18N
355: }
356:
357: public Dimension getPreferredScrollableViewportSize() {
358: Dimension pref = super
359: .getPreferredScrollableViewportSize();
360:
361: if ((this .getAutoResizeMode() != JTable.AUTO_RESIZE_OFF)
362: && (getParent() != null)) {
363: Insets insets = getParent().getInsets();
364: Dimension size = getParent().getSize();
365: pref.height = size.height - insets.top
366: - insets.bottom;
367: }
368:
369: return pref;
370: }
371:
372: /** Try to revalidate once again because we want table to have
373: * width that it asked for.
374: */
375: public void setBounds(int x, int y, int width, int height) {
376: super .setBounds(x, y, width, height);
377:
378: if (this .getAutoResizeMode() == JTable.AUTO_RESIZE_OFF) {
379: return;
380: }
381:
382: if (trytorevalidate
383: && (width != getPreferredScrollableViewportSize().width)) {
384: trytorevalidate = false;
385: compoundScrollPane.validate();
386: trytorevalidate = true;
387: }
388: }
389:
390: private class OurCancelEditingAction extends AbstractAction {
391: OurCancelEditingAction() {
392: }
393:
394: public void actionPerformed(java.awt.event.ActionEvent e) {
395: JTable table = (JTable) e.getSource();
396: table.removeEditor();
397: }
398:
399: public boolean isEnabled() {
400: return ATable.this .isEditing();
401: }
402: }
403: }
404:
405: private class MouseDragHandler extends MouseInputAdapter {
406: boolean dragging = false;
407: int lastMouseX;
408:
409: MouseDragHandler() {
410: }
411:
412: public void mousePressed(MouseEvent e) {
413: Point p = e.getPoint();
414: dragging = canResize(p);
415: lastMouseX = p.x;
416: }
417:
418: private void setCursor(Cursor c) {
419: if (header.getCursor() != c) {
420: header.setCursor(c);
421: }
422: }
423:
424: private boolean canResize(Point mousePoint) {
425: return mousePoint.x >= (header.getSize().width - 3);
426: }
427:
428: public void mouseMoved(MouseEvent e) {
429: if (canResize(e.getPoint()) || dragging) {
430: setCursor(Cursor
431: .getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
432: } else {
433: setCursor(Cursor
434: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
435: }
436: }
437:
438: public void mouseDragged(MouseEvent e) {
439: int mouseX = e.getX();
440: int deltaX = lastMouseX - mouseX;
441:
442: if (deltaX == 0) {
443: return;
444: }
445:
446: if (dragging) {
447: Dimension size = table
448: .getPreferredScrollableViewportSize();
449: int parentWidth = compoundScrollPane.getWidth();
450: int tableMinWidth = table.getMinimumSize().width;
451:
452: int newWidth;
453:
454: if ((size.width + deltaX) > (parentWidth - 20)) {
455: newWidth = parentWidth - 20;
456: } else if ((size.width + deltaX) < tableMinWidth) {
457: newWidth = tableMinWidth;
458: } else {
459: newWidth = size.width + deltaX;
460: }
461:
462: table
463: .setPreferredScrollableViewportSize(new Dimension(
464: newWidth, size.height));
465: lastMouseX = lastMouseX - (newWidth - size.width);
466:
467: table.revalidate();
468: table.repaint();
469: } else {
470: lastMouseX = mouseX;
471: }
472: }
473:
474: public void mouseReleased(MouseEvent e) {
475: dragging = false;
476: }
477: }
478: }
479:
480: /** Scrollable (better say not scrollable) pane. Used as container for
481: * left (controlling) and rigth (controlled) scroll panes.
482: */
483: private static class CompoundScrollPane extends JPanel implements
484: Scrollable {
485: CompoundScrollPane() {
486: }
487:
488: public boolean getScrollableTracksViewportWidth() {
489: return true;
490: }
491:
492: public int getScrollableBlockIncrement(Rectangle visibleRect,
493: int orientation, int direction) {
494: return 10;
495: }
496:
497: public boolean getScrollableTracksViewportHeight() {
498: return true;
499: }
500:
501: public Dimension getPreferredScrollableViewportSize() {
502: return getPreferredSize();
503: }
504:
505: public int getScrollableUnitIncrement(Rectangle visibleRect,
506: int orientation, int direction) {
507: return 10;
508: }
509: }
510:
511: /** Makes visible horizontal scroll bar of dependent scrollpane. Enables/disables
512: * horizontal scrollbar of parent scrollpane.
513: */
514: private static final class EnablingScrollPaneLayout extends
515: ScrollPaneLayout {
516: JScrollPane dependentScrollPane;
517:
518: public EnablingScrollPaneLayout(JScrollPane scrollPane) {
519: dependentScrollPane = scrollPane;
520: }
521:
522: public void layoutContainer(Container parent) {
523: super .layoutContainer(parent);
524:
525: Component view = (viewport != null) ? viewport.getView()
526: : null;
527: Dimension viewPrefSize = (view != null) ? view
528: .getPreferredSize() : new Dimension(0, 0);
529: Dimension extentSize = (viewport != null) ? viewport
530: .toViewCoordinates(viewport.getSize())
531: : new Dimension(0, 0);
532:
533: boolean viewTracksViewportWidth = (view instanceof Scrollable)
534: && ((Scrollable) view)
535: .getScrollableTracksViewportWidth();
536: boolean hsbNeeded = !viewTracksViewportWidth
537: && (viewPrefSize.width > extentSize.width);
538:
539: // enable horizontal scrollbar only if it is needed
540: if (hsb != null) {
541: hsb.setEnabled(hsbNeeded);
542: }
543:
544: // make dependent horizontal scrollbar visible by setting scrollbar policy
545: JScrollPane scrollPane = (JScrollPane) parent;
546:
547: if (scrollPane.getHorizontalScrollBarPolicy() != JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS) {
548: int newPolicy = hsbNeeded ? JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS
549: : JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED;
550:
551: if (newPolicy != dependentScrollPane
552: .getHorizontalScrollBarPolicy()) {
553: dependentScrollPane
554: .setHorizontalScrollBarPolicy(newPolicy);
555: dependentScrollPane.getViewport().invalidate();
556: }
557: }
558: }
559: }
560: }
|