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 }