package org.xmind.cathy.internal.css; import java.lang.reflect.Field; import java.lang.reflect.Method; import javax.inject.Inject; import org.eclipse.e4.ui.css.swt.dom.CTabFolderElement; import org.eclipse.e4.ui.css.swt.dom.CompositeElement; import org.eclipse.e4.ui.internal.workbench.swt.AbstractPartRenderer; import org.eclipse.e4.ui.model.application.ui.MUIElement; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabFolderRenderer; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; import org.eclipse.swt.graphics.Pattern; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Region; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.ToolBar; import org.xmind.cathy.internal.ICathyConstants; @SuppressWarnings("restriction") public class CathyCTabFolderRendering extends CTabFolderRenderer implements ICTabFolderRendering { private static final String CONTAINS_TOOLBAR = "CathyCTabFolderRendering.containsToolbar"; //$NON-NLS-1$ // Constants for circle drawing final static int LEFT_TOP = 0; final static int LEFT_BOTTOM = 1; final static int RIGHT_TOP = 2; final static int RIGHT_BOTTOM = 3; // drop shadow constants final static int SIDE_DROP_WIDTH = 3; final static int BOTTOM_DROP_WIDTH = 4; // keylines final static int OUTER_KEYLINE = 1; final static int INNER_KEYLINE = 0; final static int TOP_KEYLINE = 0; // Item Constants static final int ITEM_TOP_MARGIN = 2; static final int ITEM_BOTTOM_MARGIN = 6; static final int ITEM_LEFT_MARGIN = 8; static final int ITEM_RIGHT_MARGIN = 4; static final int INTERNAL_SPACING = 4; static final int FLAGS = SWT.DRAW_TRANSPARENT | SWT.DRAW_MNEMONIC; static final String ELLIPSIS = "..."; //$NON-NLS-1$ static final String E4_TOOLBAR_ACTIVE_IMAGE = "org.eclipse.e4.renderer.toolbar_background_active_image"; //$NON-NLS-1$ static final String E4_TOOLBAR_INACTIVE_IMAGE = "org.eclipse.e4.renderer.toolbar_background_inactive_image"; //$NON-NLS-1$ static final int BUTTON_BORDER = SWT.COLOR_WIDGET_DARK_SHADOW; static final int BUTTON_SIZE = 16; static final RGB CLOSE_FILL = new RGB(252, 160, 160); private Color closeFillColor; int[] shape; Image shadowImage, toolbarActiveImage, toolbarInactiveImage; int cornerSize = 14; //The best value boolean shadowEnabled = true; Color shadowColor; Color outerKeyline, innerKeyline; Color[] activeToolbar; int[] activePercents; Color[] inactiveToolbar; int[] inactivePercents; boolean active; Color[] selectedTabFillColors; int[] selectedTabFillPercents; Color[] selectedTabAreaColors; int[] selectedTabAreaPercents; Color[] unselectedTabsColors; int[] unselectedTabsPercents; Color[] hoverTabColors; int[] hoverTabPercents; Color tabOutlineColor; int paddingLeft = 2, paddingRight = 2, paddingTop = 2, paddingBottom = 2; private CTabFolderRendererWrapper rendererWrapper; private CTabFolderWrapper parentWrapper; private boolean textVisible = true; private boolean imageVisible = true; private boolean outerBorderVisible = true; private boolean innerBorderVisible = true; private boolean unselectedTabsBackgroundVisible = true; private Image maxImage; private Image minImage; private Image closeImage; private Image closeHoverImage; private int[] tabArea; //temp private boolean hoverBorderVisible = false; private boolean nothingToRender = false; @Inject public CathyCTabFolderRendering(CTabFolder parent) { super(parent); parentWrapper = new CTabFolderWrapper(parent); rendererWrapper = new CTabFolderRendererWrapper(this); } @Override protected Rectangle computeTrim(int part, int state, int x, int y, int width, int height) { if (!nothingToRender) { boolean onBottom = isTabOnBottom(); int borderTop = onBottom ? INNER_KEYLINE + OUTER_KEYLINE : TOP_KEYLINE + OUTER_KEYLINE; int borderBottom = onBottom ? TOP_KEYLINE + OUTER_KEYLINE : INNER_KEYLINE + OUTER_KEYLINE; int marginWidth = parent.marginWidth; int marginHeight = parent.marginHeight; int sideDropWidth = shadowEnabled ? SIDE_DROP_WIDTH : 0; int bottomDropWidth = shadowEnabled ? BOTTOM_DROP_WIDTH : 0; int headerBorderBottom = outerBorderVisible ? OUTER_KEYLINE : 0; switch (part) { //body trimmed + body client area case PART_BODY: if (state == SWT.FILL) { x = x - paddingLeft - sideDropWidth - (INNER_KEYLINE + OUTER_KEYLINE) - marginWidth; int tabHeight = parent.getTabHeight() + 1; y = onBottom ? y - paddingTop - marginHeight - borderTop - bottomDropWidth : y - paddingTop - marginHeight - tabHeight - borderTop - headerBorderBottom - bottomDropWidth; width = 2 * (INNER_KEYLINE + OUTER_KEYLINE) + paddingLeft + paddingRight + 2 * sideDropWidth + 2 * marginWidth; height += paddingTop + paddingBottom + bottomDropWidth; height += tabHeight + headerBorderBottom + borderBottom + borderTop; } else { x = x - marginWidth - OUTER_KEYLINE - INNER_KEYLINE - sideDropWidth - (cornerSize / 2) - paddingLeft; width = width + 2 * OUTER_KEYLINE + 2 * INNER_KEYLINE + 2 * marginWidth + 2 * sideDropWidth + cornerSize + paddingRight + paddingLeft; int tabHeight = parent.getTabHeight() + 1; if (parent.getMinimized()) { y = onBottom ? y - borderTop - 5 : y - tabHeight - borderTop - 5; height = borderTop + borderBottom + tabHeight; } else { y = onBottom ? y - marginHeight - borderTop - paddingTop - bottomDropWidth : y - marginHeight - tabHeight - borderTop - paddingTop - headerBorderBottom - bottomDropWidth; height = height + borderBottom + borderTop + 2 * marginHeight + tabHeight + headerBorderBottom + bottomDropWidth + paddingTop + paddingBottom; } } break; case PART_HEADER: x = x - (INNER_KEYLINE + OUTER_KEYLINE) - marginWidth - sideDropWidth; width = width + 2 * (INNER_KEYLINE + OUTER_KEYLINE) + 2 * marginWidth + 2 * sideDropWidth; y = y - borderTop - marginHeight; break; case PART_BORDER: x = x - INNER_KEYLINE - OUTER_KEYLINE - sideDropWidth - (cornerSize / 4); width = width + 2 * (INNER_KEYLINE + OUTER_KEYLINE) + 2 * sideDropWidth + cornerSize / 2; height = height + borderTop + borderBottom; y = y - borderTop; if (onBottom) { if (shadowEnabled) { height += 3; } } break; default: if (0 <= part && part < parent.getItemCount()) { x = x - ITEM_LEFT_MARGIN;// - (CORNER_SIZE/2); width = width + ITEM_LEFT_MARGIN + ITEM_RIGHT_MARGIN + 1; y = y - ITEM_TOP_MARGIN; height = height + ITEM_TOP_MARGIN + ITEM_BOTTOM_MARGIN; } break; } } return new Rectangle(x, y, width, height); } @Override protected Point computeSize(int part, int state, GC gc, int wHint, int hHint) { int width = 0, height = 0; switch (part) { case PART_HEADER: int fixedTabHeight = parentWrapper.getFixedTabHeight(); if (fixedTabHeight != SWT.DEFAULT) { //TODO use field variable instead of 1 height = fixedTabHeight == 0 ? 0 : fixedTabHeight + 1; // +1 for line drawn across top of tab } else { CTabItem[] items = parent.getItems(); if (items.length == 0) { height = gc.textExtent("Default", FLAGS).y + ITEM_TOP_MARGIN //$NON-NLS-1$ + ITEM_BOTTOM_MARGIN; } else { for (int i = 0; i < items.length; i++) { height = Math.max(height, computeSize(i, SWT.NONE, gc, wHint, hHint).y); } } height = Math.max(height, parent.getTabHeight() + 1); // gc.dispose(); } break; case PART_MAX_BUTTON: case PART_MIN_BUTTON: case PART_CLOSE_BUTTON: width = height = BUTTON_SIZE; break; case PART_CHEVRON_BUTTON: width = 3 * BUTTON_SIZE / 2; height = BUTTON_SIZE; break; default: if (0 <= part && part < parent.getItemCount()) { gc.setAdvanced(true); CTabItem item = parent.getItem(part); if (item.isDisposed()) return new Point(0, 0); if (imageVisible || shouldDrawImage(item)) { Image image = item.getImage(); if (image != null && !image.isDisposed()) { Rectangle bounds = image.getBounds(); if ((state & SWT.SELECTED) != 0 || parent.getUnselectedImageVisible()) { width += bounds.width; } height = bounds.height; } } if (textVisible) { String text = null; if ((state & MINIMUM_SIZE) != 0) { int minChars = parent.getMinimumCharacters(); text = minChars == 0 ? null : item.getText(); if (text != null && text.length() > minChars) { if (useEllipse()) { int end = minChars < ELLIPSIS.length() + 1 ? minChars : minChars - ELLIPSIS.length(); text = text.substring(0, end); if (minChars > ELLIPSIS.length() + 1) text += ELLIPSIS; } else { int end = minChars; text = text.substring(0, end); } } } else { text = item.getText(); } if (text != null) { if (width > 0) width += INTERNAL_SPACING; if (item.getFont() == null) { Point size = gc.textExtent(text, FLAGS); width += size.x; height = Math.max(height, size.y); } else { Font gcFont = gc.getFont(); gc.setFont(item.getFont()); Point size = gc.textExtent(text, FLAGS); width += size.x; height = Math.max(height, size.y); gc.setFont(gcFont); } } } if (parentWrapper.isShowClose() || item.getShowClose()) { if ((state & SWT.SELECTED) != 0 || parent.getUnselectedCloseVisible()) { if (width > 0) width += INTERNAL_SPACING; width += computeSize(PART_CLOSE_BUTTON, SWT.NONE, gc, SWT.DEFAULT, SWT.DEFAULT).x; } } } break; } Rectangle trim = computeTrim(part, state, 0, 0, width, height); width = trim.width; height = trim.height; return new Point(width, height); } private boolean useEllipse() { return parent.getSimple(); } private Color getCloseFillColor() { if (closeFillColor == null) { closeFillColor = new Color(parent.getDisplay(), CLOSE_FILL); } return closeFillColor; } @Override protected void dispose() { if (shadowImage != null && !shadowImage.isDisposed()) { shadowImage.dispose(); shadowImage = null; } if (closeFillColor != null) { closeFillColor.dispose(); closeFillColor = null; } super.dispose(); } @Override protected void draw(int part, int state, Rectangle bounds, GC gc) { if (nothingToRender) { return; } switch (part) { case PART_BACKGROUND: this.drawCustomBackground(gc, bounds, state); return; case PART_BODY: this.drawTabBody(gc, bounds, state); return; case PART_HEADER: this.drawTabHeader(gc, bounds, state); return; case PART_MAX_BUTTON: if (maxImage != null) { this.drawMaximizeButton(gc, bounds, state); return; } break; case PART_MIN_BUTTON: if (minImage != null) { this.drawMinimizeButton(gc, bounds, state); return; } break; case PART_CLOSE_BUTTON: if (closeImage != null) { this.drawCloseButton(gc, bounds, state); return; } break; default: if (0 <= part && part < parent.getItemCount()) { gc.setAdvanced(true); if (bounds.width == 0 || bounds.height == 0) return; if ((state & SWT.SELECTED) != 0) { drawSelectedTab(part, gc, bounds, state); state &= ~SWT.BACKGROUND; if ((state & SWT.SELECTED) != 0) toDrawTab(true, part, gc, bounds, state); } else { drawUnselectedTab(part, gc, bounds, state); if ((state & SWT.HOT) == 0 && !active) { gc.setAlpha(0x7f); state &= ~SWT.BACKGROUND; toDrawTab(false, part, gc, bounds, state); gc.setAlpha(0xff); } else { state &= ~SWT.BACKGROUND; toDrawTab(false, part, gc, bounds, state); } } return; } } super.draw(part, state, bounds, gc); } private void drawCloseButton(GC gc, Rectangle bounds, int state) { Image hoverImage = closeHoverImage == null ? closeImage : closeHoverImage; switch (state & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND)) { case SWT.NONE: gc.drawImage(closeImage, bounds.x, bounds.y); break; case SWT.HOT: gc.drawImage(hoverImage, bounds.x, bounds.y); break; case SWT.SELECTED: gc.drawImage(hoverImage, bounds.x + 1, bounds.y + 1); break; case SWT.BACKGROUND: break; } } private void drawMinimizeButton(GC gc, Rectangle bounds, int state) { gc.drawImage(minImage, bounds.x, bounds.y); } private void drawMaximizeButton(GC gc, Rectangle bounds, int state) { gc.drawImage(maxImage, bounds.x, bounds.y); } void drawTabHeader(GC gc, Rectangle bounds, int state) { boolean onBottom = parent.getTabPosition() == SWT.BOTTOM; // Fill in background Region clipping = new Region(); gc.getClipping(clipping); Region region = new Region(); region.add(shape); region.intersect(clipping); gc.setClipping(region); int header = shadowEnabled ? onBottom ? 6 : 3 : 1; Rectangle trim = computeTrim(PART_HEADER, state, 0, 0, 0, 0); trim.width = bounds.width - trim.width; trim.height = computeSize(PART_HEADER, state, gc, 0, 0).y; trim.x = -trim.x; trim.y = onBottom ? bounds.height - parent.getTabHeight() - 1 - header : -trim.y; draw(PART_BACKGROUND, SWT.NONE, trim, gc); gc.setClipping(clipping); clipping.dispose(); region.dispose(); if (outerKeyline == null) outerKeyline = gc.getDevice().getSystemColor(SWT.COLOR_BLACK); gc.setForeground(outerKeyline); if (outerBorderVisible) { gc.drawPolyline(tabArea); gc.drawLine(trim.x, trim.y + trim.height, trim.x + trim.width, trim.y + trim.height); } } void generateTabArea(Rectangle bounds) { int[] points = new int[1024]; int index = 0; int radius = cornerSize / 2; int marginWidth = parent.marginWidth; int marginHeight = parent.marginHeight; int delta = (INNER_KEYLINE + OUTER_KEYLINE) * 2 + marginWidth * 2; int width = bounds.width - delta; int height = Math.max( parent.getTabHeight() + INNER_KEYLINE + OUTER_KEYLINE, bounds.height - (INNER_KEYLINE + OUTER_KEYLINE + marginHeight * 2)); int circX = bounds.x + radius + delta / 2; int circY = bounds.y + radius; // Body index = 0; int[] ltt = { bounds.x + delta / 2, bounds.y + parent.getTabHeight() + delta }; System.arraycopy(ltt, 0, points, index, ltt.length); index += ltt.length; int[] lbb = drawCircle(circX, circY + height - (radius * 2), radius, LEFT_BOTTOM); System.arraycopy(lbb, 0, points, index, lbb.length); index += lbb.length; int[] rb = drawCircle(circX + width - (radius * 2), circY + height - (radius * 2), radius, RIGHT_BOTTOM); System.arraycopy(rb, 0, points, index, rb.length); index += rb.length; int[] rt = { bounds.x + delta / 2 + width, bounds.y + parent.getTabHeight() + delta }; System.arraycopy(rt, 0, points, index, rt.length); index += rt.length; // points[index++] = bounds.x + delta / 2; // points[index++] = bounds.y + parent.getTabHeight() + 1; int[] tempPoints = new int[index]; System.arraycopy(points, 0, tempPoints, 0, index); tabArea = tempPoints; } void drawTabBody(GC gc, Rectangle bounds, int state) { generateTabArea(bounds); int[] points = new int[1024]; int index = 0; int radius = cornerSize / 2; int marginWidth = parent.marginWidth; int marginHeight = parent.marginHeight; int delta = (INNER_KEYLINE + OUTER_KEYLINE) * 2 + marginWidth * 2; int width = bounds.width - delta; int height = Math.max( parent.getTabHeight() + INNER_KEYLINE + OUTER_KEYLINE, bounds.height - (INNER_KEYLINE + OUTER_KEYLINE + marginHeight * 2)); int circX = bounds.x + radius + delta / 2; int circY = bounds.y + radius; // Body index = 0; int[] ltt = drawCircle(circX, circY, radius, LEFT_TOP); System.arraycopy(ltt, 0, points, index, ltt.length); index += ltt.length; int[] lbb = drawCircle(circX, circY + height - (radius * 2), radius, LEFT_BOTTOM); System.arraycopy(lbb, 0, points, index, lbb.length); index += lbb.length; int[] rb = drawCircle(circX + width - (radius * 2), circY + height - (radius * 2), radius, RIGHT_BOTTOM); System.arraycopy(rb, 0, points, index, rb.length); index += rb.length; int[] rt = drawCircle(circX + width - (radius * 2), circY, radius, RIGHT_TOP); System.arraycopy(rt, 0, points, index, rt.length); index += rt.length; points[index++] = circX; points[index++] = circY - radius; int[] tempPoints = new int[index]; System.arraycopy(points, 0, tempPoints, 0, index); // Fill in parent background for non-rectangular shape Region r = new Region(); r.add(bounds); r.subtract(tempPoints); gc.setBackground(parent.getParent().getBackground()); Display display = parent.getDisplay(); Region clipping = new Region(); gc.getClipping(clipping); r.intersect(clipping); gc.setClipping(r); Rectangle mappedBounds = display.map(parent, parent.getParent(), bounds); parent.getParent().drawBackground(gc, bounds.x, bounds.y, bounds.width, bounds.height, mappedBounds.x, mappedBounds.y); // Shadow if (shadowEnabled) drawShadow(display, bounds, gc); gc.setClipping(clipping); clipping.dispose(); r.dispose(); // Remember for use in header drawing shape = tempPoints; } /* * Draw active and unactive selected tab item */ void drawSelectedTab(int itemIndex, GC gc, Rectangle bounds, int state) { if (parent.getSingle() && parent.getItem(itemIndex).isShowing()) return; boolean onBottom = parent.getTabPosition() == SWT.BOTTOM; int header = shadowEnabled ? 2 : 0; int width = bounds.width; int[] points = new int[1024]; int index = 0; int radius = cornerSize / 2; int circX = bounds.x + radius; int circY = onBottom ? bounds.y + bounds.height - header - radius : bounds.y + radius; int selectionX1, selectionY1, selectionX2, selectionY2; int bottomY = onBottom ? bounds.y - header : bounds.y + bounds.height; if (itemIndex == 0 && bounds.x == -computeTrim(CTabFolderRenderer.PART_HEADER, SWT.NONE, 0, 0, 0, 0).x) { // circX -= 1; // points[index++] = circX - radius; // points[index++] = bottomY; points[index++] = selectionX1 = circX - radius; points[index++] = selectionY1 = bottomY; } else { if (active) { points[index++] = shadowEnabled ? SIDE_DROP_WIDTH : 0 + INNER_KEYLINE + OUTER_KEYLINE; points[index++] = bottomY; } points[index++] = selectionX1 = bounds.x; points[index++] = selectionY1 = bottomY; } int startX = -1, endX = -1; if (!onBottom) { int[] ltt = drawCircle(circX, circY, radius, LEFT_TOP); startX = ltt[6]; for (int i = 0; i < ltt.length / 2; i += 2) { int tmp = ltt[i]; ltt[i] = ltt[ltt.length - i - 2]; ltt[ltt.length - i - 2] = tmp; tmp = ltt[i + 1]; ltt[i + 1] = ltt[ltt.length - i - 1]; ltt[ltt.length - i - 1] = tmp; } System.arraycopy(ltt, 0, points, index, ltt.length); index += ltt.length; int[] rt = drawCircle(circX + width - (radius * 2), circY, radius, RIGHT_TOP); endX = rt[rt.length - 4]; for (int i = 0; i < rt.length / 2; i += 2) { int tmp = rt[i]; rt[i] = rt[rt.length - i - 2]; rt[rt.length - i - 2] = tmp; tmp = rt[i + 1]; rt[i + 1] = rt[rt.length - i - 1]; rt[rt.length - i - 1] = tmp; } System.arraycopy(rt, 0, points, index, rt.length); index += rt.length; points[index++] = selectionX2 = bounds.width + circX - radius; points[index++] = selectionY2 = bottomY; } else { int[] ltt = drawCircle(circX, circY, radius, LEFT_BOTTOM); startX = ltt[6]; System.arraycopy(ltt, 0, points, index, ltt.length); index += ltt.length; int[] rt = drawCircle(circX + width - (radius * 2), circY, radius, RIGHT_BOTTOM); endX = rt[rt.length - 4]; System.arraycopy(rt, 0, points, index, rt.length); index += rt.length; points[index++] = selectionX2 = bounds.width + circX - radius; points[index++] = selectionY2 = bottomY; } if (active) { points[index++] = parent.getSize().x - (shadowEnabled ? SIDE_DROP_WIDTH : 0 + INNER_KEYLINE + OUTER_KEYLINE); points[index++] = bottomY; } gc.setClipping(bounds.x, onBottom ? bounds.y - header : bounds.y, parent.getSize().x - (shadowEnabled ? SIDE_DROP_WIDTH : 0 + INNER_KEYLINE + OUTER_KEYLINE), bounds.y + bounds.height); Pattern backgroundPattern = null; if (selectedTabFillColors == null) { setSelectedTabFill(gc.getDevice().getSystemColor(SWT.COLOR_WHITE)); } if (selectedTabFillColors.length == 1) { gc.setBackground(selectedTabFillColors[0]); gc.setForeground(selectedTabFillColors[0]); } else if (!onBottom && selectedTabFillColors.length == 2) { // for now we support the 2-colors gradient for selected tab backgroundPattern = new Pattern(gc.getDevice(), 0, 0, 0, bounds.height + 1, selectedTabFillColors[0], selectedTabFillColors[1]); gc.setBackgroundPattern(backgroundPattern); gc.setForeground(selectedTabFillColors[1]); } int[] tmpPoints = new int[index]; System.arraycopy(points, 0, tmpPoints, 0, index); gc.fillPolygon(tmpPoints); //cover item bottom border using background color gc.drawLine(selectionX1, selectionY1, selectionX2, selectionY2); gc.setClipping(bounds.x - 1, onBottom ? bounds.y - header : bounds.y - 1, parent.getSize().x - (shadowEnabled ? SIDE_DROP_WIDTH : 0 + INNER_KEYLINE + OUTER_KEYLINE), bounds.y + bounds.height); if (innerBorderVisible) { if (innerKeyline == null) innerKeyline = gc.getDevice().getSystemColor(SWT.COLOR_BLACK); gc.setForeground(innerKeyline); gc.drawPolyline(tmpPoints); } Rectangle rect = null; gc.setClipping(rect); if (outerBorderVisible) { if (!onBottom) { if (outerKeyline == null) outerKeyline = gc.getDevice() .getSystemColor(SWT.COLOR_BLACK); gc.setForeground(outerKeyline); gc.drawLine(startX + 2, 1, endX - 1, 1); } } if (backgroundPattern != null) { backgroundPattern.dispose(); } } private boolean isTabOnBottom() { return parent.getTabPosition() == SWT.BOTTOM; } private String getShortenedText(GC gc, String text, int width) { return useEllipse() ? getShortenedText(gc, text, width, ELLIPSIS) : getShortenedText(gc, text, width, ""); //$NON-NLS-1$ } private String getShortenedText(GC gc, String text, int width, String ellipses) { if (gc.textExtent(text, FLAGS).x <= width) return text; int ellipseWidth = gc.textExtent(ellipses, FLAGS).x; int length = text.length(); TextLayout layout = new TextLayout(parent.getDisplay()); layout.setText(text); int end = layout.getPreviousOffset(length, SWT.MOVEMENT_CLUSTER); while (end > 0) { text = text.substring(0, end); int l = gc.textExtent(text, FLAGS).x; if (l + ellipseWidth <= width) { break; } end = layout.getPreviousOffset(end, SWT.MOVEMENT_CLUSTER); } layout.dispose(); return end == 0 ? text.substring(0, 1) : text + ellipses; } private void toDrawTab(boolean selected, int itemIndex, GC gc, Rectangle bounds, int state) { CTabItem item = parent.getItem(itemIndex); int x = bounds.x; int y = bounds.y; int height = bounds.height; int width = bounds.width; int rightEdge = Math.min(x + width, parentWrapper.getRightItemEdge(gc)); if ((state & SWT.FOREGROUND) != 0) { CTabItemWrapper itemWrapper = new CTabItemWrapper(item); Rectangle itemCloseRect = itemWrapper.getCloseRect(); itemCloseRect = new Rectangle(itemCloseRect.x, itemCloseRect.y, itemCloseRect.width, itemCloseRect.height); // draw Image Rectangle trim = computeTrim(itemIndex, SWT.NONE, 0, 0, 0, 0); int xDraw = x - trim.x; if (parent.getSingle() && (parentWrapper.isShowClose() || item.getShowClose())) xDraw += itemWrapper.getCloseRect().width; if (imageVisible || shouldDrawImage(item)) { Image image = item.getImage(); if (image != null && !image.isDisposed()) { Rectangle imageBounds = image.getBounds(); // only draw image if it won't overlap with close button int maxImageWidth = rightEdge - xDraw - (trim.width + trim.x); if (!parent.getSingle() && itemCloseRect.width > 0) maxImageWidth -= itemCloseRect.width + INTERNAL_SPACING; if (imageBounds.width < maxImageWidth) { int imageX = xDraw; int imageY = y + (height - imageBounds.height) / 2; imageY += isTabOnBottom() ? -1 : 1; gc.drawImage(image, imageX, imageY); xDraw += imageBounds.width + INTERNAL_SPACING; } } } if (textVisible) { // draw Text int textWidth = rightEdge - xDraw - (trim.width + trim.x); if (!parent.getSingle() && itemCloseRect.width > 0) textWidth -= itemCloseRect.width + INTERNAL_SPACING; if (textWidth > 0) { Font gcFont = gc.getFont(); gc.setFont(item.getFont() == null ? parent.getFont() : item.getFont()); if (itemWrapper.getShortenedText() == null || itemWrapper .getShortenedTextWidth() != textWidth) { itemWrapper.setShortenedText(getShortenedText(gc, item.getText(), textWidth)); itemWrapper.setShortenedTextWidth(textWidth); } Point extent = gc.textExtent(itemWrapper.getShortenedText(), FLAGS); int textY = y + (height - extent.y) / 2; textY += isTabOnBottom() ? -1 : 1; gc.setForeground(selected ? parent.getSelectionForeground() : parent.getForeground()); gc.drawText(itemWrapper.getShortenedText(), xDraw, textY, FLAGS); gc.setFont(gcFont); if (selected) { // draw a Focus rectangle if (parent.isFocusControl()) { Display display = parent.getDisplay(); if (parent.getSimple() || parent.getSingle()) { gc.setBackground(display .getSystemColor(SWT.COLOR_BLACK)); gc.setForeground(display .getSystemColor(SWT.COLOR_WHITE)); gc.drawFocus(xDraw - 1, textY - 1, extent.x + 2, extent.y + 2); } else { gc.setForeground( display.getSystemColor(BUTTON_BORDER)); gc.drawLine(xDraw, textY + extent.y + 1, xDraw + extent.x + 1, textY + extent.y + 1); } } } } } if (parentWrapper.isShowClose() || item.getShowClose()) { if (closeImage != null) { drawCloseButton(gc, itemCloseRect, itemWrapper.getCloseImageState()); } else { drawClose2(gc, itemCloseRect, itemWrapper.getCloseImageState()); } } } } void drawClose2(GC gc, Rectangle closeRect, int closeImageState) { if (closeRect.width == 0 || closeRect.height == 0) return; Display display = parent.getDisplay(); // draw X 9x9 int x = closeRect.x + Math.max(1, (closeRect.width - 9) / 2); int y = closeRect.y + Math.max(1, (closeRect.height - 9) / 2); Color closeBorder = display.getSystemColor(BUTTON_BORDER); int[] fillShape = new int[] { x + 1, y + 1, x + 3, y + 1, x + 5, y + 3, x + 6, y + 3, x + 8, y + 1, x + 10, y + 1, x + 10, y + 3, x + 8, y + 5, x + 8, y + 6, x + 10, y + 8, x + 10, y + 10, x + 8, y + 10, x + 6, y + 8, x + 5, y + 8, x + 3, y + 10, x + 1, y + 10, x + 1, y + 8, x + 3, y + 6, x + 3, y + 5, x + 1, y + 3 }; int[] drawShape = new int[] { x, y, x + 2, y, x + 4, y + 2, x + 5, y + 2, x + 7, y, x + 9, y, x + 9, y + 2, x + 7, y + 4, x + 7, y + 5, x + 9, y + 7, x + 9, y + 9, x + 7, y + 9, x + 5, y + 7, x + 4, y + 7, x + 2, y + 9, x, y + 9, x, y + 7, x + 2, y + 5, x + 2, y + 4, x, y + 2 }; switch (closeImageState & (SWT.HOT | SWT.SELECTED | SWT.BACKGROUND)) { case SWT.NONE: { gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE)); gc.fillPolygon(fillShape); gc.setForeground(closeBorder); gc.drawPolygon(drawShape); break; } case SWT.HOT: { gc.setBackground(getCloseFillColor()); gc.fillPolygon(fillShape); gc.setForeground(closeBorder); gc.drawPolygon(drawShape); break; } case SWT.SELECTED: { gc.setBackground(getCloseFillColor()); gc.fillPolygon(fillShape); gc.setForeground(closeBorder); gc.drawPolygon(drawShape); break; } case SWT.BACKGROUND: { rendererWrapper.drawClose(gc, closeRect, closeImageState); break; } } } private boolean shouldDrawImage(CTabItem item) { Object model = item.getData(AbstractPartRenderer.OWNING_ME); if (model != null && model instanceof MUIElement) { MUIElement element = (MUIElement) model; if (element.getTags().contains(ICathyConstants.TAG_SHOW_IMAGE)) { return true; } } return false; } void drawUnselectedTab(int itemIndex, GC gc, Rectangle bounds, int state) { if ((state & SWT.HOT) != 0) { int header = shadowEnabled ? 2 : 0; int width = bounds.width; boolean onBottom = parent.getTabPosition() == SWT.BOTTOM; int[] points = new int[1024]; int[] inactive = new int[8]; int index = 0, inactive_index = 0; int radius = cornerSize / 2; int circX = bounds.x + radius; int circY = onBottom ? bounds.y + bounds.height + 1 - header - radius : bounds.y - 1 + radius; int bottomY = onBottom ? bounds.y - header : bounds.y + bounds.height; int leftIndex = circX; if (itemIndex == 0) { // if (parent.getSelectionIndex() != 0) // leftIndex -= 1; points[index++] = leftIndex - radius; points[index++] = bottomY; } else { points[index++] = bounds.x; points[index++] = bottomY; } if (!active) { System.arraycopy(points, 0, inactive, 0, index); inactive_index += 2; } int rightIndex = circX - 1; int startX = -1, endX = -1; if (!onBottom) { int[] ltt = drawCircle(leftIndex, circY, radius, LEFT_TOP); startX = ltt[6]; for (int i = 0; i < ltt.length / 2; i += 2) { int tmp = ltt[i]; ltt[i] = ltt[ltt.length - i - 2]; ltt[ltt.length - i - 2] = tmp; tmp = ltt[i + 1]; ltt[i + 1] = ltt[ltt.length - i - 1]; ltt[ltt.length - i - 1] = tmp; } System.arraycopy(ltt, 0, points, index, ltt.length); index += ltt.length; if (!active) { System.arraycopy(ltt, 0, inactive, inactive_index, 2); inactive_index += 2; } int[] rt = drawCircle(rightIndex + width - (radius * 2), circY, radius, RIGHT_TOP); endX = rt[rt.length - 4]; for (int i = 0; i < rt.length / 2; i += 2) { int tmp = rt[i]; rt[i] = rt[rt.length - i - 2]; rt[rt.length - i - 2] = tmp; tmp = rt[i + 1]; rt[i + 1] = rt[rt.length - i - 1]; rt[rt.length - i - 1] = tmp; } System.arraycopy(rt, 0, points, index, rt.length); index += rt.length; if (!active) { System.arraycopy(rt, rt.length - 4, inactive, inactive_index, 2); inactive[inactive_index] -= 1; inactive_index += 2; } } else { int[] ltt = drawCircle(leftIndex, circY, radius, LEFT_BOTTOM); startX = ltt[6]; System.arraycopy(ltt, 0, points, index, ltt.length); index += ltt.length; if (!active) { System.arraycopy(ltt, 0, inactive, inactive_index, 2); inactive_index += 2; } int[] rt = drawCircle(rightIndex + width - (radius * 2), circY, radius, RIGHT_BOTTOM); endX = rt[rt.length - 4]; System.arraycopy(rt, 0, points, index, rt.length); index += rt.length; if (!active) { System.arraycopy(rt, rt.length - 4, inactive, inactive_index, 2); inactive[inactive_index] -= 1; inactive_index += 2; } } points[index++] = bounds.width + rightIndex - radius; points[index++] = bottomY; if (!active) { System.arraycopy(points, index - 2, inactive, inactive_index, 2); inactive[inactive_index] -= 1; inactive_index += 2; } gc.setClipping(points[0], onBottom ? bounds.y - header : bounds.y - 1, parent.getSize().x - (shadowEnabled ? SIDE_DROP_WIDTH : 0 + INNER_KEYLINE + OUTER_KEYLINE), bounds.y + bounds.height); if (hoverTabColors == null) { hoverTabColors = new Color[] { gc.getDevice().getSystemColor(SWT.COLOR_WHITE) }; hoverTabPercents = new int[] { 100 }; } gc.setBackground(hoverTabColors[0]); int[] tmpPoints = new int[index]; System.arraycopy(points, 0, tmpPoints, 0, index); gc.fillPolygon(tmpPoints); Color tempBorder = new Color(gc.getDevice(), 182, 188, 204); gc.setForeground(tempBorder); tempBorder.dispose(); if (hoverBorderVisible) { if (outerBorderVisible) { if (outerKeyline == null) outerKeyline = gc.getDevice() .getSystemColor(SWT.COLOR_BLACK); gc.setForeground(outerKeyline); if (active) { gc.drawLine(startX, 1, endX, 1); } else { gc.drawLine(inactive[0], inactive[1], inactive[2], inactive[3]); gc.drawLine(inactive[4], inactive[5], inactive[6], inactive[7]); } } if (innerBorderVisible) { if (innerKeyline == null) innerKeyline = gc.getDevice() .getSystemColor(SWT.COLOR_BLACK); gc.setForeground(innerKeyline); gc.drawPolyline(tmpPoints); } } Rectangle rect = null; gc.setClipping(rect); // gc.setForeground(outerKeyline); // gc.drawPolyline(shape); } } static int[] drawCircle(int xC, int yC, int r, int circlePart) { int x = 0, y = r, u = 1, v = 2 * r - 1, e = 0; int[] points = new int[1024]; int[] pointsMirror = new int[1024]; int loop = 0; int loopMirror = 0; if (r == 0) { for (int i = 0; i < 4; i++) { points[loop++] = xC; points[loop++] = yC; } } while (x < y) { if (circlePart == RIGHT_BOTTOM) { points[loop++] = xC + x; points[loop++] = yC + y; } if (circlePart == RIGHT_TOP) { points[loop++] = xC + y; points[loop++] = yC - x; } if (circlePart == LEFT_TOP) { points[loop++] = xC - x; points[loop++] = yC - y; } if (circlePart == LEFT_BOTTOM) { points[loop++] = xC - y; points[loop++] = yC + x; } x++; e += u; u += 2; if (v < 2 * e) { y--; e -= v; v -= 2; } if (x > y) break; if (circlePart == RIGHT_BOTTOM) { pointsMirror[loopMirror++] = xC + y; pointsMirror[loopMirror++] = yC + x; } if (circlePart == RIGHT_TOP) { pointsMirror[loopMirror++] = xC + x; pointsMirror[loopMirror++] = yC - y; } if (circlePart == LEFT_TOP) { pointsMirror[loopMirror++] = xC - y; pointsMirror[loopMirror++] = yC - x; } if (circlePart == LEFT_BOTTOM) { pointsMirror[loopMirror++] = xC - x; pointsMirror[loopMirror++] = yC + y; } // grow? if ((loop + 1) > points.length) { int length = points.length * 2; int[] newPointTable = new int[length]; int[] newPointTableMirror = new int[length]; System.arraycopy(points, 0, newPointTable, 0, points.length); points = newPointTable; System.arraycopy(pointsMirror, 0, newPointTableMirror, 0, pointsMirror.length); pointsMirror = newPointTableMirror; } } int[] finalArray = new int[loop + loopMirror]; System.arraycopy(points, 0, finalArray, 0, loop); for (int i = loopMirror - 1, j = loop; i > 0; i = i - 2, j = j + 2) { int tempY = pointsMirror[i]; int tempX = pointsMirror[i - 1]; finalArray[j] = tempX; finalArray[j + 1] = tempY; } return finalArray; } static RGB blend(RGB c1, RGB c2, int ratio) { int r = blend(c1.red, c2.red, ratio); int g = blend(c1.green, c2.green, ratio); int b = blend(c1.blue, c2.blue, ratio); return new RGB(r, g, b); } static int blend(int v1, int v2, int ratio) { int b = (ratio * v1 + (100 - ratio) * v2) / 100; return Math.min(255, b); } void drawShadow(final Display display, Rectangle bounds, GC gc) { if (shadowImage == null) { createShadow(display); } int x = bounds.x; int y = bounds.y; int SIZE = shadowImage.getBounds().width / 3; int height = Math.max(bounds.height, SIZE * 2); int width = Math.max(bounds.width, SIZE * 2); // top left gc.drawImage(shadowImage, 0, 0, SIZE, SIZE, 2, 10, SIZE, 20); int fillHeight = height - SIZE * 2; int fillWidth = width + 5 - SIZE * 2; int xFill = 0; for (int i = SIZE; i < fillHeight; i += SIZE) { xFill = i; gc.drawImage(shadowImage, 0, SIZE, SIZE, SIZE, 2, i, SIZE, SIZE); } // Pad the rest of the shadow gc.drawImage(shadowImage, 0, SIZE, SIZE, fillHeight - xFill, 2, xFill + SIZE, SIZE, fillHeight - xFill); // bl gc.drawImage(shadowImage, 0, 40, 20, 20, 2, y + height - SIZE, 20, 20); int yFill = 0; for (int i = SIZE; i <= fillWidth; i += SIZE) { yFill = i; gc.drawImage(shadowImage, SIZE, SIZE * 2, SIZE, SIZE, i, y + height - SIZE, SIZE, SIZE); } // Pad the rest of the shadow gc.drawImage(shadowImage, SIZE, SIZE * 2, fillWidth - yFill, SIZE, yFill + SIZE, y + height - SIZE, fillWidth - yFill, SIZE); // br gc.drawImage(shadowImage, SIZE * 2, SIZE * 2, SIZE, SIZE, x + width - SIZE - 1, y + height - SIZE, SIZE, SIZE); // tr gc.drawImage(shadowImage, (SIZE * 2), 0, SIZE, SIZE, x + width - SIZE - 1, 10, SIZE, SIZE); xFill = 0; for (int i = SIZE; i < fillHeight; i += SIZE) { xFill = i; gc.drawImage(shadowImage, SIZE * 2, SIZE, SIZE, SIZE, x + width - SIZE - 1, i, SIZE, SIZE); } // Pad the rest of the shadow gc.drawImage(shadowImage, SIZE * 2, SIZE, SIZE, fillHeight - xFill, x + width - SIZE - 1, xFill + SIZE, SIZE, fillHeight - xFill); } void createShadow(final Display display) { if (shadowImage != null) { shadowImage.dispose(); shadowImage = null; } ImageData data = new ImageData(60, 60, 32, new PaletteData(0xFF0000, 0xFF00, 0xFF)); Image tmpImage = shadowImage = new Image(display, data); GC gc = new GC(tmpImage); if (shadowColor == null) shadowColor = gc.getDevice().getSystemColor(SWT.COLOR_GRAY); gc.setBackground(shadowColor); drawTabBody(gc, new Rectangle(0, 0, 60, 60), SWT.None); ImageData blured = blur(tmpImage, 5, 25); shadowImage = new Image(display, blured); tmpImage.dispose(); } public ImageData blur(Image src, int radius, int sigma) { float[] kernel = create1DKernel(radius, sigma); ImageData imgPixels = src.getImageData(); int width = imgPixels.width; int height = imgPixels.height; int[] inPixels = new int[width * height]; int[] outPixels = new int[width * height]; int offset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { RGB rgb = imgPixels.palette.getRGB(imgPixels.getPixel(x, y)); if (rgb.red == 255 && rgb.green == 255 && rgb.blue == 255) { inPixels[offset] = (rgb.red << 16) | (rgb.green << 8) | rgb.blue; } else { inPixels[offset] = (imgPixels.getAlpha(x, y) << 24) | (rgb.red << 16) | (rgb.green << 8) | rgb.blue; } offset++; } } convolve(kernel, inPixels, outPixels, width, height, true); convolve(kernel, outPixels, inPixels, height, width, true); ImageData dst = new ImageData(imgPixels.width, imgPixels.height, 24, new PaletteData(0xff0000, 0xff00, 0xff)); dst.setPixels(0, 0, inPixels.length, inPixels, 0); offset = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (inPixels[offset] == -1) { dst.setAlpha(x, y, 0); } else { int a = (inPixels[offset] >> 24) & 0xff; // if (a < 150) a = 0; dst.setAlpha(x, y, a); } offset++; } } return dst; } private void convolve(float[] kernel, int[] inPixels, int[] outPixels, int width, int height, boolean alpha) { int kernelWidth = kernel.length; int kernelMid = kernelWidth / 2; for (int y = 0; y < height; y++) { int index = y; int currentLine = y * width; for (int x = 0; x < width; x++) { // do point float a = 0, r = 0, g = 0, b = 0; for (int k = -kernelMid; k <= kernelMid; k++) { float val = kernel[k + kernelMid]; int xcoord = x + k; if (xcoord < 0) xcoord = 0; if (xcoord >= width) xcoord = width - 1; int pixel = inPixels[currentLine + xcoord]; // float alp = ((pixel >> 24) & 0xff); a += val * ((pixel >> 24) & 0xff); r += val * (((pixel >> 16) & 0xff)); g += val * (((pixel >> 8) & 0xff)); b += val * (((pixel) & 0xff)); } int ia = alpha ? clamp((int) (a + 0.5)) : 0xff; int ir = clamp((int) (r + 0.5)); int ig = clamp((int) (g + 0.5)); int ib = clamp((int) (b + 0.5)); outPixels[index] = (ia << 24) | (ir << 16) | (ig << 8) | ib; index += height; } } } private int clamp(int value) { if (value > 255) return 255; if (value < 0) return 0; return value; } private float[] create1DKernel(int radius, int sigma) { // guideline: 3*sigma should be the radius int size = radius * 2 + 1; float[] kernel = new float[size]; int radiusSquare = radius * radius; float sigmaSquare = 2 * sigma * sigma; float piSigma = 2 * (float) Math.PI * sigma; float sqrtSigmaPi2 = (float) Math.sqrt(piSigma); int start = size / 2; int index = 0; float total = 0; for (int i = -start; i <= start; i++) { float d = i * i; if (d > radiusSquare) { kernel[index] = 0; } else { kernel[index] = (float) Math.exp(-(d) / sigmaSquare) / sqrtSigmaPi2; } total += kernel[index]; index++; } for (int i = 0; i < size; i++) { kernel[i] /= total; } return kernel; } public Rectangle getPadding() { return new Rectangle(paddingTop, paddingRight, paddingBottom, paddingLeft); } public void setPadding(int paddingLeft, int paddingRight, int paddingTop, int paddingBottom) { this.paddingLeft = paddingLeft; this.paddingRight = paddingRight; this.paddingTop = paddingTop; this.paddingBottom = paddingBottom; parent.redraw(); } public void setCornerRadius(int radius) { cornerSize = radius; parent.redraw(); } public void setShadowVisible(boolean visible) { this.shadowEnabled = visible; parent.redraw(); } public void setShadowColor(Color color) { this.shadowColor = color; createShadow(parent.getDisplay()); parent.redraw(); } public void setOuterKeyline(Color color) { this.outerKeyline = color; // TODO: HACK! Should be set based on pseudo-state. if (color != null) { setActive(!(color.getRed() == 255 && color.getGreen() == 255 && color.getBlue() == 255)); } parent.redraw(); } public void setSelectedTabFill(Color color) { setSelectedTabFill(new Color[] { color }, new int[] { 100 }); } public void setSelectedTabFill(Color[] colors, int[] percents) { selectedTabFillColors = colors; selectedTabFillPercents = percents; parent.redraw(); } public void setSelectedTabAreaColor(Color color) { setSelectedTabAreaColor(new Color[] { color }, new int[] { 100 }); } public void setSelectedTabAreaColor(Color[] colors, int[] percents) { selectedTabAreaColors = colors; selectedTabAreaPercents = percents; parent.redraw(); } public void setUnselectedTabsColor(Color color) { setUnselectedTabsColor(new Color[] { color }, new int[] { 100 }); } public void setUnselectedTabsColor(Color[] colors, int[] percents) { unselectedTabsColors = colors; unselectedTabsPercents = percents; parent.redraw(); } public void setUnselectedTabsBackgroundVisible(boolean visible) { unselectedTabsBackgroundVisible = visible; parent.redraw(); } public void setUnselectedHotTabsColorBackground(Color color) { setHoverTabColor(new Color[] { color }, new int[] { 100 }); } public void setHoverTabColor(Color color) { setHoverTabColor(new Color[] { color }, new int[] { 100 }); } public void setHoverTabColor(Color[] colors, int[] percents) { hoverTabColors = colors; hoverTabPercents = percents; parent.redraw(); } public void setTabOutline(Color color) { this.tabOutlineColor = color; parent.redraw(); } public void setInnerKeyline(Color color) { this.innerKeyline = color; parent.redraw(); } public void setTextVisible(boolean visible) { this.textVisible = visible; parent.redraw(); } public void setImageVisible(boolean visible) { this.imageVisible = visible; parent.redraw(); } public void setOuterBorderVisible(boolean visible) { this.outerBorderVisible = visible; parent.redraw(); } public void setInnerBorderVisible(boolean visible) { this.innerBorderVisible = visible; parent.redraw(); } public void setActiveToolbarGradient(Color[] color, int[] percents) { activeToolbar = color; activePercents = percents; } public void setInactiveToolbarGradient(Color[] color, int[] percents) { inactiveToolbar = color; inactivePercents = percents; } public void setActive(boolean active) { this.active = active; } public void setMaximizeImage(Image maxImage) { this.maxImage = maxImage; } public void setMinimizeImage(Image minImage) { this.minImage = minImage; } public void setCloseImage(Image closeImage) { this.closeImage = closeImage; } public void setClsoeHoverImage(Image closeHoverImage) { this.closeHoverImage = closeHoverImage; } public void setNoneRender(boolean nothingToRender) { this.nothingToRender = nothingToRender; } private void drawCustomBackground(GC gc, Rectangle bounds, int state) { boolean selected = (state & SWT.SELECTED) != 0; Color defaultBackground = selected ? parent.getSelectionBackground() : parent.getBackground(); boolean vertical = selected ? parentWrapper.isSelectionGradientVertical() : parentWrapper.isGradientVertical(); Rectangle partHeaderBounds = bounds; if (unselectedTabsBackgroundVisible) { drawUnselectedTabBackground(gc, partHeaderBounds, state, vertical, defaultBackground); } drawTabAreaBackground(gc, partHeaderBounds, state, vertical, defaultBackground); int borderTop = isTabOnBottom() ? INNER_KEYLINE + OUTER_KEYLINE : TOP_KEYLINE + OUTER_KEYLINE; int borderBottom = isTabOnBottom() ? TOP_KEYLINE + OUTER_KEYLINE : INNER_KEYLINE + OUTER_KEYLINE; int bottomDropWidth = shadowEnabled ? BOTTOM_DROP_WIDTH : 0; int sideDropWidth = shadowEnabled ? SIDE_DROP_WIDTH : 0; int headerBorderBottom = outerBorderVisible ? OUTER_KEYLINE : 0; Rectangle underTabAreaBounds = new Rectangle( partHeaderBounds.x + paddingLeft + sideDropWidth, partHeaderBounds.y + partHeaderBounds.height + bottomDropWidth + paddingTop + headerBorderBottom, bounds.width - paddingLeft - paddingRight - 2 * sideDropWidth, parent.getBounds().height - partHeaderBounds.height - paddingTop - paddingBottom - headerBorderBottom - ((cornerSize / 4) + borderBottom + borderTop) - bottomDropWidth * 2); drawUnderTabAreaBackground(gc, underTabAreaBounds, state, vertical, defaultBackground); drawChildrenBackground(partHeaderBounds); } private void drawUnderTabAreaBackground(GC gc, Rectangle tabAreaBounds, int state, boolean vertical, Color defaultBackground) { Color[] underTabAreaColors = new Color[] { gc.getDevice().getSystemColor(SWT.COLOR_WHITE) }; int[] underTabAreaPercents = new int[] { 100 }; rendererWrapper.drawBackground(gc, tabAreaBounds.x, tabAreaBounds.y, tabAreaBounds.width, tabAreaBounds.height, defaultBackground, underTabAreaColors, underTabAreaPercents, vertical); } private void drawUnselectedTabBackground(GC gc, Rectangle partHeaderBounds, int state, boolean vertical, Color defaultBackground) { if (unselectedTabsColors == null) { boolean selected = (state & SWT.SELECTED) != 0; unselectedTabsColors = selected ? parentWrapper.getSelectionGradientColors() : parentWrapper.getGradientColors(); unselectedTabsPercents = selected ? parentWrapper.getSelectionGradientPercents() : parentWrapper.getGradientPercents(); } if (unselectedTabsColors == null) { unselectedTabsColors = new Color[] { gc.getDevice().getSystemColor(SWT.COLOR_WHITE) }; unselectedTabsPercents = new int[] { 100 }; } rendererWrapper.drawBackground(gc, partHeaderBounds.x, partHeaderBounds.y, partHeaderBounds.width, partHeaderBounds.height, defaultBackground, unselectedTabsColors, unselectedTabsPercents, vertical); } private void drawTabAreaBackground(GC gc, Rectangle partHeaderBounds, int state, boolean vertical, Color defaultBackground) { Color[] colors = selectedTabAreaColors; int[] percents = selectedTabAreaPercents; if (colors != null && colors.length == 2) { colors = new Color[] { colors[1], colors[1] }; } if (colors == null) { boolean selected = (state & SWT.SELECTED) != 0; colors = selected ? parentWrapper.getSelectionGradientColors() : parentWrapper.getGradientColors(); percents = selected ? parentWrapper.getSelectionGradientPercents() : parentWrapper.getGradientPercents(); } if (colors == null) { colors = new Color[] { gc.getDevice().getSystemColor(SWT.COLOR_WHITE) }; percents = new int[] { 100 }; } rendererWrapper.drawBackground(gc, partHeaderBounds.x, partHeaderBounds.y + partHeaderBounds.height, partHeaderBounds.width, parent.getBounds().height, defaultBackground, colors, percents, vertical); } // Workaround for the bug 433276. Remove it when the bug gets fixed private void drawChildrenBackground(Rectangle partHeaderBounds) { for (Control control : parent.getChildren()) { if (!CompositeElement.hasBackgroundOverriddenByCSS(control) && containsToolbar(control)) { drawChildBackground((Composite) control, partHeaderBounds); } } } private boolean containsToolbar(Control control) { if (control.getData(CONTAINS_TOOLBAR) != null) { return true; } if (control instanceof ToolBar) { control.setData(CONTAINS_TOOLBAR, true); return true; } if (control instanceof Composite) { for (Control child : ((Composite) control).getChildren()) { if (child instanceof ToolBar) { control.setData(CONTAINS_TOOLBAR, true); return true; } } } return false; } private void drawChildBackground(Composite composite, Rectangle partHeaderBounds) { Rectangle rec = composite.getBounds(); Color background = null; boolean partOfHeader = rec.y >= partHeaderBounds.y && rec.y < partHeaderBounds.height; if (!partOfHeader) { background = composite.getDisplay().getSystemColor(SWT.COLOR_WHITE); } CTabFolderElement.setBackgroundOverriddenDuringRenderering(composite, background); } private static class CTabFolderRendererWrapper extends ReflectionSupport<CTabFolderRenderer> { private Method drawBackgroundMethod; private Method drawCloseMethod; public CTabFolderRendererWrapper(CTabFolderRenderer instance) { super(instance); } public void drawBackground(GC gc, int x, int y, int width, int height, Color defaultBackground, Color[] colors, int[] percents, boolean vertical) { if (drawBackgroundMethod == null) { drawBackgroundMethod = getMethod("drawBackground", //$NON-NLS-1$ new Class<?>[] { GC.class, int[].class, int.class, int.class, int.class, int.class, Color.class, Image.class, Color[].class, int[].class, boolean.class }); } executeMethod(drawBackgroundMethod, new Object[] { gc, null, x, y, width, height, defaultBackground, null, colors, percents, vertical }); } public void drawClose(GC gc, Rectangle closeRect, int closeImageState) { if (drawCloseMethod == null) { drawCloseMethod = getMethod("drawClose", new Class<?>[] { //$NON-NLS-1$ GC.class, Rectangle.class, int.class }); } executeMethod(drawCloseMethod, new Object[] { gc, closeRect, closeImageState }); } } private static class CTabItemWrapper extends ReflectionSupport<CTabItem> { private Field shortenedTextField; private Field shortenedTextWidthField; private Field closeRectField; private Field closeImageStateField; public CTabItemWrapper(CTabItem instance) { super(instance); } public String getShortenedText() { if (shortenedTextField == null) { shortenedTextField = getField("shortenedText"); //$NON-NLS-1$ } return (String) getFieldValue(shortenedTextField); } public void setShortenedText(String value) { set("shortenedText", value); //$NON-NLS-1$ } public Integer getShortenedTextWidth() { if (shortenedTextWidthField == null) { shortenedTextWidthField = getField("shortenedTextWidth"); //$NON-NLS-1$ } return (Integer) getFieldValue(shortenedTextWidthField); } public void setShortenedTextWidth(int value) { set("shortenedTextWidth", value); //$NON-NLS-1$ } public Rectangle getCloseRect() { if (closeRectField == null) { closeRectField = getField("closeRect"); //$NON-NLS-1$ } return (Rectangle) getFieldValue(closeRectField); } public int getCloseImageState() { if (closeImageStateField == null) { closeImageStateField = getField("closeImageState"); //$NON-NLS-1$ } return (Integer) getFieldValue(closeImageStateField); } } private static class CTabFolderWrapper extends ReflectionSupport<CTabFolder> { private Field selectionGradientVerticalField; private Field gradientVerticalField; private Field selectionGradientColorsField; private Field selectionGradientPercentsField; private Field gradientColorsField; private Field gradientPercentsField; private Field showCloseField; private Field fixedTabHeightField; private Method getRightItemEdgeMethod; public CTabFolderWrapper(CTabFolder instance) { super(instance); } public boolean isShowClose() { if (showCloseField == null) { showCloseField = getField("showClose"); //$NON-NLS-1$ } Boolean result = (Boolean) getFieldValue(showCloseField); return result != null ? result : true; } public boolean isSelectionGradientVertical() { if (selectionGradientVerticalField == null) { selectionGradientVerticalField = getField( "selectionGradientVertical"); //$NON-NLS-1$ } Boolean result = (Boolean) getFieldValue( selectionGradientVerticalField); return result != null ? result : true; } public boolean isGradientVertical() { if (gradientVerticalField == null) { gradientVerticalField = getField("gradientVertical"); //$NON-NLS-1$ } Boolean result = (Boolean) getFieldValue(gradientVerticalField); return result != null ? result : true; } public Color[] getSelectionGradientColors() { if (selectionGradientColorsField == null) { selectionGradientColorsField = getField( "selectionGradientColorsField"); //$NON-NLS-1$ } return (Color[]) getFieldValue(selectionGradientColorsField); } public int[] getSelectionGradientPercents() { if (selectionGradientPercentsField == null) { selectionGradientPercentsField = getField( "selectionGradientPercents"); //$NON-NLS-1$ } return (int[]) getFieldValue(selectionGradientPercentsField); } public Color[] getGradientColors() { if (gradientColorsField == null) { gradientColorsField = getField("gradientColors"); //$NON-NLS-1$ } return (Color[]) getFieldValue(gradientColorsField); } public int[] getGradientPercents() { if (gradientPercentsField == null) { gradientPercentsField = getField("gradientPercents"); //$NON-NLS-1$ } return (int[]) getFieldValue(gradientPercentsField); } public int getRightItemEdge(GC gc) { if (getRightItemEdgeMethod == null) { getRightItemEdgeMethod = getMethod("getRightItemEdge", //$NON-NLS-1$ new Class<?>[] { GC.class }); } return (Integer) executeMethod(getRightItemEdgeMethod, new Object[] { gc }); } public int getFixedTabHeight() { if (fixedTabHeightField == null) { fixedTabHeightField = getField("fixedTabHeight"); //$NON-NLS-1$ } return (Integer) getFieldValue(fixedTabHeightField); } } private static class ReflectionSupport<T> { private T instance; public ReflectionSupport(T instance) { this.instance = instance; } protected Object getFieldValue(Field field) { Object value = null; if (field != null) { boolean accessible = field.isAccessible(); try { field.setAccessible(true); value = field.get(instance); } catch (Exception exc) { // do nothing } finally { field.setAccessible(accessible); } } return value; } protected Field getField(String name) { Class<?> cls = instance.getClass(); while (!cls.equals(Object.class)) { try { return cls.getDeclaredField(name); } catch (Exception exc) { cls = cls.getSuperclass(); } } return null; } public Object set(String name, Object value) { try { Field field = getField(name); boolean accessible = field.isAccessible(); field.setAccessible(true); field.set(instance, value); field.setAccessible(accessible); return value; } catch (Exception e) { throw new RuntimeException(e); } } protected Object executeMethod(Method method, Object... params) { Object value = null; if (method != null) { boolean accessible = method.isAccessible(); try { method.setAccessible(true); value = method.invoke(instance, params); } catch (Exception exc) { // do nothing } finally { method.setAccessible(accessible); } } return value; } protected Method getMethod(String name, Class<?>... params) { Class<?> cls = instance.getClass(); while (!cls.equals(Object.class)) { try { return cls.getDeclaredMethod(name, params); } catch (Exception exc) { cls = cls.getSuperclass(); } } return null; } } }