001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.swt.custom;
011:
012: import org.eclipse.swt.*;
013: import org.eclipse.swt.graphics.*;
014: import org.eclipse.swt.widgets.*;
015:
016: /**
017: * A ScrolledComposite provides scrollbars and will scroll its content when the user
018: * uses the scrollbars.
019: *
020: *
021: * <p>There are two ways to use the ScrolledComposite:
022: *
023: * <p>
024: * 1) Set the size of the control that is being scrolled and the ScrolledComposite
025: * will show scrollbars when the contained control can not be fully seen.
026: *
027: * 2) The second way imitates the way a browser would work. Set the minimum size of
028: * the control and the ScrolledComposite will show scroll bars if the visible area is
029: * less than the minimum size of the control and it will expand the size of the control
030: * if the visible area is greater than the minimum size. This requires invoking
031: * both setMinWidth(), setMinHeight() and setExpandHorizontal(), setExpandVertical().
032: *
033: * <code><pre>
034: * public static void main (String [] args) {
035: * Display display = new Display ();
036: * Color red = display.getSystemColor(SWT.COLOR_RED);
037: * Color blue = display.getSystemColor(SWT.COLOR_BLUE);
038: * Shell shell = new Shell (display);
039: * shell.setLayout(new FillLayout());
040: *
041: * // set the size of the scrolled content - method 1
042: * final ScrolledComposite sc1 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
043: * final Composite c1 = new Composite(sc1, SWT.NONE);
044: * sc1.setContent(c1);
045: * c1.setBackground(red);
046: * GridLayout layout = new GridLayout();
047: * layout.numColumns = 4;
048: * c1.setLayout(layout);
049: * Button b1 = new Button (c1, SWT.PUSH);
050: * b1.setText("first button");
051: * c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
052: *
053: * // set the minimum width and height of the scrolled content - method 2
054: * final ScrolledComposite sc2 = new ScrolledComposite(shell, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
055: * sc2.setExpandHorizontal(true);
056: * sc2.setExpandVertical(true);
057: * final Composite c2 = new Composite(sc2, SWT.NONE);
058: * sc2.setContent(c2);
059: * c2.setBackground(blue);
060: * layout = new GridLayout();
061: * layout.numColumns = 4;
062: * c2.setLayout(layout);
063: * Button b2 = new Button (c2, SWT.PUSH);
064: * b2.setText("first button");
065: * sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
066: *
067: * Button add = new Button (shell, SWT.PUSH);
068: * add.setText("add children");
069: * final int[] index = new int[]{0};
070: * add.addListener(SWT.Selection, new Listener() {
071: * public void handleEvent(Event e) {
072: * index[0]++;
073: * Button button = new Button(c1, SWT.PUSH);
074: * button.setText("button "+index[0]);
075: * // reset size of content so children can be seen - method 1
076: * c1.setSize(c1.computeSize(SWT.DEFAULT, SWT.DEFAULT));
077: * c1.layout();
078: *
079: * button = new Button(c2, SWT.PUSH);
080: * button.setText("button "+index[0]);
081: * // reset the minimum width and height so children can be seen - method 2
082: * sc2.setMinSize(c2.computeSize(SWT.DEFAULT, SWT.DEFAULT));
083: * c2.layout();
084: * }
085: * });
086: *
087: * shell.open ();
088: * while (!shell.isDisposed ()) {
089: * if (!display.readAndDispatch ()) display.sleep ();
090: * }
091: * display.dispose ();
092: * }
093: * </pre></code>
094: *
095: * <dl>
096: * <dt><b>Styles:</b><dd>H_SCROLL, V_SCROLL
097: * </dl>
098: */
099: public class ScrolledComposite extends Composite {
100:
101: Control content;
102: Listener contentListener;
103:
104: int minHeight = 0;
105: int minWidth = 0;
106: boolean expandHorizontal = false;
107: boolean expandVertical = false;
108: boolean alwaysShowScroll = false;
109:
110: /**
111: * Constructs a new instance of this class given its parent
112: * and a style value describing its behavior and appearance.
113: * <p>
114: * The style value is either one of the style constants defined in
115: * class <code>SWT</code> which is applicable to instances of this
116: * class, or must be built by <em>bitwise OR</em>'ing together
117: * (that is, using the <code>int</code> "|" operator) two or more
118: * of those <code>SWT</code> style constants. The class description
119: * lists the style constants that are applicable to the class.
120: * Style bits are also inherited from superclasses.
121: * </p>
122: *
123: * @param parent a widget which will be the parent of the new instance (cannot be null)
124: * @param style the style of widget to construct
125: *
126: * @exception IllegalArgumentException <ul>
127: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
128: * </ul>
129: * @exception SWTException <ul>
130: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
131: * </ul>
132: *
133: * @see SWT#H_SCROLL
134: * @see SWT#V_SCROLL
135: * @see #getStyle()
136: */
137: public ScrolledComposite(Composite parent, int style) {
138: super (parent, checkStyle(style));
139: super .setLayout(new ScrolledCompositeLayout());
140: ScrollBar hBar = getHorizontalBar();
141: if (hBar != null) {
142: hBar.setVisible(false);
143: hBar.addListener(SWT.Selection, new Listener() {
144: public void handleEvent(Event e) {
145: hScroll();
146: }
147: });
148: }
149:
150: ScrollBar vBar = getVerticalBar();
151: if (vBar != null) {
152: vBar.setVisible(false);
153: vBar.addListener(SWT.Selection, new Listener() {
154: public void handleEvent(Event e) {
155: vScroll();
156: }
157: });
158: }
159:
160: contentListener = new Listener() {
161: public void handleEvent(Event e) {
162: if (e.type != SWT.Resize)
163: return;
164: layout(false);
165: }
166: };
167: }
168:
169: static int checkStyle(int style) {
170: int mask = SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER
171: | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
172: return style & mask;
173: }
174:
175: /**
176: * Returns the Always Show Scrollbars flag. True if the scrollbars are
177: * always shown even if they are not required. False if the scrollbars are only
178: * visible when some part of the composite needs to be scrolled to be seen.
179: * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
180: * horizontal and vertical directions.
181: *
182: * @return the Always Show Scrollbars flag value
183: */
184: public boolean getAlwaysShowScrollBars() {
185: //checkWidget();
186: return alwaysShowScroll;
187: }
188:
189: /**
190: * Returns <code>true</code> if the content control
191: * will be expanded to fill available horizontal space.
192: *
193: * @return the receiver's horizontal expansion state
194: *
195: * @exception SWTException <ul>
196: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
197: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
198: * </ul>
199: *
200: * @since 3.2
201: */
202: public boolean getExpandHorizontal() {
203: checkWidget();
204: return expandHorizontal;
205: }
206:
207: /**
208: * Returns <code>true</code> if the content control
209: * will be expanded to fill available vertical space.
210: *
211: * @return the receiver's vertical expansion state
212: *
213: * @exception SWTException <ul>
214: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
215: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
216: * </ul>
217: *
218: * @since 3.2
219: */
220: public boolean getExpandVertical() {
221: checkWidget();
222: return expandVertical;
223: }
224:
225: /**
226: * Returns the minimum width of the content control.
227: *
228: * @return the minimum width
229: *
230: * @exception SWTException <ul>
231: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
232: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
233: * </ul>
234: *
235: * @since 3.2
236: */
237: public int getMinWidth() {
238: checkWidget();
239: return minWidth;
240: }
241:
242: /**
243: * Returns the minimum height of the content control.
244: *
245: * @return the minimum height
246: *
247: * @exception SWTException <ul>
248: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
249: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
250: * </ul>
251: *
252: * @since 3.2
253: */
254: public int getMinHeight() {
255: checkWidget();
256: return minHeight;
257: }
258:
259: /**
260: * Get the content that is being scrolled.
261: *
262: * @return the control displayed in the content area
263: */
264: public Control getContent() {
265: //checkWidget();
266: return content;
267: }
268:
269: void hScroll() {
270: if (content == null)
271: return;
272: Point location = content.getLocation();
273: ScrollBar hBar = getHorizontalBar();
274: int hSelection = hBar.getSelection();
275: content.setLocation(-hSelection, location.y);
276: }
277:
278: boolean needHScroll(Rectangle contentRect, boolean vVisible) {
279: ScrollBar hBar = getHorizontalBar();
280: if (hBar == null)
281: return false;
282:
283: Rectangle hostRect = getBounds();
284: int border = getBorderWidth();
285: hostRect.width -= 2 * border;
286: ScrollBar vBar = getVerticalBar();
287: if (vVisible && vBar != null)
288: hostRect.width -= vBar.getSize().x;
289:
290: if (!expandHorizontal && contentRect.width > hostRect.width)
291: return true;
292: if (expandHorizontal && minWidth > hostRect.width)
293: return true;
294: return false;
295: }
296:
297: boolean needVScroll(Rectangle contentRect, boolean hVisible) {
298: ScrollBar vBar = getVerticalBar();
299: if (vBar == null)
300: return false;
301:
302: Rectangle hostRect = getBounds();
303: int border = getBorderWidth();
304: hostRect.height -= 2 * border;
305: ScrollBar hBar = getHorizontalBar();
306: if (hVisible && hBar != null)
307: hostRect.height -= hBar.getSize().y;
308:
309: if (!expandVertical && contentRect.height > hostRect.height)
310: return true;
311: if (expandVertical && minHeight > hostRect.height)
312: return true;
313: return false;
314: }
315:
316: /**
317: * Return the point in the content that currently appears in the top left
318: * corner of the scrolled composite.
319: *
320: * @return the point in the content that currently appears in the top left
321: * corner of the scrolled composite. If no content has been set, this returns
322: * (0, 0).
323: *
324: * @exception SWTException <ul>
325: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
326: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
327: * </ul>
328: *
329: * @since 2.0
330: */
331: public Point getOrigin() {
332: checkWidget();
333: if (content == null)
334: return new Point(0, 0);
335: Point location = content.getLocation();
336: return new Point(-location.x, -location.y);
337: }
338:
339: /**
340: * Scrolls the content so that the specified point in the content is in the top
341: * left corner. If no content has been set, nothing will occur.
342: *
343: * Negative values will be ignored. Values greater than the maximum scroll
344: * distance will result in scrolling to the end of the scrollbar.
345: *
346: * @param origin the point on the content to appear in the top left corner
347: *
348: * @exception SWTException <ul>
349: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
350: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
351: * <li>ERROR_INVALID_ARGUMENT - value of origin is outside of content
352: * </ul>
353: * @since 2.0
354: */
355: public void setOrigin(Point origin) {
356: setOrigin(origin.x, origin.y);
357: }
358:
359: /**
360: * Scrolls the content so that the specified point in the content is in the top
361: * left corner. If no content has been set, nothing will occur.
362: *
363: * Negative values will be ignored. Values greater than the maximum scroll
364: * distance will result in scrolling to the end of the scrollbar.
365: *
366: * @param x the x coordinate of the content to appear in the top left corner
367: *
368: * @param y the y coordinate of the content to appear in the top left corner
369: *
370: * @exception SWTException <ul>
371: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
372: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
373: * </ul>
374: *
375: * @since 2.0
376: */
377: public void setOrigin(int x, int y) {
378: checkWidget();
379: if (content == null)
380: return;
381: ScrollBar hBar = getHorizontalBar();
382: if (hBar != null) {
383: hBar.setSelection(x);
384: x = -hBar.getSelection();
385: } else {
386: x = 0;
387: }
388: ScrollBar vBar = getVerticalBar();
389: if (vBar != null) {
390: vBar.setSelection(y);
391: y = -vBar.getSelection();
392: } else {
393: y = 0;
394: }
395: content.setLocation(x, y);
396: }
397:
398: /**
399: * Set the Always Show Scrollbars flag. True if the scrollbars are
400: * always shown even if they are not required. False if the scrollbars are only
401: * visible when some part of the composite needs to be scrolled to be seen.
402: * The H_SCROLL and V_SCROLL style bits are also required to enable scrollbars in the
403: * horizontal and vertical directions.
404: *
405: * @param show true to show the scrollbars even when not required, false to show scrollbars only when required
406: *
407: * @exception SWTException <ul>
408: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
409: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
410: * </ul>
411: */
412: public void setAlwaysShowScrollBars(boolean show) {
413: checkWidget();
414: if (show == alwaysShowScroll)
415: return;
416: alwaysShowScroll = show;
417: ScrollBar hBar = getHorizontalBar();
418: if (hBar != null && alwaysShowScroll)
419: hBar.setVisible(true);
420: ScrollBar vBar = getVerticalBar();
421: if (vBar != null && alwaysShowScroll)
422: vBar.setVisible(true);
423: layout(false);
424: }
425:
426: /**
427: * Set the content that will be scrolled.
428: *
429: * @param content the control to be displayed in the content area
430: *
431: * @exception SWTException <ul>
432: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
433: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
434: * </ul>
435: */
436: public void setContent(Control content) {
437: checkWidget();
438: if (this .content != null && !this .content.isDisposed()) {
439: this .content.removeListener(SWT.Resize, contentListener);
440: this .content.setBounds(new Rectangle(-200, -200, 0, 0));
441: }
442:
443: this .content = content;
444: ScrollBar vBar = getVerticalBar();
445: ScrollBar hBar = getHorizontalBar();
446: if (this .content != null) {
447: if (vBar != null) {
448: vBar.setMaximum(0);
449: vBar.setThumb(0);
450: vBar.setSelection(0);
451: }
452: if (hBar != null) {
453: hBar.setMaximum(0);
454: hBar.setThumb(0);
455: hBar.setSelection(0);
456: }
457: content.setLocation(0, 0);
458: layout(false);
459: this .content.addListener(SWT.Resize, contentListener);
460: } else {
461: if (hBar != null)
462: hBar.setVisible(alwaysShowScroll);
463: if (vBar != null)
464: vBar.setVisible(alwaysShowScroll);
465: }
466: }
467:
468: /**
469: * Configure the ScrolledComposite to resize the content object to be as wide as the
470: * ScrolledComposite when the width of the ScrolledComposite is greater than the
471: * minimum width specified in setMinWidth. If the ScrolledComposite is less than the
472: * minimum width, the content will not be resized and instead the horizontal scroll bar will be
473: * used to view the entire width.
474: * If expand is false, this behaviour is turned off. By default, this behaviour is turned off.
475: *
476: * @param expand true to expand the content control to fill available horizontal space
477: *
478: * @exception SWTException <ul>
479: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
480: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
481: * </ul>
482: */
483: public void setExpandHorizontal(boolean expand) {
484: checkWidget();
485: if (expand == expandHorizontal)
486: return;
487: expandHorizontal = expand;
488: layout(false);
489: }
490:
491: /**
492: * Configure the ScrolledComposite to resize the content object to be as tall as the
493: * ScrolledComposite when the height of the ScrolledComposite is greater than the
494: * minimum height specified in setMinHeight. If the ScrolledComposite is less than the
495: * minimum height, the content will not be resized and instead the vertical scroll bar will be
496: * used to view the entire height.
497: * If expand is false, this behaviour is turned off. By default, this behaviour is turned off.
498: *
499: * @param expand true to expand the content control to fill available vertical space
500: *
501: * @exception SWTException <ul>
502: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
503: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
504: * </ul>
505: */
506: public void setExpandVertical(boolean expand) {
507: checkWidget();
508: if (expand == expandVertical)
509: return;
510: expandVertical = expand;
511: layout(false);
512: }
513:
514: /**
515: * Sets the layout which is associated with the receiver to be
516: * the argument which may be null.
517: * <p>
518: * Note: No Layout can be set on this Control because it already
519: * manages the size and position of its children.
520: * </p>
521: *
522: * @param layout the receiver's new layout or null
523: *
524: * @exception SWTException <ul>
525: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
526: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
527: * </ul>
528: */
529: public void setLayout(Layout layout) {
530: checkWidget();
531: return;
532: }
533:
534: /**
535: * Specify the minimum height at which the ScrolledComposite will begin scrolling the
536: * content with the vertical scroll bar. This value is only relevant if
537: * setExpandVertical(true) has been set.
538: *
539: * @param height the minimum height or 0 for default height
540: *
541: * @exception SWTException <ul>
542: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
543: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
544: * </ul>
545: */
546: public void setMinHeight(int height) {
547: setMinSize(minWidth, height);
548: }
549:
550: /**
551: * Specify the minimum width and height at which the ScrolledComposite will begin scrolling the
552: * content with the horizontal scroll bar. This value is only relevant if
553: * setExpandHorizontal(true) and setExpandVertical(true) have been set.
554: *
555: * @param size the minimum size or null for the default size
556: *
557: * @exception SWTException <ul>
558: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
559: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
560: * </ul>
561: */
562: public void setMinSize(Point size) {
563: if (size == null) {
564: setMinSize(0, 0);
565: } else {
566: setMinSize(size.x, size.y);
567: }
568: }
569:
570: /**
571: * Specify the minimum width and height at which the ScrolledComposite will begin scrolling the
572: * content with the horizontal scroll bar. This value is only relevant if
573: * setExpandHorizontal(true) and setExpandVertical(true) have been set.
574: *
575: * @param width the minimum width or 0 for default width
576: * @param height the minimum height or 0 for default height
577: *
578: * @exception SWTException <ul>
579: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
580: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
581: * </ul>
582: */
583: public void setMinSize(int width, int height) {
584: checkWidget();
585: if (width == minWidth && height == minHeight)
586: return;
587: minWidth = Math.max(0, width);
588: minHeight = Math.max(0, height);
589: layout(false);
590: }
591:
592: /**
593: * Specify the minimum width at which the ScrolledComposite will begin scrolling the
594: * content with the horizontal scroll bar. This value is only relevant if
595: * setExpandHorizontal(true) has been set.
596: *
597: * @param width the minimum width or 0 for default width
598: *
599: * @exception SWTException <ul>
600: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
601: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
602: * </ul>
603: */
604: public void setMinWidth(int width) {
605: setMinSize(width, minHeight);
606: }
607:
608: void vScroll() {
609: if (content == null)
610: return;
611: Point location = content.getLocation();
612: ScrollBar vBar = getVerticalBar();
613: int vSelection = vBar.getSelection();
614: content.setLocation(location.x, -vSelection);
615: }
616: }
|