1 /*******************************************************************************
2 * Copyright (c) 2000, 2016 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.custom;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.widgets.*;
22 * Instances of this class implement a Composite that lays out its
23 * children and allows programmatic control of the layout. It draws
24 * a separator between the left and right children which can be dragged
25 * to resize the right control.
26 * CBanner is used in the workbench to layout the toolbar area and
27 * perspective switching toolbar.
29 * Note that although this class is a subclass of <code>Composite</code>,
30 * it does not make sense to set a layout on it.
33 * <dt><b>Styles:</b></dt>
35 * <dt><b>Events:</b></dt>
39 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
42 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
45 * @noextend This class is not intended to be subclassed by clients.
48 public class CBanner extends Composite {
54 boolean simple = true;
56 int[] curve = new int[0];
58 Rectangle curveRect = new Rectangle(0, 0, 0, 0);
60 int curve_indent = -2;
62 int rightWidth = SWT.DEFAULT;
63 int rightMinWidth = 0;
64 int rightMinHeight = 0;
66 boolean dragging = false;
67 int rightDragDisplacement = 0;
70 static final int OFFSCREEN = -200;
71 static final int BORDER_BOTTOM = 2;
72 static final int BORDER_TOP = 3;
73 static final int BORDER_STRIPE = 1;
74 static final int CURVE_TAIL = 200;
75 static final int BEZIER_RIGHT = 30;
76 static final int BEZIER_LEFT = 30;
77 static final int MIN_LEFT = 10;
78 static int BORDER1 = SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW;
82 * Constructs a new instance of this class given its parent
83 * and a style value describing its behavior and appearance.
85 * The style value is either one of the style constants defined in
86 * class <code>SWT</code> which is applicable to instances of this
87 * class, or must be built by <em>bitwise OR</em>'ing together
88 * (that is, using the <code>int</code> "|" operator) two or more
89 * of those <code>SWT</code> style constants. The class description
90 * lists the style constants that are applicable to the class.
91 * Style bits are also inherited from superclasses.
94 * @param parent a widget which will be the parent of the new instance (cannot be null)
95 * @param style the style of widget to construct
97 * @exception IllegalArgumentException <ul>
98 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
100 * @exception SWTException <ul>
101 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
105 public CBanner(Composite parent, int style) {
106 super(parent, checkStyle(style));
107 super.setLayout(new CBannerLayout());
108 resizeCursor = getDisplay().getSystemCursor(SWT.CURSOR_SIZEWE);
115 onMouseDown (e.x, e.y); break;
117 onMouseExit(); break;
119 onMouseMove(e.x, e.y); break;
123 onPaint(e.gc); break;
128 int[] events = new int[] {SWT.Dispose, SWT.MouseDown, SWT.MouseExit, SWT.MouseMove, SWT.MouseUp, SWT.Paint, SWT.Resize};
129 for (int i = 0; i < events.length; i++) {
130 addListener(events[i], listener);
133 static int[] bezier(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, int count) {
134 // The parametric equations for a Bezier curve for x[t] and y[t] where 0 <= t <=1 are:
135 // x[t] = x0+3(x1-x0)t+3(x0+x2-2x1)t^2+(x3-x0+3x1-3x2)t^3
136 // y[t] = y0+3(y1-y0)t+3(y0+y2-2y1)t^2+(y3-y0+3y1-3y2)t^3
138 double a1 = 3*(x1 - x0);
139 double a2 = 3*(x0 + x2 - 2*x1);
140 double a3 = x3 - x0 + 3*x1 - 3*x2;
142 double b1 = 3*(y1 - y0);
143 double b2 = 3*(y0 + y2 - 2*y1);
144 double b3 = y3 - y0 + 3*y1 - 3*y2;
146 int[] polygon = new int[2*count + 2];
147 for (int i = 0; i <= count; i++) {
148 double t = (double)i / (double)count;
149 polygon[2*i] = (int)(a0 + a1*t + a2*t*t + a3*t*t*t);
150 polygon[2*i + 1] = (int)(b0 + b1*t + b2*t*t + b3*t*t*t);
154 static int checkStyle (int style) {
158 * This class was not intended to be subclassed but this restriction
159 * cannot be enforced without breaking backward compatibility.
161 //protected void checkSubclass () {
162 // String name = getClass ().getName ();
163 // int index = name.lastIndexOf ('.');
164 // if (!name.substring (0, index + 1).equals ("org.eclipse.swt.custom.")) {
165 // SWT.error (SWT.ERROR_INVALID_SUBCLASS);
169 * Returns the Control that appears on the bottom side of the banner.
171 * @return the control that appears on the bottom side of the banner or null
173 * @exception SWTException <ul>
174 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
175 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
180 public Control getBottom() {
185 public Rectangle getClientArea() {
186 return new Rectangle(0, 0, 0, 0);
190 * Returns the Control that appears on the left side of the banner.
192 * @return the control that appears on the left side of the banner or null
194 * @exception SWTException <ul>
195 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
196 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
201 public Control getLeft() {
207 * Returns the Control that appears on the right side of the banner.
209 * @return the control that appears on the right side of the banner or null
211 * @exception SWTException <ul>
212 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
213 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
218 public Control getRight() {
223 * Returns the minimum size of the control that appears on the right of the banner.
225 * @return the minimum size of the control that appears on the right of the banner
229 public Point getRightMinimumSize() {
231 return new Point(rightMinWidth, rightMinHeight);
234 * Returns the width of the control that appears on the right of the banner.
236 * @return the width of the control that appears on the right of the banner
240 public int getRightWidth() {
242 if (right == null) return 0;
243 if (rightWidth == SWT.DEFAULT) {
244 Point size = right.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
250 * Returns <code>true</code> if the CBanner is rendered
251 * with a simple, traditional shape.
253 * @return <code>true</code> if the CBanner is rendered with a simple shape
257 public boolean getSimple() {
261 void onDispose(Event event) {
262 removeListener(SWT.Dispose, listener);
263 notifyListeners(SWT.Dispose, event);
264 event.type = SWT.None;
271 void onMouseDown (int x, int y) {
272 if (curveRect.contains(x, y)) {
274 rightDragDisplacement = curveStart - x + curve_width - curve_indent;
278 if (!dragging) setCursor(null);
280 void onMouseMove(int x, int y) {
282 Point size = getSize();
283 if (!(0 < x && x < size.x)) return;
284 rightWidth = Math.max(0, size.x - x - rightDragDisplacement);
285 if (rightMinWidth == SWT.DEFAULT) {
286 Point minSize = right.computeSize(rightMinWidth, rightMinHeight);
287 rightWidth = Math.max(minSize.x, rightWidth);
289 rightWidth = Math.max(rightMinWidth, rightWidth);
294 if (curveRect.contains(x, y)) {
295 setCursor(resizeCursor);
303 void onPaint(GC gc) {
304 // Useful for debugging paint problems
306 // Point size = getSize();
307 // gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
308 // gc.fillRectangle(-10, -10, size.x+20, size.y+20);
310 if (left == null && right == null) return;
311 Point size = getSize();
312 Color border1 = getDisplay().getSystemColor(BORDER1);
313 if (bottom != null) {
314 int y = bottom.getBounds().y - BORDER_STRIPE - 1;
315 gc.setForeground(border1);
316 gc.drawLine(0, y, size.x, y);
318 if (left == null || right == null) return;
319 int[] line1 = new int[curve.length+6];
322 line1[index++] = x + 1;
323 line1[index++] = size.y - BORDER_STRIPE;
324 for (int i = 0; i < curve.length/2; i++) {
325 line1[index++]=x+curve[2*i];
326 line1[index++]=curve[2*i+1];
328 line1[index++] = x + curve_width;
330 line1[index++] = size.x;
333 Color background = getBackground();
335 if (getDisplay().getDepth() >= 15) {
337 int[] line2 = new int[line1.length];
339 for (int i = 0; i < line1.length/2; i++) {
340 line2[index] = line1[index++] - 1;
341 line2[index] = line1[index++];
343 int[] line3 = new int[line1.length];
345 for (int i = 0; i < line1.length/2; i++) {
346 line3[index] = line1[index++] + 1;
347 line3[index] = line1[index++];
349 RGB from = border1.getRGB();
350 RGB to = background.getRGB();
351 int red = from.red + 3*(to.red - from.red)/4;
352 int green = from.green + 3*(to.green - from.green)/4;
353 int blue = from.blue + 3*(to.blue - from.blue)/4;
354 Color color = new Color(getDisplay(), red, green, blue);
355 gc.setForeground(color);
356 gc.drawPolyline(line2);
357 gc.drawPolyline(line3);
360 // draw tail fading to background
361 int x1 = Math.max(0, curveStart - CURVE_TAIL);
362 gc.setForeground(background);
363 gc.setBackground(border1);
364 gc.fillGradientRectangle(x1, size.y - BORDER_STRIPE, curveStart-x1+1, 1, false);
367 int x1 = Math.max(0, curveStart - CURVE_TAIL);
368 gc.setForeground(border1);
369 gc.drawLine(x1, size.y - BORDER_STRIPE, curveStart+1, size.y - BORDER_STRIPE);
373 gc.setForeground(border1);
374 gc.drawPolyline(line1);
378 updateCurve(getSize().y);
381 * Set the control that appears on the bottom side of the banner.
382 * The bottom control is optional. Setting the bottom control to null will remove it from
383 * the banner - however, the creator of the control must dispose of the control.
385 * @param control the control to be displayed on the bottom or null
387 * @exception SWTException <ul>
388 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
389 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
390 * <li>ERROR_INVALID_ARGUMENT - if the bottom control was not created as a child of the receiver</li>
395 public void setBottom(Control control) {
397 if (control != null && control.getParent() != this) {
398 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
400 if (bottom != null && !bottom.isDisposed()) {
401 Point size = bottom.getSize();
402 bottom.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
408 * Sets the layout which is associated with the receiver to be
409 * the argument which may be null.
411 * Note: No Layout can be set on this Control because it already
412 * manages the size and position of its children.
415 * @param layout the receiver's new layout or null
417 * @exception SWTException <ul>
418 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
419 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
423 public void setLayout (Layout layout) {
429 * Set the control that appears on the left side of the banner.
430 * The left control is optional. Setting the left control to null will remove it from
431 * the banner - however, the creator of the control must dispose of the control.
433 * @param control the control to be displayed on the left or null
435 * @exception SWTException <ul>
436 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
437 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
438 * <li>ERROR_INVALID_ARGUMENT - if the left control was not created as a child of the receiver</li>
443 public void setLeft(Control control) {
445 if (control != null && control.getParent() != this) {
446 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
448 if (left != null && !left.isDisposed()) {
449 Point size = left.getSize();
450 left.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
456 * Set the control that appears on the right side of the banner.
457 * The right control is optional. Setting the right control to null will remove it from
458 * the banner - however, the creator of the control must dispose of the control.
460 * @param control the control to be displayed on the right or null
462 * @exception SWTException <ul>
463 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
464 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
465 * <li>ERROR_INVALID_ARGUMENT - if the right control was not created as a child of the receiver</li>
470 public void setRight(Control control) {
472 if (control != null && control.getParent() != this) {
473 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
475 if (right != null && !right.isDisposed()) {
476 Point size = right.getSize();
477 right.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
483 * Set the minimum height of the control that appears on the right side of the banner.
485 * @param size the minimum size of the control on the right
487 * @exception SWTException <ul>
488 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
489 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
490 * <li>ERROR_INVALID_ARGUMENT - if the size is null or the values of size are less than SWT.DEFAULT</li>
495 public void setRightMinimumSize(Point size) {
497 if (size == null || size.x < SWT.DEFAULT || size.y < SWT.DEFAULT) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
498 rightMinWidth = size.x;
499 rightMinHeight = size.y;
503 * Set the width of the control that appears on the right side of the banner.
505 * @param width the width of the control on the right
507 * @exception SWTException <ul>
508 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
509 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
510 * <li>ERROR_INVALID_ARGUMENT - if width is less than SWT.DEFAULT</li>
515 public void setRightWidth(int width) {
517 if (width < SWT.DEFAULT) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
522 * Sets the shape that the CBanner will use to render itself.
524 * @param simple <code>true</code> if the CBanner should render itself in a simple, traditional style
526 * @exception SWTException <ul>
527 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
528 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
533 public void setSimple(boolean simple) {
535 if (this.simple != simple) {
536 this.simple = simple;
544 updateCurve(getSize().y);
549 void updateCurve(int height) {
550 int h = height - BORDER_STRIPE;
552 curve = new int[] {0,h, 1,h, 2,h-1, 3,h-2,
555 curve = bezier(0, h+1, BEZIER_LEFT, h+1,
556 curve_width-BEZIER_RIGHT, 0, curve_width, 0,