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