1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.custom.CBanner;
14 
15 import java.lang.all;
16 
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.SWTException;
19 import org.eclipse.swt.graphics.Color;
20 import org.eclipse.swt.graphics.Cursor;
21 import org.eclipse.swt.graphics.GC;
22 import org.eclipse.swt.graphics.Point;
23 import org.eclipse.swt.graphics.RGB;
24 import org.eclipse.swt.graphics.Rectangle;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.swt.widgets.Control;
27 import org.eclipse.swt.widgets.Event;
28 import org.eclipse.swt.widgets.Layout;
29 import org.eclipse.swt.widgets.Listener;
30 import org.eclipse.swt.custom.CBannerLayout;
31 
32 
33 
34 /**
35  * Instances of this class implement a Composite that lays out its
36  * children and allows programmatic control of the layout. It draws
37  * a separator between the left and right children which can be dragged
38  * to resize the right control.
39  * CBanner is used in the workbench to layout the toolbar area and
40  * perspective switching toolbar.
41  * <p>
42  * Note that although this class is a subclass of <code>Composite</code>,
43  * it does not make sense to set a layout on it.
44  * </p><p>
45  * <dl>
46  * <dt><b>Styles:</b></dt>
47  * <dd>NONE</dd>
48  * <dt><b>Events:</b></dt>
49  * <dd>(None)</dd>
50  * </dl>
51  * <p>
52  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
53  * </p>
54  *
55  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
56  *
57  * @since 3.0
58  */
59 
60 public class CBanner : Composite {
61 
62     Control left;
63     Control right;
64     Control bottom;
65 
66     bool simple = true;
67 
68     int[] curve;
69     int curveStart = 0;
70     Rectangle curveRect;
71     int curve_width = 5;
72     int curve_indent = -2;
73 
74     int rightWidth = SWT.DEFAULT;
75     int rightMinWidth = 0;
76     int rightMinHeight = 0;
77     Cursor resizeCursor;
78     bool dragging = false;
79     int rightDragDisplacement = 0;
80 
81     static const int OFFSCREEN = -200;
82     static const int BORDER_BOTTOM = 2;
83     static const int BORDER_TOP = 3;
84     static const int BORDER_STRIPE = 1;
85     static const int CURVE_TAIL = 200;
86     static const int BEZIER_RIGHT = 30;
87     static const int BEZIER_LEFT = 30;
88     static const int MIN_LEFT = 10;
89     static int BORDER1 = SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW;
90 
91 
92 /**
93  * Constructs a new instance of this class given its parent
94  * and a style value describing its behavior and appearance.
95  * <p>
96  * The style value is either one of the style constants defined in
97  * class <code>SWT</code> which is applicable to instances of this
98  * class, or must be built by <em>bitwise OR</em>'ing together
99  * (that is, using the <code>int</code> "|" operator) two or more
100  * of those <code>SWT</code> style constants. The class description
101  * lists the style constants that are applicable to the class.
102  * Style bits are also inherited from superclasses.
103  * </p>
104  *
105  * @param parent a widget which will be the parent of the new instance (cannot be null)
106  * @param style the style of widget to construct
107  *
108  * @exception IllegalArgumentException <ul>
109  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
110  * </ul>
111  * @exception SWTException <ul>
112  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
113  * </ul>
114  *
115  */
116 public this(Composite parent, int style) {
117     curveRect = new Rectangle(0, 0, 0, 0);
118     super(parent, checkStyle(style));
119     super.setLayout(new CBannerLayout());
120     resizeCursor = new Cursor(getDisplay(), SWT.CURSOR_SIZEWE);
121 
122     Listener listener = new class() Listener {
123         public void handleEvent(Event e) {
124             switch (e.type) {
125                 case SWT.Dispose:
126                     onDispose(); break;
127                 case SWT.MouseDown:
128                     onMouseDown (e.x, e.y); break;
129                 case SWT.MouseExit:
130                     onMouseExit(); break;
131                 case SWT.MouseMove:
132                     onMouseMove(e.x, e.y); break;
133                 case SWT.MouseUp:
134                     onMouseUp(); break;
135                 case SWT.Paint:
136                     onPaint(e.gc); break;
137                 case SWT.Resize:
138                     onResize(); break;
139                 default:
140             }
141         }
142     };
143     int[] events = [SWT.Dispose, SWT.MouseDown, SWT.MouseExit, SWT.MouseMove, SWT.MouseUp, SWT.Paint, SWT.Resize];
144     for (int i = 0; i < events.length; i++) {
145         addListener(events[i], listener);
146     }
147 }
148 static int[] bezier(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, int count) {
149     // The parametric equations for a Bezier curve for x[t] and y[t] where  0 <= t <=1 are:
150     // x[t] = x0+3(x1-x0)t+3(x0+x2-2x1)t^2+(x3-x0+3x1-3x2)t^3
151     // y[t] = y0+3(y1-y0)t+3(y0+y2-2y1)t^2+(y3-y0+3y1-3y2)t^3
152     double a0 = x0;
153     double a1 = 3*(x1 - x0);
154     double a2 = 3*(x0 + x2 - 2*x1);
155     double a3 = x3 - x0 + 3*x1 - 3*x2;
156     double b0 = y0;
157     double b1 = 3*(y1 - y0);
158     double b2 = 3*(y0 + y2 - 2*y1);
159     double b3 = y3 - y0 + 3*y1 - 3*y2;
160 
161     int[] polygon = new int[2*count + 2];
162     for (int i = 0; i <= count; i++) {
163         double t = cast(double)i / cast(double)count;
164         polygon[2*i] = cast(int)(a0 + a1*t + a2*t*t + a3*t*t*t);
165         polygon[2*i + 1] = cast(int)(b0 + b1*t + b2*t*t + b3*t*t*t);
166     }
167     return polygon;
168 }
169 static int checkStyle (int style) {
170     return SWT.NONE;
171 }
172 /**
173 * Returns the Control that appears on the bottom side of the banner.
174 *
175 * @return the control that appears on the bottom side of the banner or null
176 *
177 * @exception SWTException <ul>
178 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
179 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
180 * </ul>
181 *
182 * @since 3.0
183 */
184 public Control getBottom() {
185     checkWidget();
186     return bottom;
187 }
188 public override Rectangle getClientArea() {
189     return new Rectangle(0, 0, 0, 0);
190 }
191 
192 /**
193 * Returns the Control that appears on the left side of the banner.
194 *
195 * @return the control that appears on the left side of the banner or null
196 *
197 * @exception SWTException <ul>
198 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
199 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
200 * </ul>
201 *
202 * @since 3.0
203 */
204 public Control getLeft() {
205     checkWidget();
206     return left;
207 }
208 
209 /**
210 * Returns the Control that appears on the right side of the banner.
211 *
212 * @return the control that appears on the right side of the banner or null
213 *
214 * @exception SWTException <ul>
215 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
216 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
217 * </ul>
218 *
219 * @since 3.0
220 */
221 public Control getRight() {
222     checkWidget();
223     return right;
224 }
225 /**
226  * Returns the minimum size of the control that appears on the right of the banner.
227  *
228  * @return the minimum size of the control that appears on the right of the banner
229  *
230  * @since 3.1
231  */
232 public Point getRightMinimumSize() {
233     checkWidget();
234     return new Point(rightMinWidth, rightMinHeight);
235 }
236 /**
237  * Returns the width of the control that appears on the right of the banner.
238  *
239  * @return the width of the control that appears on the right of the banner
240  *
241  * @since 3.0
242  */
243 public int getRightWidth() {
244     checkWidget();
245     if (right is null) return 0;
246     if (rightWidth is SWT.DEFAULT) {
247         Point size = right.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
248         return size.x;
249     }
250     return rightWidth;
251 }
252 /**
253  * Returns <code>true</code> if the CBanner is rendered
254  * with a simple, traditional shape.
255  *
256  * @return <code>true</code> if the CBanner is rendered with a simple shape
257  *
258  * @since 3.0
259  */
260 public bool getSimple() {
261     checkWidget();
262     return simple;
263 }
264 void onDispose() {
265     if (resizeCursor !is null) resizeCursor.dispose();
266     resizeCursor = null;
267     left = null;
268     right = null;
269     bottom = null;
270 }
271 void onMouseDown (int x, int y) {
272     if (curveRect.contains(x, y)) {
273         dragging = true;
274         rightDragDisplacement = curveStart - x + curve_width - curve_indent;
275     }
276 }
277 void onMouseExit() {
278     if (!dragging) setCursor(null);
279 }
280 void onMouseMove(int x, int y) {
281     if (dragging) {
282         Point size = getSize();
283         if (!(0 < x && x < size.x)) return;
284         rightWidth = Math.max(0, size.x - x - rightDragDisplacement);
285         if (rightMinWidth is SWT.DEFAULT) {
286             Point minSize = right.computeSize(rightMinWidth, rightMinHeight);
287             rightWidth = Math.max(minSize.x, rightWidth);
288         } else {
289             rightWidth = Math.max(rightMinWidth, rightWidth);
290         }
291         layout(false);
292         return;
293     }
294     if (curveRect.contains(x, y)) {
295         setCursor(resizeCursor);
296     } else {
297         setCursor(null);
298     }
299 }
300 void onMouseUp () {
301     dragging = false;
302 }
303 void onPaint(GC gc) {
304 //   Useful for debugging paint problems
305 //  {
306 //  Point size = getSize();
307 //  gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
308 //  gc.fillRectangle(-10, -10, size.x+20, size.y+20);
309 //  }
310     if (left is null && right is null) return;
311     Point size = getSize();
312     Color border1 = getDisplay().getSystemColor(BORDER1);
313     if (bottom !is null) {
314         int y = bottom.getBounds().y - BORDER_STRIPE - 1;
315         gc.setForeground(border1);
316         gc.drawLine(0, y, size.x, y);
317     }
318     if (left is null || right is null) return;
319     int[] line1 = new int[curve.length+6];
320     int index = 0;
321     int x = curveStart;
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];
327     }
328     line1[index++] = x + curve_width;
329     line1[index++] = 0;
330     line1[index++] = size.x;
331     line1[index++] = 0;
332 
333     Color background = getBackground();
334 
335     if (getDisplay().getDepth() >= 15) {
336         // Anti- aliasing
337         int[] line2 = new int[line1.length];
338         index = 0;
339         for (int i = 0; i < line1.length/2; i++) {
340             line2[index] = line1[index++]  - 1;
341             line2[index] = line1[index++];
342         }
343         int[] line3 = new int[line1.length];
344         index = 0;
345         for (int i = 0; i < line1.length/2; i++) {
346             line3[index] = line1[index++] + 1;
347             line3[index] = line1[index++];
348         }
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);
358         color.dispose();
359 
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);
365     } else {
366         // draw solid tail
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);
370     }
371 
372     // draw border
373     gc.setForeground(border1);
374     gc.drawPolyline(line1);
375 }
376 
377 void onResize() {
378     updateCurve(getSize().y);
379 }
380 /**
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.
384 *
385 * @param control the control to be displayed on the bottom or null
386 *
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>
391 * </ul>
392 *
393 * @since 3.0
394 */
395 public void setBottom(Control control) {
396     checkWidget();
397     if (control !is null && control.getParent() !is this) {
398         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
399     }
400     if (bottom !is null && !bottom.isDisposed()) {
401         Point size = bottom.getSize();
402         bottom.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
403     }
404     bottom = control;
405     layout(false);
406 }
407 /**
408  * Sets the layout which is associated with the receiver to be
409  * the argument which may be null.
410  * <p>
411  * Note: No Layout can be set on this Control because it already
412  * manages the size and position of its children.
413  * </p>
414  *
415  * @param layout the receiver's new layout or null
416  *
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>
420  * </ul>
421  */
422 public override void setLayout (Layout layout) {
423     checkWidget();
424     return;
425 }
426 
427 /**
428 * Set the control that appears on the left side of the banner.
429 * The left control is optional.  Setting the left control to null will remove it from
430 * the banner - however, the creator of the control must dispose of the control.
431 *
432 * @param control the control to be displayed on the left or null
433 *
434 * @exception SWTException <ul>
435 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
436 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
437 *    <li>ERROR_INVALID_ARGUMENT - if the left control was not created as a child of the receiver</li>
438 * </ul>
439 *
440 * @since 3.0
441 */
442 public void setLeft(Control control) {
443     checkWidget();
444     if (control !is null && control.getParent() !is this) {
445         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
446     }
447     if (left !is null && !left.isDisposed()) {
448         Point size = left.getSize();
449         left.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
450     }
451     left = control;
452     layout(false);
453 }
454 /**
455 * Set the control that appears on the right side of the banner.
456 * The right control is optional.  Setting the right control to null will remove it from
457 * the banner - however, the creator of the control must dispose of the control.
458 *
459 * @param control the control to be displayed on the right or null
460 *
461 * @exception SWTException <ul>
462 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
463 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
464 *    <li>ERROR_INVALID_ARGUMENT - if the right control was not created as a child of the receiver</li>
465 * </ul>
466 *
467 * @since 3.0
468 */
469 public void setRight(Control control) {
470     checkWidget();
471     if (control !is null && control.getParent() !is this) {
472         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
473     }
474     if (right !is null && !right.isDisposed()) {
475         Point size = right.getSize();
476         right.setLocation(OFFSCREEN - size.x, OFFSCREEN - size.y);
477     }
478     right = control;
479     layout(false);
480 }
481 /**
482  * Set the minimum height of the control that appears on the right side of the banner.
483  *
484  * @param size the minimum size of the control on the right
485  *
486  * @exception SWTException <ul>
487  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
488  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
489  *    <li>ERROR_INVALID_ARGUMENT - if the size is null or the values of size are less than SWT.DEFAULT</li>
490  * </ul>
491  *
492  * @since 3.1
493  */
494 public void setRightMinimumSize(Point size) {
495     checkWidget();
496     if (size is null || size.x < SWT.DEFAULT || size.y < SWT.DEFAULT) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
497     rightMinWidth = size.x;
498     rightMinHeight = size.y;
499     layout(false);
500 }
501 /**
502  * Set the width of the control that appears on the right side of the banner.
503  *
504  * @param width the width of the control on the right
505  *
506  * @exception SWTException <ul>
507  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
508  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
509  *    <li>ERROR_INVALID_ARGUMENT - if width is less than SWT.DEFAULT</li>
510  * </ul>
511  *
512  * @since 3.0
513  */
514 public void setRightWidth(int width) {
515     checkWidget();
516     if (width < SWT.DEFAULT) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
517     rightWidth = width;
518     layout(false);
519 }
520 /**
521  * Sets the shape that the CBanner will use to render itself.
522  *
523  * @param simple <code>true</code> if the CBanner should render itself in a simple, traditional style
524  *
525  * @exception SWTException <ul>
526  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
527  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
528  * </ul>
529  *
530  * @since 3.0
531  */
532 public void setSimple(bool simple) {
533     checkWidget();
534     if (this.simple !is simple) {
535         this.simple = simple;
536         if (simple) {
537             curve_width = 5;
538             curve_indent = -2;
539         } else {
540             curve_width = 50;
541             curve_indent = 5;
542         }
543         updateCurve(getSize().y);
544         layout(false);
545         redraw();
546     }
547 }
548 void updateCurve(int height) {
549     int h = height - BORDER_STRIPE;
550     if (simple) {
551         curve = [0,h, 1,h, 2,h-1, 3,h-2,
552                                    3,2, 4,1, 5,0];
553     } else {
554         curve = bezier(0, h+1, BEZIER_LEFT, h+1,
555                              curve_width-BEZIER_RIGHT, 0, curve_width, 0,
556                              curve_width);
557     }
558 }
559 }