001: /*
002: * @(#)JideScrollPane.java
003: *
004: * Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
005: */
006: package com.jidesoft.swing;
007:
008: import javax.swing.*;
009: import java.awt.*;
010:
011: /**
012: * <code>JideScrollPane</code> is an enhanced version of <code>JScrollPane</code>. In
013: * <code>JScrollPane</code>, you can have rowHeader and columnHeader. However you can't
014: * have rowFooter and columnFooter. However rowFooter and columnFooter are very useful in
015: * table. For example they can be used to display "total" or "summary" type of information.
016: * <p/>
017: * Several methods related to rowFooter and columnFooter are added such as
018: * {@link #setRowFooter(javax.swing.JViewport)}, and {@link #setColumnFooter(javax.swing.JViewport)}
019: * which will set the viewport to rowFooter and columnFooter area respectively. The usage
020: * of those methods are exactly the same as {@link JScrollPane#setRowHeader(javax.swing.JViewport)}.
021: * <p/>
022: * To fully leverage the power of JideScrollPane, we also create
023: * a class called <code>TableScrollPane</code> which
024: * is part of JIDE Grids package. It will allow you to
025: * easily create table with row header, row footer and column footer.
026: * <p><code>JideScrollPane</code> also provides support for scrollbar corners. You can set them using {@link #setScrollBarCorner(String,java.awt.Component)}.
027: * Available key for scroll bar corner is defined at {@link JideScrollPaneConstants} which can be access from <code>JideScrollPane</code>.
028: * <p/>
029: * <b>Credit:</b> This implementation of scroll bar corner is based on work from Santhosh Kumar - santhosh@in.fiorano.com.
030: */
031: public class JideScrollPane extends JScrollPane implements
032: JideScrollPaneConstants {
033:
034: /**
035: * The row footer child. Default is <code>null</code>.
036: *
037: * @see #setRowFooter
038: */
039: protected JViewport _rowFooter;
040:
041: /**
042: * The column footer child. Default is <code>null</code>.
043: *
044: * @see #setColumnFooter
045: */
046: protected JViewport _columnFooter;
047:
048: /**
049: * The component to the left of horizontal scroll bar.
050: */
051: protected Component _hLeft;
052: /**
053: * The component to the right of horizontal scroll bar.
054: */
055: protected Component _hRight;
056:
057: /**
058: * The component to the top of vertical scroll bar.
059: */
060: protected Component _vTop;
061:
062: /**
063: * The component to the bottom of vertical scroll bar.
064: */
065: protected Component _vBottom;
066:
067: private boolean _horizontalScrollBarCoversWholeWidth;
068: private boolean _verticalScrollBarCoversWholeHeight;
069:
070: public static final String PROPERTY_HORIZONTAL_SCROLL_BAR_COVERS_WHOLE_WIDTH = "horizontalScrollBarCoversWholeWidth";
071:
072: public static final String PROPERTY_VERTICAL_SCROLL_BAR_COVERS_WHOLE_HEIGHT = "verticalScrollBarCoversWholeHeight";
073:
074: /**
075: * Creates a <code>JideScrollPane</code> that displays the view
076: * component in a viewport
077: * whose view position can be controlled with a pair of scrollbars.
078: * The scrollbar policies specify when the scrollbars are displayed,
079: * For example, if <code>vsbPolicy</code> is
080: * <code>VERTICAL_SCROLLBAR_AS_NEEDED</code>
081: * then the vertical scrollbar only appears if the view doesn't fit
082: * vertically. The available policy settings are listed at
083: * {@link #setVerticalScrollBarPolicy} and
084: * {@link #setHorizontalScrollBarPolicy}.
085: *
086: * @param view the component to display in the scrollpanes viewport
087: * @param vsbPolicy an integer that specifies the vertical
088: * scrollbar policy
089: * @param hsbPolicy an integer that specifies the horizontal
090: * scrollbar policy
091: * @see #setViewportView
092: */
093: public JideScrollPane(Component view, int vsbPolicy, int hsbPolicy) {
094: setLayout(new JideScrollPaneLayout.UIResource());
095: setVerticalScrollBarPolicy(vsbPolicy);
096: setHorizontalScrollBarPolicy(hsbPolicy);
097: setViewport(createViewport());
098: setVerticalScrollBar(createVerticalScrollBar());
099: setHorizontalScrollBar(createHorizontalScrollBar());
100: if (null != view) {
101: setViewportView(view);
102: }
103: setOpaque(true);
104: updateUI();
105:
106: if (!getComponentOrientation().isLeftToRight()) {
107: viewport.setViewPosition(new Point(Integer.MAX_VALUE, 0));
108: }
109: }
110:
111: /**
112: * Creates a <code>JideScrollPane</code> that displays the
113: * contents of the specified
114: * component, where both horizontal and vertical scrollbars appear
115: * whenever the component's contents are larger than the view.
116: *
117: * @param view the component to display in the scrollpane's viewport
118: * @see #setViewportView
119: */
120: public JideScrollPane(Component view) {
121: this (view, VERTICAL_SCROLLBAR_AS_NEEDED,
122: HORIZONTAL_SCROLLBAR_AS_NEEDED);
123: }
124:
125: /**
126: * Creates an empty (no viewport view) <code>JideScrollPane</code>
127: * with specified
128: * scrollbar policies. The available policy settings are listed at
129: * {@link #setVerticalScrollBarPolicy} and
130: * {@link #setHorizontalScrollBarPolicy}.
131: *
132: * @param vsbPolicy an integer that specifies the vertical
133: * scrollbar policy
134: * @param hsbPolicy an integer that specifies the horizontal
135: * scrollbar policy
136: * @see #setViewportView
137: */
138: public JideScrollPane(int vsbPolicy, int hsbPolicy) {
139: this (null, vsbPolicy, hsbPolicy);
140: }
141:
142: /**
143: * Creates an empty (no viewport view) <code>JideScrollPane</code>
144: * where both horizontal and vertical scrollbars appear when needed.
145: */
146: public JideScrollPane() {
147: this (null, VERTICAL_SCROLLBAR_AS_NEEDED,
148: HORIZONTAL_SCROLLBAR_AS_NEEDED);
149: }
150:
151: /**
152: * Returns the row footer.
153: *
154: * @return the <code>rowFooter</code> property
155: * @see #setRowFooter
156: */
157: public JViewport getRowFooter() {
158: return _rowFooter;
159: }
160:
161: /**
162: * Removes the old rowFooter, if it exists. If the new rowFooter
163: * isn't <code>null</code>, syncs the y coordinate of its
164: * viewPosition with
165: * the viewport (if there is one) and then adds it to the scrollpane.
166: *
167: * @param rowFooter the new row footer to be used; if <code>null</code>
168: * the old row footer is still removed and the new rowFooter
169: * is set to <code>null</code>
170: * @see #getRowFooter
171: * @see #setRowFooterView
172: */
173: public void setRowFooter(JViewport rowFooter) {
174: JViewport old = getRowFooter();
175: _rowFooter = rowFooter;
176: if (null != rowFooter) {
177: add(rowFooter, ROW_FOOTER);
178: } else if (null != old) {
179: remove(old);
180: }
181: firePropertyChange("rowFooter", old, rowFooter);
182: revalidate();
183: repaint();
184: JideSwingUtilities.synchronizeView(rowFooter, getViewport(),
185: SwingConstants.VERTICAL);
186: }
187:
188: /**
189: * Overwride setRowHeader method in JScrollPane and synchronize the view with the main viewport.
190: * Swing tried to implement this feature but it will break if the view position changes starts from rowHeader.
191: *
192: * @param rowHeader the new row header
193: */
194: @Override
195: public void setRowHeader(JViewport rowHeader) {
196: super .setRowHeader(rowHeader);
197: JideSwingUtilities.synchronizeView(rowHeader, getViewport(),
198: SwingConstants.VERTICAL);
199: }
200:
201: /**
202: * Creates a row-footer viewport if necessary, sets
203: * its view and then adds the row-footer viewport
204: * to the scrollpane. For example:
205: * <pre>
206: * JScrollPane scrollpane = new JideScrollPane();
207: * scrollpane.setViewportView(myBigComponentToScroll);
208: * scrollpane.setRowFooterView(myBigComponentsRowFooter);
209: * </pre>
210: *
211: * @param view the component to display as the row footer
212: * @see #setRowFooter
213: * @see JViewport#setView
214: */
215: public void setRowFooterView(Component view) {
216: if (null == getRowFooter()) {
217: setRowFooter(createViewport());
218: }
219: getRowFooter().setView(view);
220: }
221:
222: /**
223: * Returns the column footer.
224: *
225: * @return the <code>columnFooter</code> property
226: * @see #setColumnFooter
227: */
228: public JViewport getColumnFooter() {
229: return _columnFooter;
230: }
231:
232: /**
233: * Removes the old columnFooter, if it exists. If the new columnFooter
234: * isn't <code>null</code>, sync the x coordinate of the its viewPosition
235: * with the viewport (if there is one) and then add it to the scrollpane.
236: *
237: * @param columnFooter the new column footer to be used; if <code>null</code>
238: * the old column footer is still removed and the new columnFooter
239: * is set to <code>null</code>
240: * @see #getColumnFooter
241: * @see #setColumnFooterView
242: */
243: public void setColumnFooter(JViewport columnFooter) {
244: JViewport old = getColumnFooter();
245: _columnFooter = columnFooter;
246: if (null != columnFooter) {
247: add(columnFooter, COLUMN_FOOTER);
248: } else if (null != old) {
249: remove(old);
250: }
251: firePropertyChange("columnFooter", old, columnFooter);
252:
253: revalidate();
254: repaint();
255:
256: JideSwingUtilities.synchronizeView(_columnFooter,
257: getViewport(), SwingConstants.HORIZONTAL);
258: }
259:
260: /**
261: * Overrides to make column header viewport synchonizing with the main viewport.
262: *
263: * @param columnHeader
264: */
265: @Override
266: public void setColumnHeader(JViewport columnHeader) {
267: super .setColumnHeader(columnHeader);
268: JideSwingUtilities.synchronizeView(this .columnHeader,
269: getViewport(), SwingConstants.HORIZONTAL);
270: }
271:
272: /**
273: * Creates a column-footer viewport if necessary, sets
274: * its view, and then adds the column-footer viewport
275: * to the scrollpane. For example:
276: * <pre>
277: * JScrollPane scrollpane = new JideScrollPane();
278: * scrollpane.setViewportView(myBigComponentToScroll);
279: * scrollpane.setColumnFooterView(myBigComponentsColumnFooter);
280: * </pre>
281: *
282: * @param view the component to display as the column footer
283: * @see #setColumnFooter
284: * @see JViewport#setView
285: */
286: public void setColumnFooterView(Component view) {
287: if (null == getColumnFooter()) {
288: setColumnFooter(createViewport());
289: }
290: getColumnFooter().setView(view);
291: }
292:
293: /**
294: * Returns the component at the specified scroll bar corner. The
295: * <code>key</code> value specifying the corner is one of:
296: * <ul>
297: * <li>{@link JideScrollPane#HORIZONTAL_LEFT}
298: * <li>{@link JideScrollPane#HORIZONTAL_RIGHT}
299: * <li>{@link JideScrollPane#VERTICAL_TOP}
300: * <li>{@link JideScrollPane#VERTICAL_BOTTOM}
301: * <li>{@link JideScrollPane#HORIZONTAL_LEADING}
302: * <li>{@link JideScrollPane#HORIZONTAL_TRAILING}
303: * </ul>
304: *
305: * @param key one of the values as shown above
306: * @return one of the components listed below or <code>null</code>
307: * if <code>key</code> is invalid:
308: * <ul>
309: * <li>lowerLeft
310: * <li>lowerRight
311: * <li>upperLeft
312: * <li>upperRight
313: * </ul>
314: * @see #setCorner
315: */
316: public Component getScrollBarCorner(String key) {
317: boolean isLeftToRight = getComponentOrientation()
318: .isLeftToRight();
319: if (key.equals(HORIZONTAL_LEADING)) {
320: key = isLeftToRight ? HORIZONTAL_LEFT : HORIZONTAL_RIGHT;
321: } else if (key.equals(HORIZONTAL_TRAILING)) {
322: key = isLeftToRight ? HORIZONTAL_RIGHT : HORIZONTAL_LEFT;
323: }
324:
325: if (key.equals(HORIZONTAL_LEFT)) {
326: return _hLeft;
327: } else if (key.equals(HORIZONTAL_RIGHT)) {
328: return _hRight;
329: } else if (key.equals(VERTICAL_BOTTOM)) {
330: return _vBottom;
331: } else if (key.equals(VERTICAL_TOP)) {
332: return _vTop;
333: } else {
334: return null;
335: }
336: }
337:
338: /**
339: * Adds a child that will appear in one of the scroll bars
340: * corners. Scroll bar will make room to show the corner component.
341: * Legal values for
342: * the <b>key</b> are:
343: * <ul>
344: * <li>{@link JideScrollPane#HORIZONTAL_LEFT}
345: * <li>{@link JideScrollPane#HORIZONTAL_RIGHT}
346: * <li>{@link JideScrollPane#VERTICAL_TOP}
347: * <li>{@link JideScrollPane#VERTICAL_BOTTOM}
348: * <li>{@link JideScrollPane#HORIZONTAL_LEADING}
349: * <li>{@link JideScrollPane#HORIZONTAL_TRAILING}
350: * </ul>
351: * <p/>
352: * Although "corner" doesn't match any beans property
353: * signature, <code>PropertyChange</code> events are generated with the
354: * property name set to the corner key.
355: *
356: * @param key identifies which corner the component will appear in
357: * @param corner one of the following components:
358: * <ul>
359: * <li>lowerLeft
360: * <li>lowerRight
361: * <li>upperLeft
362: * <li>upperRight
363: * </ul>
364: * @throws IllegalArgumentException if corner key is invalid
365: */
366: public void setScrollBarCorner(String key, Component corner) {
367: Component old;
368: boolean isLeftToRight = getComponentOrientation()
369: .isLeftToRight();
370: if (key.equals(HORIZONTAL_LEADING)) {
371: key = isLeftToRight ? HORIZONTAL_LEFT : HORIZONTAL_RIGHT;
372: } else if (key.equals(HORIZONTAL_TRAILING)) {
373: key = isLeftToRight ? HORIZONTAL_RIGHT : HORIZONTAL_LEFT;
374: }
375:
376: if (key.equals(HORIZONTAL_LEFT)) {
377: old = _hLeft;
378: _hLeft = corner;
379: } else if (key.equals(HORIZONTAL_RIGHT)) {
380: old = _hRight;
381: _hRight = corner;
382: } else if (key.equals(VERTICAL_TOP)) {
383: old = _vTop;
384: _vTop = corner;
385: } else if (key.equals(VERTICAL_BOTTOM)) {
386: old = _vBottom;
387: _vBottom = corner;
388: } else {
389: throw new IllegalArgumentException(
390: "invalid scroll bar corner key");
391: }
392:
393: if (null != old) {
394: remove(old);
395: }
396: if (null != corner) {
397: add(corner, key);
398: }
399: firePropertyChange(key, old, corner);
400: revalidate();
401: repaint();
402: }
403:
404: @Override
405: public void updateUI() {
406: super .updateUI();
407: LookAndFeel.installBorder(this , "JideScrollPane.border");
408: }
409:
410: public boolean isVerticalScrollBarCoversWholeHeight() {
411: return _verticalScrollBarCoversWholeHeight;
412: }
413:
414: public boolean isHorizontalScrollBarCoversWholeWidth() {
415: return _horizontalScrollBarCoversWholeWidth;
416: }
417:
418: public void setHorizontalScrollBarCoversWholeWidth(
419: boolean horizontalScrollBarCoversWholeWidth) {
420: boolean old = _horizontalScrollBarCoversWholeWidth;
421: if (old != horizontalScrollBarCoversWholeWidth) {
422: _horizontalScrollBarCoversWholeWidth = horizontalScrollBarCoversWholeWidth;
423: firePropertyChange(
424: PROPERTY_HORIZONTAL_SCROLL_BAR_COVERS_WHOLE_WIDTH,
425: old, _horizontalScrollBarCoversWholeWidth);
426: invalidate();
427: doLayout();
428: if (getHorizontalScrollBar() != null) {
429: getHorizontalScrollBar().doLayout();
430: }
431: }
432: }
433:
434: public void setVerticalScrollBarCoversWholeHeight(
435: boolean verticalScrollBarCoversWholeHeight) {
436: boolean old = _verticalScrollBarCoversWholeHeight;
437: if (old != verticalScrollBarCoversWholeHeight) {
438: _verticalScrollBarCoversWholeHeight = verticalScrollBarCoversWholeHeight;
439: firePropertyChange(
440: PROPERTY_VERTICAL_SCROLL_BAR_COVERS_WHOLE_HEIGHT,
441: old, _verticalScrollBarCoversWholeHeight);
442: invalidate();
443: doLayout();
444: if (getVerticalScrollBar() != null) {
445: getVerticalScrollBar().doLayout();
446: }
447: }
448: }
449:
450: @Override
451: protected JViewport createViewport() {
452: return new JViewport() {
453: @Override
454: public Dimension getViewSize() {
455: Dimension viewSize = super .getViewSize();
456: if (getView() == JideScrollPane.this .getViewport()
457: .getView()) {
458: if (rowHeader != null) {
459: Dimension headerSize = rowHeader.getViewSize();
460: if (viewSize.height < headerSize.height) {
461: viewSize.height = headerSize.height;
462: }
463: } else if (columnHeader != null) {
464: Dimension headerSize = columnHeader
465: .getViewSize();
466: if (viewSize.width < headerSize.width) {
467: viewSize.width = headerSize.width;
468: }
469: }
470: }
471: return viewSize;
472: }
473: };
474: }
475: }
|