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.CTabFolder; 14 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.SWTException; 17 import org.eclipse.swt.accessibility.ACC; 18 import org.eclipse.swt.accessibility.Accessible; 19 import org.eclipse.swt.accessibility.AccessibleAdapter; 20 import org.eclipse.swt.accessibility.AccessibleControlAdapter; 21 import org.eclipse.swt.accessibility.AccessibleControlEvent; 22 import org.eclipse.swt.accessibility.AccessibleEvent; 23 import org.eclipse.swt.events.SelectionAdapter; 24 import org.eclipse.swt.events.SelectionEvent; 25 import org.eclipse.swt.events.SelectionListener; 26 import org.eclipse.swt.graphics.Color; 27 import org.eclipse.swt.graphics.Font; 28 import org.eclipse.swt.graphics.FontData; 29 import org.eclipse.swt.graphics.GC; 30 import org.eclipse.swt.graphics.Image; 31 import org.eclipse.swt.graphics.Point; 32 import org.eclipse.swt.graphics.RGB; 33 import org.eclipse.swt.graphics.Rectangle; 34 import org.eclipse.swt.graphics.Region; 35 import org.eclipse.swt.widgets.Composite; 36 import org.eclipse.swt.widgets.Control; 37 import org.eclipse.swt.widgets.Display; 38 import org.eclipse.swt.widgets.Event; 39 import org.eclipse.swt.widgets.Layout; 40 import org.eclipse.swt.widgets.Listener; 41 import org.eclipse.swt.widgets.Menu; 42 import org.eclipse.swt.widgets.MenuItem; 43 import org.eclipse.swt.widgets.TypedListener; 44 import org.eclipse.swt.custom.CTabItem; 45 import org.eclipse.swt.custom.CTabFolder2Listener; 46 import org.eclipse.swt.custom.CTabFolderListener; 47 import org.eclipse.swt.custom.CTabFolderLayout; 48 import org.eclipse.swt.custom.CTabFolderEvent; 49 50 import java.lang.all; 51 import java.nonstandard.UnsafeUtf; 52 53 /** 54 * 55 * Instances of this class implement the notebook user interface 56 * metaphor. It allows the user to select a notebook page from 57 * set of pages. 58 * <p> 59 * The item children that may be added to instances of this class 60 * must be of type <code>CTabItem</code>. 61 * <code>Control</code> children are created and then set into a 62 * tab item using <code>CTabItem#setControl</code>. 63 * </p><p> 64 * Note that although this class is a subclass of <code>Composite</code>, 65 * it does not make sense to set a layout on it. 66 * </p><p> 67 * <dl> 68 * <dt><b>Styles:</b></dt> 69 * <dd>CLOSE, TOP, BOTTOM, FLAT, BORDER, SINGLE, MULTI</dd> 70 * <dt><b>Events:</b></dt> 71 * <dd>Selection</dd> 72 * <dd>"CTabFolder2"</dd> 73 * </dl> 74 * <p> 75 * Note: Only one of the styles TOP and BOTTOM 76 * may be specified. 77 * </p><p> 78 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 79 * </p> 80 * 81 * @see <a href="http://www.eclipse.org/swt/snippets/#ctabfolder">CTabFolder, CTabItem snippets</a> 82 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample</a> 83 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 84 */ 85 86 public class CTabFolder : Composite { 87 88 /** 89 * marginWidth specifies the number of pixels of horizontal margin 90 * that will be placed along the left and right edges of the form. 91 * 92 * The default value is 0. 93 */ 94 public int marginWidth = 0; 95 /** 96 * marginHeight specifies the number of pixels of vertical margin 97 * that will be placed along the top and bottom edges of the form. 98 * 99 * The default value is 0. 100 */ 101 public int marginHeight = 0; 102 103 /** 104 * A multiple of the tab height that specifies the minimum width to which a tab 105 * will be compressed before scrolling arrows are used to navigate the tabs. 106 * 107 * NOTE This field is badly named and can not be fixed for backwards compatibility. 108 * It should not be capitalized. 109 * 110 * @deprecated This field is no longer used. See setMinimumCharacters(int) 111 */ 112 public int MIN_TAB_WIDTH = 4; 113 114 /** 115 * Color of innermost line of drop shadow border. 116 * 117 * NOTE This field is badly named and can not be fixed for backwards compatibility. 118 * It should be capitalized. 119 * 120 * @deprecated drop shadow border is no longer drawn in 3.0 121 */ 122 public static RGB borderInsideRGB; 123 /** 124 * Color of middle line of drop shadow border. 125 * 126 * NOTE This field is badly named and can not be fixed for backwards compatibility. 127 * It should be capitalized. 128 * 129 * @deprecated drop shadow border is no longer drawn in 3.0 130 */ 131 public static RGB borderMiddleRGB; 132 /** 133 * Color of outermost line of drop shadow border. 134 * 135 * NOTE This field is badly named and can not be fixed for backwards compatibility. 136 * It should be capitalized. 137 * 138 * @deprecated drop shadow border is no longer drawn in 3.0 139 */ 140 public static RGB borderOutsideRGB; 141 142 /* sizing, positioning */ 143 int xClient, yClient; 144 bool onBottom = false; 145 bool single = false; 146 bool simple = true; 147 int fixedTabHeight = SWT.DEFAULT; 148 int tabHeight; 149 int minChars = 20; 150 151 /* item management */ 152 CTabItem[] items; 153 int firstIndex = -1; // index of the left most visible tab. 154 int selectedIndex = -1; 155 int[] priority; 156 bool mru = false; 157 Listener listener; 158 159 /* External Listener management */ 160 CTabFolder2Listener[] folderListeners; 161 // support for deprecated listener mechanism 162 CTabFolderListener[] tabListeners; 163 164 /* Selected item appearance */ 165 Image selectionBgImage; 166 Color[] selectionGradientColors; 167 int[] selectionGradientPercents; 168 bool selectionGradientVertical; 169 Color selectionForeground; 170 Color selectionBackground; //selection fade end 171 Color selectionFadeStart; 172 173 Color selectionHighlightGradientBegin = null; //null is no highlight 174 //Although we are given new colours all the time to show different states (active, etc), 175 //some of which may have a highlight and some not, we'd like to retain the highlight colours 176 //as a cache so that we can reuse them if we're again told to show the highlight. 177 //We are relying on the fact that only one tab state usually gets a highlight, so only 178 //a single cache is required. If that happens to not be true, cache simply becomes less effective, 179 //but we don't leak colours. 180 Color[] selectionHighlightGradientColorsCache = null; //null is a legal value, check on access 181 182 /* Unselected item appearance */ 183 Image bgImage; 184 Color[] gradientColors; 185 int[] gradientPercents; 186 bool gradientVertical; 187 bool showUnselectedImage = true; 188 189 static Color borderColor; 190 191 // close, min/max and chevron buttons 192 bool showClose = false; 193 bool showUnselectedClose = true; 194 195 Rectangle chevronRect; 196 int chevronImageState = NORMAL; 197 bool showChevron = false; 198 Menu showMenu; 199 200 bool showMin = false; 201 Rectangle minRect; 202 bool minimized = false; 203 int minImageState = NORMAL; 204 205 bool showMax = false; 206 Rectangle maxRect; 207 bool maximized = false; 208 int maxImageState = NORMAL; 209 210 Control topRight; 211 Rectangle topRightRect; 212 int topRightAlignment = SWT.RIGHT; 213 214 // borders and shapes 215 int borderLeft = 0; 216 int borderRight = 0; 217 int borderTop = 0; 218 int borderBottom = 0; 219 220 int highlight_margin = 0; 221 int highlight_header = 0; 222 223 int[] curve; 224 int[] topCurveHighlightStart; 225 int[] topCurveHighlightEnd; 226 int curveWidth = 0; 227 int curveIndent = 0; 228 229 // when disposing CTabFolder, don't try to layout the items or 230 // change the selection as each child is destroyed. 231 bool inDispose = false; 232 233 // keep track of size changes in order to redraw only affected area 234 // on Resize 235 Point oldSize; 236 Font oldFont; 237 238 // internal constants 239 static const int DEFAULT_WIDTH = 64; 240 static const int DEFAULT_HEIGHT = 64; 241 static const int BUTTON_SIZE = 18; 242 243 static const int[] TOP_LEFT_CORNER = [0,6, 1,5, 1,4, 4,1, 5,1, 6,0]; 244 245 //TOP_LEFT_CORNER_HILITE is laid out in reverse (ie. top to bottom) 246 //so can fade in same direction as right swoop curve 247 static const int[] TOP_LEFT_CORNER_HILITE = [5,2, 4,2, 3,3, 2,4, 2,5, 1,6]; 248 249 static const int[] TOP_RIGHT_CORNER = [-6,0, -5,1, -4,1, -1,4, -1,5, 0,6]; 250 static const int[] BOTTOM_LEFT_CORNER = [0,-6, 1,-5, 1,-4, 4,-1, 5,-1, 6,0]; 251 static const int[] BOTTOM_RIGHT_CORNER = [-6,0, -5,-1, -4,-1, -1,-4, -1,-5, 0,-6]; 252 253 static const int[] SIMPLE_TOP_LEFT_CORNER = [0,2, 1,1, 2,0]; 254 static const int[] SIMPLE_TOP_RIGHT_CORNER = [-2,0, -1,1, 0,2]; 255 static const int[] SIMPLE_BOTTOM_LEFT_CORNER = [0,-2, 1,-1, 2,0]; 256 static const int[] SIMPLE_BOTTOM_RIGHT_CORNER = [-2,0, -1,-1, 0,-2]; 257 static const int[] SIMPLE_UNSELECTED_INNER_CORNER = [0,0]; 258 259 static const int[] TOP_LEFT_CORNER_BORDERLESS = [0,6, 1,5, 1,4, 4,1, 5,1, 6,0]; 260 static const int[] TOP_RIGHT_CORNER_BORDERLESS = [-7,0, -6,1, -5,1, -2,4, -2,5, -1,6]; 261 static const int[] BOTTOM_LEFT_CORNER_BORDERLESS = [0,-6, 1,-6, 1,-5, 2,-4, 4,-2, 5,-1, 6,-1, 6,0]; 262 static const int[] BOTTOM_RIGHT_CORNER_BORDERLESS = [-7,0, -7,-1, -6,-1, -5,-2, -3,-4, -2,-5, -2,-6, -1,-6]; 263 264 static const int[] SIMPLE_TOP_LEFT_CORNER_BORDERLESS = [0,2, 1,1, 2,0]; 265 static const int[] SIMPLE_TOP_RIGHT_CORNER_BORDERLESS= [-3,0, -2,1, -1,2]; 266 static const int[] SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS = [0,-3, 1,-2, 2,-1, 3,0]; 267 static const int[] SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS = [-4,0, -3,-1, -2,-2, -1,-3]; 268 269 static const int SELECTION_FOREGROUND = SWT.COLOR_LIST_FOREGROUND; 270 static const int SELECTION_BACKGROUND = SWT.COLOR_LIST_BACKGROUND; 271 static const int BORDER1_COLOR = SWT.COLOR_WIDGET_NORMAL_SHADOW; 272 static const int FOREGROUND = SWT.COLOR_WIDGET_FOREGROUND; 273 static const int BACKGROUND = SWT.COLOR_WIDGET_BACKGROUND; 274 static const int BUTTON_BORDER = SWT.COLOR_WIDGET_DARK_SHADOW; 275 static const int BUTTON_FILL = SWT.COLOR_LIST_BACKGROUND; 276 277 static const int NONE = 0; 278 static const int NORMAL = 1; 279 static const int HOT = 2; 280 static const int SELECTED = 3; 281 static const RGB CLOSE_FILL; 282 283 static const int CHEVRON_CHILD_ID = 0; 284 static const int MINIMIZE_CHILD_ID = 1; 285 static const int MAXIMIZE_CHILD_ID = 2; 286 static const int EXTRA_CHILD_ID_COUNT = 3; 287 288 static this(){ 289 borderInsideRGB = new RGB (132, 130, 132); 290 borderMiddleRGB = new RGB (143, 141, 138); 291 borderOutsideRGB = new RGB (171, 168, 165); 292 CLOSE_FILL = new RGB(252, 160, 160); 293 } 294 295 /** 296 * Constructs a new instance of this class given its parent 297 * and a style value describing its behavior and appearance. 298 * <p> 299 * The style value is either one of the style constants defined in 300 * class <code>SWT</code> which is applicable to instances of this 301 * class, or must be built by <em>bitwise OR</em>'ing together 302 * (that is, using the <code>int</code> "|" operator) two or more 303 * of those <code>SWT</code> style constants. The class description 304 * lists the style constants that are applicable to the class. 305 * Style bits are also inherited from superclasses. 306 * </p> 307 * 308 * @param parent a widget which will be the parent of the new instance (cannot be null) 309 * @param style the style of widget to construct 310 * 311 * @exception IllegalArgumentException <ul> 312 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 313 * </ul> 314 * @exception SWTException <ul> 315 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 316 * </ul> 317 * 318 * @see SWT#TOP 319 * @see SWT#BOTTOM 320 * @see SWT#FLAT 321 * @see SWT#BORDER 322 * @see SWT#SINGLE 323 * @see SWT#MULTI 324 * @see #getStyle() 325 */ 326 public this(Composite parent, int style) { 327 chevronRect = new Rectangle(0, 0, 0, 0); 328 minRect = new Rectangle(0, 0, 0, 0); 329 maxRect = new Rectangle(0, 0, 0, 0); 330 topRightRect = new Rectangle(0, 0, 0, 0); 331 super(parent, checkStyle (parent, style)); 332 super.setLayout(new CTabFolderLayout()); 333 int style2 = super.getStyle(); 334 oldFont = getFont(); 335 onBottom = (style2 & SWT.BOTTOM) !is 0; 336 showClose = (style2 & SWT.CLOSE) !is 0; 337 // showMin = (style2 & SWT.MIN) !is 0; - conflicts with SWT.TOP 338 // showMax = (style2 & SWT.MAX) !is 0; - conflicts with SWT.BOTTOM 339 single = (style2 & SWT.SINGLE) !is 0; 340 borderLeft = borderRight = (style & SWT.BORDER) !is 0 ? 1 : 0; 341 borderTop = onBottom ? borderLeft : 0; 342 borderBottom = onBottom ? 0 : borderLeft; 343 highlight_header = (style & SWT.FLAT) !is 0 ? 1 : 3; 344 highlight_margin = (style & SWT.FLAT) !is 0 ? 0 : 2; 345 //set up default colors 346 Display display = getDisplay(); 347 selectionForeground = display.getSystemColor(SELECTION_FOREGROUND); 348 selectionBackground = display.getSystemColor(SELECTION_BACKGROUND); 349 borderColor = display.getSystemColor(BORDER1_COLOR); 350 updateTabHeight(false); 351 352 initAccessible(); 353 354 // Add all listeners 355 listener = new class() Listener { 356 public void handleEvent(Event event) { 357 switch (event.type) { 358 case SWT.Dispose: onDispose(event); break; 359 case SWT.DragDetect: onDragDetect(event); break; 360 case SWT.FocusIn: onFocus(event); break; 361 case SWT.FocusOut: onFocus(event); break; 362 case SWT.KeyDown: onKeyDown(event); break; 363 case SWT.MouseDoubleClick: onMouseDoubleClick(event); break; 364 case SWT.MouseDown: onMouse(event); break; 365 case SWT.MouseEnter: onMouse(event); break; 366 case SWT.MouseExit: onMouse(event); break; 367 case SWT.MouseMove: onMouse(event); break; 368 case SWT.MouseUp: onMouse(event); break; 369 case SWT.Paint: onPaint(event); break; 370 case SWT.Resize: onResize(); break; 371 case SWT.Traverse: onTraverse(event); break; 372 default: 373 } 374 } 375 }; 376 377 int[] folderEvents = [ 378 SWT.Dispose, 379 SWT.DragDetect, 380 SWT.FocusIn, 381 SWT.FocusOut, 382 SWT.KeyDown, 383 SWT.MouseDoubleClick, 384 SWT.MouseDown, 385 SWT.MouseEnter, 386 SWT.MouseExit, 387 SWT.MouseMove, 388 SWT.MouseUp, 389 SWT.Paint, 390 SWT.Resize, 391 SWT.Traverse, 392 ]; 393 for (int i = 0; i < folderEvents.length; i++) { 394 addListener(folderEvents[i], listener); 395 } 396 } 397 static int checkStyle (Composite parent, int style) { 398 int mask = SWT.CLOSE | SWT.TOP | SWT.BOTTOM | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT | SWT.SINGLE | SWT.MULTI; 399 style = style & mask; 400 // TOP and BOTTOM are mutually exclusive. 401 // TOP is the default 402 if ((style & SWT.TOP) !is 0) style = style & ~SWT.BOTTOM; 403 // SINGLE and MULTI are mutually exclusive. 404 // MULTI is the default 405 if ((style & SWT.MULTI) !is 0) style = style & ~SWT.SINGLE; 406 // reduce the flash by not redrawing the entire area on a Resize event 407 style |= SWT.NO_REDRAW_RESIZE; 408 //TEMPORARY CODE 409 /* 410 * The default background on carbon and some GTK themes is not a solid color 411 * but a texture. To show the correct default background, we must allow 412 * the operating system to draw it and therefore, we can not use the 413 * NO_BACKGROUND style. The NO_BACKGROUND style is not required on platforms 414 * that use double buffering which is true in both of these cases. 415 */ 416 String platform = SWT.getPlatform(); 417 if ("carbon"==platform || "gtk"==platform) return style; //$NON-NLS-1$ //$NON-NLS-2$ 418 419 //TEMPORARY CODE 420 /* 421 * In Right To Left orientation on Windows, all GC calls that use a brush are drawing 422 * offset by one pixel. This results in some parts of the CTabFolder not drawing correctly. 423 * To alleviate some of the appearance problems, allow the OS to draw the background. 424 * This does not draw correctly but the result is less obviously wrong. 425 */ 426 if ((style & SWT.RIGHT_TO_LEFT) !is 0) return style; 427 if ((parent.getStyle() & SWT.MIRRORED) !is 0 && (style & SWT.LEFT_TO_RIGHT) is 0) return style; 428 429 return style | SWT.NO_BACKGROUND; 430 } 431 static void fillRegion(GC gc, Region region) { 432 // NOTE: region passed in to this function will be modified 433 Region clipping = new Region(); 434 gc.getClipping(clipping); 435 region.intersect(clipping); 436 gc.setClipping(region); 437 gc.fillRectangle(region.getBounds()); 438 gc.setClipping(clipping); 439 clipping.dispose(); 440 } 441 /** 442 * 443 * Adds the listener to the collection of listeners who will 444 * be notified when a tab item is closed, minimized, maximized, 445 * restored, or to show the list of items that are not 446 * currently visible. 447 * 448 * @param listener the listener which should be notified 449 * 450 * @exception IllegalArgumentException <ul> 451 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 452 * </ul> 453 * 454 * @exception SWTException <ul> 455 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 456 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 457 * </ul> 458 * 459 * @see CTabFolder2Listener 460 * @see #removeCTabFolder2Listener(CTabFolder2Listener) 461 * 462 * @since 3.0 463 */ 464 public void addCTabFolder2Listener(CTabFolder2Listener listener) { 465 checkWidget(); 466 if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 467 // add to array 468 CTabFolder2Listener[] newListeners = new CTabFolder2Listener[folderListeners.length + 1]; 469 SimpleType!(CTabFolder2Listener).arraycopy(folderListeners, 0, newListeners, 0, cast(int)/*64bit*/folderListeners.length); 470 folderListeners = newListeners; 471 folderListeners[folderListeners.length - 1] = listener; 472 } 473 /** 474 * Adds the listener to the collection of listeners who will 475 * be notified when a tab item is closed. 476 * 477 * @param listener the listener which should be notified 478 * 479 * @exception IllegalArgumentException <ul> 480 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 481 * </ul> 482 * @exception SWTException <ul> 483 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 484 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 485 * </ul> 486 * 487 * @see CTabFolderListener 488 * @see #removeCTabFolderListener(CTabFolderListener) 489 * 490 * @deprecated use addCTabFolder2Listener(CTabFolder2Listener) 491 */ 492 public void addCTabFolderListener(CTabFolderListener listener) { 493 checkWidget(); 494 if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 495 // add to array 496 CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1]; 497 SimpleType!(CTabFolderListener).arraycopy(tabListeners, 0, newTabListeners, 0, cast(int)/*64bit*/tabListeners.length); 498 tabListeners = newTabListeners; 499 tabListeners[tabListeners.length - 1] = listener; 500 // display close button to be backwards compatible 501 if (!showClose) { 502 showClose = true; 503 updateItems(); 504 redraw(); 505 } 506 } 507 /** 508 * Adds the listener to the collection of listeners who will 509 * be notified when the user changes the receiver's selection, by sending 510 * it one of the messages defined in the <code>SelectionListener</code> 511 * interface. 512 * <p> 513 * <code>widgetSelected</code> is called when the user changes the selected tab. 514 * <code>widgetDefaultSelected</code> is not called. 515 * </p> 516 * 517 * @param listener the listener which should be notified when the user changes the receiver's selection 518 * 519 * @exception IllegalArgumentException <ul> 520 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 521 * </ul> 522 * @exception SWTException <ul> 523 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 524 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 525 * </ul> 526 * 527 * @see SelectionListener 528 * @see #removeSelectionListener 529 * @see SelectionEvent 530 */ 531 public void addSelectionListener(SelectionListener listener) { 532 checkWidget(); 533 if (listener is null) { 534 SWT.error(SWT.ERROR_NULL_ARGUMENT); 535 } 536 TypedListener typedListener = new TypedListener(listener); 537 addListener(SWT.Selection, typedListener); 538 addListener(SWT.DefaultSelection, typedListener); 539 } 540 void antialias (int[] shape, RGB lineRGB, RGB innerRGB, RGB outerRGB, GC gc){ 541 // Don't perform anti-aliasing on Mac and WPF because the platform 542 // already does it. The simple style also does not require anti-aliasing. 543 if (simple || "carbon".equals(SWT.getPlatform()) || "wpf".equals(SWT.getPlatform())) return; //$NON-NLS-1$ 544 // Don't perform anti-aliasing on low resolution displays 545 if (getDisplay().getDepth() < 15) return; 546 if (outerRGB !is null) { 547 int index = 0; 548 bool left = true; 549 int oldY = onBottom ? 0 : getSize().y; 550 int[] outer = new int[shape.length]; 551 for (int i = 0; i < shape.length/2; i++) { 552 if (left && (index + 3 < shape.length)) { 553 left = onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3]; 554 oldY = shape[index+1]; 555 } 556 outer[index] = shape[index] + (left ? -1 : +1); 557 index++; 558 outer[index] = shape[index]; 559 index++; 560 } 561 RGB from = lineRGB; 562 RGB to = outerRGB; 563 int red = from.red + 2*(to.red - from.red)/3; 564 int green = from.green + 2*(to.green - from.green)/3; 565 int blue = from.blue + 2*(to.blue - from.blue)/3; 566 Color color = new Color(getDisplay(), red, green, blue); 567 gc.setForeground(color); 568 gc.drawPolyline(outer); 569 color.dispose(); 570 } 571 if (innerRGB !is null) { 572 int[] inner = new int[shape.length]; 573 int index = 0; 574 bool left = true; 575 int oldY = onBottom ? 0 : getSize().y; 576 for (int i = 0; i < shape.length/2; i++) { 577 if (left && (index + 3 < shape.length)) { 578 left = onBottom ? oldY <= shape[index+3] : oldY >= shape[index+3]; 579 oldY = shape[index+1]; 580 } 581 inner[index] = shape[index] + (left ? +1 : -1); 582 index++; 583 inner[index] = shape[index]; 584 index++; 585 } 586 RGB from = lineRGB; 587 RGB to = innerRGB; 588 int red = from.red + 2*(to.red - from.red)/3; 589 int green = from.green + 2*(to.green - from.green)/3; 590 int blue = from.blue + 2*(to.blue - from.blue)/3; 591 Color color = new Color(getDisplay(), red, green, blue); 592 gc.setForeground(color); 593 gc.drawPolyline(inner); 594 color.dispose(); 595 } 596 } 597 public override Rectangle computeTrim (int x, int y, int width, int height) { 598 checkWidget(); 599 int trimX = x - marginWidth - highlight_margin - borderLeft; 600 int trimWidth = width + borderLeft + borderRight + 2*marginWidth + 2*highlight_margin; 601 if (minimized) { 602 int trimY = onBottom ? y - borderTop : y - highlight_header - tabHeight - borderTop; 603 int trimHeight = borderTop + borderBottom + tabHeight + highlight_header; 604 return new Rectangle (trimX, trimY, trimWidth, trimHeight); 605 } else { 606 int trimY = onBottom ? y - marginHeight - highlight_margin - borderTop: y - marginHeight - highlight_header - tabHeight - borderTop; 607 int trimHeight = height + borderTop + borderBottom + 2*marginHeight + tabHeight + highlight_header + highlight_margin; 608 return new Rectangle (trimX, trimY, trimWidth, trimHeight); 609 } 610 } 611 void createItem (CTabItem item, int index) { 612 if (0 > index || index > getItemCount ())SWT.error (SWT.ERROR_INVALID_RANGE); 613 item.parent = this; 614 CTabItem[] newItems = new CTabItem [items.length + 1]; 615 System.arraycopy(items, 0, newItems, 0, index); 616 newItems[index] = item; 617 System.arraycopy(items, index, newItems, index + 1, items.length - index); 618 items = newItems; 619 if (selectedIndex >= index) selectedIndex ++; 620 int[] newPriority = new int[priority.length + 1]; 621 int next = 0, priorityIndex = cast(int)/*64bit*/priority.length; 622 for (int i = 0; i < priority.length; i++) { 623 if (!mru && priority[i] is index) { 624 priorityIndex = next++; 625 } 626 newPriority[next++] = priority[i] >= index ? priority[i] + 1 : priority[i]; 627 } 628 newPriority[priorityIndex] = index; 629 priority = newPriority; 630 631 if (items.length is 1) { 632 if (!updateTabHeight(false)) updateItems(); 633 redraw(); 634 } else { 635 updateItems(); 636 redrawTabs(); 637 } 638 } 639 void destroyItem (CTabItem item) { 640 if (inDispose) return; 641 int index = indexOf(item); 642 if (index is -1) return; 643 644 if (items.length is 1) { 645 items = new CTabItem[0]; 646 priority = new int[0]; 647 firstIndex = -1; 648 selectedIndex = -1; 649 650 Control control = item.getControl(); 651 if (control !is null && !control.isDisposed()) { 652 control.setVisible(false); 653 } 654 setToolTipText(null); 655 setButtonBounds(); 656 redraw(); 657 return; 658 } 659 660 CTabItem[] newItems = new CTabItem [items.length - 1]; 661 System.arraycopy(items, 0, newItems, 0, index); 662 System.arraycopy(items, index + 1, newItems, index, items.length - index - 1); 663 items = newItems; 664 665 int[] newPriority = new int[priority.length - 1]; 666 int next = 0; 667 for (int i = 0; i < priority.length; i++) { 668 if (priority [i] is index) continue; 669 newPriority[next++] = priority[i] > index ? priority[i] - 1 : priority [i]; 670 } 671 priority = newPriority; 672 673 // move the selection if this item is selected 674 if (selectedIndex is index) { 675 Control control = item.getControl(); 676 selectedIndex = -1; 677 int nextSelection = mru ? priority[0] : Math.max(0, index - 1); 678 setSelection(nextSelection, true); 679 if (control !is null && !control.isDisposed()) { 680 control.setVisible(false); 681 } 682 } else if (selectedIndex > index) { 683 selectedIndex --; 684 } 685 686 updateItems(); 687 redrawTabs(); 688 } 689 void drawBackground(GC gc, int[] shape, bool selected) { 690 Color defaultBackground = selected ? selectionBackground : getBackground(); 691 Image image = selected ? selectionBgImage : bgImage; 692 Color[] colors = selected ? selectionGradientColors : gradientColors; 693 int[] percents = selected ? selectionGradientPercents : gradientPercents; 694 bool vertical = selected ? selectionGradientVertical : gradientVertical; 695 Point size = getSize(); 696 int width = size.x; 697 int height = tabHeight + highlight_header; 698 int x = 0; 699 if (borderLeft > 0) { 700 x += 1; width -= 2; 701 } 702 int y = onBottom ? size.y - borderBottom - height : borderTop; 703 drawBackground(gc, shape, x, y, width, height, defaultBackground, image, colors, percents, vertical); 704 } 705 void drawBackground(GC gc, int[] shape, int x, int y, int width, int height, Color defaultBackground, Image image, Color[] colors, int[] percents, bool vertical) { 706 Region clipping = new Region(); 707 gc.getClipping(clipping); 708 Region region = new Region(); 709 region.add(shape); 710 region.intersect(clipping); 711 gc.setClipping(region); 712 713 if (image !is null) { 714 // draw the background image in shape 715 gc.setBackground(defaultBackground); 716 gc.fillRectangle(x, y, width, height); 717 Rectangle imageRect = image.getBounds(); 718 gc.drawImage(image, imageRect.x, imageRect.y, imageRect.width, imageRect.height, x, y, width, height); 719 } else if (colors !is null) { 720 // draw gradient 721 if (colors.length is 1) { 722 Color background = colors[0] !is null ? colors[0] : defaultBackground; 723 gc.setBackground(background); 724 gc.fillRectangle(x, y, width, height); 725 } else { 726 if (vertical) { 727 if (onBottom) { 728 int pos = 0; 729 if (percents[percents.length - 1] < 100) { 730 pos = percents[percents.length - 1] * height / 100; 731 gc.setBackground(defaultBackground); 732 gc.fillRectangle(x, y, width, pos); 733 } 734 Color lastColor = colors[colors.length-1]; 735 if (lastColor is null) lastColor = defaultBackground; 736 for (ptrdiff_t i = cast(ptrdiff_t) (percents.length)-1; i >= 0; i--) { 737 gc.setForeground(lastColor); 738 lastColor = colors[i]; 739 if (lastColor is null) lastColor = defaultBackground; 740 gc.setBackground(lastColor); 741 int gradientHeight = percents[i] * height / 100; 742 gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true); 743 pos += gradientHeight; 744 } 745 } else { 746 Color lastColor = colors[0]; 747 if (lastColor is null) lastColor = defaultBackground; 748 int pos = 0; 749 for (int i = 0; i < percents.length; i++) { 750 gc.setForeground(lastColor); 751 lastColor = colors[i + 1]; 752 if (lastColor is null) lastColor = defaultBackground; 753 gc.setBackground(lastColor); 754 int gradientHeight = percents[i] * height / 100; 755 gc.fillGradientRectangle(x, y+pos, width, gradientHeight, true); 756 pos += gradientHeight; 757 } 758 if (pos < height) { 759 gc.setBackground(defaultBackground); 760 gc.fillRectangle(x, pos, width, height-pos+1); 761 } 762 } 763 } else { //horizontal gradient 764 y = 0; 765 height = getSize().y; 766 Color lastColor = colors[0]; 767 if (lastColor is null) lastColor = defaultBackground; 768 int pos = 0; 769 for (int i = 0; i < percents.length; ++i) { 770 gc.setForeground(lastColor); 771 lastColor = colors[i + 1]; 772 if (lastColor is null) lastColor = defaultBackground; 773 gc.setBackground(lastColor); 774 int gradientWidth = (percents[i] * width / 100) - pos; 775 gc.fillGradientRectangle(x+pos, y, gradientWidth, height, false); 776 pos += gradientWidth; 777 } 778 if (pos < width) { 779 gc.setBackground(defaultBackground); 780 gc.fillRectangle(x+pos, y, width-pos, height); 781 } 782 } 783 } 784 } else { 785 // draw a solid background using default background in shape 786 if ((getStyle() & SWT.NO_BACKGROUND) !is 0 || defaultBackground!=getBackground()) { 787 gc.setBackground(defaultBackground); 788 gc.fillRectangle(x, y, width, height); 789 } 790 } 791 gc.setClipping(clipping); 792 clipping.dispose(); 793 region.dispose(); 794 } 795 void drawBody(Event event) { 796 GC gc = event.gc; 797 Point size = getSize(); 798 799 // fill in body 800 if (!minimized){ 801 int width = size.x - borderLeft - borderRight - 2*highlight_margin; 802 int height = size.y - borderTop - borderBottom - tabHeight - highlight_header - highlight_margin; 803 // Draw highlight margin 804 if (highlight_margin > 0) { 805 int[] shape = null; 806 if (onBottom) { 807 int x1 = borderLeft; 808 int y1 = borderTop; 809 int x2 = size.x - borderRight; 810 int y2 = size.y - borderBottom - tabHeight - highlight_header; 811 shape = [x1,y1, x2,y1, x2,y2, x2-highlight_margin,y2, 812 x2-highlight_margin, y1+highlight_margin, x1+highlight_margin,y1+highlight_margin, 813 x1+highlight_margin,y2, x1,y2]; 814 } else { 815 int x1 = borderLeft; 816 int y1 = borderTop + tabHeight + highlight_header; 817 int x2 = size.x - borderRight; 818 int y2 = size.y - borderBottom; 819 shape = [x1,y1, x1+highlight_margin,y1, x1+highlight_margin,y2-highlight_margin, 820 x2-highlight_margin,y2-highlight_margin, x2-highlight_margin,y1, 821 x2,y1, x2,y2, x1,y2]; 822 } 823 // If horizontal gradient, show gradient across the whole area 824 if (selectedIndex !is -1 && selectionGradientColors !is null && selectionGradientColors.length > 1 && !selectionGradientVertical) { 825 drawBackground(gc, shape, true); 826 } else if (selectedIndex is -1 && gradientColors !is null && gradientColors.length > 1 && !gradientVertical) { 827 drawBackground(gc, shape, false); 828 } else { 829 gc.setBackground(selectedIndex is -1 ? getBackground() : selectionBackground); 830 gc.fillPolygon(shape); 831 } 832 } 833 //Draw client area 834 if ((getStyle() & SWT.NO_BACKGROUND) !is 0) { 835 gc.setBackground(getBackground()); 836 gc.fillRectangle(xClient - marginWidth, yClient - marginHeight, width, height); 837 } 838 } else { 839 if ((getStyle() & SWT.NO_BACKGROUND) !is 0) { 840 int height = borderTop + tabHeight + highlight_header + borderBottom; 841 if (size.y > height) { 842 gc.setBackground(getParent().getBackground()); 843 gc.fillRectangle(0, height, size.x, size.y - height); 844 } 845 } 846 } 847 848 //draw 1 pixel border around outside 849 if (borderLeft > 0) { 850 gc.setForeground(borderColor); 851 int x1 = borderLeft - 1; 852 int x2 = size.x - borderRight; 853 int y1 = onBottom ? borderTop - 1 : borderTop + tabHeight; 854 int y2 = onBottom ? size.y - tabHeight - borderBottom - 1 : size.y - borderBottom; 855 gc.drawLine(x1, y1, x1, y2); // left 856 gc.drawLine(x2, y1, x2, y2); // right 857 if (onBottom) { 858 gc.drawLine(x1, y1, x2, y1); // top 859 } else { 860 gc.drawLine(x1, y2, x2, y2); // bottom 861 } 862 } 863 } 864 865 void drawChevron(GC gc) { 866 if (chevronRect.width is 0 || chevronRect.height is 0) return; 867 // draw chevron (10x7) 868 Display display = getDisplay(); 869 Point dpi = display.getDPI(); 870 int fontHeight = 72 * 10 / dpi.y; 871 FontData fd = getFont().getFontData()[0]; 872 fd.setHeight(fontHeight); 873 Font f = new Font(display, fd); 874 int fHeight = f.getFontData()[0].getHeight() * dpi.y / 72; 875 int indent = Math.max(2, (chevronRect.height - fHeight - 4) /2); 876 int x = chevronRect.x + 2; 877 int y = chevronRect.y + indent; 878 int count; 879 if (single) { 880 count = cast(int)/*64bit*/(selectedIndex is -1 ? items.length : items.length - 1); 881 } else { 882 int showCount = 0; 883 while (showCount < priority.length && items[priority[showCount]].showing) { 884 showCount++; 885 } 886 count = cast(int)/*64bit*/items.length - showCount; 887 } 888 String chevronString = count > 99 ? "99+" : String_valueOf(count); //$NON-NLS-1$ 889 switch (chevronImageState) { 890 case NORMAL: { 891 Color chevronBorder = single ? getSelectionForeground() : getForeground(); 892 gc.setForeground(chevronBorder); 893 gc.setFont(f); 894 gc.drawLine(x,y, x+2,y+2); 895 gc.drawLine(x+2,y+2, x,y+4); 896 gc.drawLine(x+1,y, x+3,y+2); 897 gc.drawLine(x+3,y+2, x+1,y+4); 898 gc.drawLine(x+4,y, x+6,y+2); 899 gc.drawLine(x+6,y+2, x+5,y+4); 900 gc.drawLine(x+5,y, x+7,y+2); 901 gc.drawLine(x+7,y+2, x+4,y+4); 902 gc.drawString(chevronString, x+7, y+3, true); 903 break; 904 } 905 case HOT: { 906 gc.setForeground(display.getSystemColor(BUTTON_BORDER)); 907 gc.setBackground(display.getSystemColor(BUTTON_FILL)); 908 gc.setFont(f); 909 gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, 6, 6); 910 gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6); 911 gc.drawLine(x,y, x+2,y+2); 912 gc.drawLine(x+2,y+2, x,y+4); 913 gc.drawLine(x+1,y, x+3,y+2); 914 gc.drawLine(x+3,y+2, x+1,y+4); 915 gc.drawLine(x+4,y, x+6,y+2); 916 gc.drawLine(x+6,y+2, x+5,y+4); 917 gc.drawLine(x+5,y, x+7,y+2); 918 gc.drawLine(x+7,y+2, x+4,y+4); 919 gc.drawString(chevronString, x+7, y+3, true); 920 break; 921 } 922 case SELECTED: { 923 gc.setForeground(display.getSystemColor(BUTTON_BORDER)); 924 gc.setBackground(display.getSystemColor(BUTTON_FILL)); 925 gc.setFont(f); 926 gc.fillRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, 6, 6); 927 gc.drawRoundRectangle(chevronRect.x, chevronRect.y, chevronRect.width - 1, chevronRect.height - 1, 6, 6); 928 gc.drawLine(x+1,y+1, x+3,y+3); 929 gc.drawLine(x+3,y+3, x+1,y+5); 930 gc.drawLine(x+2,y+1, x+4,y+3); 931 gc.drawLine(x+4,y+3, x+2,y+5); 932 gc.drawLine(x+5,y+1, x+7,y+3); 933 gc.drawLine(x+7,y+3, x+6,y+5); 934 gc.drawLine(x+6,y+1, x+8,y+3); 935 gc.drawLine(x+8,y+3, x+5,y+5); 936 gc.drawString(chevronString, x+8, y+4, true); 937 break; 938 } 939 default: 940 } 941 f.dispose(); 942 } 943 void drawMaximize(GC gc) { 944 if (maxRect.width is 0 || maxRect.height is 0) return; 945 Display display = getDisplay(); 946 // 5x4 or 7x9 947 int x = maxRect.x + (CTabFolder.BUTTON_SIZE - 10)/2; 948 int y = maxRect.y + 3; 949 950 gc.setForeground(display.getSystemColor(BUTTON_BORDER)); 951 gc.setBackground(display.getSystemColor(BUTTON_FILL)); 952 953 switch (maxImageState) { 954 case NORMAL: { 955 if (!maximized) { 956 gc.fillRectangle(x, y, 9, 9); 957 gc.drawRectangle(x, y, 9, 9); 958 gc.drawLine(x+1, y+2, x+8, y+2); 959 } else { 960 gc.fillRectangle(x, y+3, 5, 4); 961 gc.fillRectangle(x+2, y, 5, 4); 962 gc.drawRectangle(x, y+3, 5, 4); 963 gc.drawRectangle(x+2, y, 5, 4); 964 gc.drawLine(x+3, y+1, x+6, y+1); 965 gc.drawLine(x+1, y+4, x+4, y+4); 966 } 967 break; 968 } 969 case HOT: { 970 gc.fillRoundRectangle(maxRect.x, maxRect.y, maxRect.width, maxRect.height, 6, 6); 971 gc.drawRoundRectangle(maxRect.x, maxRect.y, maxRect.width - 1, maxRect.height - 1, 6, 6); 972 if (!maximized) { 973 gc.fillRectangle(x, y, 9, 9); 974 gc.drawRectangle(x, y, 9, 9); 975 gc.drawLine(x+1, y+2, x+8, y+2); 976 } else { 977 gc.fillRectangle(x, y+3, 5, 4); 978 gc.fillRectangle(x+2, y, 5, 4); 979 gc.drawRectangle(x, y+3, 5, 4); 980 gc.drawRectangle(x+2, y, 5, 4); 981 gc.drawLine(x+3, y+1, x+6, y+1); 982 gc.drawLine(x+1, y+4, x+4, y+4); 983 } 984 break; 985 } 986 case SELECTED: { 987 gc.fillRoundRectangle(maxRect.x, maxRect.y, maxRect.width, maxRect.height, 6, 6); 988 gc.drawRoundRectangle(maxRect.x, maxRect.y, maxRect.width - 1, maxRect.height - 1, 6, 6); 989 if (!maximized) { 990 gc.fillRectangle(x+1, y+1, 9, 9); 991 gc.drawRectangle(x+1, y+1, 9, 9); 992 gc.drawLine(x+2, y+3, x+9, y+3); 993 } else { 994 gc.fillRectangle(x+1, y+4, 5, 4); 995 gc.fillRectangle(x+3, y+1, 5, 4); 996 gc.drawRectangle(x+1, y+4, 5, 4); 997 gc.drawRectangle(x+3, y+1, 5, 4); 998 gc.drawLine(x+4, y+2, x+7, y+2); 999 gc.drawLine(x+2, y+5, x+5, y+5); 1000 } 1001 break; 1002 } 1003 default: 1004 } 1005 } 1006 void drawMinimize(GC gc) { 1007 if (minRect.width is 0 || minRect.height is 0) return; 1008 Display display = getDisplay(); 1009 // 5x4 or 9x3 1010 int x = minRect.x + (BUTTON_SIZE - 10)/2; 1011 int y = minRect.y + 3; 1012 1013 gc.setForeground(display.getSystemColor(BUTTON_BORDER)); 1014 gc.setBackground(display.getSystemColor(BUTTON_FILL)); 1015 1016 switch (minImageState) { 1017 case NORMAL: { 1018 if (!minimized) { 1019 gc.fillRectangle(x, y, 9, 3); 1020 gc.drawRectangle(x, y, 9, 3); 1021 } else { 1022 gc.fillRectangle(x, y+3, 5, 4); 1023 gc.fillRectangle(x+2, y, 5, 4); 1024 gc.drawRectangle(x, y+3, 5, 4); 1025 gc.drawRectangle(x+2, y, 5, 4); 1026 gc.drawLine(x+3, y+1, x+6, y+1); 1027 gc.drawLine(x+1, y+4, x+4, y+4); 1028 } 1029 break; 1030 } 1031 case HOT: { 1032 gc.fillRoundRectangle(minRect.x, minRect.y, minRect.width, minRect.height, 6, 6); 1033 gc.drawRoundRectangle(minRect.x, minRect.y, minRect.width - 1, minRect.height - 1, 6, 6); 1034 if (!minimized) { 1035 gc.fillRectangle(x, y, 9, 3); 1036 gc.drawRectangle(x, y, 9, 3); 1037 } else { 1038 gc.fillRectangle(x, y+3, 5, 4); 1039 gc.fillRectangle(x+2, y, 5, 4); 1040 gc.drawRectangle(x, y+3, 5, 4); 1041 gc.drawRectangle(x+2, y, 5, 4); 1042 gc.drawLine(x+3, y+1, x+6, y+1); 1043 gc.drawLine(x+1, y+4, x+4, y+4); 1044 } 1045 break; 1046 } 1047 case SELECTED: { 1048 gc.fillRoundRectangle(minRect.x, minRect.y, minRect.width, minRect.height, 6, 6); 1049 gc.drawRoundRectangle(minRect.x, minRect.y, minRect.width - 1, minRect.height - 1, 6, 6); 1050 if (!minimized) { 1051 gc.fillRectangle(x+1, y+1, 9, 3); 1052 gc.drawRectangle(x+1, y+1, 9, 3); 1053 } else { 1054 gc.fillRectangle(x+1, y+4, 5, 4); 1055 gc.fillRectangle(x+3, y+1, 5, 4); 1056 gc.drawRectangle(x+1, y+4, 5, 4); 1057 gc.drawRectangle(x+3, y+1, 5, 4); 1058 gc.drawLine(x+4, y+2, x+7, y+2); 1059 gc.drawLine(x+2, y+5, x+5, y+5); 1060 } 1061 break; 1062 } 1063 default: 1064 } 1065 } 1066 void drawTabArea(Event event) { 1067 GC gc = event.gc; 1068 Point size = getSize(); 1069 int[] shape = null; 1070 1071 if (tabHeight is 0) { 1072 int style = getStyle(); 1073 if ((style & SWT.FLAT) !is 0 && (style & SWT.BORDER) is 0) return; 1074 int x1 = borderLeft - 1; 1075 int x2 = size.x - borderRight; 1076 int y1 = onBottom ? size.y - borderBottom - highlight_header - 1 : borderTop + highlight_header; 1077 int y2 = onBottom ? size.y - borderBottom : borderTop; 1078 if (borderLeft > 0 && onBottom) y2 -= 1; 1079 1080 shape = [x1, y1, x1,y2, x2,y2, x2,y1]; 1081 1082 // If horizontal gradient, show gradient across the whole area 1083 if (selectedIndex !is -1 && selectionGradientColors !is null && selectionGradientColors.length > 1 && !selectionGradientVertical) { 1084 drawBackground(gc, shape, true); 1085 } else if (selectedIndex is -1 && gradientColors !is null && gradientColors.length > 1 && !gradientVertical) { 1086 drawBackground(gc, shape, false); 1087 } else { 1088 gc.setBackground(selectedIndex is -1 ? getBackground() : selectionBackground); 1089 gc.fillPolygon(shape); 1090 } 1091 1092 //draw 1 pixel border 1093 if (borderLeft > 0) { 1094 gc.setForeground(borderColor); 1095 gc.drawPolyline(shape); 1096 } 1097 return; 1098 } 1099 1100 int x = Math.max(0, borderLeft - 1); 1101 int y = onBottom ? size.y - borderBottom - tabHeight : borderTop; 1102 int width = size.x - borderLeft - borderRight + 1; 1103 int height = tabHeight - 1; 1104 1105 // Draw Tab Header 1106 if (onBottom) { 1107 TryConst!(int)[] left, right; 1108 if ((getStyle() & SWT.BORDER) !is 0) { 1109 left = simple ? SIMPLE_BOTTOM_LEFT_CORNER : BOTTOM_LEFT_CORNER; 1110 right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER : BOTTOM_RIGHT_CORNER; 1111 } else { 1112 left = simple ? SIMPLE_BOTTOM_LEFT_CORNER_BORDERLESS : BOTTOM_LEFT_CORNER_BORDERLESS; 1113 right = simple ? SIMPLE_BOTTOM_RIGHT_CORNER_BORDERLESS : BOTTOM_RIGHT_CORNER_BORDERLESS; 1114 } 1115 shape = new int[left.length + right.length + 4]; 1116 int index = 0; 1117 shape[index++] = x; 1118 shape[index++] = y-highlight_header; 1119 for (int i = 0; i < left.length/2; i++) { 1120 shape[index++] = x+left[2*i]; 1121 shape[index++] = y+height+left[2*i+1]; 1122 if (borderLeft is 0) shape[index-1] += 1; 1123 } 1124 for (int i = 0; i < right.length/2; i++) { 1125 shape[index++] = x+width+right[2*i]; 1126 shape[index++] = y+height+right[2*i+1]; 1127 if (borderLeft is 0) shape[index-1] += 1; 1128 } 1129 shape[index++] = x+width; 1130 shape[index++] = y-highlight_header; 1131 } else { 1132 TryConst!(int)[] left, right; 1133 if ((getStyle() & SWT.BORDER) !is 0) { 1134 left = simple ? SIMPLE_TOP_LEFT_CORNER : TOP_LEFT_CORNER; 1135 right = simple ? SIMPLE_TOP_RIGHT_CORNER : TOP_RIGHT_CORNER; 1136 } else { 1137 left = simple ? SIMPLE_TOP_LEFT_CORNER_BORDERLESS : TOP_LEFT_CORNER_BORDERLESS; 1138 right = simple ? SIMPLE_TOP_RIGHT_CORNER_BORDERLESS : TOP_RIGHT_CORNER_BORDERLESS; 1139 } 1140 shape = new int[left.length + right.length + 4]; 1141 int index = 0; 1142 shape[index++] = x; 1143 shape[index++] = y+height+highlight_header + 1; 1144 for (int i = 0; i < left.length/2; i++) { 1145 shape[index++] = x+left[2*i]; 1146 shape[index++] = y+left[2*i+1]; 1147 } 1148 for (int i = 0; i < right.length/2; i++) { 1149 shape[index++] = x+width+right[2*i]; 1150 shape[index++] = y+right[2*i+1]; 1151 } 1152 shape[index++] = x+width; 1153 shape[index++] = y+height+highlight_header + 1; 1154 } 1155 // Fill in background 1156 bool bkSelected = single && selectedIndex !is -1; 1157 drawBackground(gc, shape, bkSelected); 1158 // Fill in parent background for non-rectangular shape 1159 Region r = new Region(); 1160 r.add(new Rectangle(x, y, width + 1, height + 1)); 1161 r.subtract(shape); 1162 gc.setBackground(getParent().getBackground()); 1163 fillRegion(gc, r); 1164 r.dispose(); 1165 1166 // Draw the unselected tabs. 1167 if (!single) { 1168 for (int i=0; i < items.length; i++) { 1169 if (i !is selectedIndex && event.getBounds().intersects(items[i].getBounds())) { 1170 items[i].onPaint(gc, false); 1171 } 1172 } 1173 } 1174 1175 // Draw selected tab 1176 if (selectedIndex !is -1) { 1177 CTabItem item = items[selectedIndex]; 1178 item.onPaint(gc, true); 1179 } else { 1180 // if no selected tab - draw line across bottom of all tabs 1181 int x1 = borderLeft; 1182 int y1 = (onBottom) ? size.y - borderBottom - tabHeight - 1 : borderTop + tabHeight; 1183 int x2 = size.x - borderRight; 1184 gc.setForeground(borderColor); 1185 gc.drawLine(x1, y1, x2, y1); 1186 } 1187 1188 // Draw Buttons 1189 drawChevron(gc); 1190 drawMinimize(gc); 1191 drawMaximize(gc); 1192 1193 // Draw border line 1194 if (borderLeft > 0) { 1195 RGB outside = getParent().getBackground().getRGB(); 1196 antialias(shape, borderColor.getRGB(), null, outside, gc); 1197 gc.setForeground(borderColor); 1198 gc.drawPolyline(shape); 1199 } 1200 } 1201 /** 1202 * Returns <code>true</code> if the receiver's border is visible. 1203 * 1204 * @return the receiver's border visibility state 1205 * 1206 * @exception SWTException <ul> 1207 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1208 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1209 * </ul> 1210 * 1211 * @since 3.0 1212 */ 1213 public bool getBorderVisible() { 1214 checkWidget(); 1215 return borderLeft is 1; 1216 } 1217 public override Rectangle getClientArea() { 1218 checkWidget(); 1219 if (minimized) return new Rectangle(xClient, yClient, 0, 0); 1220 Point size = getSize(); 1221 int width = size.x - borderLeft - borderRight - 2*marginWidth - 2*highlight_margin; 1222 int height = size.y - borderTop - borderBottom - 2*marginHeight - highlight_margin - highlight_header; 1223 height -= tabHeight; 1224 return new Rectangle(xClient, yClient, width, height); 1225 } 1226 /** 1227 * Return the tab that is located at the specified index. 1228 * 1229 * @param index the index of the tab item 1230 * @return the item at the specified index 1231 * 1232 * @exception IllegalArgumentException <ul> 1233 * <li>ERROR_INVALID_RANGE - if the index is out of range</li> 1234 * </ul> 1235 * @exception SWTException <ul> 1236 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1237 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1238 * </ul> 1239 */ 1240 public CTabItem getItem (int index) { 1241 //checkWidget(); 1242 if (index < 0 || index >= items.length) 1243 SWT.error(SWT.ERROR_INVALID_RANGE); 1244 return items [index]; 1245 } 1246 /** 1247 * Gets the item at a point in the widget. 1248 * 1249 * @param pt the point in coordinates relative to the CTabFolder 1250 * @return the item at a point or null 1251 * 1252 * @exception SWTException <ul> 1253 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1254 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1255 * </ul> 1256 */ 1257 public CTabItem getItem (Point pt) { 1258 //checkWidget(); 1259 if (items.length is 0) return null; 1260 Point size = getSize(); 1261 if (size.x <= borderLeft + borderRight) return null; 1262 if (showChevron && chevronRect.contains(pt)) return null; 1263 for (int i = 0; i < priority.length; i++) { 1264 CTabItem item = items[priority[i]]; 1265 Rectangle rect = item.getBounds(); 1266 if (rect.contains(pt)) return item; 1267 } 1268 return null; 1269 } 1270 /** 1271 * Return the number of tabs in the folder. 1272 * 1273 * @return the number of tabs in the folder 1274 * 1275 * @exception SWTException <ul> 1276 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1277 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1278 * </ul> 1279 */ 1280 public int getItemCount(){ 1281 //checkWidget(); 1282 return cast(int)/*64bit*/items.length; 1283 } 1284 /** 1285 * Return the tab items. 1286 * 1287 * @return the tab items 1288 * 1289 * @exception SWTException <ul> 1290 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1291 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1292 * </ul> 1293 */ 1294 public CTabItem [] getItems() { 1295 //checkWidget(); 1296 CTabItem[] tabItems = new CTabItem [items.length]; 1297 System.arraycopy(items, 0, tabItems, 0, items.length); 1298 return tabItems; 1299 } 1300 /* 1301 * Return the lowercase of the first non-'&' character following 1302 * an '&' character in the given string. If there are no '&' 1303 * characters in the given string, return '\0'. 1304 */ 1305 dchar _findMnemonic (String string) { 1306 if (string is null) return '\0'; 1307 int index = 0; 1308 int length_ = cast(int)/*64bit*/string.length; 1309 do { 1310 while (index < length_ && string[index] !is '&') index++; 1311 if (++index >= length_) return '\0'; 1312 if (string[index] !is '&') return Character.toLowerCase (string.dcharAt (index)); 1313 index++; 1314 } while (index < length_); 1315 return '\0'; 1316 } 1317 String stripMnemonic (String string) { 1318 int index = 0; 1319 int length_ = cast(int)/*64bit*/string.length; 1320 do { 1321 while ((index < length_) && (string[index] !is '&')) index++; 1322 if (++index >= length_) return string; 1323 if (string[index] !is '&') { 1324 return string.substring(0, index-1) ~ string.substring(index, length_); 1325 } 1326 index++; 1327 } while (index < length_); 1328 return string; 1329 } 1330 /** 1331 * Returns <code>true</code> if the receiver is minimized. 1332 * 1333 * @return the receiver's minimized state 1334 * 1335 * @exception SWTException <ul> 1336 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1337 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1338 * </ul> 1339 * 1340 * @since 3.0 1341 */ 1342 public bool getMinimized() { 1343 checkWidget(); 1344 return minimized; 1345 } 1346 /** 1347 * Returns <code>true</code> if the minimize button 1348 * is visible. 1349 * 1350 * @return the visibility of the minimized button 1351 * 1352 * @exception SWTException <ul> 1353 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1354 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1355 * </ul> 1356 * 1357 * @since 3.0 1358 */ 1359 public bool getMinimizeVisible() { 1360 checkWidget(); 1361 return showMin; 1362 } 1363 /** 1364 * Returns the number of characters that will 1365 * appear in a fully compressed tab. 1366 * 1367 * @return number of characters that will appear in a fully compressed tab 1368 * 1369 * @since 3.0 1370 */ 1371 public int getMinimumCharacters() { 1372 checkWidget(); 1373 return minChars; 1374 } 1375 /** 1376 * Returns <code>true</code> if the receiver is maximized. 1377 * <p> 1378 * 1379 * @return the receiver's maximized state 1380 * 1381 * @exception SWTException <ul> 1382 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1383 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1384 * </ul> 1385 * 1386 * @since 3.0 1387 */ 1388 public bool getMaximized() { 1389 checkWidget(); 1390 return maximized; 1391 } 1392 /** 1393 * Returns <code>true</code> if the maximize button 1394 * is visible. 1395 * 1396 * @return the visibility of the maximized button 1397 * 1398 * @exception SWTException <ul> 1399 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1400 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1401 * </ul> 1402 * 1403 * @since 3.0 1404 */ 1405 public bool getMaximizeVisible() { 1406 checkWidget(); 1407 return showMax; 1408 } 1409 /** 1410 * Returns <code>true</code> if the receiver displays most 1411 * recently used tabs and <code>false</code> otherwise. 1412 * <p> 1413 * When there is not enough horizontal space to show all the tabs, 1414 * by default, tabs are shown sequentially from left to right in 1415 * order of their index. When the MRU visibility is turned on, 1416 * the tabs that are visible will be the tabs most recently selected. 1417 * Tabs will still maintain their left to right order based on index 1418 * but only the most recently selected tabs are visible. 1419 * <p> 1420 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2", 1421 * "Tab 3" and "Tab 4" (in order by index). The user selects 1422 * "Tab 1" and then "Tab 3". If the CTabFolder is now 1423 * compressed so that only two tabs are visible, by default, 1424 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently 1425 * selected and "Tab 2" because it is the previous item in index order). 1426 * If MRU visibility is enabled, the two visible tabs will be "Tab 1" 1427 * and "Tab 3" (in that order from left to right).</p> 1428 * 1429 * @return the receiver's header's visibility state 1430 * 1431 * @exception SWTException <ul> 1432 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1433 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1434 * </ul> 1435 * 1436 * @since 3.1 1437 */ 1438 public bool getMRUVisible() { 1439 checkWidget(); 1440 return mru; 1441 } 1442 int getRightItemEdge (){ 1443 int x = getSize().x - borderRight - 3; 1444 if (showMin) x -= BUTTON_SIZE; 1445 if (showMax) x -= BUTTON_SIZE; 1446 if (showChevron) x -= 3*BUTTON_SIZE/2; 1447 if (topRight !is null && topRightAlignment !is SWT.FILL) { 1448 Point rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT); 1449 x -= rightSize.x + 3; 1450 } 1451 return Math.max(0, x); 1452 } 1453 /** 1454 * Return the selected tab item, or null if there is no selection. 1455 * 1456 * @return the selected tab item, or null if none has been selected 1457 * 1458 * @exception SWTException <ul> 1459 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1460 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1461 * </ul> 1462 */ 1463 public CTabItem getSelection() { 1464 //checkWidget(); 1465 if (selectedIndex is -1) return null; 1466 return items[selectedIndex]; 1467 } 1468 /** 1469 * Returns the receiver's selection background color. 1470 * 1471 * @return the selection background color of the receiver 1472 * 1473 * @exception SWTException <ul> 1474 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1475 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1476 * </ul> 1477 * 1478 * @since 3.0 1479 */ 1480 public Color getSelectionBackground() { 1481 checkWidget(); 1482 return selectionBackground; 1483 } 1484 /** 1485 * Returns the receiver's selection foreground color. 1486 * 1487 * @return the selection foreground color of the receiver 1488 * 1489 * @exception SWTException <ul> 1490 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1491 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1492 * </ul> 1493 * 1494 * @since 3.0 1495 */ 1496 public Color getSelectionForeground() { 1497 checkWidget(); 1498 return selectionForeground; 1499 } 1500 /** 1501 * Return the index of the selected tab item, or -1 if there 1502 * is no selection. 1503 * 1504 * @return the index of the selected tab item or -1 1505 * 1506 * @exception SWTException <ul> 1507 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1508 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1509 * </ul> 1510 */ 1511 public int getSelectionIndex() { 1512 //checkWidget(); 1513 return selectedIndex; 1514 } 1515 /** 1516 * Returns <code>true</code> if the CTabFolder is rendered 1517 * with a simple, traditional shape. 1518 * 1519 * @return <code>true</code> if the CTabFolder is rendered with a simple shape 1520 * 1521 * @since 3.0 1522 */ 1523 public bool getSimple() { 1524 checkWidget(); 1525 return simple; 1526 } 1527 /** 1528 * Returns <code>true</code> if the CTabFolder only displays the selected tab 1529 * and <code>false</code> if the CTabFolder displays multiple tabs. 1530 * 1531 * @return <code>true</code> if the CTabFolder only displays the selected tab and <code>false</code> if the CTabFolder displays multiple tabs 1532 * 1533 * @since 3.0 1534 */ 1535 public bool getSingle() { 1536 checkWidget(); 1537 return single; 1538 } 1539 1540 public override int getStyle() { 1541 int style = super.getStyle(); 1542 style &= ~(SWT.TOP | SWT.BOTTOM); 1543 style |= onBottom ? SWT.BOTTOM : SWT.TOP; 1544 style &= ~(SWT.SINGLE | SWT.MULTI); 1545 style |= single ? SWT.SINGLE : SWT.MULTI; 1546 if (borderLeft !is 0) style |= SWT.BORDER; 1547 style &= ~SWT.CLOSE; 1548 if (showClose) style |= SWT.CLOSE; 1549 return style; 1550 } 1551 /** 1552 * Returns the height of the tab 1553 * 1554 * @return the height of the tab 1555 * 1556 * @exception SWTException <ul> 1557 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1558 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1559 * </ul> 1560 */ 1561 public int getTabHeight(){ 1562 checkWidget(); 1563 if (fixedTabHeight !is SWT.DEFAULT) return fixedTabHeight; 1564 return tabHeight - 1; // -1 for line drawn across top of tab 1565 } 1566 /** 1567 * Returns the position of the tab. Possible values are SWT.TOP or SWT.BOTTOM. 1568 * 1569 * @return the position of the tab 1570 * 1571 * @exception SWTException <ul> 1572 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1573 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1574 * </ul> 1575 */ 1576 public int getTabPosition(){ 1577 checkWidget(); 1578 return onBottom ? SWT.BOTTOM : SWT.TOP; 1579 } 1580 /** 1581 * Returns the control in the top right corner of the tab folder. 1582 * Typically this is a close button or a composite with a menu and close button. 1583 * 1584 * @return the control in the top right corner of the tab folder or null 1585 * 1586 * @exception SWTException <ul> 1587 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1588 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1589 * </ul> 1590 * 1591 * @since 2.1 1592 */ 1593 public Control getTopRight() { 1594 checkWidget(); 1595 return topRight; 1596 } 1597 /** 1598 * Returns <code>true</code> if the close button appears 1599 * when the user hovers over an unselected tabs. 1600 * 1601 * @return <code>true</code> if the close button appears on unselected tabs 1602 * 1603 * @since 3.0 1604 */ 1605 public bool getUnselectedCloseVisible() { 1606 checkWidget(); 1607 return showUnselectedClose; 1608 } 1609 /** 1610 * Returns <code>true</code> if an image appears 1611 * in unselected tabs. 1612 * 1613 * @return <code>true</code> if an image appears in unselected tabs 1614 * 1615 * @since 3.0 1616 */ 1617 public bool getUnselectedImageVisible() { 1618 checkWidget(); 1619 return showUnselectedImage; 1620 } 1621 /** 1622 * Return the index of the specified tab or -1 if the tab is not 1623 * in the receiver. 1624 * 1625 * @param item the tab item for which the index is required 1626 * 1627 * @return the index of the specified tab item or -1 1628 * 1629 * @exception IllegalArgumentException <ul> 1630 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 1631 * </ul> 1632 * 1633 * @exception SWTException <ul> 1634 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 1635 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 1636 * </ul> 1637 */ 1638 public int indexOf(CTabItem item) { 1639 checkWidget(); 1640 if (item is null) { 1641 SWT.error(SWT.ERROR_NULL_ARGUMENT); 1642 } 1643 for (int i = 0; i < items.length; i++) { 1644 if (items[i] is item) return i; 1645 } 1646 return -1; 1647 } 1648 void initAccessible() { 1649 Accessible accessible = getAccessible(); 1650 accessible.addAccessibleListener(new class() AccessibleAdapter { 1651 override 1652 public void getName(AccessibleEvent e) { 1653 String name = null; 1654 int childID = e.childID; 1655 if (childID >= 0 && childID < items.length) { 1656 name = stripMnemonic(items[childID].getText()); 1657 } else if (childID is items.length + CHEVRON_CHILD_ID) { 1658 name = SWT.getMessage("SWT_ShowList"); //$NON-NLS-1$ 1659 } else if (childID is items.length + MINIMIZE_CHILD_ID) { 1660 name = minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize"); //$NON-NLS-1$ //$NON-NLS-2$ 1661 } else if (childID is items.length + MAXIMIZE_CHILD_ID) { 1662 name = maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize"); //$NON-NLS-1$ //$NON-NLS-2$ 1663 } 1664 e.result = name; 1665 } 1666 1667 override 1668 public void getHelp(AccessibleEvent e) { 1669 String help = null; 1670 int childID = e.childID; 1671 if (childID is ACC.CHILDID_SELF) { 1672 help = getToolTipText(); 1673 } else if (childID >= 0 && childID < items.length) { 1674 help = items[childID].getToolTipText(); 1675 } 1676 e.result = help; 1677 } 1678 1679 override 1680 public void getKeyboardShortcut(AccessibleEvent e) { 1681 String shortcut = null; 1682 int childID = e.childID; 1683 if (childID >= 0 && childID < items.length) { 1684 String text = items[childID].getText(); 1685 if (text !is null) { 1686 dchar mnemonic = _findMnemonic(text); 1687 if (mnemonic !is '\0') { 1688 shortcut = "Alt+"~dcharToString(mnemonic); //$NON-NLS-1$ 1689 } 1690 } 1691 } 1692 e.result = shortcut; 1693 } 1694 }); 1695 1696 accessible.addAccessibleControlListener(new class() AccessibleControlAdapter { 1697 override 1698 public void getChildAtPoint(AccessibleControlEvent e) { 1699 Point testPoint = toControl(e.x, e.y); 1700 int childID = ACC.CHILDID_NONE; 1701 for (int i = 0; i < items.length; i++) { 1702 if (items[i].getBounds().contains(testPoint)) { 1703 childID = i; 1704 break; 1705 } 1706 } 1707 if (childID is ACC.CHILDID_NONE) { 1708 if (showChevron && chevronRect.contains(testPoint)) { 1709 childID = cast(int)/*64bit*/items.length + CHEVRON_CHILD_ID; 1710 } else if (showMin && minRect.contains(testPoint)) { 1711 childID = cast(int)/*64bit*/items.length + MINIMIZE_CHILD_ID; 1712 } else if (showMax && maxRect.contains(testPoint)) { 1713 childID = cast(int)/*64bit*/items.length + MAXIMIZE_CHILD_ID; 1714 } else { 1715 Rectangle location = getBounds(); 1716 location.height = location.height - getClientArea().height; 1717 if (location.contains(testPoint)) { 1718 childID = ACC.CHILDID_SELF; 1719 } 1720 } 1721 } 1722 e.childID = childID; 1723 } 1724 1725 override 1726 public void getLocation(AccessibleControlEvent e) { 1727 Rectangle location = null; 1728 Point pt = null; 1729 int childID = e.childID; 1730 if (childID is ACC.CHILDID_SELF) { 1731 location = getBounds(); 1732 pt = getParent().toDisplay(location.x, location.y); 1733 } else { 1734 if (childID >= 0 && childID < items.length && items[childID].isShowing()) { 1735 location = items[childID].getBounds(); 1736 } else if (showChevron && childID is items.length + CHEVRON_CHILD_ID) { 1737 location = chevronRect; 1738 } else if (showMin && childID is items.length + MINIMIZE_CHILD_ID) { 1739 location = minRect; 1740 } else if (showMax && childID is items.length + MAXIMIZE_CHILD_ID) { 1741 location = maxRect; 1742 } 1743 if (location !is null) { 1744 pt = toDisplay(location.x, location.y); 1745 } 1746 } 1747 if (location !is null && pt !is null) { 1748 e.x = pt.x; 1749 e.y = pt.y; 1750 e.width = location.width; 1751 e.height = location.height; 1752 } 1753 } 1754 1755 override 1756 public void getChildCount(AccessibleControlEvent e) { 1757 e.detail = cast(int)/*64bit*/items.length + EXTRA_CHILD_ID_COUNT; 1758 } 1759 1760 override 1761 public void getDefaultAction(AccessibleControlEvent e) { 1762 String action = null; 1763 int childID = e.childID; 1764 if (childID >= 0 && childID < items.length) { 1765 action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$ 1766 } 1767 if (childID >= items.length && childID < items.length + EXTRA_CHILD_ID_COUNT) { 1768 action = SWT.getMessage ("SWT_Press"); //$NON-NLS-1$ 1769 } 1770 e.result = action; 1771 } 1772 1773 override 1774 public void getFocus(AccessibleControlEvent e) { 1775 int childID = ACC.CHILDID_NONE; 1776 if (isFocusControl()) { 1777 if (selectedIndex is -1) { 1778 childID = ACC.CHILDID_SELF; 1779 } else { 1780 childID = selectedIndex; 1781 } 1782 } 1783 e.childID = childID; 1784 } 1785 1786 override 1787 public void getRole(AccessibleControlEvent e) { 1788 int role = 0; 1789 int childID = e.childID; 1790 if (childID is ACC.CHILDID_SELF) { 1791 role = ACC.ROLE_TABFOLDER; 1792 } else if (childID >= 0 && childID < items.length) { 1793 role = ACC.ROLE_TABITEM; 1794 } else if (childID >= items.length && childID < items.length + EXTRA_CHILD_ID_COUNT) { 1795 role = ACC.ROLE_PUSHBUTTON; 1796 } 1797 e.detail = role; 1798 } 1799 1800 override 1801 public void getSelection(AccessibleControlEvent e) { 1802 e.childID = (selectedIndex is -1) ? ACC.CHILDID_NONE : selectedIndex; 1803 } 1804 1805 override 1806 public void getState(AccessibleControlEvent e) { 1807 int state = 0; 1808 int childID = e.childID; 1809 if (childID is ACC.CHILDID_SELF) { 1810 state = ACC.STATE_NORMAL; 1811 } else if (childID >= 0 && childID < items.length) { 1812 state = ACC.STATE_SELECTABLE; 1813 if (isFocusControl()) { 1814 state |= ACC.STATE_FOCUSABLE; 1815 } 1816 if (selectedIndex is childID) { 1817 state |= ACC.STATE_SELECTED; 1818 if (isFocusControl()) { 1819 state |= ACC.STATE_FOCUSED; 1820 } 1821 } 1822 } else if (childID is items.length + CHEVRON_CHILD_ID) { 1823 state = showChevron ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE; 1824 } else if (childID is items.length + MINIMIZE_CHILD_ID) { 1825 state = showMin ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE; 1826 } else if (childID is items.length + MAXIMIZE_CHILD_ID) { 1827 state = showMax ? ACC.STATE_NORMAL : ACC.STATE_INVISIBLE; 1828 } 1829 e.detail = state; 1830 } 1831 1832 override 1833 public void getChildren(AccessibleControlEvent e) { 1834 int childIdCount = cast(int)/*64bit*/items.length + EXTRA_CHILD_ID_COUNT; 1835 Object[] children = new Object[childIdCount]; 1836 for (int i = 0; i < childIdCount; i++) { 1837 children[i] = new Integer(i); 1838 } 1839 e.children = children; 1840 } 1841 }); 1842 1843 addListener(SWT.Selection, new class(accessible) Listener { 1844 Accessible acc; 1845 this( Accessible a ){ 1846 this.acc = a; 1847 } 1848 public void handleEvent(Event event) { 1849 if (isFocusControl()) { 1850 if (selectedIndex is -1) { 1851 acc.setFocus(ACC.CHILDID_SELF); 1852 } else { 1853 acc.setFocus(selectedIndex); 1854 } 1855 } 1856 } 1857 }); 1858 1859 addListener(SWT.FocusIn, new class(accessible) Listener { 1860 Accessible acc; 1861 this( Accessible a ){ this.acc = a; } 1862 public void handleEvent(Event event) { 1863 if (selectedIndex is -1) { 1864 acc.setFocus(ACC.CHILDID_SELF); 1865 } else { 1866 acc.setFocus(selectedIndex); 1867 } 1868 } 1869 }); 1870 } 1871 1872 void onKeyDown (Event event) { 1873 switch (event.keyCode) { 1874 case SWT.ARROW_LEFT: 1875 case SWT.ARROW_RIGHT: 1876 auto count = items.length; 1877 if (count is 0) return; 1878 if (selectedIndex is -1) return; 1879 int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) !is 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT; 1880 int offset = event.keyCode is leadKey ? -1 : 1; 1881 int index; 1882 if (!mru) { 1883 index = selectedIndex + offset; 1884 } else { 1885 int[] visible = new int[items.length]; 1886 int idx = 0; 1887 int current = -1; 1888 for (int i = 0; i < items.length; i++) { 1889 if (items[i].showing) { 1890 if (i is selectedIndex) current = idx; 1891 visible [idx++] = i; 1892 } 1893 } 1894 if (current + offset >= 0 && current + offset < idx){ 1895 index = visible [current + offset]; 1896 } else { 1897 if (showChevron) { 1898 CTabFolderEvent e = new CTabFolderEvent(this); 1899 e.widget = this; 1900 e.time = event.time; 1901 e.x = chevronRect.x; 1902 e.y = chevronRect.y; 1903 e.width = chevronRect.width; 1904 e.height = chevronRect.height; 1905 e.doit = true; 1906 for (int i = 0; i < folderListeners.length; i++) { 1907 folderListeners[i].showList(e); 1908 } 1909 if (e.doit && !isDisposed()) { 1910 showList(chevronRect); 1911 } 1912 } 1913 return; 1914 } 1915 } 1916 if (index < 0 || index >= count) return; 1917 setSelection (index, true); 1918 forceFocus(); 1919 break; 1920 default: 1921 break; 1922 } 1923 } 1924 void onDispose(Event event) { 1925 removeListener(SWT.Dispose, listener); 1926 notifyListeners(SWT.Dispose, event); 1927 event.type = SWT.None; 1928 /* 1929 * Usually when an item is disposed, destroyItem will change the size of the items array, 1930 * reset the bounds of all the tabs and manage the widget associated with the tab. 1931 * Since the whole folder is being disposed, this is not necessary. For speed 1932 * the inDispose flag is used to skip over this part of the item dispose. 1933 */ 1934 inDispose = true; 1935 1936 if (showMenu !is null && !showMenu.isDisposed()) { 1937 showMenu.dispose(); 1938 showMenu = null; 1939 } 1940 int length = cast(int)/*64bit*/items.length; 1941 for (int i = 0; i < length; i++) { 1942 if (items[i] !is null) { 1943 items[i].dispose(); 1944 } 1945 } 1946 1947 selectionGradientColors = null; 1948 selectionGradientPercents = null; 1949 selectionBgImage = null; 1950 1951 selectionBackground = null; 1952 selectionForeground = null; 1953 disposeSelectionHighlightGradientColors(); 1954 } 1955 void onDragDetect(Event event) { 1956 bool consume = false; 1957 if (chevronRect.contains(event.x, event.y) || 1958 minRect.contains(event.x, event.y) || 1959 maxRect.contains(event.x, event.y)){ 1960 consume = true; 1961 } else { 1962 for (int i = 0; i < items.length; i++) { 1963 if (items[i].closeRect.contains(event.x, event.y)) { 1964 consume = true; 1965 break; 1966 } 1967 } 1968 } 1969 if (consume) { 1970 event.type = SWT.None; 1971 } 1972 } 1973 void onFocus(Event event) { 1974 checkWidget(); 1975 if (selectedIndex >= 0) { 1976 redraw(); 1977 } else { 1978 setSelection(0, true); 1979 } 1980 } 1981 bool onMnemonic (Event event) { 1982 auto key = event.character; 1983 for (int i = 0; i < items.length; i++) { 1984 if (items[i] !is null) { 1985 auto mnemonic = _findMnemonic (items[i].getText ()); 1986 if (mnemonic !is '\0') { 1987 if ( CharacterToLower(key) is mnemonic) { 1988 setSelection(i, true); 1989 return true; 1990 } 1991 } 1992 } 1993 } 1994 return false; 1995 } 1996 void onMouseDoubleClick(Event event) { 1997 if (event.button !is 1 || 1998 (event.stateMask & SWT.BUTTON2) !is 0 || 1999 (event.stateMask & SWT.BUTTON3) !is 0) return; 2000 Event e = new Event(); 2001 e.item = getItem(new Point(event.x, event.y)); 2002 if (e.item !is null) { 2003 notifyListeners(SWT.DefaultSelection, e); 2004 } 2005 } 2006 void onMouse(Event event) { 2007 int x = event.x, y = event.y; 2008 CTabItem item = null; 2009 switch (event.type) { 2010 case SWT.MouseEnter: { 2011 setToolTipText(null); 2012 break; 2013 } 2014 case SWT.MouseExit: { 2015 if (minImageState !is NORMAL) { 2016 minImageState = NORMAL; 2017 redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); 2018 } 2019 if (maxImageState !is NORMAL) { 2020 maxImageState = NORMAL; 2021 redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); 2022 } 2023 if (chevronImageState !is NORMAL) { 2024 chevronImageState = NORMAL; 2025 redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); 2026 } 2027 for (int i=0; i<items.length; i++) { 2028 item = items[i]; 2029 if (i !is selectedIndex && item.closeImageState !is NONE) { 2030 item.closeImageState = NONE; 2031 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2032 } 2033 if (i is selectedIndex && item.closeImageState !is NORMAL) { 2034 item.closeImageState = NORMAL; 2035 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2036 } 2037 } 2038 break; 2039 } 2040 case SWT.MouseDown: { 2041 if (minRect.contains(x, y)) { 2042 if (event.button !is 1) return; 2043 minImageState = SELECTED; 2044 redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); 2045 update(); 2046 return; 2047 } 2048 if (maxRect.contains(x, y)) { 2049 if (event.button !is 1) return; 2050 maxImageState = SELECTED; 2051 redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); 2052 update(); 2053 return; 2054 } 2055 if (chevronRect.contains(x, y)) { 2056 if (event.button !is 1) return; 2057 if (chevronImageState !is HOT) { 2058 chevronImageState = HOT; 2059 } else { 2060 chevronImageState = SELECTED; 2061 } 2062 redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); 2063 update(); 2064 return; 2065 } 2066 item = null; 2067 if (single) { 2068 if (selectedIndex !is -1) { 2069 Rectangle bounds = items[selectedIndex].getBounds(); 2070 if (bounds.contains(x, y)){ 2071 item = items[selectedIndex]; 2072 } 2073 } 2074 } else { 2075 for (int i=0; i<items.length; i++) { 2076 Rectangle bounds = items[i].getBounds(); 2077 if (bounds.contains(x, y)){ 2078 item = items[i]; 2079 } 2080 } 2081 } 2082 if (item !is null) { 2083 if (item.closeRect.contains(x,y)){ 2084 if (event.button !is 1) return; 2085 item.closeImageState = SELECTED; 2086 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2087 update(); 2088 return; 2089 } 2090 int index = indexOf(item); 2091 if (item.showing){ 2092 setSelection(index, true); 2093 } 2094 return; 2095 } 2096 break; 2097 } 2098 case SWT.MouseMove: { 2099 _setToolTipText(event.x, event.y); 2100 bool close = false, minimize = false, maximize = false, chevron = false; 2101 if (minRect.contains(x, y)) { 2102 minimize = true; 2103 if (minImageState !is SELECTED && minImageState !is HOT) { 2104 minImageState = HOT; 2105 redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); 2106 } 2107 } 2108 if (maxRect.contains(x, y)) { 2109 maximize = true; 2110 if (maxImageState !is SELECTED && maxImageState !is HOT) { 2111 maxImageState = HOT; 2112 redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); 2113 } 2114 } 2115 if (chevronRect.contains(x, y)) { 2116 chevron = true; 2117 if (chevronImageState !is SELECTED && chevronImageState !is HOT) { 2118 chevronImageState = HOT; 2119 redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); 2120 } 2121 } 2122 if (minImageState !is NORMAL && !minimize) { 2123 minImageState = NORMAL; 2124 redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); 2125 } 2126 if (maxImageState !is NORMAL && !maximize) { 2127 maxImageState = NORMAL; 2128 redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); 2129 } 2130 if (chevronImageState !is NORMAL && !chevron) { 2131 chevronImageState = NORMAL; 2132 redraw(chevronRect.x, chevronRect.y, chevronRect.width, chevronRect.height, false); 2133 } 2134 for (int i=0; i<items.length; i++) { 2135 item = items[i]; 2136 close = false; 2137 if (item.getBounds().contains(x, y)) { 2138 close = true; 2139 if (item.closeRect.contains(x, y)) { 2140 if (item.closeImageState !is SELECTED && item.closeImageState !is HOT) { 2141 item.closeImageState = HOT; 2142 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2143 } 2144 } else { 2145 if (item.closeImageState !is NORMAL) { 2146 item.closeImageState = NORMAL; 2147 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2148 } 2149 } 2150 } 2151 if (i !is selectedIndex && item.closeImageState !is NONE && !close) { 2152 item.closeImageState = NONE; 2153 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2154 } 2155 if (i is selectedIndex && item.closeImageState !is NORMAL && !close) { 2156 item.closeImageState = NORMAL; 2157 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2158 } 2159 } 2160 break; 2161 } 2162 case SWT.MouseUp: { 2163 if (event.button !is 1) return; 2164 if (chevronRect.contains(x, y)) { 2165 bool selected = chevronImageState is SELECTED; 2166 if (!selected) return; 2167 CTabFolderEvent e = new CTabFolderEvent(this); 2168 e.widget = this; 2169 e.time = event.time; 2170 e.x = chevronRect.x; 2171 e.y = chevronRect.y; 2172 e.width = chevronRect.width; 2173 e.height = chevronRect.height; 2174 e.doit = true; 2175 for (int i = 0; i < folderListeners.length; i++) { 2176 folderListeners[i].showList(e); 2177 } 2178 if (e.doit && !isDisposed()) { 2179 showList(chevronRect); 2180 } 2181 return; 2182 } 2183 if (minRect.contains(x, y)) { 2184 bool selected = minImageState is SELECTED; 2185 minImageState = HOT; 2186 redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); 2187 if (!selected) return; 2188 CTabFolderEvent e = new CTabFolderEvent(this); 2189 e.widget = this; 2190 e.time = event.time; 2191 for (int i = 0; i < folderListeners.length; i++) { 2192 if (minimized) { 2193 folderListeners[i].restore(e); 2194 } else { 2195 folderListeners[i].minimize(e); 2196 } 2197 } 2198 return; 2199 } 2200 if (maxRect.contains(x, y)) { 2201 bool selected = maxImageState is SELECTED; 2202 maxImageState = HOT; 2203 redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); 2204 if (!selected) return; 2205 CTabFolderEvent e = new CTabFolderEvent(this); 2206 e.widget = this; 2207 e.time = event.time; 2208 for (int i = 0; i < folderListeners.length; i++) { 2209 if (maximized) { 2210 folderListeners[i].restore(e); 2211 } else { 2212 folderListeners[i].maximize(e); 2213 } 2214 } 2215 return; 2216 } 2217 item = null; 2218 if (single) { 2219 if (selectedIndex !is -1) { 2220 Rectangle bounds = items[selectedIndex].getBounds(); 2221 if (bounds.contains(x, y)){ 2222 item = items[selectedIndex]; 2223 } 2224 } 2225 } else { 2226 for (int i=0; i<items.length; i++) { 2227 Rectangle bounds = items[i].getBounds(); 2228 if (bounds.contains(x, y)){ 2229 item = items[i]; 2230 } 2231 } 2232 } 2233 if (item !is null) { 2234 if (item.closeRect.contains(x,y)) { 2235 bool selected = item.closeImageState is SELECTED; 2236 item.closeImageState = HOT; 2237 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false); 2238 if (!selected) return; 2239 CTabFolderEvent e = new CTabFolderEvent(this); 2240 e.widget = this; 2241 e.time = event.time; 2242 e.item = item; 2243 e.doit = true; 2244 for (int j = 0; j < folderListeners.length; j++) { 2245 CTabFolder2Listener listener = folderListeners[j]; 2246 listener.close(e); 2247 } 2248 for (int j = 0; j < tabListeners.length; j++) { 2249 CTabFolderListener listener = tabListeners[j]; 2250 listener.itemClosed(e); 2251 } 2252 if (e.doit) item.dispose(); 2253 if (!isDisposed() && item.isDisposed()) { 2254 Display display = getDisplay(); 2255 Point pt = display.getCursorLocation(); 2256 pt = display.map(null, this, pt.x, pt.y); 2257 CTabItem nextItem = getItem(pt); 2258 if (nextItem !is null) { 2259 if (nextItem.closeRect.contains(pt)) { 2260 if (nextItem.closeImageState !is SELECTED && nextItem.closeImageState !is HOT) { 2261 nextItem.closeImageState = HOT; 2262 redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false); 2263 } 2264 } else { 2265 if (nextItem.closeImageState !is NORMAL) { 2266 nextItem.closeImageState = NORMAL; 2267 redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false); 2268 } 2269 } 2270 } 2271 } 2272 return; 2273 } 2274 } 2275 default: 2276 } 2277 } 2278 } 2279 bool onPageTraversal(Event event) { 2280 auto count = items.length; 2281 if (count is 0) return false; 2282 size_t index = selectedIndex; 2283 if (index is -1) { 2284 index = 0; 2285 } else { 2286 int offset = (event.detail is SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1; 2287 if (!mru) { 2288 index = (selectedIndex + offset + count) % count; 2289 } else { 2290 int[] visible = new int[items.length]; 2291 int idx = 0; 2292 int current = -1; 2293 for (int i = 0; i < items.length; i++) { 2294 if (items[i].showing) { 2295 if (i is selectedIndex) current = idx; 2296 visible [idx++] = i; 2297 } 2298 } 2299 if (current + offset >= 0 && current + offset < idx){ 2300 index = visible [current + offset]; 2301 } else { 2302 if (showChevron) { 2303 CTabFolderEvent e = new CTabFolderEvent(this); 2304 e.widget = this; 2305 e.time = event.time; 2306 e.x = chevronRect.x; 2307 e.y = chevronRect.y; 2308 e.width = chevronRect.width; 2309 e.height = chevronRect.height; 2310 e.doit = true; 2311 for (int i = 0; i < folderListeners.length; i++) { 2312 folderListeners[i].showList(e); 2313 } 2314 if (e.doit && !isDisposed()) { 2315 showList(chevronRect); 2316 } 2317 } 2318 return true; 2319 } 2320 } 2321 } 2322 setSelection (cast(int)/*64bit*/index, true); 2323 return true; 2324 } 2325 void onPaint(Event event) { 2326 if (inDispose) return; 2327 Font font = getFont(); 2328 if (oldFont is null || oldFont!=font) { 2329 // handle case where default font changes 2330 oldFont = font; 2331 if (!updateTabHeight(false)) { 2332 updateItems(); 2333 redraw(); 2334 return; 2335 } 2336 } 2337 2338 GC gc = event.gc; 2339 Font gcFont = gc.getFont(); 2340 Color gcBackground = gc.getBackground(); 2341 Color gcForeground = gc.getForeground(); 2342 2343 // Useful for debugging paint problems 2344 //{ 2345 //Point size = getSize(); 2346 //gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN)); 2347 //gc.fillRectangle(-10, -10, size.x + 20, size.y+20); 2348 //} 2349 2350 drawBody(event); 2351 2352 gc.setFont(gcFont); 2353 gc.setForeground(gcForeground); 2354 gc.setBackground(gcBackground); 2355 2356 drawTabArea(event); 2357 2358 gc.setFont(gcFont); 2359 gc.setForeground(gcForeground); 2360 gc.setBackground(gcBackground); 2361 } 2362 2363 void onResize() { 2364 if (updateItems()) redrawTabs(); 2365 2366 Point size = getSize(); 2367 if (oldSize is null) { 2368 redraw(); 2369 } else { 2370 if (onBottom && size.y !is oldSize.y) { 2371 redraw(); 2372 } else { 2373 int x1 = Math.min(size.x, oldSize.x); 2374 if (size.x !is oldSize.x) x1 -= borderRight + highlight_margin + 2; 2375 if (!simple) x1 -= 5; // rounded top right corner 2376 int y1 = Math.min(size.y, oldSize.y); 2377 if (size.y !is oldSize.y) y1 -= borderBottom + highlight_margin; 2378 int x2 = Math.max(size.x, oldSize.x); 2379 int y2 = Math.max(size.y, oldSize.y); 2380 redraw(0, y1, x2, y2 - y1, false); 2381 redraw(x1, 0, x2 - x1, y2, false); 2382 } 2383 } 2384 oldSize = size; 2385 } 2386 void onTraverse (Event event) { 2387 switch (event.detail) { 2388 case SWT.TRAVERSE_ESCAPE: 2389 case SWT.TRAVERSE_RETURN: 2390 case SWT.TRAVERSE_TAB_NEXT: 2391 case SWT.TRAVERSE_TAB_PREVIOUS: 2392 Control focusControl = getDisplay().getFocusControl(); 2393 if (focusControl is this) event.doit = true; 2394 break; 2395 case SWT.TRAVERSE_MNEMONIC: 2396 event.doit = onMnemonic(event); 2397 if (event.doit) event.detail = SWT.TRAVERSE_NONE; 2398 break; 2399 case SWT.TRAVERSE_PAGE_NEXT: 2400 case SWT.TRAVERSE_PAGE_PREVIOUS: 2401 event.doit = onPageTraversal(event); 2402 event.detail = SWT.TRAVERSE_NONE; 2403 break; 2404 default: 2405 } 2406 } 2407 void redrawTabs() { 2408 Point size = getSize(); 2409 if (onBottom) { 2410 redraw(0, size.y - borderBottom - tabHeight - highlight_header - 1, size.x, borderBottom + tabHeight + highlight_header + 1, false); 2411 } else { 2412 redraw(0, 0, size.x, borderTop + tabHeight + highlight_header + 1, false); 2413 } 2414 } 2415 /** 2416 * Removes the listener. 2417 * 2418 * @param listener the listener which should no longer be notified 2419 * 2420 * @exception IllegalArgumentException <ul> 2421 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 2422 * </ul> 2423 * 2424 * @exception SWTException <ul> 2425 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 2426 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 2427 * </ul> 2428 * 2429 * @see #addCTabFolder2Listener(CTabFolder2Listener) 2430 * 2431 * @since 3.0 2432 */ 2433 public void removeCTabFolder2Listener(CTabFolder2Listener listener) { 2434 checkWidget(); 2435 if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 2436 if (folderListeners.length is 0) return; 2437 int index = -1; 2438 for (int i = 0; i < folderListeners.length; i++) { 2439 if (listener is folderListeners[i]){ 2440 index = i; 2441 break; 2442 } 2443 } 2444 if (index is -1) return; 2445 if (folderListeners.length is 1) { 2446 folderListeners = new CTabFolder2Listener[0]; 2447 return; 2448 } 2449 CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1]; 2450 SimpleType!(CTabFolder2Listener).arraycopy(folderListeners, 0, newTabListeners, 0, index); 2451 SimpleType!(CTabFolder2Listener).arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1); 2452 folderListeners = newTabListeners; 2453 } 2454 /** 2455 * Removes the listener. 2456 * 2457 * @param listener the listener which should no longer be notified 2458 * 2459 * @exception IllegalArgumentException <ul> 2460 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 2461 * </ul> 2462 * 2463 * @exception SWTException <ul> 2464 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 2465 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 2466 * </ul> 2467 * 2468 * @deprecated see removeCTabFolderCloseListener(CTabFolderListener) 2469 */ 2470 public void removeCTabFolderListener(CTabFolderListener listener) { 2471 checkWidget(); 2472 if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 2473 if (tabListeners.length is 0) return; 2474 int index = -1; 2475 for (int i = 0; i < tabListeners.length; i++) { 2476 if (listener is tabListeners[i]){ 2477 index = i; 2478 break; 2479 } 2480 } 2481 if (index is -1) return; 2482 if (tabListeners.length is 1) { 2483 tabListeners = new CTabFolderListener[0]; 2484 return; 2485 } 2486 CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1]; 2487 SimpleType!(CTabFolderListener).arraycopy(tabListeners, 0, newTabListeners, 0, index); 2488 SimpleType!(CTabFolderListener).arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1); 2489 tabListeners = newTabListeners; 2490 } 2491 /** 2492 * Removes the listener from the collection of listeners who will 2493 * be notified when the user changes the receiver's selection. 2494 * 2495 * @param listener the listener which should no longer be notified 2496 * 2497 * @exception IllegalArgumentException <ul> 2498 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 2499 * </ul> 2500 * @exception SWTException <ul> 2501 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2502 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2503 * </ul> 2504 * 2505 * @see SelectionListener 2506 * @see #addSelectionListener 2507 */ 2508 public void removeSelectionListener(SelectionListener listener) { 2509 checkWidget(); 2510 if (listener is null) { 2511 SWT.error(SWT.ERROR_NULL_ARGUMENT); 2512 } 2513 removeListener(SWT.Selection, listener); 2514 removeListener(SWT.DefaultSelection, listener); 2515 } 2516 public override void setBackground (Color color) { 2517 super.setBackground(color); 2518 redraw(); 2519 } 2520 /** 2521 * Specify a gradient of colours to be drawn in the background of the unselected tabs. 2522 * For example to draw a gradient that varies from dark blue to blue and then to 2523 * white, use the following call to setBackground: 2524 * <pre> 2525 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), 2526 * display.getSystemColor(SWT.COLOR_BLUE), 2527 * display.getSystemColor(SWT.COLOR_WHITE), 2528 * display.getSystemColor(SWT.COLOR_WHITE)}, 2529 * new int[] {25, 50, 100}); 2530 * </pre> 2531 * 2532 * @param colors an array of Color that specifies the colors to appear in the gradient 2533 * in order of appearance left to right. The value <code>null</code> clears the 2534 * background gradient. The value <code>null</code> can be used inside the array of 2535 * Color to specify the background color. 2536 * @param percents an array of integers between 0 and 100 specifying the percent of the width 2537 * of the widget at which the color should change. The size of the percents array must be one 2538 * less than the size of the colors array. 2539 * 2540 * @exception SWTException <ul> 2541 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 2542 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 2543 * </ul> 2544 * 2545 * @since 3.0 2546 */ 2547 void setBackground(Color[] colors, int[] percents) { 2548 setBackground(colors, percents, false); 2549 } 2550 /** 2551 * Specify a gradient of colours to be drawn in the background of the unselected tab. 2552 * For example to draw a vertical gradient that varies from dark blue to blue and then to 2553 * white, use the following call to setBackground: 2554 * <pre> 2555 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), 2556 * display.getSystemColor(SWT.COLOR_BLUE), 2557 * display.getSystemColor(SWT.COLOR_WHITE), 2558 * display.getSystemColor(SWT.COLOR_WHITE)}, 2559 * new int[] {25, 50, 100}, true); 2560 * </pre> 2561 * 2562 * @param colors an array of Color that specifies the colors to appear in the gradient 2563 * in order of appearance left to right. The value <code>null</code> clears the 2564 * background gradient. The value <code>null</code> can be used inside the array of 2565 * Color to specify the background color. 2566 * @param percents an array of integers between 0 and 100 specifying the percent of the width 2567 * of the widget at which the color should change. The size of the percents array must be one 2568 * less than the size of the colors array. 2569 * 2570 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal. 2571 * 2572 * @exception SWTException <ul> 2573 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 2574 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 2575 * </ul> 2576 * 2577 * @since 3.0 2578 */ 2579 void setBackground(Color[] colors, int[] percents, bool vertical) { 2580 checkWidget(); 2581 if (colors !is null) { 2582 if (percents is null || percents.length !is colors.length - 1) { 2583 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 2584 } 2585 for (int i = 0; i < percents.length; i++) { 2586 if (percents[i] < 0 || percents[i] > 100) { 2587 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 2588 } 2589 if (i > 0 && percents[i] < percents[i-1]) { 2590 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 2591 } 2592 } 2593 if (getDisplay().getDepth() < 15) { 2594 // Don't use gradients on low color displays 2595 colors = [colors[colors.length - 1]]; 2596 percents = null; 2597 } 2598 } 2599 2600 // Are these settings the same as before? 2601 if (bgImage is null) { 2602 if ((gradientColors !is null) && (colors !is null) && 2603 (gradientColors.length is colors.length)) { 2604 bool same = false; 2605 for (int i = 0; i < gradientColors.length; i++) { 2606 if (gradientColors[i] is null) { 2607 same = colors[i] is null; 2608 } else { 2609 same = cast(bool)(gradientColors[i]==colors[i]); 2610 } 2611 if (!same) break; 2612 } 2613 if (same) { 2614 for (int i = 0; i < gradientPercents.length; i++) { 2615 same = gradientPercents[i] is percents[i]; 2616 if (!same) break; 2617 } 2618 } 2619 if (same && this.gradientVertical is vertical) return; 2620 } 2621 } else { 2622 bgImage = null; 2623 } 2624 // Store the new settings 2625 if (colors is null) { 2626 gradientColors = null; 2627 gradientPercents = null; 2628 gradientVertical = false; 2629 setBackground(cast(Color)null); 2630 } else { 2631 gradientColors = new Color[colors.length]; 2632 for (int i = 0; i < colors.length; ++i) { 2633 gradientColors[i] = colors[i]; 2634 } 2635 gradientPercents = new int[percents.length]; 2636 for (int i = 0; i < percents.length; ++i) { 2637 gradientPercents[i] = percents[i]; 2638 } 2639 gradientVertical = vertical; 2640 setBackground(gradientColors[gradientColors.length-1]); 2641 } 2642 2643 // Refresh with the new settings 2644 redraw(); 2645 } 2646 2647 /** 2648 * Set the image to be drawn in the background of the unselected tab. Image 2649 * is stretched or compressed to cover entire unselected tab area. 2650 * 2651 * @param image the image to be drawn in the background 2652 * 2653 * @exception SWTException <ul> 2654 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2655 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2656 * </ul> 2657 * 2658 * @since 3.0 2659 */ 2660 void setBackground(Image image) { 2661 checkWidget(); 2662 if (image is bgImage) return; 2663 if (image !is null) { 2664 gradientColors = null; 2665 gradientPercents = null; 2666 } 2667 bgImage = image; 2668 redraw(); 2669 } 2670 /** 2671 * Toggle the visibility of the border 2672 * 2673 * @param show true if the border should be displayed 2674 * 2675 * @exception SWTException <ul> 2676 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2677 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2678 * </ul> 2679 */ 2680 public void setBorderVisible(bool show) { 2681 checkWidget(); 2682 if ((borderLeft is 1) is show) return; 2683 borderLeft = borderRight = show ? 1 : 0; 2684 borderTop = onBottom ? borderLeft : 0; 2685 borderBottom = onBottom ? 0 : borderLeft; 2686 Rectangle rectBefore = getClientArea(); 2687 updateItems(); 2688 Rectangle rectAfter = getClientArea(); 2689 if (rectBefore!=rectAfter) { 2690 notifyListeners(SWT.Resize, new Event()); 2691 } 2692 redraw(); 2693 } 2694 void setButtonBounds() { 2695 Point size = getSize(); 2696 int oldX, oldY, oldWidth, oldHeight; 2697 // max button 2698 oldX = maxRect.x; 2699 oldY = maxRect.y; 2700 oldWidth = maxRect.width; 2701 oldHeight = maxRect.height; 2702 maxRect.x = maxRect.y = maxRect.width = maxRect.height = 0; 2703 if (showMax) { 2704 maxRect.x = size.x - borderRight - BUTTON_SIZE - 3; 2705 if (borderRight > 0) maxRect.x += 1; 2706 maxRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2; 2707 maxRect.width = BUTTON_SIZE; 2708 maxRect.height = BUTTON_SIZE; 2709 } 2710 if (oldX !is maxRect.x || oldWidth !is maxRect.width || 2711 oldY !is maxRect.y || oldHeight !is maxRect.height) { 2712 int left = Math.min(oldX, maxRect.x); 2713 int right = Math.max(oldX + oldWidth, maxRect.x + maxRect.width); 2714 int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1; 2715 redraw(left, top, right - left, tabHeight, false); 2716 } 2717 2718 // min button 2719 oldX = minRect.x; 2720 oldY = minRect.y; 2721 oldWidth = minRect.width; 2722 oldHeight = minRect.height; 2723 minRect.x = minRect.y = minRect.width = minRect.height = 0; 2724 if (showMin) { 2725 minRect.x = size.x - borderRight - maxRect.width - BUTTON_SIZE - 3; 2726 if (borderRight > 0) minRect.x += 1; 2727 minRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2; 2728 minRect.width = BUTTON_SIZE; 2729 minRect.height = BUTTON_SIZE; 2730 } 2731 if (oldX !is minRect.x || oldWidth !is minRect.width || 2732 oldY !is minRect.y || oldHeight !is minRect.height) { 2733 int left = Math.min(oldX, minRect.x); 2734 int right = Math.max(oldX + oldWidth, minRect.x + minRect.width); 2735 int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1; 2736 redraw(left, top, right - left, tabHeight, false); 2737 } 2738 2739 // top right control 2740 oldX = topRightRect.x; 2741 oldY = topRightRect.y; 2742 oldWidth = topRightRect.width; 2743 oldHeight = topRightRect.height; 2744 topRightRect.x = topRightRect.y = topRightRect.width = topRightRect.height = 0; 2745 if (topRight !is null) { 2746 CTabItem item = null; 2747 switch (topRightAlignment) { 2748 case SWT.FILL: { 2749 int rightEdge = size.x - borderRight - 3 - maxRect.width - minRect.width; 2750 if (!simple && borderRight > 0 && !showMax && !showMin) rightEdge -= 2; 2751 if (single) { 2752 if (items.length is 0 || selectedIndex is -1) { 2753 topRightRect.x = borderLeft + 3; 2754 topRightRect.width = rightEdge - topRightRect.x; 2755 } else { 2756 // fill size is 0 if item compressed 2757 item = items[selectedIndex]; 2758 if (item.x + item.width + 7 + 3*BUTTON_SIZE/2 >= rightEdge) break; 2759 topRightRect.x = item.x + item.width + 7 + 3*BUTTON_SIZE/2; 2760 topRightRect.width = rightEdge - topRightRect.x; 2761 } 2762 } else { 2763 // fill size is 0 if chevron showing 2764 if (showChevron) break; 2765 if (items.length is 0) { 2766 topRightRect.x = borderLeft + 3; 2767 } else { 2768 item = items[items.length - 1]; 2769 topRightRect.x = item.x + item.width; 2770 if (!simple && items.length - 1 is selectedIndex) topRightRect.x += curveWidth - curveIndent; 2771 } 2772 topRightRect.width = Math.max(0, rightEdge - topRightRect.x); 2773 } 2774 topRightRect.y = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1; 2775 topRightRect.height = tabHeight - 1; 2776 break; 2777 } 2778 case SWT.RIGHT: { 2779 Point topRightSize = topRight.computeSize(SWT.DEFAULT, tabHeight, false); 2780 int rightEdge = size.x - borderRight - 3 - maxRect.width - minRect.width; 2781 if (!simple && borderRight > 0 && !showMax && !showMin) rightEdge -= 2; 2782 topRightRect.x = rightEdge - topRightSize.x; 2783 topRightRect.width = topRightSize.x; 2784 topRightRect.y = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1; 2785 topRightRect.height = tabHeight - 1; 2786 break; 2787 } 2788 default: 2789 break; 2790 } 2791 topRight.setBounds(topRightRect); 2792 } 2793 if (oldX !is topRightRect.x || oldWidth !is topRightRect.width || 2794 oldY !is topRightRect.y || oldHeight !is topRightRect.height) { 2795 int left = Math.min(oldX, topRightRect.x); 2796 int right = Math.max(oldX + oldWidth, topRightRect.x + topRightRect.width); 2797 int top = onBottom ? size.y - borderBottom - tabHeight : borderTop + 1; 2798 redraw(left, top, right - left, tabHeight, false); 2799 } 2800 2801 // chevron button 2802 oldX = chevronRect.x; 2803 oldY = chevronRect.y; 2804 oldWidth = chevronRect.width; 2805 oldHeight = chevronRect.height; 2806 chevronRect.x = chevronRect.y = chevronRect.height = chevronRect.width = 0; 2807 if (single) { 2808 if (selectedIndex is -1 || items.length > 1) { 2809 chevronRect.width = 3*BUTTON_SIZE/2; 2810 chevronRect.height = BUTTON_SIZE; 2811 chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height)/2 : borderTop + (tabHeight - chevronRect.height)/2; 2812 if (selectedIndex is -1) { 2813 chevronRect.x = size.x - borderRight - 3 - minRect.width - maxRect.width - topRightRect.width - chevronRect.width; 2814 } else { 2815 CTabItem item = items[selectedIndex]; 2816 int w = size.x - borderRight - 3 - minRect.width - maxRect.width - chevronRect.width; 2817 if (topRightRect.width > 0) w -= topRightRect.width + 3; 2818 chevronRect.x = Math.min(item.x + item.width + 3, w); 2819 } 2820 if (borderRight > 0) chevronRect.x += 1; 2821 } 2822 } else { 2823 if (showChevron) { 2824 chevronRect.width = 3*BUTTON_SIZE/2; 2825 chevronRect.height = BUTTON_SIZE; 2826 int i = 0, lastIndex = -1; 2827 while (i < priority.length && items[priority[i]].showing) { 2828 lastIndex = Math.max(lastIndex, priority[i++]); 2829 } 2830 if (lastIndex is -1) lastIndex = firstIndex; 2831 CTabItem lastItem = items[lastIndex]; 2832 int w = lastItem.x + lastItem.width + 3; 2833 if (!simple && lastIndex is selectedIndex) w += curveWidth - 2*curveIndent; 2834 chevronRect.x = Math.min(w, getRightItemEdge()); 2835 chevronRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - chevronRect.height)/2 : borderTop + (tabHeight - chevronRect.height)/2; 2836 } 2837 } 2838 if (oldX !is chevronRect.x || oldWidth !is chevronRect.width || 2839 oldY !is chevronRect.y || oldHeight !is chevronRect.height) { 2840 int left = Math.min(oldX, chevronRect.x); 2841 int right = Math.max(oldX + oldWidth, chevronRect.x + chevronRect.width); 2842 int top = onBottom ? size.y - borderBottom - tabHeight: borderTop + 1; 2843 redraw(left, top, right - left, tabHeight, false); 2844 } 2845 } 2846 public override void setFont(Font font) { 2847 checkWidget(); 2848 if (font !is null && font==getFont()) return; 2849 super.setFont(font); 2850 oldFont = getFont(); 2851 if (!updateTabHeight(false)) { 2852 updateItems(); 2853 redraw(); 2854 } 2855 } 2856 public override void setForeground (Color color) { 2857 super.setForeground(color); 2858 redraw(); 2859 } 2860 /** 2861 * Display an insert marker before or after the specified tab item. 2862 * 2863 * A value of null will clear the mark. 2864 * 2865 * @param item the item with which the mark is associated or null 2866 * 2867 * @param after true if the mark should be displayed after the specified item 2868 * 2869 * @exception SWTException <ul> 2870 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2871 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2872 * </ul> 2873 */ 2874 public void setInsertMark(CTabItem item, bool after) { 2875 checkWidget(); 2876 } 2877 /** 2878 * Display an insert marker before or after the specified tab item. 2879 * 2880 * A value of -1 will clear the mark. 2881 * 2882 * @param index the index of the item with which the mark is associated or null 2883 * 2884 * @param after true if the mark should be displayed after the specified item 2885 * 2886 * @exception IllegalArgumentException<ul> 2887 * </ul> 2888 * 2889 * @exception SWTException <ul> 2890 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2891 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2892 * </ul> 2893 */ 2894 public void setInsertMark(int index, bool after) { 2895 checkWidget(); 2896 if (index < -1 || index >= getItemCount()) { 2897 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 2898 } 2899 } 2900 bool setItemLocation() { 2901 bool changed = false; 2902 if (items.length is 0) return false; 2903 Point size = getSize(); 2904 int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop; 2905 if (single) { 2906 int defaultX = getDisplay().getBounds().width + 10; // off screen 2907 for (int i = 0; i < items.length; i++) { 2908 CTabItem item = items[i]; 2909 if (i is selectedIndex) { 2910 firstIndex = selectedIndex; 2911 int oldX = item.x, oldY = item.y; 2912 item.x = borderLeft; 2913 item.y = y; 2914 item.showing = true; 2915 if (showClose || item.showClose) { 2916 item.closeRect.x = borderLeft + CTabItem.LEFT_MARGIN; 2917 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2; 2918 } 2919 if (item.x !is oldX || item.y !is oldY) changed = true; 2920 } else { 2921 item.x = defaultX; 2922 item.showing = false; 2923 } 2924 } 2925 } else { 2926 int rightItemEdge = getRightItemEdge(); 2927 int maxWidth = rightItemEdge - borderLeft; 2928 int width = 0; 2929 for (int i = 0; i < priority.length; i++) { 2930 CTabItem item = items[priority[i]]; 2931 width += item.width; 2932 item.showing = i is 0 ? true : item.width > 0 && width <= maxWidth; 2933 if (!simple && priority[i] is selectedIndex) width += curveWidth - 2*curveIndent; 2934 } 2935 int x = 0; 2936 int defaultX = getDisplay().getBounds().width + 10; // off screen 2937 firstIndex = cast(int)/*64bit*/items.length - 1; 2938 for (int i = 0; i < items.length; i++) { 2939 CTabItem item = items[i]; 2940 if (!item.showing) { 2941 if (item.x !is defaultX) changed = true; 2942 item.x = defaultX; 2943 } else { 2944 firstIndex = Math.min(firstIndex, i); 2945 if (item.x !is x || item.y !is y) changed = true; 2946 item.x = x; 2947 item.y = y; 2948 if (i is selectedIndex) { 2949 int edge = Math.min(item.x + item.width, rightItemEdge); 2950 item.closeRect.x = edge - CTabItem.RIGHT_MARGIN - BUTTON_SIZE; 2951 } else { 2952 item.closeRect.x = item.x + item.width - CTabItem.RIGHT_MARGIN - BUTTON_SIZE; 2953 } 2954 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - BUTTON_SIZE)/2: borderTop + (tabHeight - BUTTON_SIZE)/2; 2955 x = x + item.width; 2956 if (!simple && i is selectedIndex) x += curveWidth - 2*curveIndent; 2957 } 2958 } 2959 } 2960 return changed; 2961 } 2962 bool setItemSize() { 2963 bool changed = false; 2964 if (isDisposed()) return changed; 2965 Point size = getSize(); 2966 if (size.x <= 0 || size.y <= 0) return changed; 2967 xClient = borderLeft + marginWidth + highlight_margin; 2968 if (onBottom) { 2969 yClient = borderTop + highlight_margin + marginHeight; 2970 } else { 2971 yClient = borderTop + tabHeight + highlight_header + marginHeight; 2972 } 2973 showChevron = false; 2974 if (single) { 2975 showChevron = true; 2976 if (selectedIndex !is -1) { 2977 CTabItem tab = items[selectedIndex]; 2978 GC gc = new GC(this); 2979 int width = tab.preferredWidth(gc, true, false); 2980 gc.dispose(); 2981 width = Math.min(width, getRightItemEdge() - borderLeft); 2982 if (tab.height !is tabHeight || tab.width !is width) { 2983 changed = true; 2984 tab.shortenedText = null; 2985 tab.shortenedTextWidth = 0; 2986 tab.height = tabHeight; 2987 tab.width = width; 2988 tab.closeRect.width = tab.closeRect.height = 0; 2989 if (showClose || tab.showClose) { 2990 tab.closeRect.width = BUTTON_SIZE; 2991 tab.closeRect.height = BUTTON_SIZE; 2992 } 2993 } 2994 } 2995 return changed; 2996 } 2997 2998 if (items.length is 0) return changed; 2999 3000 int[] widths; 3001 GC gc = new GC(this); 3002 int tabAreaWidth = size.x - borderLeft - borderRight - 3; 3003 if (showMin) tabAreaWidth -= BUTTON_SIZE; 3004 if (showMax) tabAreaWidth -= BUTTON_SIZE; 3005 if (topRightAlignment is SWT.RIGHT && topRight !is null) { 3006 Point rightSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); 3007 tabAreaWidth -= rightSize.x + 3; 3008 } 3009 if (!simple) tabAreaWidth -= curveWidth - 2*curveIndent; 3010 tabAreaWidth = Math.max(0, tabAreaWidth); 3011 3012 // First, try the minimum tab size at full compression. 3013 int minWidth = 0; 3014 int[] minWidths = new int[items.length]; 3015 for (int i = 0; i < priority.length; i++) { 3016 int index = priority[i]; 3017 minWidths[index] = items[index].preferredWidth(gc, index is selectedIndex, true); 3018 minWidth += minWidths[index]; 3019 if (minWidth > tabAreaWidth) break; 3020 } 3021 if (minWidth > tabAreaWidth) { 3022 // full compression required and a chevron 3023 showChevron = items.length > 1; 3024 if (showChevron) tabAreaWidth -= 3*BUTTON_SIZE/2; 3025 widths = minWidths; 3026 int index = selectedIndex !is -1 ? selectedIndex : 0; 3027 if (tabAreaWidth < widths[index]) { 3028 widths[index] = Math.max(0, tabAreaWidth); 3029 } 3030 } else { 3031 int maxWidth = 0; 3032 int[] maxWidths = new int[items.length]; 3033 for (int i = 0; i < items.length; i++) { 3034 maxWidths[i] = items[i].preferredWidth(gc, i is selectedIndex, false); 3035 maxWidth += maxWidths[i]; 3036 } 3037 if (maxWidth <= tabAreaWidth) { 3038 // no compression required 3039 widths = maxWidths; 3040 } else { 3041 // determine compression for each item 3042 auto extra = (tabAreaWidth - minWidth) / items.length; 3043 while (true) { 3044 int large = 0, totalWidth = 0; 3045 for (int i = 0 ; i < items.length; i++) { 3046 if (maxWidths[i] > minWidths[i] + extra) { 3047 totalWidth += minWidths[i] + extra; 3048 large++; 3049 } else { 3050 totalWidth += maxWidths[i]; 3051 } 3052 } 3053 if (totalWidth >= tabAreaWidth) { 3054 extra--; 3055 break; 3056 } 3057 if (large is 0 || tabAreaWidth - totalWidth < large) break; 3058 extra++; 3059 } 3060 widths = new int[items.length]; 3061 for (int i = 0; i < items.length; i++) { 3062 widths[i] = cast(int)/*64bit*/Math.min 3063 (maxWidths[i], minWidths[i] + extra); 3064 } 3065 } 3066 } 3067 gc.dispose(); 3068 3069 for (int i = 0; i < items.length; i++) { 3070 CTabItem tab = items[i]; 3071 int width = widths[i]; 3072 if (tab.height !is tabHeight || tab.width !is width) { 3073 changed = true; 3074 tab.shortenedText = null; 3075 tab.shortenedTextWidth = 0; 3076 tab.height = tabHeight; 3077 tab.width = width; 3078 tab.closeRect.width = tab.closeRect.height = 0; 3079 if (showClose || tab.showClose) { 3080 if (i is selectedIndex || showUnselectedClose) { 3081 tab.closeRect.width = BUTTON_SIZE; 3082 tab.closeRect.height = BUTTON_SIZE; 3083 } 3084 } 3085 } 3086 } 3087 return changed; 3088 } 3089 /** 3090 * Marks the receiver's maximize button as visible if the argument is <code>true</code>, 3091 * and marks it invisible otherwise. 3092 * 3093 * @param visible the new visibility state 3094 * 3095 * @exception SWTException <ul> 3096 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3097 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3098 * </ul> 3099 * 3100 * @since 3.0 3101 */ 3102 public void setMaximizeVisible(bool visible) { 3103 checkWidget(); 3104 if (showMax is visible) return; 3105 // display maximize button 3106 showMax = visible; 3107 updateItems(); 3108 redraw(); 3109 } 3110 /** 3111 * Sets the layout which is associated with the receiver to be 3112 * the argument which may be null. 3113 * <p> 3114 * Note: No Layout can be set on this Control because it already 3115 * manages the size and position of its children. 3116 * </p> 3117 * 3118 * @param layout the receiver's new layout or null 3119 * 3120 * @exception SWTException <ul> 3121 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3122 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3123 * </ul> 3124 */ 3125 public override void setLayout (Layout layout) { 3126 checkWidget(); 3127 return; 3128 } 3129 /** 3130 * Sets the maximized state of the receiver. 3131 * 3132 * @param maximize the new maximized state 3133 * 3134 * @exception SWTException <ul> 3135 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3136 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3137 * </ul> 3138 * 3139 * @since 3.0 3140 */ 3141 public void setMaximized(bool maximize) { 3142 checkWidget (); 3143 if (this.maximized is maximize) return; 3144 if (maximize && this.minimized) setMinimized(false); 3145 this.maximized = maximize; 3146 redraw(maxRect.x, maxRect.y, maxRect.width, maxRect.height, false); 3147 } 3148 /** 3149 * Marks the receiver's minimize button as visible if the argument is <code>true</code>, 3150 * and marks it invisible otherwise. 3151 * 3152 * @param visible the new visibility state 3153 * 3154 * @exception SWTException <ul> 3155 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3156 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3157 * </ul> 3158 * 3159 * @since 3.0 3160 */ 3161 public void setMinimizeVisible(bool visible) { 3162 checkWidget(); 3163 if (showMin is visible) return; 3164 // display minimize button 3165 showMin = visible; 3166 updateItems(); 3167 redraw(); 3168 } 3169 /** 3170 * Sets the minimized state of the receiver. 3171 * 3172 * @param minimize the new minimized state 3173 * 3174 * @exception SWTException <ul> 3175 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3176 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3177 * </ul> 3178 * 3179 * @since 3.0 3180 */ 3181 public void setMinimized(bool minimize) { 3182 checkWidget (); 3183 if (this.minimized is minimize) return; 3184 if (minimize && this.maximized) setMaximized(false); 3185 this.minimized = minimize; 3186 redraw(minRect.x, minRect.y, minRect.width, minRect.height, false); 3187 } 3188 3189 /** 3190 * Sets the minimum number of characters that will 3191 * be displayed in a fully compressed tab. 3192 * 3193 * @param count the minimum number of characters that will be displayed in a fully compressed tab 3194 * 3195 * @exception SWTException <ul> 3196 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3197 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3198 * <li>ERROR_INVALID_RANGE - if the count is less than zero</li> 3199 * </ul> 3200 * 3201 * @since 3.0 3202 */ 3203 public void setMinimumCharacters(int count) { 3204 checkWidget (); 3205 if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE); 3206 if (minChars is count) return; 3207 minChars = count; 3208 if (updateItems()) redrawTabs(); 3209 } 3210 3211 /** 3212 * When there is not enough horizontal space to show all the tabs, 3213 * by default, tabs are shown sequentially from left to right in 3214 * order of their index. When the MRU visibility is turned on, 3215 * the tabs that are visible will be the tabs most recently selected. 3216 * Tabs will still maintain their left to right order based on index 3217 * but only the most recently selected tabs are visible. 3218 * <p> 3219 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2", 3220 * "Tab 3" and "Tab 4" (in order by index). The user selects 3221 * "Tab 1" and then "Tab 3". If the CTabFolder is now 3222 * compressed so that only two tabs are visible, by default, 3223 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently 3224 * selected and "Tab 2" because it is the previous item in index order). 3225 * If MRU visibility is enabled, the two visible tabs will be "Tab 1" 3226 * and "Tab 3" (in that order from left to right).</p> 3227 * 3228 * @param show the new visibility state 3229 * 3230 * @exception SWTException <ul> 3231 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3232 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3233 * </ul> 3234 * 3235 * @since 3.1 3236 */ 3237 public void setMRUVisible(bool show) { 3238 checkWidget(); 3239 if (mru is show) return; 3240 mru = show; 3241 if (!mru) { 3242 int idx = firstIndex; 3243 int next = 0; 3244 for (int i = firstIndex; i < items.length; i++) { 3245 priority[next++] = i; 3246 } 3247 for (int i = 0; i < idx; i++) { 3248 priority[next++] = i; 3249 } 3250 if (updateItems()) redrawTabs(); 3251 } 3252 } 3253 /** 3254 * Set the selection to the tab at the specified item. 3255 * 3256 * @param item the tab item to be selected 3257 * 3258 * @exception IllegalArgumentException <ul> 3259 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 3260 * </ul> 3261 * 3262 * @exception SWTException <ul> 3263 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 3264 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 3265 * </ul> 3266 */ 3267 public void setSelection(CTabItem item) { 3268 checkWidget(); 3269 if (item is null) SWT.error(SWT.ERROR_NULL_ARGUMENT); 3270 int index = indexOf(item); 3271 setSelection(index); 3272 } 3273 /** 3274 * Set the selection to the tab at the specified index. 3275 * 3276 * @param index the index of the tab item to be selected 3277 * 3278 * @exception SWTException <ul> 3279 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3280 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3281 * </ul> 3282 */ 3283 public void setSelection(int index) { 3284 checkWidget(); 3285 if (index < 0 || index >= items.length) return; 3286 CTabItem selection = items[index]; 3287 if (selectedIndex is index) { 3288 showItem(selection); 3289 return; 3290 } 3291 3292 int oldIndex = selectedIndex; 3293 selectedIndex = index; 3294 if (oldIndex !is -1) { 3295 items[oldIndex].closeImageState = NONE; 3296 } 3297 selection.closeImageState = NORMAL; 3298 selection.showing = false; 3299 3300 Control newControl = selection.control; 3301 Control oldControl = null; 3302 if (oldIndex !is -1) { 3303 oldControl = items[oldIndex].control; 3304 } 3305 3306 if (newControl !is oldControl) { 3307 if (newControl !is null && !newControl.isDisposed()) { 3308 newControl.setBounds(getClientArea()); 3309 newControl.setVisible(true); 3310 } 3311 if (oldControl !is null && !oldControl.isDisposed()) { 3312 oldControl.setVisible(false); 3313 } 3314 } 3315 showItem(selection); 3316 redraw(); 3317 } 3318 void setSelection(int index, bool notify) { 3319 int oldSelectedIndex = selectedIndex; 3320 setSelection(index); 3321 if (notify && selectedIndex !is oldSelectedIndex && selectedIndex !is -1) { 3322 Event event = new Event(); 3323 event.item = getItem(selectedIndex); 3324 notifyListeners(SWT.Selection, event); 3325 } 3326 } 3327 /** 3328 * Sets the receiver's selection background color to the color specified 3329 * by the argument, or to the default system color for the control 3330 * if the argument is null. 3331 * 3332 * @param color the new color (or null) 3333 * 3334 * @exception IllegalArgumentException <ul> 3335 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 3336 * </ul> 3337 * @exception SWTException <ul> 3338 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3339 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3340 * </ul> 3341 * 3342 * @since 3.0 3343 */ 3344 public void setSelectionBackground (Color color) { 3345 checkWidget(); 3346 setSelectionHighlightGradientColor(null); 3347 if (selectionBackground is color) return; 3348 if (color is null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND); 3349 selectionBackground = color; 3350 if (selectedIndex > -1) redraw(); 3351 } 3352 /** 3353 * Specify a gradient of colours to be draw in the background of the selected tab. 3354 * For example to draw a gradient that varies from dark blue to blue and then to 3355 * white, use the following call to setBackground: 3356 * <pre> 3357 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), 3358 * display.getSystemColor(SWT.COLOR_BLUE), 3359 * display.getSystemColor(SWT.COLOR_WHITE), 3360 * display.getSystemColor(SWT.COLOR_WHITE)}, 3361 * new int[] {25, 50, 100}); 3362 * </pre> 3363 * 3364 * @param colors an array of Color that specifies the colors to appear in the gradient 3365 * in order of appearance left to right. The value <code>null</code> clears the 3366 * background gradient. The value <code>null</code> can be used inside the array of 3367 * Color to specify the background color. 3368 * @param percents an array of integers between 0 and 100 specifying the percent of the width 3369 * of the widget at which the color should change. The size of the percents array must be one 3370 * less than the size of the colors array. 3371 * 3372 * @exception SWTException <ul> 3373 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 3374 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 3375 * </ul> 3376 */ 3377 public void setSelectionBackground(Color[] colors, int[] percents) { 3378 setSelectionBackground(colors, percents, false); 3379 } 3380 /** 3381 * Specify a gradient of colours to be draw in the background of the selected tab. 3382 * For example to draw a vertical gradient that varies from dark blue to blue and then to 3383 * white, use the following call to setBackground: 3384 * <pre> 3385 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), 3386 * display.getSystemColor(SWT.COLOR_BLUE), 3387 * display.getSystemColor(SWT.COLOR_WHITE), 3388 * display.getSystemColor(SWT.COLOR_WHITE)}, 3389 * new int[] {25, 50, 100}, true); 3390 * </pre> 3391 * 3392 * @param colors an array of Color that specifies the colors to appear in the gradient 3393 * in order of appearance left to right. The value <code>null</code> clears the 3394 * background gradient. The value <code>null</code> can be used inside the array of 3395 * Color to specify the background color. 3396 * @param percents an array of integers between 0 and 100 specifying the percent of the width 3397 * of the widget at which the color should change. The size of the percents array must be one 3398 * less than the size of the colors array. 3399 * 3400 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal. 3401 * 3402 * @exception SWTException <ul> 3403 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> 3404 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> 3405 * </ul> 3406 * 3407 * @since 3.0 3408 */ 3409 public void setSelectionBackground(Color[] colors, int[] percents, bool vertical) { 3410 checkWidget(); 3411 int colorsLength; 3412 Color highlightBeginColor = null; //null is no highlight 3413 3414 if (colors !is null) { 3415 //The colors array can optionally have an extra entry which describes the highlight top color 3416 //Thus its either one or two larger than the percents array 3417 if (percents is null || 3418 ! ((percents.length is colors.length - 1) || (percents.length is colors.length - 2))){ 3419 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3420 } 3421 for (int i = 0; i < percents.length; i++) { 3422 if (percents[i] < 0 || percents[i] > 100) { 3423 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3424 } 3425 if (i > 0 && percents[i] < percents[i-1]) { 3426 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3427 } 3428 } 3429 //If the colors is exactly two more than percents then last is highlight 3430 //Keep track of *real* colorsLength (minus the highlight) 3431 if(percents.length is colors.length - 2) { 3432 highlightBeginColor = colors[colors.length - 1]; 3433 colorsLength = cast(int)/*64bit*/colors.length - 1; 3434 } else { 3435 colorsLength = cast(int)/*64bit*/colors.length; 3436 } 3437 if (getDisplay().getDepth() < 15) { 3438 // Don't use gradients on low color displays 3439 colors = [colors[colorsLength - 1]]; 3440 colorsLength = cast(int)/*64bit*/colors.length; 3441 percents = null; 3442 } 3443 } else { 3444 colorsLength = 0; 3445 } 3446 3447 // Are these settings the same as before? 3448 if (selectionBgImage is null) { 3449 if ((selectionGradientColors !is null) && (colors !is null) && 3450 (selectionGradientColors.length is colorsLength)) { 3451 bool same = false; 3452 for (int i = 0; i < selectionGradientColors.length; i++) { 3453 if (selectionGradientColors[i] is null) { 3454 same = colors[i] is null; 3455 } else { 3456 same = cast(bool)(selectionGradientColors[i]==colors[i]); 3457 } 3458 if (!same) break; 3459 } 3460 if (same) { 3461 for (int i = 0; i < selectionGradientPercents.length; i++) { 3462 same = selectionGradientPercents[i] is percents[i]; 3463 if (!same) break; 3464 } 3465 } 3466 if (same && this.selectionGradientVertical is vertical) return; 3467 } 3468 } else { 3469 selectionBgImage = null; 3470 } 3471 // Store the new settings 3472 if (colors is null) { 3473 selectionGradientColors = null; 3474 selectionGradientPercents = null; 3475 selectionGradientVertical = false; 3476 setSelectionBackground(cast(Color)null); 3477 setSelectionHighlightGradientColor(null); 3478 } else { 3479 selectionGradientColors = new Color[colorsLength]; 3480 for (int i = 0; i < colorsLength; ++i) { 3481 selectionGradientColors[i] = colors[i]; 3482 } 3483 selectionGradientPercents = new int[percents.length]; 3484 for (int i = 0; i < percents.length; ++i) { 3485 selectionGradientPercents[i] = percents[i]; 3486 } 3487 selectionGradientVertical = vertical; 3488 setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]); 3489 setSelectionHighlightGradientColor(highlightBeginColor); 3490 } 3491 3492 // Refresh with the new settings 3493 if (selectedIndex > -1) redraw(); 3494 } 3495 3496 /* 3497 * Set the color for the highlight start for selected tabs. 3498 * Update the cache of highlight gradient colors if required. 3499 */ 3500 3501 void setSelectionHighlightGradientColor(Color start) { 3502 //Set to null to match all the early return cases. 3503 //For early returns, don't realloc the cache, we may get a cache hit next time we're given the highlight 3504 selectionHighlightGradientBegin = null; 3505 3506 if(start is null) 3507 return; 3508 3509 //don't bother on low colour 3510 if (getDisplay().getDepth() < 15) 3511 return; 3512 3513 //don't bother if we don't have a background gradient 3514 if(selectionGradientColors.length < 2) 3515 return; 3516 3517 //OK we know its a valid gradient now 3518 selectionHighlightGradientBegin = start; 3519 3520 if(! isSelectionHighlightColorsCacheHit(start)) 3521 createSelectionHighlightGradientColors(start); //if no cache hit then compute new ones 3522 } 3523 3524 /* 3525 * Return true if given start color, the cache of highlight colors we have 3526 * would match the highlight colors we'd compute. 3527 */ 3528 bool isSelectionHighlightColorsCacheHit(Color start) { 3529 3530 if(selectionHighlightGradientColorsCache is null) 3531 return false; 3532 3533 //this case should never happen but check to be safe before accessing array indexes 3534 if(selectionHighlightGradientColorsCache.length < 2) 3535 return false; 3536 3537 Color highlightBegin = selectionHighlightGradientColorsCache[0]; 3538 Color highlightEnd = selectionHighlightGradientColorsCache[selectionHighlightGradientColorsCache.length - 1]; 3539 3540 if( highlightBegin!=start) 3541 return false; 3542 3543 //Compare number of colours we have vs. we'd compute 3544 if(selectionHighlightGradientColorsCache.length !is tabHeight) 3545 return false; 3546 3547 //Compare existing highlight end to what it would be (selectionBackground) 3548 if( highlightEnd!=selectionBackground) 3549 return false; 3550 3551 return true; 3552 } 3553 3554 /** 3555 * Set the image to be drawn in the background of the selected tab. Image 3556 * is stretched or compressed to cover entire selection tab area. 3557 * 3558 * @param image the image to be drawn in the background 3559 * 3560 * @exception SWTException <ul> 3561 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3562 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3563 * </ul> 3564 */ 3565 public void setSelectionBackground(Image image) { 3566 checkWidget(); 3567 setSelectionHighlightGradientColor(null); 3568 if (image is selectionBgImage) return; 3569 if (image !is null) { 3570 selectionGradientColors = null; 3571 selectionGradientPercents = null; 3572 disposeSelectionHighlightGradientColors(); 3573 } 3574 selectionBgImage = image; 3575 if (selectedIndex > -1) redraw(); 3576 } 3577 /** 3578 * Set the foreground color of the selected tab. 3579 * 3580 * @param color the color of the text displayed in the selected tab 3581 * 3582 * @exception SWTException <ul> 3583 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3584 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3585 * </ul> 3586 */ 3587 public void setSelectionForeground (Color color) { 3588 checkWidget(); 3589 if (selectionForeground is color) return; 3590 if (color is null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND); 3591 selectionForeground = color; 3592 if (selectedIndex > -1) redraw(); 3593 } 3594 3595 /* 3596 * Allocate colors for the highlight line. 3597 * Colours will be a gradual blend ranging from to. 3598 * Blend length will be tab height. 3599 * Recompute this if tab height changes. 3600 * Could remain null if there'd be no gradient (start=end or low colour display) 3601 */ 3602 void createSelectionHighlightGradientColors(Color start) { 3603 disposeSelectionHighlightGradientColors(); //dispose if existing 3604 3605 if(start is null) //shouldn't happen but just to be safe 3606 return; 3607 3608 //alloc colours for entire height to ensure it matches wherever we stop drawing 3609 int fadeGradientSize = tabHeight; 3610 3611 RGB from = start.getRGB(); 3612 RGB to = selectionBackground.getRGB(); 3613 3614 selectionHighlightGradientColorsCache = new Color[fadeGradientSize]; 3615 int denom = fadeGradientSize - 1; 3616 3617 for (int i = 0; i < fadeGradientSize; i++) { 3618 int propFrom = denom - i; 3619 int propTo = i; 3620 int red = (to.red * propTo + from.red * propFrom) / denom; 3621 int green = (to.green * propTo + from.green * propFrom) / denom; 3622 int blue = (to.blue * propTo + from.blue * propFrom) / denom; 3623 selectionHighlightGradientColorsCache[i] = new Color(getDisplay(), red, green, blue); 3624 } 3625 } 3626 3627 void disposeSelectionHighlightGradientColors() { 3628 if(selectionHighlightGradientColorsCache is null) 3629 return; 3630 for (int i = 0; i < selectionHighlightGradientColorsCache.length; i++) { 3631 selectionHighlightGradientColorsCache[i].dispose(); 3632 } 3633 selectionHighlightGradientColorsCache = null; 3634 } 3635 3636 /* 3637 * Return the gradient start color for selected tabs, which is the start of the tab fade 3638 * (end is selectionBackground). 3639 */ 3640 Color getSelectionBackgroundGradientBegin() { 3641 if (selectionGradientColors is null) 3642 return getSelectionBackground(); 3643 if (selectionGradientColors.length is 0) 3644 return getSelectionBackground(); 3645 return selectionGradientColors[0]; 3646 } 3647 3648 /** 3649 * Sets the shape that the CTabFolder will use to render itself. 3650 * 3651 * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style 3652 * 3653 * @exception SWTException <ul> 3654 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3655 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3656 * </ul> 3657 * 3658 * @since 3.0 3659 */ 3660 public void setSimple(bool simple) { 3661 checkWidget(); 3662 if (this.simple !is simple) { 3663 this.simple = simple; 3664 Rectangle rectBefore = getClientArea(); 3665 updateItems(); 3666 Rectangle rectAfter = getClientArea(); 3667 if (rectBefore!=rectAfter) { 3668 notifyListeners(SWT.Resize, new Event()); 3669 } 3670 redraw(); 3671 } 3672 } 3673 /** 3674 * Sets the number of tabs that the CTabFolder should display 3675 * 3676 * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown. 3677 * 3678 * @exception SWTException <ul> 3679 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3680 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3681 * </ul> 3682 * 3683 * @since 3.0 3684 */ 3685 public void setSingle(bool single) { 3686 checkWidget(); 3687 if (this.single !is single) { 3688 this.single = single; 3689 if (!single) { 3690 for (int i = 0; i < items.length; i++) { 3691 if (i !is selectedIndex && items[i].closeImageState is NORMAL) { 3692 items[i].closeImageState = NONE; 3693 } 3694 } 3695 } 3696 Rectangle rectBefore = getClientArea(); 3697 updateItems(); 3698 Rectangle rectAfter = getClientArea(); 3699 if (rectBefore!=rectAfter) { 3700 notifyListeners(SWT.Resize, new Event()); 3701 } 3702 redraw(); 3703 } 3704 } 3705 /** 3706 * Specify a fixed height for the tab items. If no height is specified, 3707 * the default height is the height of the text or the image, whichever 3708 * is greater. Specifying a height of -1 will revert to the default height. 3709 * 3710 * @param height the pixel value of the height or -1 3711 * 3712 * @exception SWTException <ul> 3713 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3714 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3715 * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li> 3716 * </ul> 3717 */ 3718 public void setTabHeight(int height) { 3719 checkWidget(); 3720 if (height < -1) { 3721 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3722 } 3723 fixedTabHeight = height; 3724 updateTabHeight(false); 3725 } 3726 /** 3727 * Specify whether the tabs should appear along the top of the folder 3728 * or along the bottom of the folder. 3729 * 3730 * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom 3731 * 3732 * @exception SWTException <ul> 3733 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3734 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3735 * <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li> 3736 * </ul> 3737 * 3738 * @since 3.0 3739 */ 3740 public void setTabPosition(int position) { 3741 checkWidget(); 3742 if (position !is SWT.TOP && position !is SWT.BOTTOM) { 3743 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3744 } 3745 if (onBottom !is (position is SWT.BOTTOM)) { 3746 onBottom = position is SWT.BOTTOM; 3747 borderTop = onBottom ? borderLeft : 0; 3748 borderBottom = onBottom ? 0 : borderRight; 3749 updateTabHeight(true); 3750 Rectangle rectBefore = getClientArea(); 3751 updateItems(); 3752 Rectangle rectAfter = getClientArea(); 3753 if (rectBefore!=rectAfter) { 3754 notifyListeners(SWT.Resize, new Event()); 3755 } 3756 redraw(); 3757 } 3758 } 3759 /** 3760 * Set the control that appears in the top right corner of the tab folder. 3761 * Typically this is a close button or a composite with a Menu and close button. 3762 * The topRight control is optional. Setting the top right control to null will 3763 * remove it from the tab folder. 3764 * 3765 * @param control the control to be displayed in the top right corner or null 3766 * 3767 * @exception SWTException <ul> 3768 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3769 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3770 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li> 3771 * </ul> 3772 * 3773 * @since 2.1 3774 */ 3775 public void setTopRight(Control control) { 3776 setTopRight(control, SWT.RIGHT); 3777 } 3778 /** 3779 * Set the control that appears in the top right corner of the tab folder. 3780 * Typically this is a close button or a composite with a Menu and close button. 3781 * The topRight control is optional. Setting the top right control to null 3782 * will remove it from the tab folder. 3783 * <p> 3784 * The alignment parameter sets the layout of the control in the tab area. 3785 * <code>SWT.RIGHT</code> will cause the control to be positioned on the far 3786 * right of the folder and it will have its default size. <code>SWT.FILL</code> 3787 * will size the control to fill all the available space to the right of the 3788 * last tab. If there is no available space, the control will not be visible. 3789 * </p> 3790 * 3791 * @param control the control to be displayed in the top right corner or null 3792 * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code> 3793 * 3794 * @exception SWTException <ul> 3795 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3796 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3797 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li> 3798 * </ul> 3799 * 3800 * @since 3.0 3801 */ 3802 public void setTopRight(Control control, int alignment) { 3803 checkWidget(); 3804 if (alignment !is SWT.RIGHT && alignment !is SWT.FILL) { 3805 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3806 } 3807 if (control !is null && control.getParent() !is this) { 3808 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3809 } 3810 topRight = control; 3811 topRightAlignment = alignment; 3812 if (updateItems()) redraw(); 3813 } 3814 /** 3815 * Specify whether the close button appears 3816 * when the user hovers over an unselected tabs. 3817 * 3818 * @param visible <code>true</code> makes the close button appear 3819 * 3820 * @exception SWTException <ul> 3821 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3822 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3823 * </ul> 3824 * 3825 * @since 3.0 3826 */ 3827 public void setUnselectedCloseVisible(bool visible) { 3828 checkWidget(); 3829 if (showUnselectedClose is visible) return; 3830 // display close button when mouse hovers 3831 showUnselectedClose = visible; 3832 updateItems(); 3833 redraw(); 3834 } 3835 /** 3836 * Specify whether the image appears on unselected tabs. 3837 * 3838 * @param visible <code>true</code> makes the image appear 3839 * 3840 * @exception SWTException <ul> 3841 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3842 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3843 * </ul> 3844 * 3845 * @since 3.0 3846 */ 3847 public void setUnselectedImageVisible(bool visible) { 3848 checkWidget(); 3849 if (showUnselectedImage is visible) return; 3850 // display image on unselected items 3851 showUnselectedImage = visible; 3852 updateItems(); 3853 redraw(); 3854 } 3855 /** 3856 * Shows the item. If the item is already showing in the receiver, 3857 * this method simply returns. Otherwise, the items are scrolled until 3858 * the item is visible. 3859 * 3860 * @param item the item to be shown 3861 * 3862 * @exception IllegalArgumentException <ul> 3863 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 3864 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> 3865 * </ul> 3866 * @exception SWTException <ul> 3867 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3868 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3869 * </ul> 3870 * 3871 * @see CTabFolder#showSelection() 3872 * 3873 * @since 2.0 3874 */ 3875 public void showItem (CTabItem item) { 3876 checkWidget(); 3877 if (item is null) SWT.error (SWT.ERROR_NULL_ARGUMENT); 3878 if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3879 int index = indexOf(item); 3880 if (index is -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT); 3881 int idx = -1; 3882 for (int i = 0; i < priority.length; i++) { 3883 if (priority[i] is index) { 3884 idx = i; 3885 break; 3886 } 3887 } 3888 if (mru) { 3889 // move to front of mru order 3890 int[] newPriority = new int[priority.length]; 3891 System.arraycopy(priority, 0, newPriority, 1, idx); 3892 System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1); 3893 newPriority[0] = index; 3894 priority = newPriority; 3895 } 3896 if (item.isShowing()) return; 3897 updateItems(index); 3898 redrawTabs(); 3899 } 3900 void showList (Rectangle rect) { 3901 if (items.length is 0 || !showChevron) return; 3902 if (showMenu is null || showMenu.isDisposed()) { 3903 showMenu = new Menu(this); 3904 } else { 3905 MenuItem[] items = showMenu.getItems(); 3906 for (int i = 0; i < items.length; i++) { 3907 items[i].dispose(); 3908 } 3909 } 3910 static const String id = "CTabFolder_showList_Index"; //$NON-NLS-1$ 3911 for (int i = 0; i < items.length; i++) { 3912 CTabItem tab = items[i]; 3913 if (tab.showing) continue; 3914 MenuItem item = new MenuItem(showMenu, SWT.NONE); 3915 item.setText(tab.getText()); 3916 item.setImage(tab.getImage()); 3917 item.setData(id, tab); 3918 item.addSelectionListener(new class() SelectionAdapter { 3919 override 3920 public void widgetSelected(SelectionEvent e) { 3921 MenuItem menuItem = cast(MenuItem)e.widget; 3922 int index = indexOf(cast(CTabItem)menuItem.getData(id)); 3923 this.outer.setSelection(index, true); 3924 } 3925 }); 3926 } 3927 int x = rect.x; 3928 int y = rect.y + rect.height; 3929 Point location = getDisplay().map(this, null, x, y); 3930 showMenu.setLocation(location.x, location.y); 3931 showMenu.setVisible(true); 3932 } 3933 /** 3934 * Shows the selection. If the selection is already showing in the receiver, 3935 * this method simply returns. Otherwise, the items are scrolled until 3936 * the selection is visible. 3937 * 3938 * @exception SWTException <ul> 3939 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3940 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3941 * </ul> 3942 * 3943 * @see CTabFolder#showItem(CTabItem) 3944 * 3945 * @since 2.0 3946 */ 3947 public void showSelection () { 3948 checkWidget (); 3949 if (selectedIndex !is -1) { 3950 showItem(getSelection()); 3951 } 3952 } 3953 3954 void _setToolTipText (int x, int y) { 3955 String oldTip = getToolTipText(); 3956 String newTip = _getToolTip(x, y); 3957 if (newTip is null || newTip!=oldTip) { 3958 setToolTipText(newTip); 3959 } 3960 } 3961 3962 bool updateItems() { 3963 return updateItems(selectedIndex); 3964 } 3965 3966 bool updateItems(int showIndex) { 3967 if (!single && !mru && showIndex !is -1) { 3968 // make sure selected item will be showing 3969 int firstIndex = showIndex; 3970 if (priority[0] < showIndex) { 3971 int maxWidth = getRightItemEdge() - borderLeft; 3972 if (!simple) maxWidth -= curveWidth - 2*curveIndent; 3973 int width = 0; 3974 int[] widths = new int[items.length]; 3975 GC gc = new GC(this); 3976 for (int i = priority[0]; i <= showIndex; i++) { 3977 widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); 3978 width += widths[i]; 3979 if (width > maxWidth) break; 3980 } 3981 if (width > maxWidth) { 3982 width = 0; 3983 for (int i = showIndex; i >= 0; i--) { 3984 if (widths[i] is 0) widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); 3985 width += widths[i]; 3986 if (width > maxWidth) break; 3987 firstIndex = i; 3988 } 3989 } else { 3990 firstIndex = priority[0]; 3991 for (int i = showIndex + 1; i < items.length; i++) { 3992 widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); 3993 width += widths[i]; 3994 if (width >= maxWidth) break; 3995 } 3996 if (width < maxWidth) { 3997 for (int i = priority[0] - 1; i >= 0; i--) { 3998 if (widths[i] is 0) widths[i] = items[i].preferredWidth(gc, i is selectedIndex, true); 3999 width += widths[i]; 4000 if (width > maxWidth) break; 4001 firstIndex = i; 4002 } 4003 } 4004 } 4005 gc.dispose(); 4006 } 4007 if (firstIndex !is priority[0]) { 4008 int index = 0; 4009 for (int i = firstIndex; i < items.length; i++) { 4010 priority[index++] = i; 4011 } 4012 for (int i = 0; i < firstIndex; i++) { 4013 priority[index++] = i; 4014 } 4015 } 4016 } 4017 4018 bool oldShowChevron = showChevron; 4019 bool changed = setItemSize(); 4020 changed |= setItemLocation(); 4021 setButtonBounds(); 4022 changed |= showChevron !is oldShowChevron; 4023 if (changed && getToolTipText() !is null) { 4024 Point pt = getDisplay().getCursorLocation(); 4025 pt = toControl(pt); 4026 _setToolTipText(pt.x, pt.y); 4027 } 4028 return changed; 4029 } 4030 bool updateTabHeight(bool force){ 4031 int style = getStyle(); 4032 if (fixedTabHeight is 0 && (style & SWT.FLAT) !is 0 && (style & SWT.BORDER) is 0) highlight_header = 0; 4033 int oldHeight = tabHeight; 4034 if (fixedTabHeight !is SWT.DEFAULT) { 4035 tabHeight = fixedTabHeight is 0 ? 0 : fixedTabHeight + 1; // +1 for line drawn across top of tab 4036 } else { 4037 int tempHeight = 0; 4038 GC gc = new GC(this); 4039 if (items.length is 0) { 4040 tempHeight = gc.textExtent("Default", CTabItem.FLAGS).y + CTabItem.TOP_MARGIN + CTabItem.BOTTOM_MARGIN; //$NON-NLS-1$ 4041 } else { 4042 for (int i=0; i < items.length; i++) { 4043 tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc)); 4044 } 4045 } 4046 gc.dispose(); 4047 tabHeight = tempHeight; 4048 } 4049 if (!force && tabHeight is oldHeight) return false; 4050 4051 oldSize = null; 4052 if (onBottom) { 4053 int d = tabHeight - 12; 4054 curve = [0,13+d, 0,12+d, 2,12+d, 3,11+d, 5,11+d, 6,10+d, 7,10+d, 9,8+d, 10,8+d, 4055 11,7+d, 11+d,7, 4056 12+d,6, 13+d,6, 15+d,4, 16+d,4, 17+d,3, 19+d,3, 20+d,2, 22+d,2, 23+d,1]; 4057 curveWidth = 26+d; 4058 curveIndent = curveWidth/3; 4059 } else { 4060 int d = tabHeight - 12; 4061 curve = [0,0, 0,1, 2,1, 3,2, 5,2, 6,3, 7,3, 9,5, 10,5, 4062 11,6, 11+d,6+d, 4063 12+d,7+d, 13+d,7+d, 15+d,9+d, 16+d,9+d, 17+d,10+d, 19+d,10+d, 20+d,11+d, 22+d,11+d, 23+d,12+d]; 4064 curveWidth = 26+d; 4065 curveIndent = curveWidth/3; 4066 4067 //this could be static but since values depend on curve, better to keep in one place 4068 topCurveHighlightStart = [ 4069 0, 2, 1, 2, 2, 2, 4070 3, 3, 4, 3, 5, 3, 4071 6, 4, 7, 4, 4072 8, 5, 4073 9, 6, 10, 6]; 4074 4075 //also, by adding in 'd' here we save some math cost when drawing the curve 4076 topCurveHighlightEnd = [ 4077 10+d, 6+d, 4078 11+d, 7+d, 4079 12+d, 8+d, 13+d, 8+d, 4080 14+d, 9+d, 4081 15+d, 10+d, 16+d, 10+d, 4082 17+d, 11+d, 18+d, 11+d, 19+d, 11+d, 4083 20+d, 12+d, 21+d, 12+d, 22+d, 12+d ]; 4084 } 4085 notifyListeners(SWT.Resize, new Event()); 4086 return true; 4087 } 4088 String _getToolTip(int x, int y) { 4089 if (showMin && minRect.contains(x, y)) return minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize"); //$NON-NLS-1$ //$NON-NLS-2$ 4090 if (showMax && maxRect.contains(x, y)) return maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize"); //$NON-NLS-1$ //$NON-NLS-2$ 4091 if (showChevron && chevronRect.contains(x, y)) return SWT.getMessage("SWT_ShowList"); //$NON-NLS-1$ 4092 CTabItem item = getItem(new Point (x, y)); 4093 if (item is null) return null; 4094 if (!item.showing) return null; 4095 if ((showClose || item.showClose) && item.closeRect.contains(x, y)) { 4096 return SWT.getMessage("SWT_Close"); //$NON-NLS-1$ 4097 } 4098 return item.getToolTipText(); 4099 } 4100 }