0001: /*******************************************************************************
0002: * Copyright (c) 2001, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.ui.internal.views.properties.tabbed.view;
0011:
0012: import org.eclipse.jface.resource.JFaceResources;
0013: import org.eclipse.swt.SWT;
0014: import org.eclipse.swt.accessibility.ACC;
0015: import org.eclipse.swt.accessibility.Accessible;
0016: import org.eclipse.swt.accessibility.AccessibleAdapter;
0017: import org.eclipse.swt.accessibility.AccessibleControlAdapter;
0018: import org.eclipse.swt.accessibility.AccessibleControlEvent;
0019: import org.eclipse.swt.accessibility.AccessibleEvent;
0020: import org.eclipse.swt.events.ControlAdapter;
0021: import org.eclipse.swt.events.ControlEvent;
0022: import org.eclipse.swt.events.FocusEvent;
0023: import org.eclipse.swt.events.FocusListener;
0024: import org.eclipse.swt.events.MouseAdapter;
0025: import org.eclipse.swt.events.MouseEvent;
0026: import org.eclipse.swt.events.MouseMoveListener;
0027: import org.eclipse.swt.events.MouseTrackAdapter;
0028: import org.eclipse.swt.events.PaintEvent;
0029: import org.eclipse.swt.events.PaintListener;
0030: import org.eclipse.swt.events.TraverseEvent;
0031: import org.eclipse.swt.events.TraverseListener;
0032: import org.eclipse.swt.graphics.Color;
0033: import org.eclipse.swt.graphics.FontMetrics;
0034: import org.eclipse.swt.graphics.GC;
0035: import org.eclipse.swt.graphics.Point;
0036: import org.eclipse.swt.graphics.RGB;
0037: import org.eclipse.swt.graphics.Rectangle;
0038: import org.eclipse.swt.layout.FormAttachment;
0039: import org.eclipse.swt.layout.FormData;
0040: import org.eclipse.swt.layout.FormLayout;
0041: import org.eclipse.swt.widgets.Canvas;
0042: import org.eclipse.swt.widgets.Composite;
0043: import org.eclipse.swt.widgets.Display;
0044: import org.eclipse.swt.widgets.Event;
0045: import org.eclipse.swt.widgets.Listener;
0046: import org.eclipse.swt.widgets.Shell;
0047: import org.eclipse.ui.forms.FormColors;
0048: import org.eclipse.ui.internal.views.properties.tabbed.l10n.TabbedPropertyMessages;
0049: import org.eclipse.ui.views.properties.tabbed.ITabItem;
0050: import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
0051:
0052: /**
0053: * Shows the list of tabs in the tabbed property sheet page.
0054: *
0055: * @author Anthony Hunter
0056: */
0057: public class TabbedPropertyList extends Composite {
0058:
0059: private static final ListElement[] ELEMENTS_EMPTY = new ListElement[0];
0060:
0061: protected static final int NONE = -1;
0062:
0063: protected static final int INDENT = 7;
0064:
0065: private ListElement[] elements;
0066:
0067: private int selectedElementIndex = NONE;
0068:
0069: private int topVisibleIndex = NONE;
0070:
0071: private int bottomVisibleIndex = NONE;
0072:
0073: private TopNavigationElement topNavigationElement;
0074:
0075: private BottomNavigationElement bottomNavigationElement;
0076:
0077: private int widestLabelIndex = NONE;
0078:
0079: private int tabsThatFitInComposite = NONE;
0080:
0081: private Color widgetForeground;
0082:
0083: private Color widgetBackground;
0084:
0085: private Color widgetNormalShadow;
0086:
0087: private Color widgetDarkShadow;
0088:
0089: private Color listBackground;
0090:
0091: private Color hoverGradientStart;
0092:
0093: private Color hoverGradientEnd;
0094:
0095: private Color defaultGradientStart;
0096:
0097: private Color defaultGradientEnd;
0098:
0099: private Color indentedDefaultBackground;
0100:
0101: private Color indentedHoverBackground;
0102:
0103: private Color navigationElementShadowStroke;
0104:
0105: private Color bottomNavigationElementShadowStroke1;
0106:
0107: private Color bottomNavigationElementShadowStroke2;
0108:
0109: private TabbedPropertySheetWidgetFactory factory;
0110:
0111: /**
0112: * One of the tabs in the tabbed property list.
0113: */
0114: public class ListElement extends Canvas {
0115:
0116: private ITabItem tab;
0117:
0118: private int index;
0119:
0120: private boolean selected;
0121:
0122: private boolean hover;
0123:
0124: /**
0125: * Constructor for ListElement.
0126: *
0127: * @param parent
0128: * the parent Composite.
0129: * @param tab
0130: * the tab item for the element.
0131: * @param index
0132: * the index in the list.
0133: */
0134: public ListElement(Composite parent, final ITabItem tab,
0135: int index) {
0136: super (parent, SWT.NO_FOCUS);
0137: this .tab = tab;
0138: hover = false;
0139: selected = false;
0140: this .index = index;
0141:
0142: addPaintListener(new PaintListener() {
0143:
0144: public void paintControl(PaintEvent e) {
0145: paint(e);
0146: }
0147: });
0148: addMouseListener(new MouseAdapter() {
0149:
0150: public void mouseUp(MouseEvent e) {
0151: if (!selected) {
0152: select(getIndex(ListElement.this ));
0153: /*
0154: * We set focus to the tabbed property composite so that
0155: * focus is moved to the appropriate widget in the
0156: * section.
0157: */
0158: Composite tabbedPropertyComposite = getParent();
0159: while (!(tabbedPropertyComposite instanceof TabbedPropertyComposite)) {
0160: tabbedPropertyComposite = tabbedPropertyComposite
0161: .getParent();
0162: }
0163: tabbedPropertyComposite.setFocus();
0164: }
0165: }
0166: });
0167: addMouseMoveListener(new MouseMoveListener() {
0168:
0169: public void mouseMove(MouseEvent e) {
0170: if (!hover) {
0171: hover = true;
0172: redraw();
0173: }
0174: }
0175: });
0176: addMouseTrackListener(new MouseTrackAdapter() {
0177:
0178: public void mouseExit(MouseEvent e) {
0179: hover = false;
0180: redraw();
0181: }
0182: });
0183: }
0184:
0185: /**
0186: * Set selected value for this element.
0187: *
0188: * @param selected
0189: * the selected value.
0190: */
0191: public void setSelected(boolean selected) {
0192: this .selected = selected;
0193: redraw();
0194: }
0195:
0196: /**
0197: * Paint the element.
0198: *
0199: * @param e
0200: * the paint event.
0201: */
0202: private void paint(PaintEvent e) {
0203: /*
0204: * draw the top two lines of the tab, same for selected, hover and
0205: * default
0206: */
0207: Rectangle bounds = getBounds();
0208: e.gc.setForeground(widgetNormalShadow);
0209: e.gc.drawLine(0, 0, bounds.width - 1, 0);
0210: e.gc.setForeground(listBackground);
0211: e.gc.drawLine(0, 1, bounds.width - 1, 1);
0212:
0213: /* draw the fill in the tab */
0214: if (selected) {
0215: e.gc.setBackground(listBackground);
0216: e.gc.fillRectangle(0, 2, bounds.width,
0217: bounds.height - 1);
0218: } else if (hover && tab.isIndented()) {
0219: e.gc.setBackground(indentedHoverBackground);
0220: e.gc.fillRectangle(0, 2, bounds.width - 1,
0221: bounds.height - 1);
0222: } else if (hover) {
0223: e.gc.setForeground(hoverGradientStart);
0224: e.gc.setBackground(hoverGradientEnd);
0225: e.gc.fillGradientRectangle(0, 2, bounds.width - 1,
0226: bounds.height - 1, true);
0227: } else if (tab.isIndented()) {
0228: e.gc.setBackground(indentedDefaultBackground);
0229: e.gc.fillRectangle(0, 2, bounds.width - 1,
0230: bounds.height - 1);
0231: } else {
0232: e.gc.setForeground(defaultGradientStart);
0233: e.gc.setBackground(defaultGradientEnd);
0234: e.gc.fillGradientRectangle(0, 2, bounds.width - 1,
0235: bounds.height - 1, true);
0236: }
0237:
0238: if (!selected) {
0239: e.gc.setForeground(widgetNormalShadow);
0240: e.gc.drawLine(bounds.width - 1, 1, bounds.width - 1,
0241: bounds.height + 1);
0242: }
0243:
0244: int textIndent = INDENT;
0245: FontMetrics fm = e.gc.getFontMetrics();
0246: int height = fm.getHeight();
0247: int textMiddle = (bounds.height - height) / 2;
0248:
0249: if (selected && tab.getImage() != null
0250: && !tab.getImage().isDisposed()) {
0251: /* draw the icon for the selected tab */
0252: if (tab.isIndented()) {
0253: textIndent = textIndent + INDENT;
0254: } else {
0255: textIndent = textIndent - 3;
0256: }
0257: e.gc.drawImage(tab.getImage(), textIndent,
0258: textMiddle - 1);
0259: textIndent = textIndent + 16 + 5;
0260: } else if (tab.isIndented()) {
0261: textIndent = textIndent + INDENT;
0262: }
0263:
0264: /* draw the text */
0265: e.gc.setForeground(widgetForeground);
0266: if (selected) {
0267: /* selected tab is bold font */
0268: e.gc.setFont(JFaceResources.getFontRegistry().getBold(
0269: JFaceResources.DEFAULT_FONT));
0270: }
0271: e.gc.drawText(tab.getText(), textIndent, textMiddle, true);
0272:
0273: /* draw the bottom line on the tab for selected and default */
0274: if (!hover) {
0275: e.gc.setForeground(listBackground);
0276: e.gc.drawLine(0, bounds.height - 1, bounds.width - 2,
0277: bounds.height - 1);
0278: }
0279: }
0280:
0281: /**
0282: * Get the tab item.
0283: *
0284: * @return the tab item.
0285: */
0286: public ITabItem getTabItem() {
0287: return tab;
0288: }
0289:
0290: public String toString() {
0291: return tab.getText();
0292: }
0293: }
0294:
0295: /**
0296: * The top navigation element in the tabbed property list. It looks like a
0297: * scroll button when scrolling is needed or is just a spacer when no
0298: * scrolling is required.
0299: */
0300: public class TopNavigationElement extends Canvas {
0301:
0302: /**
0303: * Constructor for TopNavigationElement.
0304: *
0305: * @param parent
0306: * the parent Composite.
0307: */
0308: public TopNavigationElement(Composite parent) {
0309: super (parent, SWT.NO_FOCUS);
0310: addPaintListener(new PaintListener() {
0311:
0312: public void paintControl(PaintEvent e) {
0313: paint(e);
0314: }
0315: });
0316: addMouseListener(new MouseAdapter() {
0317:
0318: public void mouseUp(MouseEvent e) {
0319: if (isUpScrollRequired()) {
0320: bottomVisibleIndex--;
0321: if (topVisibleIndex != 0) {
0322: topVisibleIndex--;
0323: }
0324: layoutTabs();
0325: topNavigationElement.redraw();
0326: bottomNavigationElement.redraw();
0327: }
0328: }
0329: });
0330: }
0331:
0332: /**
0333: * Paint the element.
0334: *
0335: * @param e
0336: * the paint event.
0337: */
0338: private void paint(PaintEvent e) {
0339: e.gc.setBackground(widgetBackground);
0340: e.gc.setForeground(widgetForeground);
0341: Rectangle bounds = getBounds();
0342:
0343: if (elements.length != 0) {
0344: e.gc.fillRectangle(0, 0, bounds.width, bounds.height);
0345: e.gc.setForeground(widgetNormalShadow);
0346: e.gc.drawLine(bounds.width - 1, 0, bounds.width - 1,
0347: bounds.height - 1);
0348: } else {
0349: e.gc.setBackground(listBackground);
0350: e.gc.fillRectangle(0, 0, bounds.width, bounds.height);
0351: int textIndent = INDENT;
0352: FontMetrics fm = e.gc.getFontMetrics();
0353: int height = fm.getHeight();
0354: int textMiddle = (bounds.height - height) / 2;
0355: e.gc.setForeground(widgetForeground);
0356: String properties_not_available = TabbedPropertyMessages.TabbedPropertyList_properties_not_available;
0357: e.gc.drawText(properties_not_available, textIndent,
0358: textMiddle);
0359: }
0360:
0361: if (isUpScrollRequired()) {
0362: e.gc.setForeground(widgetDarkShadow);
0363: int middle = bounds.width / 2;
0364: e.gc.drawLine(middle + 1, 3, middle + 5, 7);
0365: e.gc.drawLine(middle, 3, middle - 4, 7);
0366: e.gc.drawLine(middle - 3, 7, middle + 4, 7);
0367:
0368: e.gc.setForeground(listBackground);
0369: e.gc.drawLine(middle, 4, middle + 1, 4);
0370: e.gc.drawLine(middle - 1, 5, middle + 2, 5);
0371: e.gc.drawLine(middle - 2, 6, middle + 3, 6);
0372:
0373: e.gc.setForeground(widgetNormalShadow);
0374: e.gc.drawLine(0, 0, bounds.width - 2, 0);
0375: e.gc.setForeground(navigationElementShadowStroke);
0376: e.gc.drawLine(0, 1, bounds.width - 2, 1);
0377: e.gc.drawLine(0, bounds.height - 1, bounds.width - 2,
0378: bounds.height - 1);
0379: }
0380: }
0381: }
0382:
0383: /**
0384: * The top navigation element in the tabbed property list. It looks like a
0385: * scroll button when scrolling is needed or is just a spacer when no
0386: * scrolling is required.
0387: */
0388: public class BottomNavigationElement extends Canvas {
0389:
0390: /**
0391: * Constructor for BottomNavigationElement.
0392: *
0393: * @param parent
0394: * the parent Composite.
0395: */
0396: public BottomNavigationElement(Composite parent) {
0397: super (parent, SWT.NO_FOCUS);
0398: addPaintListener(new PaintListener() {
0399:
0400: public void paintControl(PaintEvent e) {
0401: paint(e);
0402: }
0403: });
0404: addMouseListener(new MouseAdapter() {
0405:
0406: public void mouseUp(MouseEvent e) {
0407: if (isDownScrollRequired()) {
0408: topVisibleIndex++;
0409: if (bottomVisibleIndex != elements.length - 1) {
0410: bottomVisibleIndex++;
0411: }
0412: layoutTabs();
0413: topNavigationElement.redraw();
0414: bottomNavigationElement.redraw();
0415: }
0416: }
0417: });
0418: }
0419:
0420: /**
0421: * Paint the element.
0422: *
0423: * @param e
0424: * the paint event.
0425: */
0426: private void paint(PaintEvent e) {
0427: e.gc.setBackground(widgetBackground);
0428: e.gc.setForeground(widgetForeground);
0429: Rectangle bounds = getBounds();
0430:
0431: if (elements.length != 0) {
0432: e.gc.fillRectangle(0, 0, bounds.width, bounds.height);
0433: e.gc.setForeground(widgetNormalShadow);
0434: e.gc.drawLine(bounds.width - 1, 0, bounds.width - 1,
0435: bounds.height - 1);
0436: e.gc.drawLine(0, 0, bounds.width - 1, 0);
0437:
0438: e.gc
0439: .setForeground(bottomNavigationElementShadowStroke1);
0440: e.gc.drawLine(0, 1, bounds.width - 2, 1);
0441: e.gc
0442: .setForeground(bottomNavigationElementShadowStroke2);
0443: e.gc.drawLine(0, 2, bounds.width - 2, 2);
0444: } else {
0445: e.gc.setBackground(listBackground);
0446: e.gc.fillRectangle(0, 0, bounds.width, bounds.height);
0447: }
0448:
0449: if (isDownScrollRequired()) {
0450: e.gc.setForeground(widgetDarkShadow);
0451: int middle = bounds.width / 2;
0452: int bottom = bounds.height - 3;
0453: e.gc.drawLine(middle + 1, bottom, middle + 5,
0454: bottom - 4);
0455: e.gc.drawLine(middle, bottom, middle - 4, bottom - 4);
0456: e.gc.drawLine(middle - 3, bottom - 4, middle + 4,
0457: bottom - 4);
0458:
0459: e.gc.setForeground(listBackground);
0460: e.gc.drawLine(middle, bottom - 1, middle + 1,
0461: bottom - 1);
0462: e.gc.drawLine(middle - 1, bottom - 2, middle + 2,
0463: bottom - 2);
0464: e.gc.drawLine(middle - 2, bottom - 3, middle + 3,
0465: bottom - 3);
0466:
0467: e.gc.setForeground(widgetNormalShadow);
0468: e.gc.drawLine(0, bottom - 7, bounds.width - 2,
0469: bottom - 7);
0470: e.gc.setForeground(navigationElementShadowStroke);
0471: e.gc.drawLine(0, bottom + 2, bounds.width - 2,
0472: bottom + 2);
0473: e.gc.drawLine(0, bottom - 6, bounds.width - 2,
0474: bottom - 6);
0475: }
0476: }
0477: }
0478:
0479: /**
0480: * Constructor for TabbedPropertyList.
0481: *
0482: * @param parent
0483: * the parent widget.
0484: * @param factory
0485: * the widget factory.
0486: */
0487: public TabbedPropertyList(Composite parent,
0488: TabbedPropertySheetWidgetFactory factory) {
0489: super (parent, SWT.NO_FOCUS);
0490: this .factory = factory;
0491: removeAll();
0492: setLayout(new FormLayout());
0493: initColours();
0494: initAccessible();
0495: topNavigationElement = new TopNavigationElement(this );
0496: bottomNavigationElement = new BottomNavigationElement(this );
0497:
0498: this .addFocusListener(new FocusListener() {
0499:
0500: public void focusGained(FocusEvent e) {
0501: int i = getSelectionIndex();
0502: if (i >= 0) {
0503: elements[i].redraw();
0504: }
0505: }
0506:
0507: public void focusLost(FocusEvent e) {
0508: int i = getSelectionIndex();
0509: if (i >= 0) {
0510: elements[i].redraw();
0511: }
0512: }
0513: });
0514: this .addControlListener(new ControlAdapter() {
0515:
0516: public void controlResized(ControlEvent e) {
0517: computeTopAndBottomTab();
0518: }
0519: });
0520: this .addTraverseListener(new TraverseListener() {
0521:
0522: public void keyTraversed(TraverseEvent e) {
0523: if (e.detail == SWT.TRAVERSE_ARROW_PREVIOUS
0524: || e.detail == SWT.TRAVERSE_ARROW_NEXT) {
0525: int nMax = elements.length - 1;
0526: int nCurrent = getSelectionIndex();
0527: if (e.detail == SWT.TRAVERSE_ARROW_PREVIOUS) {
0528: nCurrent -= 1;
0529: nCurrent = Math.max(0, nCurrent);
0530: } else if (e.detail == SWT.TRAVERSE_ARROW_NEXT) {
0531: nCurrent += 1;
0532: nCurrent = Math.min(nCurrent, nMax);
0533: }
0534: select(nCurrent);
0535: redraw();
0536: } else {
0537: e.doit = true;
0538: }
0539: }
0540: });
0541: }
0542:
0543: /**
0544: * Calculate the number of tabs that will fit in the tab list composite.
0545: */
0546: protected void computeTabsThatFitInComposite() {
0547: tabsThatFitInComposite = Math.round((getSize().y - 22)
0548: / getTabHeight());
0549: if (tabsThatFitInComposite <= 0) {
0550: tabsThatFitInComposite = 1;
0551: }
0552: }
0553:
0554: /**
0555: * Returns the element with the given index from this list viewer. Returns
0556: * <code>null</code> if the index is out of range.
0557: *
0558: * @param index
0559: * the zero-based index
0560: * @return the element at the given index, or <code>null</code> if the
0561: * index is out of range
0562: */
0563: public Object getElementAt(int index) {
0564: if (index >= 0 && index < elements.length) {
0565: return elements[index];
0566: }
0567: return null;
0568: }
0569:
0570: /**
0571: * Returns the zero-relative index of the item which is currently selected
0572: * in the receiver, or -1 if no item is selected.
0573: *
0574: * @return the index of the selected item
0575: */
0576: public int getSelectionIndex() {
0577: return selectedElementIndex;
0578: }
0579:
0580: /**
0581: * Removes all elements from this list.
0582: */
0583: public void removeAll() {
0584: if (elements != null) {
0585: for (int i = 0; i < elements.length; i++) {
0586: elements[i].dispose();
0587: }
0588: }
0589: elements = ELEMENTS_EMPTY;
0590: selectedElementIndex = NONE;
0591: widestLabelIndex = NONE;
0592: topVisibleIndex = NONE;
0593: bottomVisibleIndex = NONE;
0594: }
0595:
0596: /**
0597: * Sets the new list elements.
0598: *
0599: * @param children
0600: */
0601: public void setElements(Object[] children) {
0602: if (elements != ELEMENTS_EMPTY) {
0603: removeAll();
0604: }
0605: elements = new ListElement[children.length];
0606: if (children.length == 0) {
0607: widestLabelIndex = NONE;
0608: } else {
0609: widestLabelIndex = 0;
0610: for (int i = 0; i < children.length; i++) {
0611: elements[i] = new ListElement(this ,
0612: (ITabItem) children[i], i);
0613: elements[i].setVisible(false);
0614: elements[i].setLayoutData(null);
0615:
0616: if (i != widestLabelIndex) {
0617: String label = ((ITabItem) children[i]).getText();
0618: int width = getTextDimension(label).x;
0619: if (((ITabItem) children[i]).getImage() != null) {
0620: width = width + 16 + 5;
0621: }
0622: if (((ITabItem) children[i]).isIndented()) {
0623: width = width + INDENT;
0624: }
0625: if (width > getTextDimension(((ITabItem) children[widestLabelIndex])
0626: .getText()).x) {
0627: widestLabelIndex = i;
0628: }
0629: }
0630: }
0631: }
0632:
0633: computeTopAndBottomTab();
0634: }
0635:
0636: /**
0637: * Selects one of the elements in the list.
0638: *
0639: * @param index
0640: * the index of the element to select.
0641: */
0642: protected void select(int index) {
0643: if (getSelectionIndex() == index) {
0644: /*
0645: * this index is already selected.
0646: */
0647: return;
0648: }
0649: if (index >= 0 && index < elements.length) {
0650: int lastSelected = getSelectionIndex();
0651: elements[index].setSelected(true);
0652: selectedElementIndex = index;
0653: if (lastSelected != NONE) {
0654: elements[lastSelected].setSelected(false);
0655: if (getSelectionIndex() != elements.length - 1) {
0656: /*
0657: * redraw the next tab to fix the border by calling
0658: * setSelected()
0659: */
0660: elements[getSelectionIndex() + 1]
0661: .setSelected(false);
0662: }
0663: }
0664: topNavigationElement.redraw();
0665: bottomNavigationElement.redraw();
0666:
0667: if (selectedElementIndex < topVisibleIndex
0668: || selectedElementIndex > bottomVisibleIndex) {
0669: computeTopAndBottomTab();
0670: }
0671: }
0672: notifyListeners(SWT.Selection, new Event());
0673: }
0674:
0675: /**
0676: * Deselects all the elements in the list.
0677: */
0678: public void deselectAll() {
0679: if (getSelectionIndex() != NONE) {
0680: elements[getSelectionIndex()].setSelected(false);
0681: selectedElementIndex = NONE;
0682: }
0683: }
0684:
0685: private int getIndex(ListElement element) {
0686: return element.index;
0687: }
0688:
0689: public Point computeSize(int wHint, int hHint, boolean changed) {
0690: Point result = super .computeSize(hHint, wHint, changed);
0691: if (widestLabelIndex == -1) {
0692: String properties_not_available = TabbedPropertyMessages.TabbedPropertyList_properties_not_available;
0693: result.x = getTextDimension(properties_not_available).x
0694: + INDENT;
0695: } else {
0696: ITabItem widestTab = elements[widestLabelIndex]
0697: .getTabItem();
0698: int width = getTextDimension(widestTab.getText()).x
0699: + INDENT;
0700: /*
0701: * To anticipate for the icon placement we should always keep the
0702: * space available after the label. So when the active tab includes
0703: * an icon the width of the tab doesn't change.
0704: */
0705: if (widestTab.getImage() != null) {
0706: width = width + 16 + 4;
0707: }
0708: if (widestTab.isIndented()) {
0709: width = width + 10;
0710: }
0711: /*
0712: * Add 10 pixels to the right of the longest string as a margin.
0713: */
0714: result.x = width + 10;
0715: }
0716: return result;
0717: }
0718:
0719: /**
0720: * Get the dimensions of the provided string.
0721: *
0722: * @param text
0723: * the string.
0724: * @return the dimensions of the provided string.
0725: */
0726: private Point getTextDimension(String text) {
0727: Shell shell = new Shell();
0728: GC gc = new GC(shell);
0729: gc.setFont(JFaceResources.getFontRegistry().getBold(
0730: JFaceResources.DEFAULT_FONT));
0731: Point point = gc.textExtent(text);
0732: point.x++;
0733: gc.dispose();
0734: shell.dispose();
0735: return point;
0736: }
0737:
0738: /**
0739: * Initialize the colours used in the list.
0740: */
0741: private void initColours() {
0742: /*
0743: * Colour 3 COLOR_LIST_BACKGROUND
0744: */
0745: listBackground = Display.getCurrent().getSystemColor(
0746: SWT.COLOR_LIST_BACKGROUND);
0747:
0748: /*
0749: * Colour 13 COLOR_WIDGET_BACKGROUND
0750: */
0751: widgetBackground = Display.getCurrent().getSystemColor(
0752: SWT.COLOR_WIDGET_BACKGROUND);
0753:
0754: /*
0755: * Colour 15 COLOR_WIDGET_DARK_SHADOW
0756: */
0757: widgetDarkShadow = Display.getCurrent().getSystemColor(
0758: SWT.COLOR_WIDGET_DARK_SHADOW);
0759:
0760: /*
0761: * Colour 16 COLOR_WIDGET_FOREGROUND
0762: */
0763: widgetForeground = Display.getCurrent().getSystemColor(
0764: SWT.COLOR_WIDGET_FOREGROUND);
0765:
0766: /*
0767: * Colour 19 COLOR_WIDGET_NORMAL_SHADOW
0768: */
0769: widgetNormalShadow = Display.getCurrent().getSystemColor(
0770: SWT.COLOR_WIDGET_NORMAL_SHADOW);
0771:
0772: RGB infoBackground = Display.getCurrent().getSystemColor(
0773: SWT.COLOR_INFO_BACKGROUND).getRGB();
0774: RGB white = Display.getCurrent()
0775: .getSystemColor(SWT.COLOR_WHITE).getRGB();
0776: RGB black = Display.getCurrent()
0777: .getSystemColor(SWT.COLOR_BLACK).getRGB();
0778:
0779: /*
0780: * gradient in the default tab: start colour WIDGET_NORMAL_SHADOW 100% +
0781: * white 20% + INFO_BACKGROUND 60% end colour WIDGET_NORMAL_SHADOW 100% +
0782: * INFO_BACKGROUND 40%
0783: */
0784: defaultGradientStart = factory.getColors().createColor(
0785: "TabbedPropertyList.defaultTabGradientStart", //$NON-NLS-1$
0786: FormColors.blend(infoBackground, FormColors.blend(
0787: white, widgetNormalShadow.getRGB(), 20), 60));
0788: defaultGradientEnd = factory.getColors().createColor(
0789: "TabbedPropertyList.defaultTabGradientEnd", //$NON-NLS-1$
0790: FormColors.blend(infoBackground, widgetNormalShadow
0791: .getRGB(), 40));
0792:
0793: navigationElementShadowStroke = factory.getColors()
0794: .createColor(
0795: "TabbedPropertyList.shadowStroke", //$NON-NLS-1$
0796: FormColors.blend(white, widgetNormalShadow
0797: .getRGB(), 55));
0798: bottomNavigationElementShadowStroke1 = factory.getColors()
0799: .createColor(
0800: "TabbedPropertyList.tabShadowStroke1", //$NON-NLS-1$
0801: FormColors.blend(black, widgetBackground
0802: .getRGB(), 10));
0803: bottomNavigationElementShadowStroke2 = factory.getColors()
0804: .createColor(
0805: "TabbedPropertyList.tabShadowStroke2", //$NON-NLS-1$
0806: FormColors.blend(black, widgetBackground
0807: .getRGB(), 5));
0808:
0809: /*
0810: * gradient in the hover tab: start colour WIDGET_BACKGROUND 100% +
0811: * white 20% end colour WIDGET_BACKGROUND 100% + WIDGET_NORMAL_SHADOW
0812: * 10%
0813: */
0814: hoverGradientStart = factory.getColors().createColor(
0815: "TabbedPropertyList.hoverBackgroundGradientStart", //$NON-NLS-1$
0816: FormColors.blend(white, widgetBackground.getRGB(), 20));
0817: hoverGradientEnd = factory.getColors().createColor(
0818: "TabbedPropertyList.hoverBackgroundGradientEnd", //$NON-NLS-1$
0819: FormColors.blend(widgetNormalShadow.getRGB(),
0820: widgetBackground.getRGB(), 10));
0821:
0822: indentedDefaultBackground = factory.getColors().createColor(
0823: "TabbedPropertyList.indentedDefaultBackground", //$NON-NLS-1$
0824: FormColors.blend(white, widgetBackground.getRGB(), 10));
0825: indentedHoverBackground = factory.getColors().createColor(
0826: "TabbedPropertyList.indentedHoverBackground", //$NON-NLS-1$
0827: FormColors.blend(white, widgetBackground.getRGB(), 75));
0828: }
0829:
0830: public void dispose() {
0831: hoverGradientStart.dispose();
0832: hoverGradientEnd.dispose();
0833: defaultGradientStart.dispose();
0834: defaultGradientEnd.dispose();
0835: indentedDefaultBackground.dispose();
0836: indentedHoverBackground.dispose();
0837: navigationElementShadowStroke.dispose();
0838: bottomNavigationElementShadowStroke1.dispose();
0839: bottomNavigationElementShadowStroke2.dispose();
0840: super .dispose();
0841: }
0842:
0843: /**
0844: * Get the height of a tab. The height of the tab is the height of the text
0845: * plus buffer.
0846: *
0847: * @return the height of a tab.
0848: */
0849: private int getTabHeight() {
0850: int tabHeight = getTextDimension("").y + INDENT; //$NON-NLS-1$
0851: if (tabsThatFitInComposite == 1) {
0852: /*
0853: * if only one tab will fix, reduce the size of the tab height so
0854: * that the navigation elements fit.
0855: */
0856: int ret = getBounds().height - 20;
0857: return (ret > tabHeight) ? tabHeight : (ret < 5) ? 5 : ret;
0858: }
0859: return tabHeight;
0860: }
0861:
0862: /**
0863: * Determine if a downward scrolling is required.
0864: *
0865: * @return true if downward scrolling is required.
0866: */
0867: private boolean isDownScrollRequired() {
0868: return elements.length > tabsThatFitInComposite
0869: && bottomVisibleIndex != elements.length - 1;
0870: }
0871:
0872: /**
0873: * Determine if an upward scrolling is required.
0874: *
0875: * @return true if upward scrolling is required.
0876: */
0877: private boolean isUpScrollRequired() {
0878: return elements.length > tabsThatFitInComposite
0879: && topVisibleIndex != 0;
0880: }
0881:
0882: /**
0883: * Based on available space, figure out the top and bottom tabs in the list.
0884: */
0885: private void computeTopAndBottomTab() {
0886: computeTabsThatFitInComposite();
0887: if (elements.length == 0) {
0888: /*
0889: * no tabs to display.
0890: */
0891: topVisibleIndex = 0;
0892: bottomVisibleIndex = 0;
0893: } else if (tabsThatFitInComposite >= elements.length) {
0894: /*
0895: * all the tabs fit.
0896: */
0897: topVisibleIndex = 0;
0898: bottomVisibleIndex = elements.length - 1;
0899: } else if (getSelectionIndex() == NONE) {
0900: /*
0901: * there is no selected tab yet, assume that tab one would
0902: * be selected for now.
0903: */
0904: topVisibleIndex = 0;
0905: bottomVisibleIndex = tabsThatFitInComposite - 1;
0906: } else if (getSelectionIndex() + tabsThatFitInComposite > elements.length) {
0907: /*
0908: * the selected tab is near the bottom.
0909: */
0910: bottomVisibleIndex = elements.length - 1;
0911: topVisibleIndex = bottomVisibleIndex
0912: - tabsThatFitInComposite + 1;
0913: } else {
0914: /*
0915: * the selected tab is near the top.
0916: */
0917: topVisibleIndex = selectedElementIndex;
0918: bottomVisibleIndex = selectedElementIndex
0919: + tabsThatFitInComposite - 1;
0920: }
0921: layoutTabs();
0922: }
0923:
0924: /**
0925: * Layout the tabs.
0926: *
0927: * @param up
0928: * if <code>true</code>, then we are laying out as a result of
0929: * an scroll up request.
0930: */
0931: private void layoutTabs() {
0932: //System.out.println("TabFit " + tabsThatFitInComposite + " length "
0933: // + elements.length + " top " + topVisibleIndex + " bottom "
0934: // + bottomVisibleIndex);
0935: if (tabsThatFitInComposite == NONE || elements.length == 0) {
0936: FormData formData = new FormData();
0937: formData.left = new FormAttachment(0, 0);
0938: formData.right = new FormAttachment(100, 0);
0939: formData.top = new FormAttachment(0, 0);
0940: formData.height = getTabHeight();
0941: topNavigationElement.setLayoutData(formData);
0942:
0943: formData = new FormData();
0944: formData.left = new FormAttachment(0, 0);
0945: formData.right = new FormAttachment(100, 0);
0946: formData.top = new FormAttachment(topNavigationElement, 0);
0947: formData.bottom = new FormAttachment(100, 0);
0948: bottomNavigationElement.setLayoutData(formData);
0949: } else {
0950:
0951: FormData formData = new FormData();
0952: formData.left = new FormAttachment(0, 0);
0953: formData.right = new FormAttachment(100, 0);
0954: formData.top = new FormAttachment(0, 0);
0955: formData.height = 10;
0956: topNavigationElement.setLayoutData(formData);
0957:
0958: /*
0959: * use nextElement to attach the layout to the previous canvas
0960: * widget in the list.
0961: */
0962: Canvas nextElement = topNavigationElement;
0963:
0964: for (int i = 0; i < elements.length; i++) {
0965: //System.out.print(i + " [" + elements[i].getText() + "]");
0966: if (i < topVisibleIndex || i > bottomVisibleIndex) {
0967: /*
0968: * this tab is not visible
0969: */
0970: elements[i].setLayoutData(null);
0971: elements[i].setVisible(false);
0972: } else {
0973: /*
0974: * this tab is visible.
0975: */
0976: //System.out.print(" visible");
0977: formData = new FormData();
0978: formData.height = getTabHeight();
0979: formData.left = new FormAttachment(0, 0);
0980: formData.right = new FormAttachment(100, 0);
0981: formData.top = new FormAttachment(nextElement, 0);
0982: nextElement = elements[i];
0983: elements[i].setLayoutData(formData);
0984: elements[i].setVisible(true);
0985: }
0986:
0987: //if (i == selectedElementIndex) {
0988: // System.out.print(" selected");
0989: //}
0990: //System.out.println("");
0991: }
0992: formData = new FormData();
0993: formData.left = new FormAttachment(0, 0);
0994: formData.right = new FormAttachment(100, 0);
0995: formData.top = new FormAttachment(nextElement, 0);
0996: formData.bottom = new FormAttachment(100, 0);
0997: formData.height = 10;
0998: bottomNavigationElement.setLayoutData(formData);
0999: }
1000: //System.out.println("");
1001:
1002: // layout so that we have enough space for the new labels
1003: Composite grandparent = getParent().getParent();
1004: grandparent.layout(true);
1005: layout(true);
1006: }
1007:
1008: /**
1009: * Initialize the accessibility adapter.
1010: */
1011: private void initAccessible() {
1012: final Accessible accessible = getAccessible();
1013: accessible.addAccessibleListener(new AccessibleAdapter() {
1014:
1015: public void getName(AccessibleEvent e) {
1016: if (getSelectionIndex() != NONE) {
1017: e.result = elements[getSelectionIndex()]
1018: .getTabItem().getText();
1019: }
1020: }
1021:
1022: public void getHelp(AccessibleEvent e) {
1023: if (getSelectionIndex() != NONE) {
1024: e.result = elements[getSelectionIndex()]
1025: .getTabItem().getText();
1026: }
1027: }
1028: });
1029:
1030: accessible
1031: .addAccessibleControlListener(new AccessibleControlAdapter() {
1032:
1033: public void getChildAtPoint(AccessibleControlEvent e) {
1034: Point pt = toControl(new Point(e.x, e.y));
1035: e.childID = (getBounds().contains(pt)) ? ACC.CHILDID_SELF
1036: : ACC.CHILDID_NONE;
1037: }
1038:
1039: public void getLocation(AccessibleControlEvent e) {
1040: if (getSelectionIndex() != NONE) {
1041: Rectangle location = elements[getSelectionIndex()]
1042: .getBounds();
1043: Point pt = toDisplay(new Point(location.x,
1044: location.y));
1045: e.x = pt.x;
1046: e.y = pt.y;
1047: e.width = location.width;
1048: e.height = location.height;
1049: }
1050: }
1051:
1052: public void getChildCount(AccessibleControlEvent e) {
1053: e.detail = 0;
1054: }
1055:
1056: public void getRole(AccessibleControlEvent e) {
1057: e.detail = ACC.ROLE_TABITEM;
1058: }
1059:
1060: public void getState(AccessibleControlEvent e) {
1061: e.detail = ACC.STATE_NORMAL
1062: | ACC.STATE_SELECTABLE
1063: | ACC.STATE_SELECTED
1064: | ACC.STATE_FOCUSED
1065: | ACC.STATE_FOCUSABLE;
1066: }
1067: });
1068:
1069: addListener(SWT.Selection, new Listener() {
1070:
1071: public void handleEvent(Event event) {
1072: if (isFocusControl()) {
1073: accessible.setFocus(ACC.CHILDID_SELF);
1074: }
1075: }
1076: });
1077:
1078: addListener(SWT.FocusIn, new Listener() {
1079:
1080: public void handleEvent(Event event) {
1081: accessible.setFocus(ACC.CHILDID_SELF);
1082: }
1083: });
1084: }
1085: }
|