package org.eclipse.swt.custom; | |
/* | |
* Copyright (c) 2000, 2002 IBM Corp. All rights reserved. | |
* This file is made available under the terms of the Common Public License v1.0 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v10.html | |
*/ | |
import org.eclipse.swt.*; | |
import org.eclipse.swt.events.*; | |
import org.eclipse.swt.graphics.*; | |
import org.eclipse.swt.widgets.*; | |
import org.eclipse.swt.accessibility.*; | |
/** | |
* Instances of this class implement the notebook user interface | |
* metaphor. It allows the user to select a notebook page from | |
* set of pages. | |
* <p> | |
* The item children that may be added to instances of this class | |
* must be of type <code>CTabItem</code>. | |
* <code>Control</code> children are created and then set into a | |
* tab item using <code>CTabItem#setControl</code>. | |
* </p><p> | |
* Note that although this class is a subclass of <code>Composite</code>, | |
* it does not make sense to set a layout on it. | |
* </p><p> | |
* <dl> | |
* <dt><b>Styles:</b></dt> | |
* <dd>TOP, BOTTOM, FLAT</dd> | |
* <dt><b>Events:</b></dt> | |
* <dd>Selection</dd> | |
* <dd>"CTabFolder"</dd> | |
* </dl> | |
* <p> | |
* Note: Only one of the styles TOP and BOTTOM | |
* may be specified. | |
* </p><p> | |
* IMPORTANT: This class is <em>not</em> intended to be subclassed. | |
* </p> | |
*/ | |
public class CTabFolder extends Composite { | |
/** | |
* marginWidth specifies the number of pixels of horizontal margin | |
* that will be placed along the left and right edges of the form. | |
* | |
* The default value is 0. | |
*/ | |
public int marginWidth = 0; | |
/** | |
* marginHeight specifies the number of pixels of vertical margin | |
* that will be placed along the top and bottom edges of the form. | |
* | |
* The default value is 0. | |
*/ | |
public int marginHeight = 0; | |
/** | |
* Color of innermost line of drop shadow border. | |
*/ | |
public static RGB borderInsideRGB = new RGB (132, 130, 132); | |
/** | |
* Color of middle line of drop shadow border. | |
*/ | |
public static RGB borderMiddleRGB = new RGB (143, 141, 138); | |
/** | |
* Color of outermost line of drop shadow border. | |
*/ | |
public static RGB borderOutsideRGB = new RGB (171, 168, 165); | |
/* | |
* A multiple of the tab height that specifies the minimum width to which a tab | |
* will be compressed before scrolling arrows are used to navigate the tabs. | |
*/ | |
public int MIN_TAB_WIDTH = 3; | |
/* sizing, positioning */ | |
int xClient, yClient; | |
boolean onBottom = false; | |
boolean fixedTabHeight; | |
int tabHeight; | |
/* item management */ | |
private CTabItem items[] = new CTabItem[0]; | |
private int selectedIndex = -1; | |
int topTabIndex = -1; // index of the left most visible tab. | |
/* External Listener management */ | |
private CTabFolderListener[] tabListeners = new CTabFolderListener[0]; | |
/* Color appearance */ | |
Image backgroundImage; | |
Color[] gradientColors; | |
int[] gradientPercents; | |
Color selectionForeground; | |
Color background; | |
// internal constants | |
private static final int DEFAULT_WIDTH = 64; | |
private static final int DEFAULT_HEIGHT = 64; | |
// scrolling arrows | |
private ToolBar arrowBar; | |
private Image arrowLeftImage; | |
private Image arrowRightImage; | |
private Control topRight; | |
// close button | |
boolean showClose = false; | |
private Image closeImage; | |
ToolBar closeBar; | |
private ToolBar inactiveCloseBar; | |
private CTabItem inactiveItem; | |
// borders | |
boolean showBorders = false; | |
private int borderBottom = 0; | |
private int borderLeft = 0; | |
private int borderRight = 0; | |
private int borderTop = 0; | |
private Color borderColor1; | |
private Color borderColor2; | |
private Color borderColor3; | |
// when disposing CTabFolder, don't try to layout the items or | |
// change the selection as each child is destroyed. | |
private boolean inDispose = false; | |
// keep track of size changes in order to redraw only affected area | |
// on Resize | |
private Point oldSize; | |
private Font oldFont; | |
// insertion marker | |
int insertionIndex = -2; // Index of insert marker. Marker always shown after index. | |
// -2 means no insert marker | |
// tool tip | |
private Shell tip; | |
private Label label; | |
private boolean showToolTip = false; | |
private CTabItem toolTipItem; | |
/** | |
* Constructs a new instance of this class given its parent | |
* and a style value describing its behavior and appearance. | |
* <p> | |
* The style value is either one of the style constants defined in | |
* class <code>SWT</code> which is applicable to instances of this | |
* class, or must be built by <em>bitwise OR</em>'ing together | |
* (that is, using the <code>int</code> "|" operator) two or more | |
* of those <code>SWT</code> style constants. The class description | |
* lists the style constants that are applicable to the class. | |
* Style bits are also inherited from superclasses. | |
* </p> | |
* | |
* @param parent a widget which will be the parent of the new instance (cannot be null) | |
* @param style the style of widget to construct | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> | |
* </ul> | |
* | |
* @see SWT#TOP | |
* @see SWT#BOTTOM | |
* @see SWT#FLAT | |
* @see #getStyle | |
*/ | |
public CTabFolder(Composite parent, int style) { | |
super(parent, checkStyle (style)); | |
onBottom = (getStyle() & SWT.BOTTOM) != 0; | |
borderColor1 = new Color(getDisplay(), borderInsideRGB); | |
borderColor2 = new Color(getDisplay(), borderMiddleRGB); | |
borderColor3 = new Color(getDisplay(), borderOutsideRGB); | |
// tool tip support | |
tip = new Shell (getShell(), SWT.ON_TOP); | |
label = new Label (tip, SWT.CENTER); | |
// Add all listeners | |
Listener listener = new Listener() { | |
public void handleEvent(Event event) { | |
switch (event.type) { | |
case SWT.Dispose: onDispose(); break; | |
case SWT.Paint: onPaint(event); break; | |
case SWT.Resize: onResize(); break; | |
case SWT.MouseDoubleClick: onMouseDoubleClick(event); break; | |
case SWT.MouseDown: onMouseDown(event); break; | |
case SWT.MouseExit: onMouseExit(event); break; | |
case SWT.MouseHover: onMouseHover(event); break; | |
case SWT.MouseMove: onMouseMove(event); break; | |
case SWT.FocusIn: onFocus(event); break; | |
case SWT.FocusOut: onFocus(event); break; | |
case SWT.KeyDown: onKeyDown(event); break; | |
case SWT.Traverse: onTraverse(event); break; | |
} | |
} | |
}; | |
int[] folderEvents = new int[]{ | |
SWT.Dispose, | |
SWT.Paint, | |
SWT.Resize, | |
SWT.MouseDoubleClick, | |
SWT.MouseDown, | |
SWT.MouseExit, | |
SWT.MouseHover, | |
SWT.MouseMove, | |
SWT.FocusIn, | |
SWT.FocusOut, | |
SWT.KeyDown, | |
SWT.Traverse, | |
}; | |
for (int i = 0; i < folderEvents.length; i++) { | |
addListener(folderEvents[i], listener); | |
} | |
createArrowBar(); | |
createCloseBar(); | |
setBorderVisible((style & SWT.BORDER) != 0); | |
initAccessible(); | |
} | |
private static int checkStyle (int style) { | |
int mask = SWT.TOP | SWT.BOTTOM | SWT.FLAT; | |
style = style & mask; | |
// TOP and BOTTOM are mutually exlusive. | |
// TOP is the default | |
if ((style & SWT.TOP) != 0) | |
style = style & ~(SWT.TOP | SWT.BOTTOM) | SWT.TOP; | |
// reduce the flash by not redrawing the entire area on a Resize event | |
style |= SWT.NO_REDRAW_RESIZE; | |
return style; | |
} | |
/** | |
* Adds the listener to receive events. | |
* <p> | |
* | |
* @param listener the listener | |
* | |
* @exception SWTError <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* <li>ERROR_NULL_ARGUMENT when listener is null</li> | |
* </ul> | |
*/ | |
public void addSelectionListener(SelectionListener listener) { | |
checkWidget(); | |
if (listener == null) { | |
SWT.error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
TypedListener typedListener = new TypedListener(listener); | |
addListener(SWT.Selection, typedListener); | |
addListener(SWT.DefaultSelection, typedListener); | |
} | |
/** | |
* Adds the listener to the collection of listeners who will | |
* be notified when a tab item is closed. | |
* | |
* @param listener the listener which should be notified | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
* </ul> | |
* | |
* @see CTabFolderListener | |
* @see #removeCTabFolderListener | |
*/ | |
public void addCTabFolderListener(CTabFolderListener listener) { | |
checkWidget(); | |
if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); | |
// add to array | |
CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length + 1]; | |
System.arraycopy(tabListeners, 0, newTabListeners, 0, tabListeners.length); | |
tabListeners = newTabListeners; | |
tabListeners[tabListeners.length - 1] = listener; | |
showClose = true; | |
setButtonBounds(); | |
} | |
private void closeNotify(CTabItem item, int time) { | |
if (item == null) return; | |
CTabFolderEvent event = new CTabFolderEvent(this); | |
event.widget = this; | |
event.time = time; | |
event.item = item; | |
event.doit = true; | |
if (tabListeners != null) { | |
for (int i = 0; i < tabListeners.length; i++) { | |
tabListeners[i].itemClosed(event); | |
} | |
} | |
if (event.doit) { | |
item.dispose(); | |
} | |
} | |
public Point computeSize (int wHint, int hHint, boolean changed) { | |
checkWidget(); | |
int minWidth = 0; | |
int minHeight = 0; | |
// preferred width of tab area to show all tabs | |
GC gc = new GC(this); | |
for (int i = 0; i < items.length; i++) { | |
minWidth += items[i].preferredWidth(gc); | |
} | |
gc.dispose(); | |
// preferred size of controls in tab items | |
for (int i = 0; i < items.length; i++) { | |
Control control = items[i].getControl(); | |
if (control != null && !control.isDisposed()){ | |
Point size = control.computeSize (wHint, hHint); | |
minWidth = Math.max (minWidth, size.x); | |
minHeight = Math.max (minHeight, size.y); | |
} | |
} | |
if (minWidth == 0) minWidth = DEFAULT_WIDTH; | |
if (minHeight == 0) minHeight = DEFAULT_HEIGHT; | |
if (wHint != SWT.DEFAULT) minWidth = wHint; | |
if (hHint != SWT.DEFAULT) minHeight = hHint; | |
Rectangle trim = computeTrim(0, 0, minWidth, minHeight); | |
return new Point (trim.width, trim.height); | |
} | |
public Rectangle computeTrim (int x, int y, int width, int height) { | |
checkWidget(); | |
if (items.length == 0) { | |
if (!showBorders) return new Rectangle(x, y, width, height); | |
int trimX = x - borderRight - 1; | |
int trimY = y - borderBottom - 1; | |
int trimWidth = width + borderRight + 2; | |
int trimHeight = height + borderBottom + 2; | |
return new Rectangle (trimX, trimY, trimWidth, trimHeight); | |
} else { | |
int trimX = x - marginWidth - borderLeft; | |
int trimY = y - marginHeight - tabHeight - borderTop - 1; | |
// -1 is for the line at the bottom of the tabs | |
if (onBottom) { | |
trimY = y - marginHeight - borderTop; | |
} | |
int trimWidth = width + borderLeft + borderRight + 2*marginWidth; | |
int trimHeight = height + borderTop + borderBottom + 2*marginHeight + tabHeight + 1; | |
return new Rectangle (trimX, trimY, trimWidth, trimHeight); | |
} | |
} | |
/** | |
* Create the specified item at 'index'. | |
*/ | |
void createItem (CTabItem item, int index) { | |
if (0 > index || index > getItemCount ()){ | |
SWT.error (SWT.ERROR_INVALID_RANGE); | |
} | |
// grow by one and rearrange the array. | |
CTabItem[] newItems = new CTabItem [items.length + 1]; | |
System.arraycopy(items, 0, newItems, 0, index); | |
newItems[index] = item; | |
System.arraycopy(items, index, newItems, index + 1, items.length - index); | |
items = newItems; | |
item.parent = this; | |
if (selectedIndex >= index) { | |
selectedIndex ++; | |
} | |
if (items.length == 1) { | |
topTabIndex = 0; | |
resetTabSize(true); | |
} else { | |
setItemBounds(); | |
showItem(item); | |
} | |
if (items.length == 1) { | |
redraw(); | |
} else { | |
redrawTabArea(-1); | |
} | |
} | |
private void createArrowBar() { | |
// create arrow buttons for scrolling | |
arrowBar = new ToolBar(this, SWT.FLAT); | |
arrowBar.setVisible(false); | |
arrowBar.setBackground(background); | |
ToolItem scrollLeft = new ToolItem(arrowBar, SWT.PUSH); | |
scrollLeft.setEnabled(false); | |
ToolItem scrollRight = new ToolItem(arrowBar, SWT.PUSH); | |
scrollRight.setEnabled(false); | |
scrollLeft.addListener(SWT.Selection, new Listener() { | |
public void handleEvent(Event event) { | |
scroll_scrollLeft(); | |
} | |
}); | |
scrollRight.addListener(SWT.Selection, new Listener() { | |
public void handleEvent(Event event) { | |
scroll_scrollRight(); | |
} | |
}); | |
} | |
private void createCloseBar() { | |
closeBar = new ToolBar(this, SWT.FLAT); | |
closeBar.setVisible(false); | |
if (gradientColors != null && gradientColors.length > 0) { | |
closeBar.setBackground(gradientColors[gradientColors.length - 1]); | |
} else { | |
closeBar.setBackground(background); | |
} | |
ToolItem closeItem = new ToolItem(closeBar, SWT.PUSH); | |
inactiveCloseBar = new ToolBar(this, SWT.FLAT); | |
inactiveCloseBar.setVisible(false); | |
inactiveCloseBar.setBackground(background); | |
ToolItem inactiveCloseItem = new ToolItem(inactiveCloseBar, SWT.PUSH); | |
closeItem.addListener(SWT.Selection, new Listener() { | |
public void handleEvent(Event event) { | |
closeNotify(getSelection(), event.time); | |
} | |
}); | |
inactiveCloseItem.addListener(SWT.Selection, new Listener() { | |
public void handleEvent(Event event) { | |
closeNotify(inactiveItem, event.time); | |
inactiveCloseBar.setVisible(false); | |
inactiveItem = null; | |
} | |
}); | |
inactiveCloseBar.addListener (SWT.MouseExit, new Listener() { | |
public void handleEvent(Event event) { | |
if (inactiveItem != null) { | |
Rectangle itemBounds = inactiveItem.getBounds(); | |
if (itemBounds.contains(event.x, event.y)) return; | |
} | |
inactiveCloseBar.setVisible(false); | |
inactiveItem = null; | |
} | |
}); | |
} | |
/** | |
* Destroy the specified item. | |
*/ | |
void destroyItem (CTabItem item) { | |
if (inDispose) return; | |
int index = indexOf(item); | |
if (index == -1) return; // should this trigger an error? | |
insertionIndex = -2; | |
if (items.length == 1) { | |
items = new CTabItem[0]; | |
selectedIndex = -1; | |
topTabIndex = 0; | |
Control control = item.getControl(); | |
if (control != null && !control.isDisposed()) { | |
control.setVisible(false); | |
} | |
closeBar.setVisible(false); | |
if (!fixedTabHeight) tabHeight = 0; | |
redraw(); | |
return; | |
} | |
// shrink by one and rearrange the array. | |
CTabItem[] newItems = new CTabItem [items.length - 1]; | |
System.arraycopy(items, 0, newItems, 0, index); | |
System.arraycopy(items, index + 1, newItems, index, items.length - index - 1); | |
items = newItems; | |
if (topTabIndex == items.length) { | |
--topTabIndex; | |
} | |
// move the selection if this item is selected | |
if (selectedIndex == index) { | |
Control control = item.getControl(); | |
if (control != null && !control.isDisposed()) { | |
control.setVisible(false); | |
} | |
selectedIndex = -1; | |
setSelection(Math.max(0, index - 1), true); | |
} else if (selectedIndex > index) { | |
selectedIndex --; | |
} | |
setItemBounds(); | |
redrawTabArea(-1); | |
} | |
private void onKeyDown(Event e) { | |
if (e.keyCode == SWT.ARROW_LEFT) { | |
if (selectedIndex > 0) { | |
setSelection(selectedIndex - 1, true); | |
} | |
} | |
if (e.keyCode == SWT.ARROW_RIGHT) { | |
if (selectedIndex < items.length - 1) { | |
setSelection(selectedIndex + 1, true); | |
} | |
} | |
} | |
/** | |
* Dispose the items of the receiver | |
*/ | |
private void onDispose() { | |
/* | |
* Usually when an item is disposed, destroyItem will change the size of the items array, | |
* reset the bounds of all the tabs and manage the widget associated with the tab. | |
* Since the whole folder is being disposed, this is not necessary. For speed | |
* the inDispose flag is used to skip over this part of the item dispose. | |
*/ | |
inDispose = true; | |
int length = items.length; | |
for (int i = 0; i < length; i++) { | |
if (items[i] != null) { | |
items[i].dispose(); | |
} | |
} | |
// clean up resources | |
if (tip != null && !tip.isDisposed()) { | |
tip.dispose(); | |
tip = null; | |
label = null; | |
} | |
if (arrowLeftImage != null) arrowLeftImage.dispose(); | |
arrowLeftImage = null; | |
if (arrowRightImage != null) arrowRightImage.dispose(); | |
arrowRightImage = null; | |
if (closeImage != null) closeImage.dispose(); | |
closeImage = null; | |
gradientColors = null; | |
gradientPercents = null; | |
backgroundImage = null; | |
if (borderColor1 != null) borderColor1.dispose(); | |
borderColor1 = null; | |
if (borderColor2 != null) borderColor2.dispose(); | |
borderColor2 = null; | |
if (borderColor3 != null) borderColor3.dispose(); | |
borderColor3 = null; | |
} | |
private void onFocus(Event e) { | |
checkWidget(); | |
if (selectedIndex >= 0) { | |
redrawTabArea(selectedIndex); | |
} else { | |
setSelection(0, true); | |
} | |
} | |
/** | |
* Draw a border around the receiver. | |
*/ | |
private void drawBorder(GC gc) { | |
Rectangle d = super.getClientArea(); | |
if (showBorders) { | |
if ((getStyle() & SWT.FLAT) != 0) { | |
gc.setForeground(borderColor1); | |
gc.drawRectangle(d.x, d.y, d.x + d.width - 1, d.y + d.height - 1); | |
} else { | |
gc.setForeground(borderColor1); | |
gc.drawRectangle(d.x, d.y, d.x + d.width - 3, d.y + d.height - 3); | |
gc.setForeground(borderColor2); | |
gc.drawLine(d.x + 1, d.y + d.height - 2, d.x + d.width - 1, d.y + d.height - 2); | |
gc.drawLine(d.x + d.width - 2, d.y + 1, d.x + d.width - 2, d.y + d.height - 1); | |
gc.setForeground(borderColor3); | |
gc.drawLine(d.x + 2, d.y + d.height - 1, d.x + d.width - 2, d.y + d.height - 1); | |
gc.drawLine(d.x + d.width - 1, d.y + 2, d.x + d.width - 1, d.y + d.height - 2); | |
// fill in corners with parent's background | |
gc.setForeground(getParent().getBackground()); | |
gc.drawLine(d.x + d.width - 2, d.y, d.x + d.width - 1, d.y); | |
gc.drawLine(d.x + d.width - 1, d.y + 1, d.x + d.width - 1, d.y + 1); | |
gc.drawLine(d.x, d.y + d.height - 2, d.x, d.y + d.height - 2); | |
gc.drawLine(d.x, d.y + d.height - 1, d.x + 1, d.y + d.height - 1); | |
gc.drawLine(d.x + d.width - 1, d.y + d.height - 1, d.x + d.width - 1, d.y + d.height - 1); | |
} | |
} | |
// draw a separator line | |
if (items.length > 0) { | |
int lineY = d.y + borderTop + tabHeight; | |
if (onBottom) { | |
lineY = d.y + d.height - borderBottom - tabHeight - 1; | |
} | |
gc.setForeground(borderColor1); | |
gc.drawLine(d.x + borderLeft, lineY, d.x + d.width - borderRight, lineY); | |
} | |
gc.setForeground(getForeground()); | |
} | |
public Rectangle getClientArea() { | |
checkWidget(); | |
Point size = getSize(); | |
if (items.length == 0) { | |
if (!showBorders) return super.getClientArea(); | |
int width = size.x - borderRight - 2; | |
int height = size.y - borderBottom - 2; | |
return new Rectangle(borderRight + 1, borderBottom + 1, width, height); | |
} else { | |
int width = size.x - 2*marginWidth - borderLeft - borderRight; | |
int height = size.y - 2*marginHeight - borderTop - borderBottom - tabHeight - 1; | |
return new Rectangle(xClient, yClient, width, height); | |
} | |
} | |
/** | |
* Returns the height of the tab | |
* | |
* @return the height of the tab | |
* | |
* @exception SWTError <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* </ul> | |
*/ | |
public int getTabHeight(){ | |
checkWidget(); | |
return tabHeight; | |
} | |
/** | |
* Return the tab that is located at the specified index. | |
* | |
* @return the item at the specified index | |
*/ | |
public CTabItem getItem (int index) { | |
//checkWidget(); | |
if (index < 0 || index >= items.length) | |
SWT.error(SWT.ERROR_INVALID_RANGE); | |
return items [index]; | |
} | |
/** | |
* Gets the item at a point in the widget. | |
* <p> | |
* | |
* @return the item at a point | |
*/ | |
public CTabItem getItem (Point pt) { | |
//checkWidget(); | |
if (items.length == 0) return null; | |
int lastItem = getLastItem(); | |
lastItem = Math.min(items.length - 1, lastItem + 1); | |
for (int i = topTabIndex; i <= lastItem; i++) { | |
Rectangle bounds = items[i].getBounds(); | |
if (bounds.contains(pt)) return items[i]; | |
} | |
return null; | |
} | |
/** | |
* Return the number of tabs in the folder. | |
* | |
* @return the number of tabs in the folder | |
*/ | |
public int getItemCount(){ | |
//checkWidget(); | |
return items.length; | |
} | |
/** | |
* Return the tab items. | |
* | |
* @return the tab items | |
*/ | |
public CTabItem [] getItems() { | |
//checkWidget(); | |
CTabItem[] tabItems = new CTabItem [items.length]; | |
System.arraycopy(items, 0, tabItems, 0, items.length); | |
return tabItems; | |
} | |
private int getLastItem(){ | |
if (items.length == 0) return -1; | |
Rectangle area = getClientArea(); | |
if (area.width <= 0) return 0; | |
Rectangle toolspace = getToolSpace(); | |
if (toolspace.width == 0) return items.length -1; | |
int width = area.width - toolspace.width; | |
int index = topTabIndex; | |
int tabWidth = items[index].width; | |
while (index < items.length - 1) { | |
tabWidth += items[index + 1].width; | |
if (tabWidth > width) break; | |
index++; | |
} | |
return index; | |
} | |
/** | |
* Return the selected tab item, or an empty array if there | |
* is no selection. | |
* | |
* @return the selected tab item | |
*/ | |
public CTabItem getSelection() { | |
//checkWidget(); | |
if (selectedIndex == -1) return null; | |
return items[selectedIndex]; | |
} | |
/** | |
* Return the index of the selected tab item, or -1 if there | |
* is no selection. | |
* | |
* @return the index of the selected tab item or -1 | |
*/ | |
public int getSelectionIndex() { | |
//checkWidget(); | |
return selectedIndex; | |
} | |
private Rectangle getToolSpace() { | |
boolean showArrows = scroll_leftVisible() || scroll_rightVisible(); | |
if (!showArrows && topRight == null) return new Rectangle(0, 0, 0, 0); | |
Rectangle toolspace; | |
if (showArrows) { | |
toolspace = arrowBar.getBounds(); | |
toolspace.width += borderRight; | |
if (topRight != null) toolspace.width += topRight.getSize().x; | |
} else { | |
toolspace = topRight.getBounds(); | |
toolspace.width += borderRight; | |
} | |
return toolspace; | |
} | |
/** | |
* Returns the control in the top right corner of the tab folder. | |
* Typically this is a close button or a composite with a menu and close button. | |
* | |
* @since 2.1 | |
* | |
* @return the control in the top right corner of the tab folder or null | |
* | |
* @exception SWTError <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* </ul> | |
*/ | |
public Control getTopRight() { | |
checkWidget(); | |
return topRight; | |
} | |
/** | |
* Return the index of the specified tab or -1 if the tab is not | |
* in the receiver. | |
* | |
* @return the index of the specified tab item or -1 | |
* | |
* @exception SWTError <ul> | |
* <li>ERROR_NULL_ARGUMENT when the item is null</li> | |
* </ul> | |
*/ | |
public int indexOf(CTabItem item) { | |
//checkWidget(); | |
if (item == null) { | |
SWT.error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
for (int i = 0; i < items.length; i++) { | |
if (items[i] == item) return i; | |
} | |
return -1; | |
} | |
private void initAccessible() { | |
final Accessible accessible = getAccessible(); | |
accessible.addAccessibleListener(new AccessibleAdapter() { | |
public void getName(AccessibleEvent e) { | |
String name = null; | |
int childID = e.childID; | |
if (childID >= 0 && childID < items.length) { | |
name = items[childID].getText(); | |
int index = name.indexOf('&'); | |
if (index > 0) { | |
name = name.substring(0, index) + name.substring(index + 1); | |
} | |
} | |
e.result = name; | |
} | |
public void getHelp(AccessibleEvent e) { | |
String help = null; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) { | |
help = getToolTipText(); | |
} else if (childID >= 0 && childID < items.length) { | |
help = items[childID].getToolTipText(); | |
} | |
e.result = help; | |
} | |
public void getKeyboardShortcut(AccessibleEvent e) { | |
String shortcut = null; | |
int childID = e.childID; | |
if (childID >= 0 && childID < items.length) { | |
String text = items[childID].getText(); | |
if (text != null) { | |
char mnemonic = getMnemonic(text); | |
if (mnemonic != '\0') { | |
shortcut = "Alt+"+mnemonic; | |
} | |
} | |
} | |
e.result = shortcut; | |
} | |
}); | |
accessible.addAccessibleControlListener(new AccessibleControlAdapter() { | |
public void getChildAtPoint(AccessibleControlEvent e) { | |
Point testPoint = toControl(new Point(e.x, e.y)); | |
int childID = ACC.CHILDID_NONE; | |
for (int i = 0; i < items.length; i++) { | |
if (items[i].getBounds().contains(testPoint)) { | |
childID = i; | |
break; | |
} | |
} | |
if (childID == ACC.CHILDID_NONE) { | |
Rectangle location = getBounds(); | |
location.height = location.height - getClientArea().height; | |
if (location.contains(testPoint)) { | |
childID = ACC.CHILDID_SELF; | |
} | |
} | |
e.childID = childID; | |
} | |
public void getLocation(AccessibleControlEvent e) { | |
Rectangle location = null; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) { | |
location = getBounds(); | |
} | |
if (childID >= 0 && childID < items.length) { | |
location = items[childID].getBounds(); | |
} | |
if (location != null) { | |
Point pt = toDisplay(new Point(location.x, location.y)); | |
e.x = pt.x; | |
e.y = pt.y; | |
e.width = location.width; | |
e.height = location.height; | |
} | |
} | |
public void getChildCount(AccessibleControlEvent e) { | |
e.detail = items.length; | |
} | |
public void getDefaultAction(AccessibleControlEvent e) { | |
String action = null; | |
int childID = e.childID; | |
if (childID >= 0 && childID < items.length) { | |
action = "Switch"; | |
} | |
e.result = action; | |
} | |
public void getFocus(AccessibleControlEvent e) { | |
int childID = ACC.CHILDID_NONE; | |
if (isFocusControl()) { | |
if (selectedIndex == -1) { | |
childID = ACC.CHILDID_SELF; | |
} else { | |
childID = selectedIndex; | |
} | |
} | |
e.childID = childID; | |
} | |
public void getRole(AccessibleControlEvent e) { | |
int role = 0; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) { | |
role = ACC.ROLE_TABFOLDER; | |
} else if (childID >= 0 && childID < items.length) { | |
role = ACC.ROLE_TABITEM; | |
} | |
e.detail = role; | |
} | |
public void getSelection(AccessibleControlEvent e) { | |
e.childID = (selectedIndex == -1) ? ACC.CHILDID_NONE : selectedIndex; | |
} | |
public void getState(AccessibleControlEvent e) { | |
int state = 0; | |
int childID = e.childID; | |
if (childID == ACC.CHILDID_SELF) { | |
state = ACC.STATE_NORMAL; | |
} else if (childID >= 0 && childID < items.length) { | |
state = ACC.STATE_SELECTABLE; | |
if (isFocusControl()) { | |
state |= ACC.STATE_FOCUSABLE; | |
} | |
if (selectedIndex == childID) { | |
state |= ACC.STATE_SELECTED; | |
if (isFocusControl()) { | |
state |= ACC.STATE_FOCUSED; | |
} | |
} | |
} | |
e.detail = state; | |
} | |
public void getChildren(AccessibleControlEvent e) { | |
Object[] children = new Object[items.length]; | |
for (int i = 0; i < items.length; i++) { | |
children[i] = new Integer(i); | |
} | |
e.children = children; | |
} | |
}); | |
addListener(SWT.Selection, new Listener() { | |
public void handleEvent(Event event) { | |
if (isFocusControl()) { | |
if (selectedIndex == -1) { | |
accessible.setFocus(ACC.CHILDID_SELF); | |
} else { | |
accessible.setFocus(selectedIndex); | |
} | |
} | |
} | |
}); | |
addListener(SWT.FocusIn, new Listener() { | |
public void handleEvent(Event event) { | |
if (selectedIndex == -1) { | |
accessible.setFocus(ACC.CHILDID_SELF); | |
} else { | |
accessible.setFocus(selectedIndex); | |
} | |
} | |
}); | |
} | |
private void setButtonBounds() { | |
updateArrowBar(); | |
updateCloseBar(); | |
Rectangle area = super.getClientArea(); | |
int offset = 0; | |
if (topRight != null) { | |
Point size = topRight.computeSize(SWT.DEFAULT, tabHeight); | |
int x = area.x + area.width - borderRight - size.x; | |
int y = onBottom ? area.y + area.height - borderBottom - size.y : area.y + borderTop; | |
topRight.setBounds(x, y, size.x, size.y); | |
offset = size.x; | |
} | |
boolean leftVisible = scroll_leftVisible(); | |
boolean rightVisible = scroll_rightVisible(); | |
if (leftVisible || rightVisible) { | |
Point size = arrowBar.computeSize(SWT.DEFAULT, tabHeight); | |
int x = area.x + area.width - borderRight - size.x - offset; | |
int y = (onBottom) ? area.y + area.height - borderBottom - size.y : area.y + borderTop; | |
arrowBar.setBounds(x, y, size.x, size.y); | |
ToolItem[] items = arrowBar.getItems(); | |
items[0].setEnabled(leftVisible); | |
items[1].setEnabled(rightVisible); | |
arrowBar.setVisible(true); | |
} else { | |
arrowBar.setVisible(false); | |
} | |
// When the close button is right at the edge of the Tab folder, hide it because | |
// otherwise it may block off a part of the border on the right | |
if (showClose) { | |
inactiveCloseBar.setVisible(false); | |
CTabItem item = getSelection(); | |
if (item == null) { | |
closeBar.setVisible(false); | |
} else { | |
int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle | |
Point size = closeBar.computeSize(SWT.DEFAULT, toolbarHeight); | |
int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim | |
int y = item.y + Math.max(0, (item.height - toolbarHeight)/2); | |
closeBar.setBounds(x, y, size.x, toolbarHeight); | |
Rectangle toolspace = getToolSpace(); | |
Point folderSize = getSize(); | |
boolean visible = (toolspace.width == 0 || x < toolspace.x) && x + size.x < folderSize.x - borderRight; | |
closeBar.setVisible(visible); | |
} | |
} | |
} | |
private boolean setItemLocation() { | |
if (items.length == 0) return false; | |
Rectangle area = super.getClientArea(); | |
int x = area.x; | |
int y = area.y + borderTop; | |
if (onBottom) y = Math.max(0, area.y + area.height - borderBottom - tabHeight); | |
boolean changed = false; | |
for (int i = topTabIndex - 1; i>=0; i--) { | |
// if the first visible tab is not the first tab | |
CTabItem tab = items[i]; | |
x -= tab.width; | |
if (!changed && (tab.x != x || tab.y != y) ) changed = true; | |
// layout tab items from right to left thus making them invisible | |
tab.x = x; | |
tab.y = y; | |
} | |
x = area.x + borderLeft; | |
for (int i = topTabIndex; i < items.length; i++) { | |
// continue laying out remaining, visible items left to right | |
CTabItem tab = items[i]; | |
tab.x = x; | |
tab.y = y; | |
x = x + tab.width; | |
} | |
setButtonBounds(); | |
return changed; | |
} | |
private void setLastItem(int index) { | |
if (index < 0 || index > items.length - 1) return; | |
Rectangle area = getClientArea(); | |
if (area.width <= 0) return; | |
int maxWidth = area.width; | |
Rectangle toolspace = getToolSpace(); | |
if (toolspace.width > 0){ | |
maxWidth -= toolspace.width; | |
} | |
int tabWidth = items[index].width; | |
while (index > 0) { | |
tabWidth += items[index - 1].width; | |
if (tabWidth > maxWidth) break; | |
index--; | |
} | |
topTabIndex = index; | |
setItemLocation(); | |
redrawTabArea(-1); | |
} | |
/** | |
* Layout the items and store the client area size. | |
*/ | |
boolean setItemBounds() { | |
boolean changed = false; | |
if (isDisposed()) return changed; | |
Rectangle area = super.getClientArea(); | |
xClient = area.x + borderLeft + marginWidth; | |
if (onBottom) { | |
yClient = area.y + borderTop + marginHeight; | |
} else { | |
yClient = area.y + borderTop + tabHeight + 1 + marginHeight; | |
// +1 is for the line at the bottom of the tabs | |
} | |
if (area.width <= 0 || area.height <= 0 || items.length == 0) return changed; | |
int[] widths = new int[items.length]; | |
GC gc = new GC(this); | |
for (int i = 0; i < items.length; i++) { | |
widths[i] = items[i].preferredWidth(gc); | |
} | |
gc.dispose(); | |
int oldAverageWidth = 0; | |
int averageWidth = (area.width - borderLeft - borderRight) / items.length; | |
while (averageWidth > oldAverageWidth) { | |
int width = area.width - borderLeft - borderRight; | |
int count = items.length; | |
for (int i = 0; i < items.length; i++) { | |
if (widths[i] < averageWidth) { | |
width -= widths[i]; | |
count--; | |
} | |
} | |
oldAverageWidth = averageWidth; | |
if (count > 0) { | |
averageWidth = width / count; | |
} | |
} | |
averageWidth = Math.max(averageWidth, MIN_TAB_WIDTH * tabHeight); | |
for (int i = 0; i < items.length; i++) { | |
if (widths[i] > averageWidth) { | |
widths[i] = averageWidth; | |
} | |
} | |
int totalWidth = 0; | |
for (int i = 0; i < items.length; i++) { | |
CTabItem tab = items[i]; | |
if (tab.height != tabHeight || tab.width != widths[i]) changed = true; | |
tab.height = tabHeight; | |
tab.width = widths[i]; | |
totalWidth += widths[i]; | |
} | |
int areaWidth = area.x + area.width - borderRight; | |
if (totalWidth <= areaWidth) { | |
topTabIndex = 0; | |
} | |
if (setItemLocation()) changed = true; | |
// Is there a gap after last item showing | |
if (correctLastItem()) changed = true; | |
return changed; | |
} | |
private boolean onMnemonic (Event event) { | |
char key = event.character; | |
for (int i = 0; i < items.length; i++) { | |
if (items[i] != null) { | |
char mnemonic = getMnemonic (items[i].getText ()); | |
if (mnemonic != '\0') { | |
if (Character.toUpperCase (key) == Character.toUpperCase (mnemonic)) { | |
setSelection(i, true); | |
return true; | |
} | |
} | |
} | |
} | |
return false; | |
} | |
/** | |
* Paint the receiver. | |
*/ | |
private void onPaint(Event event) { | |
Font font = getFont(); | |
if (oldFont == null || !oldFont.equals(font)) { | |
oldFont = font; | |
resetTabSize(true); | |
} | |
GC gc = event.gc; | |
Rectangle rect = super.getClientArea(); | |
if (items.length == 0) { | |
if (showBorders) { | |
if ((getStyle() & SWT.FLAT) != 0) { | |
gc.setForeground(borderColor1); | |
gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 1, rect.y + rect.height - 1); | |
} else { | |
gc.setForeground(borderColor1); | |
gc.drawRectangle(rect.x, rect.y, rect.x + rect.width - 3, rect.y + rect.height - 3); | |
// fill in right and bottom edges with parent's background | |
gc.setBackground(getParent().getBackground()); | |
gc.fillRectangle(rect.x + rect.width - 2, rect.y, 2, rect.height); | |
gc.fillRectangle(rect.x, rect.y + rect.height - 2, rect.width, 2); | |
} | |
gc.setForeground(getForeground()); | |
} | |
return; | |
} | |
// redraw the Border | |
drawBorder(gc); | |
rect.x += borderLeft; | |
rect.y += borderTop; | |
rect.width -= borderLeft + borderRight; | |
rect.height -= borderTop + borderBottom; | |
Rectangle clip = gc.getClipping (); | |
gc.setClipping(clip.intersection(rect)); | |
// Draw the unselected tabs first. | |
for (int i=0; i < items.length; i++) { | |
if (i != selectedIndex && event.getBounds().intersects(items[i].getBounds())) { | |
items[i].onPaint(gc, false); | |
} | |
} | |
// Selected tab comes last | |
if (selectedIndex != -1) { | |
items[selectedIndex].onPaint(gc, true); | |
} | |
// draw insertion mark | |
if (insertionIndex > -2) { | |
gc.setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_SELECTION)); | |
if (insertionIndex == -1) { | |
Rectangle bounds = items[0].getBounds(); | |
gc.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height - 1); | |
gc.drawLine(bounds.x - 2, bounds.y, bounds.x + 2, bounds.y); | |
gc.drawLine(bounds.x - 1, bounds.y + 1, bounds.x + 1, bounds.y + 1); | |
gc.drawLine(bounds.x - 1, bounds.y + bounds.height - 2, bounds.x + 1, bounds.y + bounds.height - 2); | |
gc.drawLine(bounds.x - 2, bounds.y + bounds.height - 1, bounds.x + 2, bounds.y + bounds.height - 1); | |
} else { | |
Rectangle bounds = items[insertionIndex].getBounds(); | |
gc.drawLine(bounds.x + bounds.width, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height - 1); | |
gc.drawLine(bounds.x + bounds.width - 2, bounds.y, bounds.x + bounds.width + 2, bounds.y); | |
gc.drawLine(bounds.x + bounds.width - 1, bounds.y + 1, bounds.x + bounds.width + 1, bounds.y + 1); | |
gc.drawLine(bounds.x + bounds.width - 1, bounds.y + bounds.height - 2, bounds.x + bounds.width + 1, bounds.y + bounds.height - 2); | |
gc.drawLine(bounds.x + bounds.width - 2, bounds.y + bounds.height - 1, bounds.x + bounds.width + 2, bounds.y + bounds.height - 1); | |
} | |
} | |
gc.setForeground(getForeground()); | |
gc.setBackground(getBackground()); | |
} | |
private void redrawTabArea(int index) { | |
int x = 0, y = 0, width = 0, height = 0; | |
if (index == -1) { | |
Rectangle area = super.getClientArea(); | |
if (area.width == 0 || area.height == 0) return; | |
width = area.x + area.width - borderLeft - borderRight; | |
height = tabHeight + 1; // +1 causes top line between content and tabs to be redrawn | |
x = area.x + borderLeft; | |
y = area.y + borderTop; | |
if (onBottom) { | |
y = Math.max(0, area.y + area.height - borderBottom - height); | |
} | |
} else { | |
CTabItem item = items[index]; | |
x = item.x; | |
y = item.y; | |
Rectangle area = super.getClientArea(); | |
width = area.x + area.width - x; | |
height = item.height; | |
} | |
redraw(x, y, width, height, false); | |
} | |
/** | |
* Removes the listener. | |
* | |
* @param listener the listener | |
* | |
* @exception SWTError | |
* <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* <li>ERROR_NULL_ARGUMENT when listener is null</li></ul> | |
*/ | |
public void removeSelectionListener(SelectionListener listener) { | |
checkWidget(); | |
if (listener == null) { | |
SWT.error(SWT.ERROR_NULL_ARGUMENT); | |
} | |
removeListener(SWT.Selection, listener); | |
removeListener(SWT.DefaultSelection, listener); | |
} | |
/** | |
* Removes the listener. | |
* | |
* @param listener the listener | |
* | |
* @exception SWTError | |
* <ul><li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* <li>ERROR_NULL_ARGUMENT when listener is null</li></ul> | |
*/ | |
public void removeCTabFolderListener(CTabFolderListener listener) { | |
checkWidget(); | |
if (listener == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); | |
if (tabListeners.length == 0) return; | |
int index = -1; | |
for (int i = 0; i < tabListeners.length; i++) { | |
if (listener == tabListeners[i]){ | |
index = i; | |
break; | |
} | |
} | |
if (index == -1) return; | |
if (tabListeners.length == 1) { | |
tabListeners = new CTabFolderListener[0]; | |
showClose = false; | |
setButtonBounds(); | |
return; | |
} | |
CTabFolderListener[] newTabListeners = new CTabFolderListener[tabListeners.length - 1]; | |
System.arraycopy(tabListeners, 0, newTabListeners, 0, index); | |
System.arraycopy(tabListeners, index + 1, newTabListeners, index, tabListeners.length - index - 1); | |
tabListeners = newTabListeners; | |
} | |
/** | |
* The widget was resized. Adjust the size of the currently selected page. | |
*/ | |
private void onResize() { | |
if (items.length == 0) { | |
redraw(); | |
return; | |
} | |
if (setItemBounds()) { | |
redrawTabArea(-1); | |
} | |
Point size = getSize(); | |
if (oldSize == null) { | |
redraw(); | |
} else { | |
if (onBottom && size.y != oldSize.y) { | |
redraw(); | |
} else { | |
int x1 = Math.min(size.x, oldSize.x); | |
if (size.x != oldSize.x) x1 -= 10; | |
int y1 = Math.min(size.y, oldSize.y); | |
if (size.y != oldSize.y) y1 -= 10; | |
int x2 = Math.max(size.x, oldSize.x); | |
int y2 = Math.max(size.y, oldSize.y); | |
redraw(0, y1, x2 + 10, y2 - y1, false); | |
redraw(x1, 0, x2 - x1, y2, false); | |
} | |
} | |
oldSize = size; | |
// resize content | |
if (selectedIndex != -1) { | |
Control control = items[selectedIndex].getControl(); | |
if (control != null && !control.isDisposed()) { | |
control.setBounds(getClientArea()); | |
} | |
} | |
} | |
public void setBackground (Color color) { | |
super.setBackground(color); | |
background = color; | |
// init inactive close button | |
inactiveCloseBar.setBackground(color); | |
// init scroll buttons | |
arrowBar.setBackground(color); | |
//init topRight control | |
if (topRight != null) | |
topRight.setBackground(color); | |
// init close button | |
if (gradientColors == null) { | |
closeBar.setBackground(color); | |
} | |
} | |
/** | |
* Specify a gradient of colours to be draw in the background of the selected tab. | |
* For example to draw a gradient that varies from dark blue to blue and then to | |
* white, use the following call to setBackground: | |
* <pre> | |
* cfolder.setBackground(new Color[]{display.getSystemColor(SWT.COLOR_DARK_BLUE), | |
* display.getSystemColor(SWT.COLOR_BLUE), | |
* display.getSystemColor(SWT.COLOR_WHITE), | |
* display.getSystemColor(SWT.COLOR_WHITE)}, | |
* new int[] {25, 50, 100}); | |
* </pre> | |
* | |
* @param colors an array of Color that specifies the colors to appear in the gradient | |
* in order of appearance left to right. The value <code>null</code> clears the | |
* background gradient. The value <code>null</code> can be used inside the array of | |
* Color to specify the background color. | |
* @param percents an array of integers between 0 and 100 specifying the percent of the width | |
* of the widget at which the color should change. The size of the percents array must be one | |
* less than the size of the colors array. | |
* | |
* @exception SWTError <ul> | |
* <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li> | |
* <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li> | |
* </ul> | |
*/ | |
public void setSelectionBackground(Color[] colors, int[] percents) { | |
checkWidget(); | |
if (colors != null) { | |
if (percents == null || percents.length != colors.length - 1) { | |
SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
} | |
if (getDisplay().getDepth() < 15) { | |
// Don't use gradients on low color displays | |
colors = new Color[] { colors[0] }; | |
percents = new int[] { }; | |
} | |
for (int i = 0; i < percents.length; i++) { | |
if (percents[i] < 0 || percents[i] > 100) { | |
SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
} | |
if (i > 0 && percents[i] < percents[i-1]) { | |
SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
} | |
} | |
} | |
// Are these settings the same as before? | |
if (backgroundImage == null) { | |
if ((gradientColors != null) && (colors != null) && | |
(gradientColors.length == colors.length)) { | |
boolean same = false; | |
for (int i = 0; i < gradientColors.length; i++) { | |
if (gradientColors[i] == null) { | |
same = colors[i] == null; | |
} else { | |
same = gradientColors[i].equals(colors[i]); | |
} | |
if (!same) break; | |
} | |
if (same) { | |
for (int i = 0; i < gradientPercents.length; i++) { | |
same = gradientPercents[i] == percents[i]; | |
if (!same) break; | |
} | |
} | |
if (same) return; | |
} | |
} else { | |
backgroundImage = null; | |
} | |
// Store the new settings | |
if (colors == null) { | |
gradientColors = null; | |
gradientPercents = null; | |
closeBar.setBackground(background); | |
} else { | |
gradientColors = new Color[colors.length]; | |
for (int i = 0; i < colors.length; ++i) | |
gradientColors[i] = colors[i]; | |
gradientPercents = new int[percents.length]; | |
for (int i = 0; i < percents.length; ++i) | |
gradientPercents[i] = percents[i]; | |
if (getDisplay().getDepth() < 15) closeBar.setBackground(background); | |
else closeBar.setBackground(gradientColors[gradientColors.length - 1]); | |
} | |
// Refresh with the new settings | |
if (selectedIndex > -1) redrawTabArea(selectedIndex); | |
} | |
/** | |
* Set the image to be drawn in the background of the selected tab. | |
* | |
* @param image the image to be drawn in the background | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void setSelectionBackground(Image image) { | |
checkWidget(); | |
if (image == backgroundImage) return; | |
if (image != null) { | |
gradientColors = null; | |
gradientPercents = null; | |
} | |
backgroundImage = image; | |
redrawTabArea(selectedIndex); | |
} | |
/** | |
* Toggle the visibility of the border | |
* | |
* @param show true if the border should be displayed | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void setBorderVisible(boolean show) { | |
checkWidget(); | |
if (showBorders == show) return; | |
showBorders = show; | |
if (showBorders) { | |
if ((getStyle() & SWT.FLAT) != 0) { | |
borderBottom = borderTop = borderLeft = borderRight = 1; | |
} else { | |
borderLeft = borderTop = 1; | |
borderRight = borderBottom = 3; | |
} | |
} else { | |
borderBottom = borderTop = borderLeft = borderRight = 0; | |
} | |
oldSize = null; | |
notifyListeners(SWT.Resize, new Event()); | |
} | |
public void setFont(Font font) { | |
checkWidget(); | |
if (font != null && font.equals(getFont())) return; | |
super.setFont(font); | |
oldFont = getFont(); | |
resetTabSize(true); | |
} | |
/** | |
* Set the foreground color of the selected tab. | |
* | |
* @param color the color of the text displayed in the selected tab | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void setSelectionForeground (Color color) { | |
checkWidget(); | |
if (selectionForeground == color) return; | |
if (color == null) color = getForeground(); | |
selectionForeground = color; | |
if (selectedIndex > -1) { | |
redrawTabArea(selectedIndex); | |
} | |
} | |
/** | |
* Display an insert marker before or after the specified tab item. | |
* Null will clear the mark. | |
* | |
* @param item the item with which the mark is associated or null | |
* | |
* @param after true if the mark should be displayed after the specified item | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void setInsertMark(CTabItem item, boolean after) { | |
checkWidget(); | |
int index = -1; | |
if (item != null) { | |
index = indexOf(item); | |
} | |
setInsertMark(index, after); | |
} | |
/** | |
* Display an insert marker before or after the specified tab item. | |
* -1 will clear the mark. | |
* | |
* @param item the index of the item with which the mark is associated or null | |
* | |
* @param after true if the mark should be displayed after the specified item | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void setInsertMark(int index, boolean after) { | |
checkWidget(); | |
if (index < -1 || index >= getItemCount()) { | |
SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
} | |
if (index == -1) { | |
index = -2; | |
} else { | |
index = after ? index : --index; | |
} | |
if (insertionIndex == index) return; | |
int oldIndex = insertionIndex; | |
insertionIndex = index; | |
if (index > -1) redrawTabArea(index); | |
if (oldIndex > 1) redrawTabArea(oldIndex); | |
} | |
/** | |
* Set the selection to the tab at the specified index. | |
* | |
* @param index the index of the tab item to be selected | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
*/ | |
public void setSelection(int index) { | |
checkWidget(); | |
if (index < 0 || index >= items.length) return; | |
if (selectedIndex == index) return; | |
int oldIndex = selectedIndex; | |
selectedIndex = index; | |
Control control = items[index].control; | |
if (control != null && !control.isDisposed()) { | |
control.setBounds(getClientArea()); | |
control.setVisible(true); | |
} | |
if (oldIndex != -1) { | |
control = items[oldIndex].control; | |
if (control != null && !control.isDisposed()) { | |
control.setVisible(false); | |
} | |
} | |
showItem(items[selectedIndex]); | |
setButtonBounds(); | |
redrawTabArea(-1); | |
} | |
/** | |
* Set the control that appears in the top right corner of the tab folder. | |
* Typically this is a close button or a composite with a Menu and close button. | |
* The topRight control is optional. Setting the top right control to null will remove it from the tab folder. | |
* | |
* @since 2.1 | |
* | |
* @param control the control to be displayed in the top right corner or null | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* <li>ERROR_INVALID_ARGUMENT - if the control is not a child of this CTabFolder</li> | |
* </ul> | |
*/ | |
public void setTopRight(Control control) { | |
checkWidget(); | |
if (control != null && control.getParent() != this) { | |
SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
} | |
topRight = control; | |
resetTabSize(true); | |
} | |
/** | |
* Shows the item. If the item is already showing in the receiver, | |
* this method simply returns. Otherwise, the items are scrolled until | |
* the item is visible. | |
* | |
* @param item the item to be shown | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the item is null</li> | |
* <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
* | |
* @see CTabFolder#showSelection() | |
* | |
* @since 2.0 | |
*/ | |
public void showItem (CTabItem item) { | |
checkWidget(); | |
if (item == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); | |
if (item.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
int index = indexOf(item); | |
if (index < topTabIndex) { | |
topTabIndex = index; | |
setItemLocation(); | |
redrawTabArea(-1); | |
return; | |
} | |
Rectangle area = getClientArea(); | |
if (area.width <= 0) { | |
topTabIndex = index; | |
return; | |
} | |
int rightEdge = area.x + area.width; | |
Rectangle rect = getToolSpace(); | |
if (rect.width > 0) { | |
rightEdge -= rect.width; | |
} | |
if (item.x + item.width < rightEdge) return; | |
setLastItem(index); | |
} | |
/** | |
* Shows the selection. If the selection is already showing in the receiver, | |
* this method simply returns. Otherwise, the items are scrolled until | |
* the selection is visible. | |
* | |
* @exception IllegalArgumentException <ul> | |
* <li>ERROR_NULL_ARGUMENT - if the listener is null</li> | |
* </ul> | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* </ul> | |
* | |
* @see CTabFolder#showItem(CTabItem) | |
* | |
* @since 2.0 | |
* | |
*/ | |
public void showSelection () { | |
checkWidget (); | |
if (selectedIndex != -1) { | |
showItem(getSelection()); | |
} | |
} | |
char getMnemonic (String string) { | |
int index = 0; | |
int length = string.length (); | |
do { | |
while ((index < length) && (string.charAt (index) != '&')) index++; | |
if (++index >= length) return '\0'; | |
if (string.charAt (index) != '&') return string.charAt (index); | |
index++; | |
} while (index < length); | |
return '\0'; | |
} | |
/** | |
* Set the selection to the tab at the specified item. | |
* | |
* @param index the tab item to be selected | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* <li>ERROR_NULL_ARGUMENT - if argument is null</li> | |
* </ul> | |
*/ | |
public void setSelection(CTabItem item) { | |
checkWidget(); | |
if (item == null) | |
SWT.error(SWT.ERROR_NULL_ARGUMENT); | |
int index = indexOf(item); | |
setSelection(index); | |
} | |
/** | |
* Set the selection to the tab at the specified index. | |
*/ | |
private void setSelection(int index, boolean notify) { | |
int oldSelectedIndex = selectedIndex; | |
setSelection(index); | |
if (notify && selectedIndex != oldSelectedIndex && selectedIndex != -1) { | |
Event event = new Event(); | |
event.item = getItem(selectedIndex); | |
notifyListeners(SWT.Selection, event); | |
} | |
} | |
private void updateCloseBar() { | |
int imageHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN - 2; | |
if (imageHeight < 8) return; | |
if (closeImage != null && closeImage.getBounds().height == imageHeight) return; | |
if (closeBar != null) closeBar.dispose(); | |
closeBar = null; | |
if (inactiveCloseBar != null) inactiveCloseBar.dispose(); | |
inactiveCloseBar = null; | |
createCloseBar(); | |
ToolItem closeItem = closeBar.getItems()[0]; | |
ToolItem inactiveCloseItem = inactiveCloseBar.getItems()[0]; | |
if (closeImage != null) closeImage.dispose(); | |
Display display = getDisplay(); | |
Color foreground = getForeground(); | |
Color black = display.getSystemColor(SWT.COLOR_BLACK); | |
Color background = getBackground(); | |
PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()}); | |
ImageData imageData = new ImageData(imageHeight, imageHeight, 4, palette); | |
imageData.transparentPixel = 1; | |
closeImage = new Image(display, imageData); | |
GC gc = new GC(closeImage); | |
gc.setBackground(background); | |
gc.fillRectangle(0, 0, imageHeight, imageHeight); | |
gc.setForeground(black); | |
//draw an 8x7 'x' centered in image | |
int h = (imageHeight / 2 )* 2; | |
int inset = (h - 8) / 2; | |
gc.drawLine( inset, inset, h - inset - 1, h - inset - 1); | |
gc.drawLine( inset + 1, inset, h - inset, h - inset - 1); | |
gc.drawLine( inset, h - inset - 1, h - inset - 1, inset); | |
gc.drawLine( inset + 1, h - inset - 1, h - inset, inset); | |
gc.dispose(); | |
closeItem.setImage(closeImage); | |
inactiveCloseItem.setImage(closeImage); | |
} | |
private void updateArrowBar() { | |
int imageHeight = tabHeight - 2; | |
if (imageHeight < 10) return; | |
if (arrowLeftImage != null && arrowLeftImage.getBounds().height == imageHeight) return; | |
if (arrowBar != null) arrowBar.dispose(); | |
arrowBar = null; | |
if (arrowLeftImage != null) arrowLeftImage.dispose(); | |
if (arrowRightImage != null) arrowRightImage.dispose(); | |
createArrowBar(); | |
ToolItem[] items = arrowBar.getItems(); | |
ToolItem left = items[0]; | |
ToolItem right = items[1]; | |
Display display = getDisplay(); | |
Color foreground = getForeground(); | |
Color black = display.getSystemColor(SWT.COLOR_BLACK); | |
Color background = getBackground(); | |
PaletteData palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()}); | |
ImageData imageData = new ImageData(7, imageHeight, 4, palette); | |
imageData.transparentPixel = 1; | |
arrowLeftImage = new Image(display, imageData); | |
GC gc = new GC(arrowLeftImage); | |
gc.setBackground(background); | |
gc.fillRectangle(0, 0, 7, imageHeight); | |
gc.setBackground(black); | |
//draw a 10x5 '<' centered vertically in image | |
int h = (imageHeight / 2 )* 2; | |
int midpoint = h / 2; | |
int[] pointArr = new int[] {6, midpoint - 5, | |
1, midpoint, | |
6, midpoint + 5,}; | |
gc.fillPolygon(pointArr); | |
gc.dispose(); | |
palette = new PaletteData(new RGB[]{foreground.getRGB(), background.getRGB(), black.getRGB()}); | |
imageData = new ImageData(7, imageHeight, 4, palette); | |
imageData.transparentPixel = 1; | |
arrowRightImage = new Image(display, imageData); | |
gc = new GC(arrowRightImage); | |
gc.setBackground(background); | |
gc.fillRectangle(0, 0, 7, imageHeight); | |
gc.setBackground(black); | |
//draw a 10x5 '>' centered vertically in image | |
pointArr = new int[] {1, midpoint - 5, | |
6, midpoint, | |
1, midpoint + 5,}; | |
gc.fillPolygon(pointArr); | |
gc.dispose(); | |
left.setImage(arrowLeftImage); | |
right.setImage(arrowRightImage); | |
} | |
private void onMouseDoubleClick(Event event) { | |
Event e = new Event(); | |
e.item = getItem(new Point(event.x, event.y)); | |
notifyListeners(SWT.DefaultSelection, e); | |
} | |
/** | |
* A mouse button was pressed down. | |
* If a tab was hit select the tab. | |
*/ | |
private void onMouseDown(Event event) { | |
for (int i=0; i<items.length; i++) { | |
if (items[i].getBounds().contains(new Point(event.x, event.y))) { | |
if (i == selectedIndex) return; | |
forceFocus(); | |
setSelection(i, true); | |
if (isFocusControl()) setFocus(); | |
return; | |
} | |
} | |
} | |
private void onMouseExit(Event event) { | |
Rectangle inactiveBounds = inactiveCloseBar.getBounds(); | |
if (inactiveBounds.contains(event.x, event.y)) return; | |
inactiveCloseBar.setVisible(false); | |
inactiveItem = null; | |
showToolTip = false; | |
toolTipItem = null; | |
if (tip != null && !tip.isDisposed() && tip.isVisible()) tip.setVisible(false); | |
} | |
private void onMouseHover(Event event) { | |
if (tip == null || tip.isDisposed()) return; | |
showToolTip = true; | |
showToolTip(event.x, event.y); | |
} | |
private void showToolTip (int x, int y) { | |
CTabItem item = getItem(new Point (x, y)); | |
if (item != null) { | |
if (item == toolTipItem) return; | |
toolTipItem = item; | |
String tooltip = item.getToolTipText(); | |
if (tooltip != null) { | |
Display display = tip.getDisplay(); | |
label.setForeground (display.getSystemColor (SWT.COLOR_INFO_FOREGROUND)); | |
label.setBackground (display.getSystemColor (SWT.COLOR_INFO_BACKGROUND)); | |
label.setText(tooltip); | |
Point labelSize = label.computeSize(SWT.DEFAULT, SWT.DEFAULT); | |
labelSize.x += 2; labelSize.y += 2; | |
label.setSize(labelSize); | |
tip.pack(); | |
/* | |
* On some platforms, there is a minimum size for a shell | |
* which may be greater than the label size. | |
* To avoid having the background of the tip shell showing | |
* around the label, force the label to fill the entire client area. | |
*/ | |
Rectangle area = tip.getClientArea(); | |
label.setSize(area.width, area.height); | |
/* | |
* Position the tooltip and ensure that it is not located off | |
* the screen. | |
*/ | |
Point pt = new Point(item.x + item.width / 4, item.y + item.height + 2); | |
pt = toDisplay(pt); | |
Rectangle rect = display.getBounds(); | |
Point tipSize = tip.getSize(); | |
pt.x = Math.max (0, Math.min (pt.x, rect.width - tipSize.x)); | |
pt.y = Math.max (0, Math.min (pt.y, rect.height - tipSize.y)); | |
tip.setLocation(pt); | |
tip.setVisible(true); | |
return; | |
} | |
} | |
toolTipItem = null; | |
if (tip != null && !tip.isDisposed() && tip.isVisible()) tip.setVisible(false); | |
} | |
private void onMouseMove(Event event) { | |
if (showToolTip) { | |
showToolTip(event.x, event.y); | |
} | |
if (!showClose) return; | |
CTabItem item = null; | |
for (int i=0; i<items.length; i++) { | |
Rectangle rect = items[i].getBounds(); | |
if (rect.contains(new Point(event.x, event.y))) { | |
item = items[i]; | |
break; | |
} | |
} | |
if (item == inactiveItem) return; | |
inactiveCloseBar.setVisible(false); | |
inactiveItem = null; | |
if (item == null || item == getSelection()) return; | |
int toolbarHeight = tabHeight - CTabItem.TOP_MARGIN - CTabItem.BOTTOM_MARGIN + 2; // +2 to ignore gap between focus rectangle | |
Point size = inactiveCloseBar.computeSize(SWT.DEFAULT, toolbarHeight); | |
int x = item.x + item.width - size.x - 2; // -2 to not overlap focus rectangle and trim | |
int y = item.y + Math.max(0, (item.height - toolbarHeight)/2); | |
Rectangle toolspace = getToolSpace(); | |
Point folderSize = getSize(); | |
if ((toolspace.width == 0 || x < toolspace.x) && x + size.x < folderSize.x - borderRight) { | |
inactiveCloseBar.setBounds(x, y, size.x, toolbarHeight); | |
inactiveCloseBar.setVisible(true); | |
inactiveItem = item; | |
} | |
} | |
private void onTraverse (Event event) { | |
switch (event.detail) { | |
case SWT.TRAVERSE_ESCAPE: | |
// TEMPORARY CODE See bug report 17372 | |
// case SWT.TRAVERSE_RETURN: | |
case SWT.TRAVERSE_TAB_NEXT: | |
case SWT.TRAVERSE_TAB_PREVIOUS: | |
event.doit = true; | |
break; | |
case SWT.TRAVERSE_MNEMONIC: | |
event.doit = onMnemonic(event); | |
if (event.doit) event.detail = SWT.TRAVERSE_NONE; | |
break; | |
case SWT.TRAVERSE_PAGE_NEXT: | |
case SWT.TRAVERSE_PAGE_PREVIOUS: | |
event.doit = onPageTraversal(event); | |
if (event.doit) event.detail = SWT.TRAVERSE_NONE; | |
break; | |
} | |
} | |
private boolean onPageTraversal(Event event) { | |
int count = getItemCount (); | |
if (count == 0) return false; | |
int index = getSelectionIndex (); | |
if (index == -1) { | |
index = 0; | |
} else { | |
int offset = (event.detail == SWT.TRAVERSE_PAGE_NEXT) ? 1 : -1; | |
index = (index + offset + count) % count; | |
} | |
setSelection (index, true); | |
return true; | |
} | |
/** | |
* Answer true if not all tabs can be visible in the receive | |
* thus requiring the scroll buttons to be visible. | |
*/ | |
private boolean scroll_leftVisible() { | |
return topTabIndex > 0; | |
} | |
/** | |
* Answer true if not all tabs can be visible in the receive | |
* thus requiring the scroll buttons to be visible. | |
*/ | |
private boolean scroll_rightVisible() { | |
// only show Scroll buttons if there is more than one item | |
// and if we are not already at the last item | |
if (items.length < 2) return false; | |
Rectangle area = getClientArea(); | |
int rightEdge = area.x + area.width; | |
if (rightEdge <= 0) return false; | |
if (topTabIndex > 0) { | |
rightEdge -= arrowBar.getSize().x; | |
} | |
if (topRight != null) { | |
rightEdge -= topRight.getSize().x; | |
} | |
CTabItem item = items[items.length-1]; | |
return (item.x + item.width > rightEdge); | |
} | |
/** | |
* Scroll the tab items to the left. | |
*/ | |
private void scroll_scrollLeft() { | |
if (items.length == 0) return; | |
setLastItem(topTabIndex - 1); | |
} | |
/** | |
* Scroll the tab items to the right. | |
*/ | |
private void scroll_scrollRight() { | |
int lastIndex = getLastItem(); | |
topTabIndex = lastIndex + 1; | |
setItemLocation(); | |
correctLastItem(); | |
redrawTabArea(-1); | |
} | |
private boolean correctLastItem() { | |
Rectangle area = getClientArea(); | |
int rightEdge = area.x + area.width; | |
if (rightEdge <= 0) return false; | |
Rectangle toolspace = getToolSpace(); | |
if (toolspace.width > 0) { | |
rightEdge -= toolspace.width; | |
} | |
CTabItem item = items[items.length - 1]; | |
if (item.x + item.width < rightEdge) { | |
setLastItem(items.length - 1); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Specify a fixed height for the tab items. If no height is specified, | |
* the default height is the height of the text or the image, whichever | |
* is greater. Specifying a height of 0 will revert to the default height. | |
* | |
* @param height the pixel value of the height or 0 | |
* | |
* @exception SWTException <ul> | |
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> | |
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> | |
* <li>ERROR_INVALID_ARGUMENT - if called with a height of less than 0</li> | |
* </ul> | |
*/ | |
public void setTabHeight(int height) { | |
checkWidget(); | |
if (height < 0) { | |
SWT.error(SWT.ERROR_INVALID_ARGUMENT); | |
} | |
fixedTabHeight = true; | |
if (tabHeight == height) return; | |
tabHeight = height; | |
oldSize = null; | |
notifyListeners(SWT.Resize, new Event()); | |
} | |
void resetTabSize(boolean checkHeight){ | |
int oldHeight = tabHeight; | |
if (!fixedTabHeight && checkHeight) { | |
int tempHeight = 0; | |
GC gc = new GC(this); | |
for (int i=0; i < items.length; i++) { | |
tempHeight = Math.max(tempHeight, items[i].preferredHeight(gc)); | |
} | |
gc.dispose(); | |
if (topRight != null) tempHeight = Math.max(tempHeight, topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT).y); | |
tabHeight = tempHeight; | |
} | |
if (tabHeight != oldHeight){ | |
oldSize = null; | |
notifyListeners(SWT.Resize, new Event()); | |
} else { | |
setItemBounds(); | |
redraw(); | |
} | |
} | |
} |