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 location = items[childID].getBounds();
1467 }
1468 if (location != null) {
1469 pt = toDisplay(location.x, location.y);
1470 }
1471 }
1472 if (location != null && pt != null) {
1473 e.x = pt.x;
1474 e.y = pt.y;
1475 e.width = location.width;
1476 e.height = location.height;
1477 }
1478 }
1479
1480 @Override
1481 public void getChildCount(AccessibleControlEvent e) {
1482 e.detail = items.length;
1483 }
1484
1485 @Override
1486 public void getDefaultAction(AccessibleControlEvent e) {
1487 String action = null;
1488 int childID = e.childID;
1489 if (childID >= 0 && childID < items.length) {
1490 action = SWT.getMessage ("SWT_Switch"); //$NON-NLS-1$
1491 }
1492 e.result = action;
1493 }
1494
1495 @Override
1496 public void getFocus(AccessibleControlEvent e) {
1497 int childID = ACC.CHILDID_NONE;
1498 if (isFocusControl()) {
1499 if (selectedIndex == -1) {
1500 childID = ACC.CHILDID_SELF;
1501 } else {
1502 childID = selectedIndex;
1503 }
1504 }
1505 e.childID = childID;
1506 }
1507
1508 @Override
1509 public void getRole(AccessibleControlEvent e) {
1510 int role = 0;
1511 int childID = e.childID;
1512 if (childID == ACC.CHILDID_SELF) {
1513 role = ACC.ROLE_TABFOLDER;
1514 } else if (childID >= 0 && childID < items.length) {
1515 role = ACC.ROLE_TABITEM;
1516 }
1517 e.detail = role;
1518 }
1519
1520 @Override
1521 public void getSelection(AccessibleControlEvent e) {
1522 e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex;
1523 }
1524
1525 @Override
1526 public void getState(AccessibleControlEvent e) {
1527 int state = 0;
1528 int childID = e.childID;
1529 if (childID == ACC.CHILDID_SELF) {
1530 state = ACC.STATE_NORMAL;
1531 } else if (childID >= 0 && childID < items.length) {
1532 state = ACC.STATE_SELECTABLE;
1533 if (isFocusControl()) {
1534 state |= ACC.STATE_FOCUSABLE;
1535 }
1536 if (selectedIndex == childID) {
1537 state |= ACC.STATE_SELECTED;
1538 if (isFocusControl()) {
1539 state |= ACC.STATE_FOCUSED;
1540 }
1541 }
1542 }
1543 e.detail = state;
1544 }
1545
1546 @Override
1547 public void getChildren(AccessibleControlEvent e) {
1548 int childIdCount = items.length;
1549 Object[] children = new Object[childIdCount];
1550 for (int i = 0; i < childIdCount; i++) {
1551 children[i] = Integer.valueOf(i);
1552 }
1553 e.children = children;
1554 }
1555 });
1556
1557 addListener(SWT.Selection, event -> {
1558 if (isFocusControl()) {
1559 if (selectedIndex == -1) {
1560 accessible.setFocus(ACC.CHILDID_SELF);
1561 } else {
1562 accessible.setFocus(selectedIndex);
1563 }
1564 }
1565 });
1566
1567 addListener(SWT.FocusIn, event -> {
1568 if (selectedIndex == -1) {
1569 accessible.setFocus(ACC.CHILDID_SELF);
1570 } else {
1571 accessible.setFocus(selectedIndex);
1572 }
1573 });
1574 }
initAccessibleMinMaxTb()1575 void initAccessibleMinMaxTb() {
1576 minMaxTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1577 @Override
1578 public void getName(AccessibleEvent e) {
1579 if (e.childID != ACC.CHILDID_SELF) {
1580 if (minItem != null && e.childID == minMaxTb.indexOf(minItem)) {
1581 e.result = minItem.getToolTipText();
1582 } else if (maxItem != null && e.childID == minMaxTb.indexOf(maxItem)) {
1583 e.result = maxItem.getToolTipText();
1584 }
1585 }
1586 }
1587 });
1588 }
initAccessibleChevronTb()1589 void initAccessibleChevronTb() {
1590 chevronTb.getAccessible().addAccessibleListener(new AccessibleAdapter() {
1591 @Override
1592 public void getName(AccessibleEvent e) {
1593 if (e.childID != ACC.CHILDID_SELF) {
1594 if (chevronItem != null && e.childID == chevronTb.indexOf(chevronItem)) {
1595 e.result = chevronItem.getToolTipText();
1596 }
1597 }
1598 }
1599 });
1600 }
onKeyDown(Event event)1601 void onKeyDown (Event event) {
1602 runUpdate();
1603 switch (event.keyCode) {
1604 case SWT.ARROW_LEFT:
1605 case SWT.ARROW_RIGHT:
1606 int count = items.length;
1607 if (count == 0) return;
1608 if (selectedIndex == -1) return;
1609 int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
1610 int offset = event.keyCode == leadKey ? -1 : 1;
1611 int index;
1612 if (!mru) {
1613 index = selectedIndex + offset;
1614 } else {
1615 int[] visible = new int[items.length];
1616 int idx = 0;
1617 int current = -1;
1618 for (int i = 0; i < items.length; i++) {
1619 if (items[i].showing) {
1620 if (i == selectedIndex) current = idx;
1621 visible [idx++] = i;
1622 }
1623 }
1624 if (current + offset >= 0 && current + offset < idx){
1625 index = visible [current + offset];
1626 } else {
1627 if (showChevron) {
1628 Rectangle chevronRect = chevronItem.getBounds();
1629 chevronRect = event.display.map(chevronTb, this, chevronRect);
1630 CTabFolderEvent e = new CTabFolderEvent(this);
1631 e.widget = this;
1632 e.time = event.time;
1633 e.x = chevronRect.x;
1634 e.y = chevronRect.y;
1635 e.width = chevronRect.width;
1636 e.height = chevronRect.height;
1637 e.doit = true;
1638 for (CTabFolder2Listener folderListener : folderListeners) {
1639 folderListener.showList(e);
1640 }
1641 if (e.doit && !isDisposed()) {
1642 showList(chevronRect);
1643 }
1644 }
1645 return;
1646 }
1647 }
1648 if (index < 0 || index >= count) return;
1649 setSelection (index, true);
1650 forceFocus();
1651 }
1652 }
onDispose(Event event)1653 void onDispose(Event event) {
1654 removeListener(SWT.Dispose, listener);
1655 notifyListeners(SWT.Dispose, event);
1656 event.type = SWT.None;
1657 /*
1658 * Usually when an item is disposed, destroyItem will change the size of the items array,
1659 * reset the bounds of all the tabs and manage the widget associated with the tab.
1660 * Since the whole folder is being disposed, this is not necessary. For speed
1661 * the inDispose flag is used to skip over this part of the item dispose.
1662 */
1663 inDispose = true;
1664
1665 if (showMenu != null && !showMenu.isDisposed()) {
1666 showMenu.dispose();
1667 showMenu = null;
1668 }
1669 int length = items.length;
1670 for (int i = 0; i < length; i++) {
1671 if (items[i] != null) {
1672 items[i].dispose();
1673 }
1674 }
1675
1676 gradientColors = null;
1677
1678 selectionGradientColors = null;
1679 selectionGradientPercents = null;
1680 selectionBgImage = null;
1681
1682 selectionBackground = null;
1683 selectionForeground = null;
1684
1685 if (controlBkImages != null) {
1686 for (int i = 0; i < controlBkImages.length; i++) {
1687 if (controlBkImages[i] != null) {
1688 controlBkImages[i].dispose();
1689 controlBkImages[i] = null;
1690 }
1691 }
1692 controlBkImages = null;
1693 }
1694 controls = null;
1695 controlAlignments = null;
1696 controlRects = null;
1697
1698 if (maxImage != null) maxImage.dispose();
1699 maxImage = null;
1700
1701 if (minImage != null) minImage.dispose();
1702 minImage = null;
1703
1704 if (chevronImage != null) chevronImage.dispose();
1705 chevronImage = null;
1706
1707 if (renderer != null) renderer.dispose();
1708 renderer = null;
1709
1710 minItem = null;
1711 maxItem = null;
1712 minMaxTb = null;
1713
1714 chevronItem = null;
1715 chevronTb = null;
1716
1717 if (folderListeners.length != 0) folderListeners = new CTabFolder2Listener[0];
1718 if (tabListeners.length != 0) tabListeners = new CTabFolderListener[0];
1719 }
onDragDetect(Event event)1720 void onDragDetect(Event event) {
1721 boolean consume = false;
1722 for (CTabItem item : items) {
1723 if (item.closeRect.contains(event.x, event.y)) {
1724 consume = true;
1725 break;
1726 }
1727 }
1728 if (consume) {
1729 event.type = SWT.None;
1730 }
1731 }
onFocus(Event event)1732 void onFocus(Event event) {
1733 checkWidget();
1734 if (selectedIndex >= 0) {
1735 redraw();
1736 } else {
1737 setSelection(0, true);
1738 }
1739 }
onMnemonic(Event event, boolean doit)1740 boolean onMnemonic (Event event, boolean doit) {
1741 char key = event.character;
1742 for (int i = 0; i < items.length; i++) {
1743 if (items[i] != null) {
1744 char mnemonic = _findMnemonic (items[i].getText ());
1745 if (mnemonic != '\0') {
1746 if (Character.toLowerCase (key) == mnemonic) {
1747 if (doit) {
1748 setSelection(i, true);
1749 forceFocus();
1750 }
1751 return true;
1752 }
1753 }
1754 }
1755 }
1756 return false;
1757 }
onMenuDetect(Event event)1758 void onMenuDetect(Event event) {
1759 if (event.detail == SWT.MENU_KEYBOARD) {
1760 if (selectedIndex != -1) {
1761 CTabItem item = items[selectedIndex];
1762 Rectangle rect = getDisplay().map(this, null, item.getBounds());
1763 if (!rect.contains(event.x, event.y)) {
1764 /* If the mouse is not in the currently-selected tab,
1765 * then pop up the menu near the top-right corner of the current tab.
1766 */
1767 Rectangle itemTrim = renderer.computeTrim(selectedIndex, SWT.NONE, 0, 0, 0, 0);
1768 Rectangle closeTrim = renderer.computeTrim(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, 0, 0, 0, 0);
1769 event.x = rect.x + rect.width - item.closeRect.width + itemTrim.x - closeTrim.width;
1770 event.y = rect.y - itemTrim.y - closeTrim.y;
1771 }
1772 }
1773 }
1774 }
onMouseDoubleClick(Event event)1775 void onMouseDoubleClick(Event event) {
1776 if (event.button != 1 ||
1777 (event.stateMask & SWT.BUTTON2) != 0 ||
1778 (event.stateMask & SWT.BUTTON3) != 0) return;
1779 Event e = new Event();
1780 e.item = getItem(new Point(event.x, event.y));
1781 if (e.item != null) {
1782 notifyListeners(SWT.DefaultSelection, e);
1783 }
1784 }
onMouse(Event event)1785 void onMouse(Event event) {
1786 if( isDisposed() ) {
1787 return;
1788 }
1789 int x = event.x, y = event.y;
1790 switch (event.type) {
1791 case SWT.MouseEnter: {
1792 setToolTipText(null);
1793 break;
1794 }
1795 case SWT.MouseExit: {
1796 for (int i=0; i<items.length; i++) {
1797 CTabItem item = items[i];
1798 if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND) {
1799 item.closeImageState = SWT.BACKGROUND;
1800 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1801 }
1802 if ((item.state & SWT.HOT) != 0) {
1803 item.state &= ~SWT.HOT;
1804 redraw(item.x, item.y, item.width, item.height, false);
1805 }
1806 if (i == selectedIndex && item.closeImageState != SWT.NONE) {
1807 item.closeImageState = SWT.NONE;
1808 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1809 }
1810 }
1811 break;
1812 }
1813 case SWT.MouseHover:
1814 case SWT.MouseDown: {
1815 if (hoverTb && hoverRect.contains(x, y) && !hovering) {
1816 hovering = true;
1817 updateItems();
1818 hoverTimerRunning = true;
1819 event.display.timerExec(2000, new Runnable() {
1820 @Override
1821 public void run() {
1822 if (isDisposed()) return;
1823 if (hovering) {
1824 Display display = getDisplay();
1825 Control c = display.getCursorControl();
1826 boolean reschedule = false;
1827 if (c != null) {
1828 for (Control control : controls) {
1829 Control temp = c;
1830 do {
1831 if (temp.equals(control)) {
1832 reschedule = true;
1833 } else {
1834 temp = temp.getParent();
1835 if (temp == null || temp.equals(CTabFolder.this)) break;
1836 }
1837 } while (!reschedule);
1838 if (reschedule) break;
1839 }
1840 }
1841 if (reschedule && hoverTimerRunning) {
1842 display.timerExec(2000, this);
1843 } else {
1844 hovering = false;
1845 updateItems();
1846 }
1847 }
1848 }
1849 });
1850 return;
1851 }
1852 if (event.button != 1) return;
1853 CTabItem item = null;
1854 if (single) {
1855 if (selectedIndex != -1) {
1856 Rectangle bounds = items[selectedIndex].getBounds();
1857 if (bounds.contains(x, y)){
1858 item = items[selectedIndex];
1859 }
1860 }
1861 } else {
1862 for (CTabItem tabItem : items) {
1863 Rectangle bounds = tabItem.getBounds();
1864 if (bounds.contains(x, y)){
1865 item = tabItem;
1866 }
1867 }
1868 }
1869 if (item != null) {
1870 if (item.closeRect.contains(x,y)){
1871 item.closeImageState = SWT.SELECTED;
1872 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1873 update();
1874 return;
1875 }
1876 int index = indexOf(item);
1877 if (item.showing){
1878 int oldSelectedIndex = selectedIndex;
1879 setSelection(index, true);
1880 if (oldSelectedIndex == selectedIndex) {
1881 /* If the click is on the selected tabitem, then set focus to the tabfolder */
1882 forceFocus();
1883 }
1884 }
1885 return;
1886 }
1887 break;
1888 }
1889 case SWT.MouseMove: {
1890 _setToolTipText(event.x, event.y);
1891 boolean close = false;
1892 for (int i=0; i<items.length; i++) {
1893 CTabItem item = items[i];
1894 close = false;
1895 if (item.getBounds().contains(x, y)) {
1896 close = true;
1897 if (item.closeRect.contains(x, y)) {
1898 if (item.closeImageState != SWT.SELECTED && item.closeImageState != SWT.HOT) {
1899 item.closeImageState = SWT.HOT;
1900 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1901 }
1902 } else {
1903 if (item.closeImageState != SWT.NONE) {
1904 item.closeImageState = SWT.NONE;
1905 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1906 }
1907 }
1908 if ((item.state & SWT.HOT) == 0) {
1909 item.state |= SWT.HOT;
1910 redraw(item.x, item.y, item.width, item.height, false);
1911 }
1912 }
1913 if (i != selectedIndex && item.closeImageState != SWT.BACKGROUND && !close) {
1914 item.closeImageState = SWT.BACKGROUND;
1915 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1916 }
1917 if ((item.state & SWT.HOT) != 0 && !close) {
1918 item.state &= ~SWT.HOT;
1919 redraw(item.x, item.y, item.width, item.height, false);
1920 }
1921 if (i == selectedIndex && item.closeImageState != SWT.NONE && !close) {
1922 item.closeImageState = SWT.NONE;
1923 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1924 }
1925 }
1926 break;
1927 }
1928 case SWT.MouseUp: {
1929 if (event.button != 1) return;
1930 CTabItem item = null;
1931 if (single) {
1932 if (selectedIndex != -1) {
1933 Rectangle bounds = items[selectedIndex].getBounds();
1934 if (bounds.contains(x, y)){
1935 item = items[selectedIndex];
1936 }
1937 }
1938 } else {
1939 for (CTabItem tabItem : items) {
1940 Rectangle bounds = tabItem.getBounds();
1941 if (bounds.contains(x, y)){
1942 item = tabItem;
1943 }
1944 }
1945 }
1946 if (item != null) {
1947 if (item.closeRect.contains(x,y)) {
1948 boolean selected = item.closeImageState == SWT.SELECTED;
1949 item.closeImageState = SWT.HOT;
1950 redraw(item.closeRect.x, item.closeRect.y, item.closeRect.width, item.closeRect.height, false);
1951 if (!selected) return;
1952 CTabFolderEvent e = new CTabFolderEvent(this);
1953 e.widget = this;
1954 e.time = event.time;
1955 e.item = item;
1956 e.doit = true;
1957 for (CTabFolder2Listener listener : folderListeners) {
1958 listener.close(e);
1959 }
1960 for (CTabFolderListener listener : tabListeners) {
1961 listener.itemClosed(e);
1962 }
1963 if (e.doit) item.dispose();
1964 if (!isDisposed() && item.isDisposed()) {
1965 Display display = getDisplay();
1966 Point pt = display.getCursorLocation();
1967 pt = display.map(null, this, pt.x, pt.y);
1968 CTabItem nextItem = getItem(pt);
1969 if (nextItem != null) {
1970 if (nextItem.closeRect.contains(pt)) {
1971 if (nextItem.closeImageState != SWT.SELECTED && nextItem.closeImageState != SWT.HOT) {
1972 nextItem.closeImageState = SWT.HOT;
1973 redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1974 }
1975 } else {
1976 if (nextItem.closeImageState != SWT.NONE) {
1977 nextItem.closeImageState = SWT.NONE;
1978 redraw(nextItem.closeRect.x, nextItem.closeRect.y, nextItem.closeRect.width, nextItem.closeRect.height, false);
1979 }
1980 }
1981 }
1982 }
1983 return;
1984 }
1985 }
1986 }
1987 }
1988 }
onPageTraversal(Event event)1989 void onPageTraversal(Event event) {
1990 int count = items.length;
1991 if (count == 0) return;
1992 int index = selectedIndex;
1993 if (index == -1) {
1994 index = 0;
1995 } else {
1996 int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1;
1997 if (!mru) {
1998 index = (selectedIndex + offset + count) % count;
1999 } else {
2000 int[] visible = new int[items.length];
2001 int idx = 0;
2002 int current = -1;
2003 for (int i = 0; i < items.length; i++) {
2004 if (items[i].showing) {
2005 if (i == selectedIndex) current = idx;
2006 visible [idx++] = i;
2007 }
2008 }
2009 if (current + offset >= 0 && current + offset < idx){
2010 index = visible [current + offset];
2011 } else {
2012 if (showChevron) {
2013 Rectangle chevronRect = chevronItem.getBounds();
2014 chevronRect = event.display.map(chevronTb, this, chevronRect);
2015 CTabFolderEvent e = new CTabFolderEvent(this);
2016 e.widget = this;
2017 e.time = event.time;
2018 e.x = chevronRect.x;
2019 e.y = chevronRect.y;
2020 e.width = chevronRect.width;
2021 e.height = chevronRect.height;
2022 e.doit = true;
2023 for (CTabFolder2Listener folderListener : folderListeners) {
2024 folderListener.showList(e);
2025 }
2026 if (e.doit && !isDisposed()) {
2027 showList(chevronRect);
2028 }
2029 }
2030 }
2031 }
2032 }
2033 setSelection (index, true);
2034 }
onPaint(Event event)2035 void onPaint(Event event) {
2036 if (inDispose) return;
2037 Font font = getFont();
2038 if (oldFont == null || !oldFont.equals(font)) {
2039 // handle case where default font changes
2040 oldFont = font;
2041 if (!updateTabHeight(false)) {
2042 updateItems();
2043 redraw();
2044 return;
2045 }
2046 }
2047
2048 GC gc = event.gc;
2049 Font gcFont = gc.getFont();
2050 Color gcBackground = gc.getBackground();
2051 Color gcForeground = gc.getForeground();
2052
2053 // Useful for debugging paint problems
2054 //{
2055 //Point size = getSize();
2056 //gc.setBackground(getDisplay().getSystemColor(SWT.COLOR_GREEN));
2057 //gc.fillRectangle(-10, -10, size.x + 20, size.y+20);
2058 //}
2059
2060 Point size = getSize();
2061 Rectangle bodyRect = new Rectangle(0, 0, size.x, size.y);
2062 renderer.draw(CTabFolderRenderer.PART_BODY, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2063
2064 gc.setFont(gcFont);
2065 gc.setForeground(gcForeground);
2066 gc.setBackground(gcBackground);
2067
2068 renderer.draw(CTabFolderRenderer.PART_HEADER, SWT.BACKGROUND | SWT.FOREGROUND, bodyRect, gc);
2069
2070 gc.setFont(gcFont);
2071 gc.setForeground(gcForeground);
2072 gc.setBackground(gcBackground);
2073
2074 if (!single) {
2075 for (int i=0; i < items.length; i++) {
2076 Rectangle itemBounds = items[i].getBounds();
2077 if (i != selectedIndex && event.getBounds().intersects(itemBounds)) {
2078 renderer.draw(i, SWT.BACKGROUND | SWT.FOREGROUND | items[i].state , itemBounds, gc);
2079 }
2080 }
2081 }
2082
2083 gc.setFont(gcFont);
2084 gc.setForeground(gcForeground);
2085 gc.setBackground(gcBackground);
2086
2087 if (selectedIndex != -1) {
2088 renderer.draw(selectedIndex, items[selectedIndex].state | SWT.BACKGROUND | SWT.FOREGROUND, items[selectedIndex].getBounds(), gc);
2089 }
2090
2091 gc.setFont(gcFont);
2092 gc.setForeground(gcForeground);
2093 gc.setBackground(gcBackground);
2094
2095 if (hoverTb) {
2096 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2097 int x = getSize().x - (trim.width + trim.x);
2098 hoverRect = new Rectangle(x - 16 - SPACING, 2, 16, getTabHeight() - 2);
2099 gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
2100 x = hoverRect.x;
2101 int y = hoverRect.y;
2102 gc.setBackground(gc.getDevice().getSystemColor(SWT.COLOR_WHITE));
2103 gc.fillRectangle(x + hoverRect.width - 6, y, 5, 5);
2104 gc.drawRectangle(x + hoverRect.width - 6, y, 5, 5);
2105 gc.drawLine(x + hoverRect.width - 6, y+2, x + hoverRect.width - 6 + 5, y + 2);
2106 gc.fillRectangle(x, y, 5 , 2);
2107 gc.drawRectangle(x, y, 5 , 2);
2108 }
2109 gc.setFont(gcFont);
2110 gc.setForeground(gcForeground);
2111 gc.setBackground(gcBackground);
2112 }
2113
onResize(Event event)2114 void onResize(Event event) {
2115 if (inDispose) return;
2116 if (ignoreResize) return;
2117 if (updateItems()) {
2118 redrawTabs();
2119 }
2120 Point size = getSize();
2121 if (oldSize == null) {
2122 redraw();
2123 } else {
2124 if (onBottom && size.y != oldSize.y) {
2125 redraw();
2126 } else {
2127 int x1 = Math.min(size.x, oldSize.x);
2128 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2129 if (size.x != oldSize.x) x1 -= trim.width + trim.x - marginWidth + 2;
2130 if (!simple) x1 -= 5; // rounded top right corner
2131 int y1 = Math.min(size.y, oldSize.y);
2132 if (size.y != oldSize.y) y1 -= trim.height + trim.y - marginHeight;
2133 int x2 = Math.max(size.x, oldSize.x);
2134 int y2 = Math.max(size.y, oldSize.y);
2135 redraw(0, y1, x2, y2 - y1, false);
2136 redraw(x1, 0, x2 - x1, y2, false);
2137 if (hoverTb) {
2138 redraw(hoverRect.x, hoverRect.y, hoverRect.width, hoverRect.height, false);
2139 }
2140 }
2141 }
2142 oldSize = size;
2143 }
onSelection(Event event)2144 void onSelection(Event event) {
2145 if (hovering) {
2146 hovering = false;
2147 updateItems();
2148 }
2149 if (event.widget == maxItem) {
2150 CTabFolderEvent e = new CTabFolderEvent(this);
2151 e.widget = CTabFolder.this;
2152 e.time = event.time;
2153 for (CTabFolder2Listener folderListener : folderListeners) {
2154 if (maximized) {
2155 folderListener.restore(e);
2156 } else {
2157 folderListener.maximize(e);
2158 }
2159 }
2160 } else if (event.widget == minItem) {
2161 CTabFolderEvent e = new CTabFolderEvent(this);
2162 e.widget = CTabFolder.this;
2163 e.time = event.time;
2164 for (CTabFolder2Listener folderListener : folderListeners) {
2165 if (minimized) {
2166 folderListener.restore(e);
2167 } else {
2168 folderListener.minimize(e);
2169 }
2170 }
2171 } else if (event.widget == chevronItem) {
2172 Rectangle chevronRect = chevronItem.getBounds();
2173 chevronRect = event.display.map(chevronTb, this, chevronRect);
2174 CTabFolderEvent e = new CTabFolderEvent(this);
2175 e.widget = this;
2176 e.time = event.time;
2177 e.x = chevronRect.x;
2178 e.y = chevronRect.y;
2179 e.width = chevronRect.width;
2180 e.height = chevronRect.height;
2181 e.doit = true;
2182 for (CTabFolder2Listener folderListener : folderListeners) {
2183 folderListener.showList(e);
2184 }
2185 if (e.doit && !isDisposed()) {
2186 showList(chevronRect);
2187 }
2188 }
2189 }
onTraverse(Event event)2190 void onTraverse (Event event) {
2191 if (ignoreTraverse) return;
2192 runUpdate();
2193 switch (event.detail) {
2194 case SWT.TRAVERSE_ESCAPE:
2195 case SWT.TRAVERSE_RETURN:
2196 case SWT.TRAVERSE_TAB_NEXT:
2197 case SWT.TRAVERSE_TAB_PREVIOUS:
2198 Control focusControl = getDisplay().getFocusControl();
2199 if (focusControl == this) event.doit = true;
2200 break;
2201 case SWT.TRAVERSE_MNEMONIC:
2202 event.doit = onMnemonic(event, false);
2203 break;
2204 case SWT.TRAVERSE_PAGE_NEXT:
2205 case SWT.TRAVERSE_PAGE_PREVIOUS:
2206 event.doit = items.length > 0;
2207 break;
2208 }
2209 ignoreTraverse = true;
2210 notifyListeners(SWT.Traverse, event);
2211 ignoreTraverse = false;
2212 event.type = SWT.None;
2213 if (isDisposed()) return;
2214 if (!event.doit) return;
2215 switch (event.detail) {
2216 case SWT.TRAVERSE_MNEMONIC:
2217 onMnemonic(event, true);
2218 event.detail = SWT.TRAVERSE_NONE;
2219 break;
2220 case SWT.TRAVERSE_PAGE_NEXT:
2221 case SWT.TRAVERSE_PAGE_PREVIOUS:
2222 onPageTraversal(event);
2223 event.detail = SWT.TRAVERSE_NONE;
2224 break;
2225 }
2226 }
redrawTabs()2227 void redrawTabs() {
2228 Point size = getSize();
2229 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BODY, SWT.NONE, 0, 0, 0, 0);
2230 if (onBottom) {
2231 int h = trim.height + trim.y - marginHeight;
2232 redraw(0, size.y - h - 1, size.x, h + 1, false);
2233 } else {
2234 redraw(0, 0, size.x, -trim.y - marginHeight + 1, false);
2235 }
2236 }
2237 /**
2238 * Removes the listener.
2239 *
2240 * @param listener the listener which should no longer be notified
2241 *
2242 * @exception IllegalArgumentException <ul>
2243 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2244 * </ul>
2245 *
2246 * @exception SWTException <ul>
2247 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2248 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2249 * </ul>
2250 *
2251 * @see #addCTabFolder2Listener(CTabFolder2Listener)
2252 *
2253 * @since 3.0
2254 */
removeCTabFolder2Listener(CTabFolder2Listener listener)2255 public void removeCTabFolder2Listener(CTabFolder2Listener listener) {
2256 checkWidget();
2257 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2258 if (folderListeners.length == 0) return;
2259 int index = -1;
2260 for (int i = 0; i < folderListeners.length; i++) {
2261 if (listener == folderListeners[i]){
2262 index = i;
2263 break;
2264 }
2265 }
2266 if (index == -1) return;
2267 if (folderListeners.length == 1) {
2268 folderListeners = new CTabFolder2Listener[0];
2269 return;
2270 }
2271 CTabFolder2Listener[] newTabListeners = new CTabFolder2Listener[folderListeners.length - 1];
2272 System.arraycopy(folderListeners, 0, newTabListeners, 0, index);
2273 System.arraycopy(folderListeners, index + 1, newTabListeners, index, folderListeners.length - index - 1);
2274 folderListeners = newTabListeners;
2275 }
2276 /**
2277 * Removes the listener.
2278 *
2279 * @param listener the listener which should no longer be notified
2280 *
2281 * @exception IllegalArgumentException <ul>
2282 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2283 * </ul>
2284 *
2285 * @exception SWTException <ul>
2286 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2287 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2288 * </ul>
2289 *
2290 * @deprecated see removeCTabFolderCloseListener(CTabFolderListener)
2291 */
2292 @Deprecated
removeCTabFolderListener(CTabFolderListener listener)2293 public void removeCTabFolderListener(CTabFolderListener listener) {
2294 checkWidget();
2295 if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2296 if (tabListeners.length == 0) return;
2297 int index = -1;
2298 for (int i = 0; i < tabListeners.length; i++) {
2299 if (listener == tabListeners[i]){
2300 index = i;
2301 break;
2302 }
2303 }
2304 if (index == -1) return;
2305 if (tabListeners.length == 1) {
2306 tabListeners = new CTabFolderListener[0];
2307 return;
2308 }
2309 CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1];
2310 System.arraycopy(tabListeners, 0, newTabListeners, 0, index);
2311 System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1);
2312 tabListeners = newTabListeners;
2313 }
2314 /**
2315 * Removes the listener from the collection of listeners who will
2316 * be notified when the user changes the receiver's selection.
2317 *
2318 * @param listener the listener which should no longer be notified
2319 *
2320 * @exception IllegalArgumentException <ul>
2321 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
2322 * </ul>
2323 * @exception SWTException <ul>
2324 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2325 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2326 * </ul>
2327 *
2328 * @see SelectionListener
2329 * @see #addSelectionListener
2330 */
removeSelectionListener(SelectionListener listener)2331 public void removeSelectionListener(SelectionListener listener) {
2332 checkWidget();
2333 if (listener == null) {
2334 SWT.error(SWT.ERROR_NULL_ARGUMENT);
2335 }
2336 removeListener(SWT.Selection, listener);
2337 removeListener(SWT.DefaultSelection, listener);
2338 }
2339
2340 @Override
reskin(int flags)2341 public void reskin(int flags) {
2342 super.reskin(flags);
2343 for (CTabItem item : items) {
2344 item.reskin(flags);
2345 }
2346 }
2347
2348 @Override
setBackground(Color color)2349 public void setBackground (Color color) {
2350 super.setBackground(color);
2351 renderer.createAntialiasColors(); //TODO: need better caching strategy
2352 updateBkImages();
2353 redraw();
2354 }
2355 /**
2356 * Specify a gradient of colors to be drawn in the background of the unselected tabs.
2357 * For example to draw a gradient that varies from dark blue to blue and then to
2358 * white, use the following call to setBackground:
2359 * <pre>
2360 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2361 * display.getSystemColor(SWT.COLOR_BLUE),
2362 * display.getSystemColor(SWT.COLOR_WHITE),
2363 * display.getSystemColor(SWT.COLOR_WHITE)},
2364 * new int[] {25, 50, 100});
2365 * </pre>
2366 *
2367 * @param colors an array of Color that specifies the colors to appear in the gradient
2368 * in order of appearance left to right. The value <code>null</code> clears the
2369 * background gradient. The value <code>null</code> can be used inside the array of
2370 * Color to specify the background color.
2371 * @param percents an array of integers between 0 and 100 specifying the percent of the width
2372 * of the widget at which the color should change. The size of the <code>percents</code>
2373 * array must be one less than the size of the <code>colors</code> array.
2374 *
2375 * @exception SWTException <ul>
2376 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2377 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2378 * </ul>
2379 *
2380 * @since 3.6
2381 */
setBackground(Color[] colors, int[] percents)2382 public void setBackground(Color[] colors, int[] percents) {
2383 setBackground(colors, percents, false);
2384 }
2385 /**
2386 * Specify a gradient of colors to be drawn in the background of the unselected tab.
2387 * For example to draw a vertical gradient that varies from dark blue to blue and then to
2388 * white, use the following call to setBackground:
2389 * <pre>
2390 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
2391 * display.getSystemColor(SWT.COLOR_BLUE),
2392 * display.getSystemColor(SWT.COLOR_WHITE),
2393 * display.getSystemColor(SWT.COLOR_WHITE)},
2394 * new int[] {25, 50, 100}, true);
2395 * </pre>
2396 *
2397 * @param colors an array of Color that specifies the colors to appear in the gradient
2398 * in order of appearance left to right. The value <code>null</code> clears the
2399 * background gradient. The value <code>null</code> can be used inside the array of
2400 * Color to specify the background color.
2401 * @param percents an array of integers between 0 and 100 specifying the percent of the width
2402 * of the widget at which the color should change. The size of the <code>percents</code>
2403 * array must be one less than the size of the <code>colors</code> array.
2404 *
2405 * @param vertical indicate the direction of the gradient. <code>True</code> is vertical and <code>false</code> is horizontal.
2406 *
2407 * @exception SWTException <ul>
2408 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
2409 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
2410 * </ul>
2411 *
2412 * @since 3.6
2413 */
setBackground(Color[] colors, int[] percents, boolean vertical)2414 public void setBackground(Color[] colors, int[] percents, boolean vertical) {
2415 checkWidget();
2416 if (colors != null) {
2417 if (percents == null || percents.length != colors.length - 1) {
2418 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2419 }
2420 for (int i = 0; i < percents.length; i++) {
2421 if (percents[i] < 0 || percents[i] > 100) {
2422 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2423 }
2424 if (i > 0 && percents[i] < percents[i-1]) {
2425 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2426 }
2427 }
2428 if (getDisplay().getDepth() < 15) {
2429 // Don't use gradients on low color displays
2430 colors = new Color[] {colors[colors.length - 1]};
2431 percents = new int[] {};
2432 }
2433 }
2434
2435 // Are these settings the same as before?
2436 if ((gradientColors != null) && (colors != null) &&
2437 (gradientColors.length == colors.length)) {
2438 boolean same = false;
2439 for (int i = 0; i < gradientColors.length; i++) {
2440 if (gradientColors[i] == null) {
2441 same = colors[i] == null;
2442 } else {
2443 same = gradientColors[i].equals(colors[i]);
2444 }
2445 if (!same) break;
2446 }
2447 if (same) {
2448 for (int i = 0; i < gradientPercents.length; i++) {
2449 same = gradientPercents[i] == percents[i];
2450 if (!same) break;
2451 }
2452 }
2453 if (same && this.gradientVertical == vertical) return;
2454 }
2455 // Store the new settings
2456 if (colors == null) {
2457 gradientColors = null;
2458 gradientPercents = null;
2459 gradientVertical = false;
2460 setBackground((Color)null);
2461 } else {
2462 gradientColors = new Color[colors.length];
2463 for (int i = 0; i < colors.length; ++i) {
2464 gradientColors[i] = colors[i];
2465 }
2466 gradientPercents = new int[percents.length];
2467 for (int i = 0; i < percents.length; ++i) {
2468 gradientPercents[i] = percents[i];
2469 }
2470 gradientVertical = vertical;
2471 setBackground(gradientColors[gradientColors.length-1]);
2472 }
2473
2474 // Refresh with the new settings
2475 redraw();
2476 }
2477 @Override
setBackgroundImage(Image image)2478 public void setBackgroundImage(Image image) {
2479 super.setBackgroundImage(image);
2480 renderer.createAntialiasColors(); //TODO: need better caching strategy
2481 redraw();
2482 }
2483 /**
2484 * Toggle the visibility of the border
2485 *
2486 * @param show true if the border should be displayed
2487 *
2488 * @exception SWTException <ul>
2489 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2490 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2491 * </ul>
2492 */
setBorderVisible(boolean show)2493 public void setBorderVisible(boolean show) {
2494 checkWidget();
2495 if (borderVisible == show) return;
2496 this.borderVisible = show;
2497 updateFolder(REDRAW);
2498 }
2499
2500 /**
2501 * Create or dispose min/max buttons.
2502 */
updateButtons()2503 void updateButtons() {
2504 // max button
2505 Display display = getDisplay();
2506 if (showMax) {
2507 if (minMaxTb == null) {
2508 minMaxTb = new ToolBar(this, SWT.FLAT);
2509 initAccessibleMinMaxTb();
2510 addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2511 }
2512 if (maxItem == null) {
2513 maxItem = new ToolItem(minMaxTb, SWT.PUSH);
2514 if (maxImage == null) {
2515 maxImage = createButtonImage(display, CTabFolderRenderer.PART_MAX_BUTTON);
2516 }
2517 maxItem.setImage(maxImage);
2518 maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
2519 maxItem.addListener(SWT.Selection, listener);
2520 }
2521 } else {
2522 //might need to remove it if already there
2523 if (maxItem != null) {
2524 maxItem.dispose();
2525 maxItem = null;
2526 }
2527 }
2528 // min button
2529 if (showMin) {
2530 if (minMaxTb == null) {
2531 minMaxTb = new ToolBar(this, SWT.FLAT);
2532 initAccessibleMinMaxTb();
2533 addTabControl(minMaxTb, SWT.TRAIL, 0, false);
2534 }
2535 if (minItem == null) {
2536 minItem = new ToolItem(minMaxTb, SWT.PUSH, 0);
2537 if (minImage == null) {
2538 minImage = createButtonImage(display, CTabFolderRenderer.PART_MIN_BUTTON);
2539 }
2540 minItem.setImage(minImage);
2541 minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
2542 minItem.addListener(SWT.Selection, listener);
2543 }
2544 } else {
2545 //might need to remove it if already there
2546 if (minItem != null) {
2547 minItem.dispose();
2548 minItem = null;
2549 }
2550 }
2551 if (minMaxTb != null && minMaxTb.getItemCount() == 0) {
2552 removeTabControl(minMaxTb, false);
2553 minMaxTb.dispose();
2554 minMaxTb = null;
2555 }
2556 }
2557
2558 /**
2559 * Update button bounds for min/max and update chevron button.
2560 */
setButtonBounds()2561 void setButtonBounds() {
2562 if (showChevron) {
2563 updateChevronImage(false);
2564 }
2565
2566 Point size = getSize();
2567 boolean[][] overflow = new boolean[1][0];
2568 Rectangle[] rects = computeControlBounds(size, overflow);
2569 if (fixedTabHeight != SWT.DEFAULT) {
2570 int height = fixedTabHeight;
2571 if (!hovering) {
2572 hoverTb = false;
2573 Rectangle tabBounds = this.getBounds();
2574 for (int i = 0; i < rects.length; i++) {
2575 if (!(overflow[0][i])) {
2576 if (rects[i].height > height) {
2577 hoverTb = true;
2578 break;
2579 }
2580 }
2581 }
2582 if (hoverTb) {
2583 for (int i = 0; i < rects.length; i++) {
2584 if (!(overflow[0][i])) {
2585 if (rects[i].height > height) {
2586 rects[i].x = tabBounds.width + 20;
2587 }
2588 }
2589 }
2590 }
2591 }
2592 }
2593 int headerHeight = 0;
2594 for (int i = 0; i < rects.length; i++) {
2595 if (!overflow[0][i]) headerHeight = Math.max(rects[i].height, headerHeight);
2596 }
2597 boolean changed = false;
2598 ignoreResize = true;
2599 for (int i = 0; i < controls.length; i++) {
2600 if (!controls[i].isDisposed()) {
2601 if (overflow[0][i]) {
2602 controls[i].setBounds(rects[i]);
2603 } else {
2604 controls[i].moveAbove(null);
2605 controls[i].setBounds(rects[i].x, rects[i].y, rects[i].width, headerHeight);
2606 }
2607 }
2608 if (!changed && !rects[i].equals(controlRects[i])) changed = true;
2609 }
2610 ignoreResize = false;
2611 controlRects = rects;
2612 if (changed || hovering) updateBkImages();
2613 }
2614
2615 /**
2616 * Get the number of hidden items or the number which is to be drawn in the
2617 * chevon item.
2618 * <p>
2619 * Note: do not confuse this with {@link #chevronCount} which contains the count
2620 * from the last time the cached chevron image was drawn. It can be different
2621 * from the value returned by this method.
2622 * </p>
2623 *
2624 * @return the chevron count
2625 */
getChevronCount()2626 int getChevronCount() {
2627 int itemCount = items.length;
2628 int count;
2629 if (single) {
2630 count = selectedIndex == -1 ? itemCount : itemCount - 1;
2631 } else {
2632 int showCount = 0;
2633 while (showCount < priority.length && items[priority[showCount]].showing) {
2634 showCount++;
2635 }
2636 count = itemCount - showCount;
2637 }
2638 return count;
2639 }
2640
2641 /**
2642 * Update the cached chevron image.
2643 *
2644 * @param styleChange <code>true</code> if the update is required for changed
2645 * appearance of the chevron. In this case the image is not
2646 * created if it does not already exist and is updated even
2647 * if the drawn number (chevonCount) has not changed.
2648 */
updateChevronImage(boolean styleChange)2649 private void updateChevronImage(boolean styleChange) {
2650 if (styleChange && chevronImage == null) return;
2651 int newCount = getChevronCount();
2652 if (!styleChange && chevronCount == newCount) return;
2653 if (chevronImage != null) chevronImage.dispose();
2654 chevronImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_CHEVRON_BUTTON);
2655 chevronItem.setImage(chevronImage);
2656 chevronCount = newCount;
2657 }
2658 @Override
setFocus()2659 public boolean setFocus () {
2660 checkWidget ();
2661
2662 /*
2663 * Feature in SWT. When a new tab item is selected
2664 * and the previous tab item had focus, removing focus
2665 * from the previous tab item causes fixFocus() to give
2666 * focus to the first child, which is usually one of the
2667 * toolbars. This is unexpected.
2668 * The fix is to try to set focus on the first tab item
2669 * if fixFocus() is called.
2670 */
2671 Control focusControl = getDisplay().getFocusControl ();
2672 boolean fixFocus = isAncestor (focusControl);
2673 if (fixFocus) {
2674 CTabItem item = getSelection();
2675 if (item != null) {
2676 if (item.setFocus ()) return true;
2677 }
2678 }
2679 return super.setFocus ();
2680 }
2681 /* Copy of isFocusAncestor from Control. */
isAncestor(Control control)2682 boolean isAncestor (Control control) {
2683 while (control != null && control != this && !(control instanceof Shell)) {
2684 control = control.getParent();
2685 }
2686 return control == this;
2687 }
2688 @Override
setFont(Font font)2689 public void setFont(Font font) {
2690 checkWidget();
2691 if (font != null && font.equals(getFont())) return;
2692 super.setFont(font);
2693 oldFont = getFont();
2694 // Chevron painting is cached as image and only recreated if number of hidden tabs changed.
2695 // To apply the new font the cached image must be recreated with new font.
2696 // Redraw request alone would only redraw the cached image with old font.
2697 renderer.chevronFont = null; // renderer will pickup and adjust(!) the new font automatically
2698 updateChevronImage(true);
2699 updateFolder(REDRAW);
2700 }
2701 @Override
setForeground(Color color)2702 public void setForeground (Color color) {
2703 super.setForeground(color);
2704 // Chevron painting is cached as image and only recreated if number of hidden tabs changed.
2705 // To apply the new foreground color the image must be recreated with new foreground color.
2706 // redraw() alone would only redraw the cached image with old color.
2707 updateChevronImage(true);
2708 redraw();
2709 }
2710 /**
2711 * Display an insert marker before or after the specified tab item.
2712 *
2713 * A value of null will clear the mark.
2714 *
2715 * @param item the item with which the mark is associated or null
2716 *
2717 * @param after true if the mark should be displayed after the specified item
2718 *
2719 * @exception SWTException <ul>
2720 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2721 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2722 * </ul>
2723 */
setInsertMark(CTabItem item, boolean after)2724 public void setInsertMark(CTabItem item, boolean after) {
2725 checkWidget();
2726 }
2727 /**
2728 * Display an insert marker before or after the specified tab item.
2729 *
2730 * A value of -1 will clear the mark.
2731 *
2732 * @param index the index of the item with which the mark is associated or -1
2733 *
2734 * @param after true if the mark should be displayed after the specified item
2735 *
2736 * @exception IllegalArgumentException <ul>
2737 * <li>ERROR_INVALID_ARGUMENT when the index is invalid</li>
2738 * </ul>
2739 *
2740 * @exception SWTException <ul>
2741 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2742 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2743 * </ul>
2744 */
setInsertMark(int index, boolean after)2745 public void setInsertMark(int index, boolean after) {
2746 checkWidget();
2747 if (index < -1 || index >= getItemCount()) {
2748 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
2749 }
2750 }
setItemLocation(GC gc)2751 boolean setItemLocation(GC gc) {
2752 boolean changed = false;
2753 if (items.length == 0) return false;
2754 Rectangle trim = renderer.computeTrim(CTabFolderRenderer.PART_BORDER, SWT.NONE, 0, 0, 0, 0);
2755 int borderBottom = trim.height + trim.y;
2756 int borderTop = -trim.y;
2757 Point size = getSize();
2758 int y = onBottom ? Math.max(borderBottom, size.y - borderBottom - tabHeight) : borderTop;
2759 Point closeButtonSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, 0, gc, SWT.DEFAULT, SWT.DEFAULT);
2760 int leftItemEdge = getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
2761 if (single) {
2762 int defaultX = getDisplay().getBounds().width + 10; // off screen
2763 for (int i = 0; i < items.length; i++) {
2764 CTabItem item = items[i];
2765 if (i == selectedIndex) {
2766 firstIndex = selectedIndex;
2767 int oldX = item.x, oldY = item.y;
2768 item.x = leftItemEdge;
2769 item.y = y;
2770 item.showing = true;
2771 if (showClose || item.showClose) {
2772 item.closeRect.x = leftItemEdge - renderer.computeTrim(i, SWT.NONE, 0, 0, 0, 0).x;
2773 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2774 }
2775 if (item.x != oldX || item.y != oldY) changed = true;
2776 } else {
2777 item.x = defaultX;
2778 item.showing = false;
2779 }
2780 }
2781 } else {
2782 int rightItemEdge = getRightItemEdge(gc);
2783 int maxWidth = rightItemEdge - leftItemEdge;
2784 int width = 0;
2785 for (int i = 0; i < priority.length; i++) {
2786 CTabItem item = items[priority[i]];
2787 width += item.width;
2788 item.showing = i == 0 ? true : item.width > 0 && width <= maxWidth;
2789 }
2790 int x = getLeftItemEdge(gc, CTabFolderRenderer.PART_HEADER);
2791 int defaultX = getDisplay().getBounds().width + 10; // off screen
2792 firstIndex = items.length - 1;
2793 for (int i = 0; i < items.length; i++) {
2794 CTabItem item = items[i];
2795 if (!item.showing) {
2796 if (item.x != defaultX) changed = true;
2797 item.x = defaultX;
2798 } else {
2799 firstIndex = Math.min(firstIndex, i);
2800 if (item.x != x || item.y != y) changed = true;
2801 item.x = x;
2802 item.y = y;
2803 int state = SWT.NONE;
2804 if (i == selectedIndex) state |= SWT.SELECTED;
2805 Rectangle edgeTrim = renderer.computeTrim(i, state, 0, 0, 0, 0);
2806 item.closeRect.x = item.x + item.width - (edgeTrim.width + edgeTrim.x) - closeButtonSize.x;
2807 item.closeRect.y = onBottom ? size.y - borderBottom - tabHeight + (tabHeight - closeButtonSize.y)/2: borderTop + (tabHeight - closeButtonSize.y)/2;
2808 x = x + item.width;
2809 if (!simple && i == selectedIndex) x -= renderer.curveIndent; //TODO: fix next item position
2810 }
2811 }
2812 }
2813 return changed;
2814 }
2815 /**
2816 * Reorder the items of the receiver.
2817 * @param indices an array containing the new indices for all items
2818 *
2819 * @exception IllegalArgumentException <ul>
2820 * <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
2821 * <li>ERROR_INVALID_ARGUMENT - if the indices array is not the same length as the number of items,
2822 * if there are duplicate indices or an index is out of range.</li>
2823 * </ul>
2824 *
2825 * @exception SWTException <ul>
2826 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2827 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2828 * </ul>
2829 */
setItemOrder(int[] indices)2830 /*public*/ void setItemOrder (int[] indices) {
2831 checkWidget();
2832 if (indices == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
2833 if (indices.length != items.length) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2834 int newSelectedIndex = -1;
2835 boolean[] seen = new boolean[items.length];
2836 CTabItem[] temp = new CTabItem[items.length];
2837 for (int i=0; i<indices.length; i++) {
2838 int index = indices[i];
2839 if (!(0 <= index && index < items.length)) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2840 if (seen[index]) SWT.error (SWT.ERROR_INVALID_ARGUMENT);
2841 seen[index] = true;
2842 if (index == selectedIndex) newSelectedIndex = i;
2843 temp[i] = items[index];
2844 }
2845 items = temp;
2846 selectedIndex = newSelectedIndex;
2847 updateFolder(REDRAW);
2848 }
setItemSize(GC gc)2849 boolean setItemSize(GC gc) {
2850 boolean changed = false;
2851 if (isDisposed()) return changed;
2852 Point size = getSize();
2853 if (size.x <= 0 || size.y <= 0) return changed;
2854 ToolBar chevron = getChevron();
2855 if (chevron != null) chevron.setVisible(false);
2856 showChevron = false;
2857 if (single) {
2858 showChevron = chevronVisible && items.length > 1;
2859 if (showChevron) {
2860 chevron.setVisible(true);
2861 }
2862 if (selectedIndex != -1) {
2863 CTabItem tab = items[selectedIndex];
2864 int width = renderer.computeSize(selectedIndex, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2865 width = Math.min(width, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2866 if (tab.height != tabHeight || tab.width != width) {
2867 changed = true;
2868 tab.shortenedText = null;
2869 tab.shortenedTextWidth = 0;
2870 tab.height = tabHeight;
2871 tab.width = width;
2872 tab.closeRect.width = tab.closeRect.height = 0;
2873 if (showClose || tab.showClose) {
2874 Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.SELECTED, gc, SWT.DEFAULT, SWT.DEFAULT);
2875 tab.closeRect.width = closeSize.x;
2876 tab.closeRect.height = closeSize.y;
2877 }
2878 }
2879 }
2880 return changed;
2881 }
2882
2883 if (items.length == 0) return changed;
2884 int[] widths;
2885 int tabAreaWidth = Math.max(0, getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER));
2886 // First, try the minimum tab size at full compression.
2887 int minWidth = 0;
2888 int[] minWidths = new int[items.length];
2889 for (int element : priority) {
2890 int index = element;
2891 int state = CTabFolderRenderer.MINIMUM_SIZE;
2892 if (index == selectedIndex) state |= SWT.SELECTED;
2893 minWidths[index] = renderer.computeSize(index, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2894 minWidth += minWidths[index];
2895 if (minWidth > tabAreaWidth) break;
2896 }
2897 if (minWidth > tabAreaWidth) {
2898 // full compression required and a chevron
2899 showChevron = chevronVisible && items.length > 1;
2900 if (showChevron) {
2901 tabAreaWidth -= chevron.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
2902 chevron.setVisible(true);
2903 }
2904 widths = minWidths;
2905 int index = selectedIndex != -1 ? selectedIndex : 0;
2906 if (tabAreaWidth < widths[index]) {
2907 widths[index] = Math.max(0, tabAreaWidth);
2908 }
2909 } else {
2910 int maxWidth = 0;
2911 int[] maxWidths = new int[items.length];
2912 for (int i = 0; i < items.length; i++) {
2913 int state = 0;
2914 if (i == selectedIndex) state |= SWT.SELECTED;
2915 maxWidths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
2916 maxWidth += maxWidths[i];
2917 }
2918 if (maxWidth <= tabAreaWidth) {
2919 // no compression required
2920 widths = maxWidths;
2921 } else {
2922 // determine compression for each item
2923 int extra = (tabAreaWidth - minWidth) / items.length;
2924 while (true) {
2925 int large = 0, totalWidth = 0;
2926 for (int i = 0 ; i < items.length; i++) {
2927 if (maxWidths[i] > minWidths[i] + extra) {
2928 totalWidth += minWidths[i] + extra;
2929 large++;
2930 } else {
2931 totalWidth += maxWidths[i];
2932 }
2933 }
2934 if (totalWidth >= tabAreaWidth) {
2935 extra--;
2936 break;
2937 }
2938 if (large == 0 || tabAreaWidth - totalWidth < large) break;
2939 extra++;
2940 }
2941 widths = new int[items.length];
2942 for (int i = 0; i < items.length; i++) {
2943 widths[i] = Math.min(maxWidths[i], minWidths[i] + extra);
2944 }
2945 }
2946 }
2947
2948 for (int i = 0; i < items.length; i++) {
2949 CTabItem tab = items[i];
2950 int width = widths[i];
2951 if (tab.height != tabHeight || tab.width != width) {
2952 changed = true;
2953 tab.shortenedText = null;
2954 tab.shortenedTextWidth = 0;
2955 tab.height = tabHeight;
2956 tab.width = width;
2957 tab.closeRect.width = tab.closeRect.height = 0;
2958 if (showClose || tab.showClose) {
2959 if (i == selectedIndex || showUnselectedClose) {
2960 Point closeSize = renderer.computeSize(CTabFolderRenderer.PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT);
2961 tab.closeRect.width = closeSize.x;
2962 tab.closeRect.height = closeSize.y;
2963 }
2964 }
2965 }
2966 }
2967 return changed;
2968 }
2969 /**
2970 * Marks the receiver's maximize button as visible if the argument is <code>true</code>,
2971 * and marks it invisible otherwise.
2972 *
2973 * @param visible the new visibility state
2974 *
2975 * @exception SWTException <ul>
2976 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2977 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2978 * </ul>
2979 *
2980 * @since 3.0
2981 */
setMaximizeVisible(boolean visible)2982 public void setMaximizeVisible(boolean visible) {
2983 checkWidget();
2984 if (showMax == visible) return;
2985 // display maximize button
2986 showMax = visible;
2987 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
2988 }
2989 /**
2990 * Sets the layout which is associated with the receiver to be
2991 * the argument which may be null.
2992 * <p>
2993 * Note: No Layout can be set on this Control because it already
2994 * manages the size and position of its children.
2995 * </p>
2996 *
2997 * @param layout the receiver's new layout or null
2998 *
2999 * @exception SWTException <ul>
3000 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3001 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3002 * </ul>
3003 */
3004 @Override
setLayout(Layout layout)3005 public void setLayout (Layout layout) {
3006 checkWidget();
3007 return;
3008 }
3009 /**
3010 * Sets the maximized state of the receiver.
3011 *
3012 * @param maximize the new maximized state
3013 *
3014 * @exception SWTException <ul>
3015 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3016 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3017 * </ul>
3018 *
3019 * @since 3.0
3020 */
setMaximized(boolean maximize)3021 public void setMaximized(boolean maximize) {
3022 checkWidget ();
3023 if (this.maximized == maximize) return;
3024 if (maximize && this.minimized) setMinimized(false);
3025 this.maximized = maximize;
3026 if (minMaxTb != null && maxItem != null) {
3027 if (maxImage != null) maxImage.dispose();
3028 maxImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MAX_BUTTON);
3029 maxItem.setImage(maxImage);
3030 maxItem.setToolTipText(maximized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Maximize")); //$NON-NLS-1$ //$NON-NLS-2$
3031 }
3032 }
3033 /**
3034 * Marks the receiver's minimize button as visible if the argument is <code>true</code>,
3035 * and marks it invisible otherwise.
3036 *
3037 * @param visible the new visibility state
3038 *
3039 * @exception SWTException <ul>
3040 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3041 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3042 * </ul>
3043 *
3044 * @since 3.0
3045 */
setMinimizeVisible(boolean visible)3046 public void setMinimizeVisible(boolean visible) {
3047 checkWidget();
3048 if (showMin == visible) return;
3049 // display minimize button
3050 showMin = visible;
3051 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3052 }
3053 /**
3054 * Sets the minimized state of the receiver.
3055 *
3056 * @param minimize the new minimized state
3057 *
3058 * @exception SWTException <ul>
3059 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3060 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3061 * </ul>
3062 *
3063 * @since 3.0
3064 */
setMinimized(boolean minimize)3065 public void setMinimized(boolean minimize) {
3066 checkWidget ();
3067 if (this.minimized == minimize) return;
3068 if (minimize && this.maximized) setMaximized(false);
3069 this.minimized = minimize;
3070 if (minMaxTb != null && minItem != null) {
3071 if (minImage != null) minImage.dispose();
3072 minImage = createButtonImage(getDisplay(), CTabFolderRenderer.PART_MIN_BUTTON);
3073 minItem.setImage(minImage);
3074 minItem.setToolTipText(minimized ? SWT.getMessage("SWT_Restore") : SWT.getMessage("SWT_Minimize")); //$NON-NLS-1$ //$NON-NLS-2$
3075 }
3076 }
3077
3078 /**
3079 * Sets the minimum number of characters that will
3080 * be displayed in a fully compressed tab.
3081 *
3082 * @param count the minimum number of characters that will be displayed in a fully compressed tab
3083 *
3084 * @exception SWTException <ul>
3085 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3086 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3087 * <li>ERROR_INVALID_RANGE - if the count is less than zero</li>
3088 * </ul>
3089 *
3090 * @since 3.0
3091 */
setMinimumCharacters(int count)3092 public void setMinimumCharacters(int count) {
3093 checkWidget ();
3094 if (count < 0) SWT.error(SWT.ERROR_INVALID_RANGE);
3095 if (minChars == count) return;
3096 minChars = count;
3097 updateFolder(REDRAW_TABS);
3098 }
3099
3100 /**
3101 * When there is not enough horizontal space to show all the tabs,
3102 * by default, tabs are shown sequentially from left to right in
3103 * order of their index. When the MRU visibility is turned on,
3104 * the tabs that are visible will be the tabs most recently selected.
3105 * Tabs will still maintain their left to right order based on index
3106 * but only the most recently selected tabs are visible.
3107 * <p>
3108 * For example, consider a CTabFolder that contains "Tab 1", "Tab 2",
3109 * "Tab 3" and "Tab 4" (in order by index). The user selects
3110 * "Tab 1" and then "Tab 3". If the CTabFolder is now
3111 * compressed so that only two tabs are visible, by default,
3112 * "Tab 2" and "Tab 3" will be shown ("Tab 3" since it is currently
3113 * selected and "Tab 2" because it is the previous item in index order).
3114 * If MRU visibility is enabled, the two visible tabs will be "Tab 1"
3115 * and "Tab 3" (in that order from left to right).</p>
3116 *
3117 * @param show the new visibility state
3118 *
3119 * @exception SWTException <ul>
3120 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3121 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3122 * </ul>
3123 *
3124 * @since 3.1
3125 */
setMRUVisible(boolean show)3126 public void setMRUVisible(boolean show) {
3127 checkWidget();
3128 if (mru == show) return;
3129 mru = show;
3130 if (!mru) {
3131 if (firstIndex == -1) return;
3132 int idx = firstIndex;
3133 int next = 0;
3134 for (int i = firstIndex; i < items.length; i++) {
3135 priority[next++] = i;
3136 }
3137 for (int i = 0; i < idx; i++) {
3138 priority[next++] = i;
3139 }
3140 updateFolder(REDRAW_TABS);
3141 }
3142 }
3143 /**
3144 * Sets the renderer which is associated with the receiver to be
3145 * the argument which may be null. In the case of null, the default
3146 * renderer is used.
3147 *
3148 * @param renderer a new renderer
3149 *
3150 * @exception SWTException <ul>
3151 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3152 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3153 * </ul>
3154 *
3155 * @see CTabFolderRenderer
3156 *
3157 * @since 3.6
3158 */
setRenderer(CTabFolderRenderer renderer)3159 public void setRenderer(CTabFolderRenderer renderer) {
3160 checkWidget();
3161 if (this.renderer == renderer || (useDefaultRenderer && renderer == null)) return;
3162 if (this.renderer != null) this.renderer.dispose();
3163 useDefaultRenderer = renderer == null;
3164 if (useDefaultRenderer) renderer = new CTabFolderRenderer(this);
3165 this.renderer = renderer;
3166 updateFolder(REDRAW);
3167 }
3168 /**
3169 * Set the selection to the tab at the specified item.
3170 *
3171 * @param item the tab item to be selected
3172 *
3173 * @exception IllegalArgumentException <ul>
3174 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3175 * </ul>
3176 *
3177 * @exception SWTException <ul>
3178 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3179 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3180 * </ul>
3181 */
setSelection(CTabItem item)3182 public void setSelection(CTabItem item) {
3183 checkWidget();
3184 if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
3185 int index = indexOf(item);
3186 setSelection(index);
3187 }
3188 /**
3189 * Set the selection to the tab at the specified index.
3190 *
3191 * @param index the index of the tab item to be selected
3192 *
3193 * @exception SWTException <ul>
3194 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3195 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3196 * </ul>
3197 */
setSelection(int index)3198 public void setSelection(int index) {
3199 checkWidget();
3200 if (index < 0 || index >= items.length) return;
3201 CTabItem selection = items[index];
3202 if (selectedIndex == index) {
3203 showItem(selection);
3204 return;
3205 }
3206
3207 int oldIndex = selectedIndex;
3208 selectedIndex = index;
3209 if (oldIndex != -1) {
3210 items[oldIndex].closeImageState = SWT.BACKGROUND;
3211 items[oldIndex].state &= ~SWT.SELECTED;
3212 }
3213 selection.closeImageState = SWT.NONE;
3214 selection.showing = false;
3215 selection.state |= SWT.SELECTED;
3216
3217 Control newControl = selection.control;
3218 Control oldControl = null;
3219 if (oldIndex != -1) {
3220 oldControl = items[oldIndex].control;
3221 }
3222
3223 if (newControl != oldControl) {
3224 if (newControl != null && !newControl.isDisposed()) {
3225 newControl.setBounds(getClientArea());
3226 newControl.setVisible(true);
3227 }
3228 if (oldControl != null && !oldControl.isDisposed()) {
3229 oldControl.setVisible(false);
3230 }
3231 }
3232 showItem(selection);
3233 redraw();
3234 }
setSelection(int index, boolean notify)3235 void setSelection(int index, boolean notify) {
3236 int oldSelectedIndex = selectedIndex;
3237 setSelection(index);
3238 if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) {
3239 Event event = new Event();
3240 event.item = getItem(selectedIndex);
3241 notifyListeners(SWT.Selection, event);
3242 }
3243 }
3244 /**
3245 * Sets the receiver's selection background color to the color specified
3246 * by the argument, or to the default system color for the control
3247 * if the argument is null.
3248 *
3249 * @param color the new color (or null)
3250 *
3251 * @exception IllegalArgumentException <ul>
3252 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
3253 * </ul>
3254 * @exception SWTException <ul>
3255 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3256 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3257 * </ul>
3258 *
3259 * @since 3.0
3260 */
setSelectionBackground(Color color)3261 public void setSelectionBackground (Color color) {
3262 if (inDispose) return;
3263 checkWidget();
3264 setSelectionHighlightGradientColor(null);
3265 if (selectionBackground == color) return;
3266 if (color == null) color = getDisplay().getSystemColor(SELECTION_BACKGROUND);
3267 selectionBackground = color;
3268 renderer.createAntialiasColors(); //TODO: need better caching strategy
3269 if (selectedIndex > -1) redraw();
3270 }
3271 /**
3272 * Specify a gradient of colours to be draw in the background of the selected tab.
3273 * For example to draw a gradient that varies from dark blue to blue and then to
3274 * white, use the following call to setBackground:
3275 * <pre>
3276 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3277 * display.getSystemColor(SWT.COLOR_BLUE),
3278 * display.getSystemColor(SWT.COLOR_WHITE),
3279 * display.getSystemColor(SWT.COLOR_WHITE)},
3280 * new int[] {25, 50, 100});
3281 * </pre>
3282 *
3283 * @param colors an array of Color that specifies the colors to appear in the gradient
3284 * in order of appearance left to right. The value <code>null</code> clears the
3285 * background gradient. The value <code>null</code> can be used inside the array of
3286 * Color to specify the background color.
3287 * @param percents an array of integers between 0 and 100 specifying the percent of the width
3288 * of the widget at which the color should change. The size of the percents array must be one
3289 * less than the size of the colors array.
3290 *
3291 * @exception SWTException <ul>
3292 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3293 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3294 * </ul>
3295 */
setSelectionBackground(Color[] colors, int[] percents)3296 public void setSelectionBackground(Color[] colors, int[] percents) {
3297 setSelectionBackground(colors, percents, false);
3298 }
3299 /**
3300 * Specify a gradient of colours to be draw in the background of the selected tab.
3301 * For example to draw a vertical gradient that varies from dark blue to blue and then to
3302 * white, use the following call to setBackground:
3303 * <pre>
3304 * cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE),
3305 * display.getSystemColor(SWT.COLOR_BLUE),
3306 * display.getSystemColor(SWT.COLOR_WHITE),
3307 * display.getSystemColor(SWT.COLOR_WHITE)},
3308 * new int[] {25, 50, 100}, true);
3309 * </pre>
3310 *
3311 * @param colors an array of Color that specifies the colors to appear in the gradient
3312 * in order of appearance left to right. The value <code>null</code> clears the
3313 * background gradient. The value <code>null</code> can be used inside the array of
3314 * Color to specify the background color.
3315 * @param percents an array of integers between 0 and 100 specifying the percent of the width
3316 * of the widget at which the color should change. The size of the percents array must be one
3317 * less than the size of the colors array.
3318 *
3319 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal.
3320 *
3321 * @exception SWTException <ul>
3322 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
3323 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
3324 * </ul>
3325 *
3326 * @since 3.0
3327 */
setSelectionBackground(Color[] colors, int[] percents, boolean vertical)3328 public void setSelectionBackground(Color[] colors, int[] percents, boolean vertical) {
3329 checkWidget();
3330 int colorsLength;
3331 Color highlightBeginColor = null; //null == no highlight
3332
3333 if (colors != null) {
3334 //The colors array can optionally have an extra entry which describes the highlight top color
3335 //Thus its either one or two larger than the percents array
3336 if (percents == null ||
3337 ! ((percents.length == colors.length - 1) || (percents.length == colors.length - 2))){
3338 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3339 }
3340 for (int i = 0; i < percents.length; i++) {
3341 if (percents[i] < 0 || percents[i] > 100) {
3342 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3343 }
3344 if (i > 0 && percents[i] < percents[i-1]) {
3345 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3346 }
3347 }
3348 //If the colors is exactly two more than percents then last is highlight
3349 //Keep track of *real* colorsLength (minus the highlight)
3350 if(percents.length == colors.length - 2) {
3351 highlightBeginColor = colors[colors.length - 1];
3352 colorsLength = colors.length - 1;
3353 } else {
3354 colorsLength = colors.length;
3355 }
3356 if (getDisplay().getDepth() < 15) {
3357 // Don't use gradients on low color displays
3358 colors = new Color[] {colors[colorsLength - 1]};
3359 colorsLength = colors.length;
3360 percents = new int[] {};
3361 }
3362 } else {
3363 colorsLength = 0;
3364 }
3365
3366 // Are these settings the same as before?
3367 if (selectionBgImage == null) {
3368 if ((selectionGradientColors != null) && (colors != null) &&
3369 (selectionGradientColors.length == colorsLength)) {
3370 boolean same = false;
3371 for (int i = 0; i < selectionGradientColors.length; i++) {
3372 if (selectionGradientColors[i] == null) {
3373 same = colors[i] == null;
3374 } else {
3375 same = selectionGradientColors[i].equals(colors[i]);
3376 }
3377 if (!same) break;
3378 }
3379 if (same) {
3380 for (int i = 0; i < selectionGradientPercents.length; i++) {
3381 same = selectionGradientPercents[i] == percents[i];
3382 if (!same) break;
3383 }
3384 }
3385 if (same && this.selectionGradientVertical == vertical) return;
3386 }
3387 } else {
3388 selectionBgImage = null;
3389 }
3390 // Store the new settings
3391 if (colors == null) {
3392 selectionGradientColors = null;
3393 selectionGradientPercents = null;
3394 selectionGradientVertical = false;
3395 setSelectionBackground((Color)null);
3396 setSelectionHighlightGradientColor(null);
3397 } else {
3398 selectionGradientColors = new Color[colorsLength];
3399 for (int i = 0; i < colorsLength; ++i) {
3400 selectionGradientColors[i] = colors[i];
3401 }
3402 selectionGradientPercents = new int[percents.length];
3403 for (int i = 0; i < percents.length; ++i) {
3404 selectionGradientPercents[i] = percents[i];
3405 }
3406 selectionGradientVertical = vertical;
3407 setSelectionBackground(selectionGradientColors[selectionGradientColors.length-1]);
3408 setSelectionHighlightGradientColor(highlightBeginColor);
3409 }
3410
3411 // Refresh with the new settings
3412 if (selectedIndex > -1) redraw();
3413 }
3414
3415 /*
3416 * Set the color for the highlight start for selected tabs.
3417 * Update the cache of highlight gradient colors if required.
3418 */
setSelectionHighlightGradientColor(Color start)3419 void setSelectionHighlightGradientColor(Color start) {
3420 if (inDispose) return;
3421 renderer.setSelectionHighlightGradientColor(start); //TODO: need better caching strategy
3422 }
3423
3424 /**
3425 * Set the image to be drawn in the background of the selected tab. Image
3426 * is stretched or compressed to cover entire selection tab area.
3427 *
3428 * @param image the image to be drawn in the background
3429 *
3430 * @exception SWTException <ul>
3431 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3432 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3433 * </ul>
3434 */
setSelectionBackground(Image image)3435 public void setSelectionBackground(Image image) {
3436 checkWidget();
3437 setSelectionHighlightGradientColor(null);
3438 if (image == selectionBgImage) return;
3439 if (image != null) {
3440 selectionGradientColors = null;
3441 selectionGradientPercents = null;
3442 renderer.disposeSelectionHighlightGradientColors(); //TODO: need better caching strategy
3443 }
3444 selectionBgImage = image;
3445 renderer.createAntialiasColors(); //TODO: need better caching strategy
3446 if (selectedIndex > -1) redraw();
3447 }
3448 /**
3449 * Set the foreground color of the selected tab.
3450 *
3451 * @param color the color of the text displayed in the selected tab
3452 *
3453 * @exception SWTException <ul>
3454 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3455 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3456 * </ul>
3457 */
setSelectionForeground(Color color)3458 public void setSelectionForeground (Color color) {
3459 checkWidget();
3460 if (selectionForeground == color) return;
3461 if (color == null) color = getDisplay().getSystemColor(SELECTION_FOREGROUND);
3462 selectionForeground = color;
3463 if (selectedIndex > -1) redraw();
3464 }
3465
3466 /**
3467 * Sets the shape that the CTabFolder will use to render itself.
3468 *
3469 * @param simple <code>true</code> if the CTabFolder should render itself in a simple, traditional style
3470 *
3471 * @exception SWTException <ul>
3472 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3473 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3474 * </ul>
3475 *
3476 * @since 3.0
3477 */
setSimple(boolean simple)3478 public void setSimple(boolean simple) {
3479 checkWidget();
3480 if (this.simple != simple) {
3481 this.simple = simple;
3482 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3483 }
3484 }
3485 /**
3486 * Sets the number of tabs that the CTabFolder should display
3487 *
3488 * @param single <code>true</code> if only the selected tab should be displayed otherwise, multiple tabs will be shown.
3489 *
3490 * @exception SWTException <ul>
3491 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3492 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3493 * </ul>
3494 *
3495 * @since 3.0
3496 */
setSingle(boolean single)3497 public void setSingle(boolean single) {
3498 checkWidget();
3499 if (this.single != single) {
3500 this.single = single;
3501 if (!single) {
3502 for (int i = 0; i < items.length; i++) {
3503 if (i != selectedIndex && items[i].closeImageState == SWT.NONE) {
3504 items[i].closeImageState = SWT.BACKGROUND;
3505 }
3506 }
3507 }
3508 updateFolder(REDRAW);
3509 }
3510 }
3511
getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i)3512 int getControlY(Point size, Rectangle[] rects, int borderBottom, int borderTop, int i) {
3513 int center = fixedTabHeight != SWT.DEFAULT ? 0 : (tabHeight - rects[i].height)/2;
3514 return onBottom ? size.y - borderBottom - tabHeight + center : 1 + borderTop + center;
3515 }
3516
3517 /**
3518 * Specify a fixed height for the tab items. If no height is specified,
3519 * the default height is the height of the text or the image, whichever
3520 * is greater. Specifying a height of -1 will revert to the default height.
3521 *
3522 * @param height the point value of the height or -1
3523 *
3524 * @exception SWTException <ul>
3525 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3526 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3527 * <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li>
3528 * </ul>
3529 */
setTabHeight(int height)3530 public void setTabHeight(int height) {
3531 checkWidget();
3532 if (height < -1) {
3533 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3534 }
3535 fixedTabHeight = height;
3536 updateFolder(UPDATE_TAB_HEIGHT);
3537 }
3538 /**
3539 * Specify whether the tabs should appear along the top of the folder
3540 * or along the bottom of the folder.
3541 *
3542 * @param position <code>SWT.TOP</code> for tabs along the top or <code>SWT.BOTTOM</code> for tabs along the bottom
3543 *
3544 * @exception SWTException <ul>
3545 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3546 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3547 * <li>ERROR_INVALID_ARGUMENT - if the position value is not either SWT.TOP or SWT.BOTTOM</li>
3548 * </ul>
3549 *
3550 * @since 3.0
3551 */
setTabPosition(int position)3552 public void setTabPosition(int position) {
3553 checkWidget();
3554 if (position != SWT.TOP && position != SWT.BOTTOM) {
3555 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3556 }
3557 if (onBottom != (position == SWT.BOTTOM)) {
3558 onBottom = position == SWT.BOTTOM;
3559 updateFolder(REDRAW);
3560 }
3561 }
3562 /**
3563 * Set the control that appears in the top right corner of the tab folder.
3564 * Typically this is a close button or a composite with a Menu and close button.
3565 * The topRight control is optional. Setting the top right control to null will
3566 * remove it from the tab folder.
3567 *
3568 * @param control the control to be displayed in the top right corner or null
3569 *
3570 * @exception SWTException <ul>
3571 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3572 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3573 * <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3574 * </ul>
3575 *
3576 * @since 2.1
3577 */
setTopRight(Control control)3578 public void setTopRight(Control control) {
3579 setTopRight(control, SWT.RIGHT);
3580 }
3581 /**
3582 * Set the control that appears in the top right corner of the tab folder.
3583 * Typically this is a close button or a composite with a Menu and close button.
3584 * The topRight control is optional. Setting the top right control to null
3585 * will remove it from the tab folder.
3586 * <p>
3587 * The alignment parameter sets the layout of the control in the tab area.
3588 * <code>SWT.RIGHT</code> will cause the control to be positioned on the far
3589 * right of the folder and it will have its default size. <code>SWT.FILL</code>
3590 * will size the control to fill all the available space to the right of the
3591 * last tab. If there is no available space, the control will not be visible.
3592 * <code>SWT.RIGHT | SWT.WRAP</code> will allow the control to wrap below the
3593 * tabs if there is not enough available space to the right of the last tab.
3594 * </p>
3595 *
3596 * @param control the control to be displayed in the top right corner or null
3597 * @param alignment <code>SWT.RIGHT</code> or <code>SWT.FILL</code> or <code>SWT.RIGHT | SWT.WRAP</code>
3598 *
3599 * @exception SWTException <ul>
3600 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3601 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3602 * <li>ERROR_INVALID_ARGUMENT - if the control is disposed, or not a child of this CTabFolder</li>
3603 * </ul>
3604 *
3605 * @since 3.0
3606 */
setTopRight(Control control, int alignment)3607 public void setTopRight(Control control, int alignment) {
3608 checkWidget();
3609 if (alignment != SWT.RIGHT && alignment != SWT.FILL && alignment != (SWT.RIGHT | SWT.WRAP)) {
3610 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3611 }
3612 if (control != null && (control.isDisposed() || control.getParent() != this)) {
3613 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3614 }
3615 if (topRight == control && topRightAlignment == alignment) return;
3616 if (topRight != null && !topRight.isDisposed()) removeTabControl(topRight, false);
3617 topRight = control;
3618 topRightAlignment = alignment;
3619 alignment &= ~SWT.RIGHT;
3620 if (control != null) addTabControl(control, SWT.TRAIL | alignment, -1, false);
3621 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
3622 }
3623
3624
3625 /**
3626 * Specify whether the close button appears
3627 * when the user hovers over an unselected tabs.
3628 *
3629 * @param visible <code>true</code> makes the close button appear
3630 *
3631 * @exception SWTException <ul>
3632 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3633 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3634 * </ul>
3635 *
3636 * @since 3.0
3637 */
setUnselectedCloseVisible(boolean visible)3638 public void setUnselectedCloseVisible(boolean visible) {
3639 checkWidget();
3640 if (showUnselectedClose == visible) return;
3641 // display close button when mouse hovers
3642 showUnselectedClose = visible;
3643 updateFolder(REDRAW);
3644 }
3645 /**
3646 * Specify whether the image appears on unselected tabs.
3647 *
3648 * @param visible <code>true</code> makes the image appear
3649 *
3650 * @exception SWTException <ul>
3651 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3652 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3653 * </ul>
3654 *
3655 * @since 3.0
3656 */
setUnselectedImageVisible(boolean visible)3657 public void setUnselectedImageVisible(boolean visible) {
3658 checkWidget();
3659 if (showUnselectedImage == visible) return;
3660 // display image on unselected items
3661 showUnselectedImage = visible;
3662 updateFolder(REDRAW);
3663 }
3664 /**
3665 * Shows the item. If the item is already showing in the receiver,
3666 * this method simply returns. Otherwise, the items are scrolled until
3667 * the item is visible.
3668 *
3669 * @param item the item to be shown
3670 *
3671 * @exception IllegalArgumentException <ul>
3672 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
3673 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
3674 * </ul>
3675 * @exception SWTException <ul>
3676 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3677 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3678 * </ul>
3679 *
3680 * @see CTabFolder#showSelection()
3681 *
3682 * @since 2.0
3683 */
showItem(CTabItem item)3684 public void showItem (CTabItem item) {
3685 checkWidget();
3686 if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
3687 if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3688 int index = indexOf(item);
3689 if (index == -1) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3690 int idx = -1;
3691 for (int i = 0; i < priority.length; i++) {
3692 if (priority[i] == index) {
3693 idx = i;
3694 break;
3695 }
3696 }
3697 if (mru) {
3698 // move to front of mru order
3699 int[] newPriority = new int[priority.length];
3700 System.arraycopy(priority, 0, newPriority, 1, idx);
3701 System.arraycopy(priority, idx+1, newPriority, idx+1, priority.length - idx - 1);
3702 newPriority[0] = index;
3703 priority = newPriority;
3704 }
3705 if (item.showing) return;
3706 updateFolder(REDRAW_TABS);
3707 }
showList(Rectangle rect)3708 void showList (Rectangle rect) {
3709 if (items.length == 0 || !showChevron) return;
3710 if (showMenu == null || showMenu.isDisposed()) {
3711 showMenu = new Menu(getShell(), getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT));
3712 } else {
3713 for (MenuItem item : showMenu.getItems()) {
3714 item.dispose();
3715 }
3716 }
3717 final String id = "CTabFolder_showList_Index"; //$NON-NLS-1$
3718 for (CTabItem tab : items) {
3719 if (tab.showing) continue;
3720 MenuItem item = new MenuItem(showMenu, SWT.NONE);
3721 // 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.
3722 item.setText(tab.getText().replace("\n", " "));
3723 item.setImage(tab.getImage());
3724 item.setData(id, tab);
3725 item.addSelectionListener(new SelectionAdapter() {
3726 @Override
3727 public void widgetSelected(SelectionEvent e) {
3728 MenuItem menuItem = (MenuItem)e.widget;
3729 int index = indexOf((CTabItem)menuItem.getData(id));
3730 CTabFolder.this.setSelection(index, true);
3731 }
3732 });
3733 }
3734 int x = rect.x;
3735 int y = rect.y + rect.height;
3736 Point location = getDisplay().map(this, null, x, y);
3737 showMenu.setLocation(location.x, location.y);
3738 showMenu.setVisible(true);
3739 }
3740 /**
3741 * Shows the selection. If the selection is already showing in the receiver,
3742 * this method simply returns. Otherwise, the items are scrolled until
3743 * the selection is visible.
3744 *
3745 * @exception SWTException <ul>
3746 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3747 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3748 * </ul>
3749 *
3750 * @see CTabFolder#showItem(CTabItem)
3751 *
3752 * @since 2.0
3753 */
showSelection()3754 public void showSelection () {
3755 checkWidget ();
3756 if (selectedIndex != -1) {
3757 showItem(getSelection());
3758 }
3759 }
3760
_setToolTipText(int x, int y)3761 void _setToolTipText (int x, int y) {
3762 String oldTip = getToolTipText();
3763 String newTip = _getToolTip(x, y);
3764 if (newTip == null || !newTip.equals(oldTip)) {
3765 setToolTipText(newTip);
3766 }
3767 }
3768
updateItems()3769 boolean updateItems() {
3770 return updateItems(selectedIndex);
3771 }
3772
updateItems(int showIndex)3773 boolean updateItems (int showIndex) {
3774 GC gc = new GC(this);
3775 if (!single && !mru && showIndex != -1) {
3776 // make sure selected item will be showing
3777 int firstIndex = showIndex;
3778 if (priority[0] < showIndex) {
3779 int maxWidth = getRightItemEdge(gc) - getLeftItemEdge(gc, CTabFolderRenderer.PART_BORDER);
3780 int width = 0;
3781 int[] widths = new int[items.length];
3782 for (int i = priority[0]; i <= showIndex; i++) {
3783 int state = CTabFolderRenderer.MINIMUM_SIZE;
3784 if (i == selectedIndex) state |= SWT.SELECTED;
3785 widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3786 width += widths[i];
3787 if (width > maxWidth) break;
3788 }
3789 if (width > maxWidth) {
3790 width = 0;
3791 for (int i = showIndex; i >= 0; i--) {
3792 int state = CTabFolderRenderer.MINIMUM_SIZE;
3793 if (i == selectedIndex) state |= SWT.SELECTED;
3794 if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3795 width += widths[i];
3796 if (width > maxWidth) break;
3797 firstIndex = i;
3798 }
3799 } else {
3800 firstIndex = priority[0];
3801 for (int i = showIndex + 1; i < items.length; i++) {
3802 int state = CTabFolderRenderer.MINIMUM_SIZE;
3803 if (i == selectedIndex) state |= SWT.SELECTED;
3804 widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3805 width += widths[i];
3806 if (width >= maxWidth) break;
3807 }
3808 if (width < maxWidth) {
3809 for (int i = priority[0] - 1; i >= 0; i--) {
3810 int state = CTabFolderRenderer.MINIMUM_SIZE;
3811 if (i == selectedIndex) state |= SWT.SELECTED;
3812 if (widths[i] == 0) widths[i] = renderer.computeSize(i, state, gc, SWT.DEFAULT, SWT.DEFAULT).x;
3813 width += widths[i];
3814 if (width > maxWidth) break;
3815 firstIndex = i;
3816 }
3817 }
3818 }
3819
3820 }
3821 if (firstIndex != priority[0]) {
3822 int index = 0;
3823 // enumerate tabs from first visible to the last existing one (sorted ascending)
3824 for (int i = firstIndex; i < items.length; i++) {
3825 priority[index++] = i;
3826 }
3827 // enumerate hidden tabs on the left hand from first visible one
3828 // in the inverse order (sorted descending) so that the originally
3829 // first opened tab is always at the end of the list
3830 for (int i = firstIndex - 1; i >= 0; i--) {
3831 priority[index++] = i;
3832 }
3833 }
3834 }
3835
3836 boolean oldShowChevron = showChevron;
3837 boolean changed = setItemSize(gc);
3838 updateButtons();
3839 boolean chevronChanged = showChevron != oldShowChevron;
3840 if (chevronChanged) {
3841 if (updateTabHeight(false)) {
3842 // Tab height has changed. Item sizes have to be set again.
3843 changed |= setItemSize(gc);
3844 }
3845 }
3846 changed |= setItemLocation(gc);
3847 setButtonBounds();
3848 changed |= chevronChanged;
3849 if (changed && getToolTipText() != null) {
3850 Point pt = getDisplay().getCursorLocation();
3851 pt = toControl(pt);
3852 _setToolTipText(pt.x, pt.y);
3853 }
3854 gc.dispose();
3855 return changed;
3856 }
updateTabHeight(boolean force)3857 boolean updateTabHeight(boolean force){
3858 int oldHeight = tabHeight;
3859 GC gc = new GC(this);
3860 tabHeight = renderer.computeSize(CTabFolderRenderer.PART_HEADER, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).y;
3861 gc.dispose();
3862 if (fixedTabHeight == SWT.DEFAULT && controls != null && controls.length > 0) {
3863 for (int i = 0; i < controls.length; i++) {
3864 if ((controlAlignments[i] & SWT.WRAP) == 0 && !controls[i].isDisposed() && controls[i].getVisible()) {
3865 int topHeight = controls[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
3866 topHeight += renderer.computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0,0,0,0).height + 1;
3867 tabHeight = Math.max(topHeight, tabHeight);
3868 }
3869 }
3870 }
3871 if (!force && tabHeight == oldHeight) return false;
3872 oldSize = null;
3873 return true;
3874 }
3875
updateFolder(int flags)3876 void updateFolder (int flags) {
3877 updateFlags |= flags;
3878 if (updateRun != null) return;
3879 updateRun = () -> {
3880 updateRun = null;
3881 if (isDisposed()) return;
3882 runUpdate();
3883 };
3884 getDisplay().asyncExec(updateRun);
3885 }
3886
runUpdate()3887 void runUpdate() {
3888 if (updateFlags == 0) return;
3889 int flags = updateFlags;
3890 updateFlags = 0;
3891 Rectangle rectBefore = getClientArea();
3892 updateButtons();
3893 updateTabHeight(false);
3894 updateItems(selectedIndex);
3895 if ((flags & REDRAW) != 0) {
3896 redraw();
3897 } else if ((flags & REDRAW_TABS) != 0) {
3898 redrawTabs();
3899 }
3900 Rectangle rectAfter = getClientArea();
3901 if (!rectBefore.equals(rectAfter)) {
3902 notifyListeners(SWT.Resize, new Event());
3903 layout();
3904 }
3905 }
3906
updateBkImages()3907 void updateBkImages() {
3908 if (controls != null && controls.length > 0) {
3909 for (int i = 0; i < controls.length; i++) {
3910 Control control = controls[i];
3911 if (!control.isDisposed()) {
3912 if (hovering) {
3913 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
3914 control.setBackgroundImage(null);
3915 control.setBackground(getBackground());
3916 } else {
3917 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_DEFAULT);
3918 Rectangle bounds = control.getBounds();
3919 int tabHeight = getTabHeight();
3920 int height = this.getSize().y;
3921 boolean wrapped = onBottom ? bounds.y + bounds.height < height - tabHeight : bounds.y > tabHeight;
3922 if (wrapped || gradientColors == null) {
3923 control.setBackgroundImage(null);
3924 control.setBackground(getBackground());
3925 } else {
3926 bounds.width = 10;
3927 if (!onBottom) {
3928 bounds.y = -bounds.y;
3929 bounds.height -= 2*bounds.y - 1;
3930 } else {
3931 bounds.height += height - (bounds.y + bounds.height);
3932 bounds.y = -1;
3933 }
3934 bounds.x = 0;
3935 if (controlBkImages[i] != null) controlBkImages[i].dispose();
3936 controlBkImages[i] = new Image(control.getDisplay(), bounds);
3937 GC gc = new GC(controlBkImages[i]);
3938 renderer.draw(CTabFolderRenderer.PART_BACKGROUND, 0, bounds, gc);
3939 gc.dispose();
3940 control.setBackground(null);
3941 control.setBackgroundImage(controlBkImages[i]);
3942 }
3943 }
3944 }
3945 }
3946
3947 }
3948 }
_getToolTip(int x, int y)3949 String _getToolTip(int x, int y) {
3950 CTabItem item = getItem(new Point (x, y));
3951 if (item == null) return null;
3952 if (!item.showing) return null;
3953 if ((showClose || item.showClose) && item.closeRect.contains(x, y)) {
3954 return SWT.getMessage("SWT_Close"); //$NON-NLS-1$
3955 }
3956 return item.getToolTipText();
3957 }
3958 /**
3959 * Set a control that can appear to the left or to the right of the folder tabs.
3960 * This method can also be used instead of #setTopRight(Control). To remove a tab
3961 * control, see#removeTabControl(Control);
3962 * <p>
3963 * The flags parameter sets the layout of the control in the tab area.
3964 * <code>SWT.LEAD</code> will cause the control to be positioned on the left
3965 * of the tabs. <code>SWT.TRAIL</code> will cause the control to be positioned on
3966 * the far right of the folder and it will have its default size. <code>SWT.TRAIL</code>
3967 * can be combined with <code>SWT.FILL</code>to fill all the available space to the
3968 * right of the last tab. <code>SWT.WRAP</code> can also be added to <code>SWT.TRAIL</code>
3969 * only to cause a control to wrap if there is not enough space to display it in its
3970 * entirety.
3971 * </p>
3972 * @param control the control to be displayed in the top right corner or null
3973 *
3974 * @param flags valid combinations are:
3975 * <ul><li>SWT.LEAD
3976 * <li> SWT.TRAIL (| SWT.FILL | SWT.WRAP)
3977 * </ul>
3978 * @exception SWTException <ul>
3979 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
3980 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
3981 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
3982 * </ul>
3983 */
addTabControl(Control control, int flags)3984 /*public*/ void addTabControl(Control control, int flags) {
3985 checkWidget();
3986 addTabControl(control, flags, -1, true);
3987 }
3988
addTabControl(Control control, int flags, int index, boolean update)3989 void addTabControl(Control control, int flags, int index, boolean update) {
3990 switch (flags) {
3991 case SWT.TRAIL:
3992 case SWT.TRAIL | SWT.WRAP:
3993 case SWT.TRAIL | SWT.FILL:
3994 case SWT.TRAIL | SWT.FILL | SWT.WRAP:
3995 case SWT.LEAD:
3996 break;
3997 default:
3998 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
3999 break;
4000 }
4001 if (control != null && control.getParent() != this) {
4002 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4003 }
4004 //check for duplicates
4005 for (Control ctrl : controls) {
4006 if (ctrl == control) {
4007 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4008 }
4009 }
4010 int length = controls.length;
4011
4012 control.addListener(SWT.Resize, listener);
4013
4014 //Grow all 4 arrays
4015 Control[] newControls = new Control [length + 1];
4016 System.arraycopy(controls, 0, newControls, 0, length);
4017 controls = newControls;
4018 int[] newAlignment = new int [length + 1];
4019 System.arraycopy(controlAlignments, 0, newAlignment, 0, length);
4020 controlAlignments = newAlignment;
4021 Rectangle[] newRect = new Rectangle [length + 1];
4022 System.arraycopy(controlRects, 0, newRect, 0, length);
4023 controlRects = newRect;
4024 Image[] newImage = new Image [length + 1];
4025 System.arraycopy(controlBkImages, 0, newImage, 0, length);
4026 controlBkImages = newImage;
4027 if (index == -1) {
4028 index = length;
4029 if (chevronTb != null && control != chevronTb) index--;
4030 }
4031 System.arraycopy (controls, index, controls, index + 1, length - index);
4032 System.arraycopy (controlAlignments, index, controlAlignments, index + 1, length - index);
4033 System.arraycopy (controlRects, index, controlRects, index + 1, length - index);
4034 System.arraycopy (controlBkImages, index, controlBkImages, index + 1, length - index);
4035 controls[index] = control;
4036 controlAlignments[index] = flags;
4037 controlRects[index] = new Rectangle(0, 0, 0, 0);
4038 if (update) {
4039 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4040 }
4041 }
4042
4043 /**
4044 * Removes the control from the list of tab controls.
4045 *
4046 * @param control the control to be removed
4047 *
4048 * @exception SWTException <ul>
4049 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
4050 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
4051 * <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li>
4052 * </ul>
4053 */
removeTabControl(Control control)4054 /*public*/ void removeTabControl (Control control) {
4055 checkWidget();
4056 removeTabControl (control, true);
4057 }
4058
removeTabControl(Control control, boolean update)4059 void removeTabControl (Control control, boolean update) {
4060 if (control != null && control.getParent() != this) {
4061 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
4062 }
4063 int index = -1;
4064 for (int i = 0; i < controls.length; i++) {
4065 if (controls[i] == control){
4066 index = i;
4067 break;
4068 }
4069 }
4070 if (index == -1) return;
4071
4072 if (!control.isDisposed()) {
4073 control.removeListener(SWT.Resize, listener);
4074 control.setBackground (null);
4075 control.setBackgroundImage (null);
4076 if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE);
4077 }
4078
4079 if (controlBkImages[index] != null && !controlBkImages[index].isDisposed()) controlBkImages[index].dispose();
4080 if (controls.length == 1) {
4081 controls = new Control[0];
4082 controlAlignments = new int[0];
4083 controlRects = new Rectangle[0];
4084 controlBkImages = new Image[0];
4085 } else {
4086 Control[] newControls = new Control [controls.length - 1];
4087 System.arraycopy(controls, 0, newControls, 0, index);
4088 System.arraycopy(controls, index + 1, newControls, index, controls.length - index - 1);
4089 controls = newControls;
4090
4091 int[] newAlignments = new int [controls.length];
4092 System.arraycopy(controlAlignments, 0, newAlignments, 0, index);
4093 System.arraycopy(controlAlignments, index + 1, newAlignments, index, controls.length - index);
4094 controlAlignments = newAlignments;
4095
4096 Rectangle[] newRects = new Rectangle [controls.length];
4097 System.arraycopy(controlRects, 0, newRects, 0, index);
4098 System.arraycopy(controlRects, index + 1, newRects, index, controls.length - index);
4099 controlRects = newRects;
4100
4101 Image[] newBkImages = new Image [controls.length];
4102 System.arraycopy(controlBkImages, 0, newBkImages, 0, index);
4103 System.arraycopy(controlBkImages, index + 1, newBkImages, index, controls.length - index);
4104 controlBkImages = newBkImages;
4105 }
4106 if (update) {
4107 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4108 }
4109 }
4110
getWrappedHeight(Point size)4111 int getWrappedHeight (Point size) {
4112 boolean[][] positions = new boolean[1][];
4113 Rectangle[] rects = computeControlBounds(size, positions);
4114 int minY = Integer.MAX_VALUE, maxY = 0, wrapHeight = 0;
4115 for (int i = 0; i < rects.length; i++) {
4116 if (positions[0][i]) {
4117 minY = Math.min(minY, rects[i].y);
4118 maxY = Math.max(maxY, rects[i].y + rects[i].height);
4119 wrapHeight = maxY - minY;
4120 }
4121 }
4122 return wrapHeight;
4123 }
4124
4125 /**
4126 * Sets whether a chevron is shown when there are more items to be displayed.
4127 *
4128 * @exception IllegalArgumentException <ul>
4129 * <li>ERROR_INVALID_RANGE - if the index is out of range</li>
4130 * </ul>
4131 * @exception SWTException <ul>
4132 * <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
4133 * <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
4134 * </ul>
4135 *
4136 */
setChevronVisible(boolean visible)4137 /*public*/ void setChevronVisible(boolean visible) {
4138 checkWidget();
4139 if (chevronVisible == visible) return;
4140 chevronVisible = visible;
4141 updateFolder(UPDATE_TAB_HEIGHT | REDRAW);
4142 }
4143
shouldHighlight()4144 boolean shouldHighlight() {
4145 return this.highlight && highlightEnabled;
4146 }
4147
4148 /**
4149 * Sets whether the selected tab is rendered as highlighted.
4150 *
4151 * @param enabled
4152 * {@code true} if the selected tab should be highlighted,
4153 * {@code false} otherwise.
4154 * @exception SWTException
4155 * <ul>
4156 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4157 * disposed</li>
4158 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4159 * thread that created the receiver</li>
4160 * </ul>
4161 * @since 3.106
4162 */
setHighlightEnabled(boolean enabled)4163 public void setHighlightEnabled(boolean enabled) {
4164 checkWidget();
4165 if (highlightEnabled == enabled) {
4166 return;
4167 }
4168 highlightEnabled = enabled;
4169 updateFolder(REDRAW);
4170 }
4171
4172 /**
4173 * Returns <code>true</code> if the selected tab is rendered as
4174 * highlighted.
4175 *
4176 * @return <code>true</code> if the selected tab is rendered as
4177 * highlighted
4178 *
4179 * @exception SWTException
4180 * <ul>
4181 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
4182 * disposed</li>
4183 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
4184 * thread that created the receiver</li>
4185 * </ul>
4186 * @since 3.106
4187 */
getHighlightEnabled()4188 public boolean getHighlightEnabled() {
4189 checkWidget();
4190 return highlightEnabled;
4191 }
4192 }
4193