/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.navigation.ui.swt.lnf.renderer; import org.eclipse.core.runtime.Assert; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.riena.ui.swt.facades.GCFacade; import org.eclipse.riena.ui.swt.facades.SWTFacade; import org.eclipse.riena.ui.swt.lnf.AbstractLnfRenderer; import org.eclipse.riena.ui.swt.lnf.FlasherSupportForRenderer; import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants; import org.eclipse.riena.ui.swt.lnf.LnfManager; import org.eclipse.riena.ui.swt.lnf.rienadefault.RienaDefaultLnf; import org.eclipse.riena.ui.swt.utils.ImageStore; import org.eclipse.riena.ui.swt.utils.SwtUtilities; /** * Renderer of a tab of the switcher between sub-applications. */ public class SubApplicationTabRenderer extends AbstractLnfRenderer { /** * @since 6.2 */ protected Color defaultColor = null; /** * @since 6.2 */ protected static int ACTIVE_Y_OFFSET = SwtUtilities.convertYToDpiTruncate(2); /** * @since 6.2 */ protected static int ACTIVE_BOTTOM_INSET = SwtUtilities.convertYToDpiTruncate(6); /** * @since 6.2 */ protected static int ACTIVE_LEFT_INSET = SwtUtilities.convertXToDpiTruncate(3); /** * @since 6.2 */ protected static int ACTIVE_RIGHT_INSET = SwtUtilities.convertXToDpiTruncate(3); /** * @since 6.2 */ protected static int BORDER_TOP_WIDTH = SwtUtilities.convertYToDpiTruncate(3); /** * @since 6.2 */ protected static int BORDER_BOTTOM_WIDTH = SwtUtilities.convertYToDpiTruncate(1); /** * @since 6.2 */ protected static int BORDER_LEFT_WIDTH = SwtUtilities.convertXToDpiTruncate(2); /** * @since 6.2 */ protected static int BORDER_RIGHT_WIDTH = SwtUtilities.convertXToDpiTruncate(2); // if !tabsWithEqualHeader then TEXT_TOP_INSET is always the same /** * @since 6.2 */ protected final static int TEXT_TOP_INSET = SwtUtilities.convertYToDpiTruncate(3); // if tabsWithEqualHeader then TEXT_TOP_INSET varies depending whether the tab is active or passive aka TEXT_TOP_INSET_ACTIV or TEXT_TOP_INSET_PASSIV /** * @since 6.2 */ protected final static int TEXT_TOP_INSET_ACTIV = SwtUtilities.convertYToDpiTruncate(3); /** * @since 6.2 */ protected static int TEXT_TOP_INSET_PASSIV = SwtUtilities.convertYToDpiTruncate(5); /** * @since 6.2 */ protected static int TEXT_BOTTOM_INSET = SwtUtilities.convertYToDpiTruncate(4); /** * @since 6.2 */ protected static int TEXT_LEFT_INSET = SwtUtilities.convertXToDpiTruncate(6); /** * @since 6.2 */ protected static int TEXT_RIGHT_INSET = SwtUtilities.convertXToDpiTruncate(6); /** * @since 6.2 */ protected static int ICON_TEXT_GAP = SwtUtilities.convertXToDpiTruncate(4); /** * @since 6.2 */ protected Color selStartColor; /** * @since 6.2 */ protected Color selEndColor; /** * @since 6.2 */ protected Image image; /** * @since 6.2 */ protected String icon; /** * @since 6.2 */ protected String label; /** * @since 6.2 */ protected boolean active; /** * @since 6.2 */ protected Control control; /** * @since 6.2 */ protected final FlasherSupportForRenderer flasherSupport; /** * @since 6.2 */ protected boolean tabsWithEqualHeight = false; private boolean initialized = false; /** * Create a new instance of the renderer of a tab of the sub-application switcher. */ public SubApplicationTabRenderer() { super(); flasherSupport = new FlasherSupportForRenderer(this, new MarkerUpdater()); } /** * @since 6.2 */ protected void checkInit() { if (initialized) { return; } tabsWithEqualHeight = LnfManager.getLnf().getBooleanSetting(LnfKeyConstants.SUB_APPLICATION_SWITCHER_ALL_TABS_WITH_SAME_HEIGHT, tabsWithEqualHeight); initialized = true; } /** * @see org.eclipse.riena.ui.swt.lnf.AbstractLnfRenderer#paint(org.eclipse.swt.graphics.GC, java.lang.Object) */ @Override public void paint(final GC gc, final Object value) { super.paint(gc, value); Assert.isNotNull(gc); Assert.isNotNull(value); Assert.isTrue(value instanceof Control); control = (Control) value; if (getBounds().y - ACTIVE_Y_OFFSET < 0) { final Rectangle bounds = new Rectangle(getBounds().x, ACTIVE_Y_OFFSET, getBounds().width, getBounds().height); setBounds(bounds); } final RienaDefaultLnf lnf = LnfManager.getLnf(); final Font font = getTabFont(); gc.setFont(font); paintBackground(gc); final int leftInset = getLeftInset(); final int rightInset = getRightInset(); Color borderTopRightColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_BORDER_TOP_RIGHT_COLOR); Color borderBottomLeftColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_BORDER_BOTTOM_LEFT_COLOR); if (!isEnabled()) { borderTopRightColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_DISABLED_BORDER_TOP_RIGHT_COLOR); borderBottomLeftColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_DISABLED_BORDER_BOTTOM_LEFT_COLOR); } Color borderBottomLeftSecondaryColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_BORDER_BOTTOM_LEFT_SECONDARY_COLOR); // Border // - left gc.setForeground(borderBottomLeftColor); int x = getBounds().x - leftInset; int y = getBounds().y + BORDER_TOP_WIDTH - 1; int x2 = x; int y2 = getBounds().y + getHeight() - 1; if (isActive()) { GCFacade.getDefault().setAntialias(gc, SWT.OFF); } gc.drawLine(x, y, x2, y2); if (isActive()) { gc.setForeground(borderBottomLeftSecondaryColor); gc.drawPoint(x, y2); gc.drawLine(x + 1, y, x2 + 1, y2 - 1); } final Color innerBorderColor = getInnerBorderColor(lnf); if (!isActive()) { gc.setForeground(innerBorderColor); x += 1; x2 += 1; gc.drawLine(x, y, x2, y2); } // -top gc.setForeground(borderTopRightColor); x = getBounds().x + BORDER_LEFT_WIDTH - leftInset; y = getBounds().y; x2 = x + getWidth() - BORDER_LEFT_WIDTH - BORDER_RIGHT_WIDTH + rightInset; y2 = y; gc.drawLine(x, y, x2, y2); // --top-left // gc.setAntialias(SWT.ON); if (!isActive()) { x = getBounds().x - leftInset; y = getBounds().y + BORDER_TOP_WIDTH - 1; x2 = x + 1; y2 = y; gc.drawLine(x, y, x2, y2); } x = getBounds().x + 1 - leftInset; y = getBounds().y + BORDER_TOP_WIDTH - 2; x2 = x + 1; y2 = y; gc.drawLine(x, y, x2, y2); // --top-right if (!isActive()) { x = getBounds().x + getWidth() + rightInset; y = getBounds().y + BORDER_TOP_WIDTH - 1; x2 = x - 1; y2 = y; gc.drawLine(x, y, x2, y2); } x = getBounds().x + getWidth() - 1 + rightInset; y = getBounds().y + BORDER_TOP_WIDTH - 2; x2 = x - 1; y2 = y; gc.drawLine(x, y, x2, y2); // -right gc.setForeground(borderTopRightColor); int topBorder; if (tabsWithEqualHeight) { topBorder = BORDER_TOP_WIDTH - (isActive() ? 2 : 1); } else { topBorder = BORDER_TOP_WIDTH - 1; } x = getBounds().x + getWidth() + rightInset; y = getBounds().y + topBorder; x2 = x; y2 = getBounds().y + getHeight() - topBorder; gc.drawLine(x, y, x2, y2); if (!isActive()) { gc.setForeground(innerBorderColor); } else { gc.setForeground(lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_BORDER_TOP_RIGHT_SECONDARY_COLOR)); gc.drawPoint(x, y2 + 1); } x -= 1; x2 -= 1; gc.drawLine(x, y, x2, y2); // - bottom if (isActive()) { final Color backgroundEndColor = getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_ACTIVE_BACKGROUND_END_COLOR, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PASSIVE_BACKGROUND_END_COLOR, null, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PROCESS_FINISHED_BACKGROUND_END_COLOR); gc.setForeground(backgroundEndColor); borderBottomLeftSecondaryColor = lnf.getColor(LnfKeyConstants.TITLELESS_SHELL_BACKGROUND); } else { gc.setForeground(borderBottomLeftColor); } x = getBounds().x - leftInset + (isActive() ? 1 : 0); y = getBounds().y + getHeight() - 1; x2 = getBounds().x + getWidth() + rightInset - (isActive() ? 2 : 0); y2 = y; gc.drawLine(x, y, x2, y2); // - bottom secondary gc.setForeground(borderBottomLeftSecondaryColor); gc.drawLine(x - 1, y + 1, x2 + 2, y2 + 1); // Icon x = getBounds().x + getBounds().width / 2 - getImageTextWidth(gc) / 2; if (getImage() != null) { y = getBounds().y + BORDER_TOP_WIDTH + getTextTopInset(); final FontMetrics fontMetrics = gc.getFontMetrics(); y += fontMetrics.getHeight() / 2; y -= getImage().getBounds().height / 2; gc.drawImage(getImage(), x, y); x += getImage().getBounds().width + ICON_TEXT_GAP; } // Text final Color textColor = getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_ACTIVE_FOREGROUND, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PASSIVE_FOREGROUND, LnfKeyConstants.SUB_APPLICATION_SWITCHER_DISABLED_FOREGROUND, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PASSIVE_FOREGROUND); gc.setForeground(textColor); y = getBounds().y + BORDER_TOP_WIDTH + getTextTopInset(); final int fontHeight = gc.getFontMetrics().getHeight(); if (control.getBounds().height - (y + fontHeight) < 0) { y = control.getBounds().height - fontHeight; } gc.drawText(getLabel(), x, y, SWT.DRAW_TRANSPARENT | SWTFacade.DRAW_MNEMONIC); // Selection if (isActive() || flasherSupport.isProcessMarkerVisible()) { paintSelection(gc); } flasherSupport.startFlasher(); GCFacade.getDefault().setAntialias(gc, SWT.DEFAULT); } /** * @since 6.2 */ protected void paintSelection(final GC gc) { final RienaDefaultLnf lnf = LnfManager.getLnf(); final Color selColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_TOP_SELECTION_COLOR); gc.setForeground(selColor); gc.setBackground(selColor); int x = getBounds().x - getLeftInset(); int y = getBounds().y; int w = 2; int h = 2; gc.fillRectangle(x, y, w, h); gc.drawPoint(x + 1, y - 1); x = getBounds().x + getWidth() - 1 + getRightInset(); y = getBounds().y; gc.fillRectangle(x, y, w, h); gc.drawPoint(x, y - 1); gc.setForeground(getSelectionStartColor()); gc.setBackground(getSelectionEndColor()); x = getBounds().x + BORDER_LEFT_WIDTH - getLeftInset(); y = getBounds().y - ACTIVE_Y_OFFSET; w = getWidth() - BORDER_LEFT_WIDTH - BORDER_RIGHT_WIDTH + 1 + getRightInset() + getLeftInset(); h = 4; gc.fillGradientRectangle(x, y, w, h, true); } /** * @since 6.2 */ protected Color getInnerBorderColor(final RienaDefaultLnf lnf) { Color innerBorderColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_INNER_BORDER_COLOR); if (!isEnabled()) { innerBorderColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_INNER_DISABLED_BORDER_COLOR); } if (isProcessFinishedInBackground()) { innerBorderColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_INNER_PROCESS_FINISHED_BORDER_COLOR); } return innerBorderColor; } /** * Paints the background of one tab. * * @param gc * Graphic Context * @since 6.2 */ protected void paintBackground(final GC gc) { final Color backgroundStartColor = getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_ACTIVE_BACKGROUND_START_COLOR, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PASSIVE_BACKGROUND_START_COLOR, null, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PROCESS_FINISHED_BACKGROUND_START_COLOR); gc.setForeground(backgroundStartColor); final Color backgroundEndColor = getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_ACTIVE_BACKGROUND_END_COLOR, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PASSIVE_BACKGROUND_END_COLOR, null, LnfKeyConstants.SUB_APPLICATION_SWITCHER_PROCESS_FINISHED_BACKGROUND_END_COLOR); gc.setBackground(backgroundEndColor); final int x = getBounds().x + BORDER_LEFT_WIDTH - 1 - getLeftInset(); final int y = getBounds().y + 1; final int w = getWidth() - BORDER_LEFT_WIDTH - BORDER_RIGHT_WIDTH + 3 + getLeftInset() + getRightInset(); final int h = getHeight() - 1; gc.fillGradientRectangle(x, y, w, h, true); } /** * @since 6.2 */ protected int getLeftInset() { if (isActive()) { return ACTIVE_LEFT_INSET; } else { return 0; } } /** * @since 6.2 */ protected int getRightInset() { if (isActive()) { return ACTIVE_RIGHT_INSET; } else { return 0; } } /** * Returns whether a process finished in the background but the flashing state is not active anymore. * * @return true if a process has finished in the background but the tab is not flashing anymore, false otherwise * @since 6.2 */ protected boolean isProcessFinishedInBackground() { return flasherSupport.isProcessMarkerVisible() && !flasherSupport.isFlashing(); } /** * Returns whether the processFinishedMarker is visible AND the flashing state is true. * * @return true if the tab is flashing and the marker is visible as well, false otherwise * @since 6.2 */ protected boolean isProcessFinishedMarkerVisibleWhileFlashing() { return (flasherSupport.isProcessMarkerVisible() && flasherSupport.isFlashing()); } /** * @since 6.2 */ protected int getTextTopInset() { if (tabsWithEqualHeight) { if (isActive()) { return getHorizontalInset(TEXT_TOP_INSET_ACTIV); } else { return getHorizontalInset(TEXT_TOP_INSET_PASSIV); } } else { return getHorizontalInset(TEXT_TOP_INSET); } } /** * @since 6.2 */ protected int getTextBottomInset() { return getHorizontalInset(TEXT_BOTTOM_INSET); } /** * @since 6.2 */ protected int getHorizontalInset(final int defaultValue) { if (control != null && control.getBounds() != null) { double yDelta = control.getBounds().height - (getBounds().y + getBounds().height); if (yDelta < 0) { yDelta *= -1; return Math.max(0, (int) (defaultValue - (yDelta / 2))); } } return defaultValue; } /** * @since 6.2 */ protected Color getSelectionStartColor() { final RienaDefaultLnf lnf = LnfManager.getLnf(); if ((selStartColor == null) || selStartColor.isDisposed()) { final Color selColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_TOP_SELECTION_COLOR); selStartColor = SwtUtilities.makeBrighter(selColor, 0.9f); } return selStartColor; } /** * @since 6.2 */ protected Color getSelectionEndColor() { final RienaDefaultLnf lnf = LnfManager.getLnf(); if ((selEndColor == null) || selStartColor.isDisposed()) { final Color selColor = lnf.getColor(LnfKeyConstants.SUB_APPLICATION_SWITCHER_TOP_SELECTION_COLOR); selEndColor = SwtUtilities.makeBrighter(selColor, 1.1f); } return selEndColor; } /** * @see org.eclipse.riena.navigation.ui.swt.lnf.ILnfRenderer#dispose() */ public void dispose() { SwtUtilities.dispose(selStartColor); SwtUtilities.dispose(selEndColor); SwtUtilities.dispose(defaultColor); } /** * Computes the size of a tab. * * @param gc * @param value * @return size of tab */ public Point computeSize(final GC gc, final Object value) { checkInit(); final Font font = getTabFont(); gc.setFont(font); final FontMetrics fontMetrics = gc.getFontMetrics(); int width = getImageTextWidth(gc); width = width + BORDER_LEFT_WIDTH + BORDER_RIGHT_WIDTH + TEXT_LEFT_INSET + TEXT_RIGHT_INSET; final int minWidth = getSubApplicationSwitcherTabMinWidth(); width = Math.max(width, minWidth); int height = fontMetrics.getHeight(); height = height + BORDER_TOP_WIDTH + BORDER_BOTTOM_WIDTH + getTextTopInset() + getTextBottomInset(); if (isActive() && !tabsWithEqualHeight) { height += ACTIVE_BOTTOM_INSET; } if (isProcessFinishedMarkerVisibleWhileFlashing() || isProcessFinishedInBackground()) { height = height - SwtUtilities.convertYToDpiTruncate(2); TEXT_TOP_INSET_PASSIV = 3; } else { TEXT_TOP_INSET_PASSIV = 5; } return new Point(width, height); } /** * Adds the width of the image, the text and the gap between both. * * @param gc * @return width * @since 6.2 */ protected int getImageTextWidth(final GC gc) { final Font font = getTabFont(); gc.setFont(font); String tabLabel = getLabel(); tabLabel = tabLabel.replaceFirst("&", ""); //$NON-NLS-1$ int width = SwtUtilities.calcTextWidth(gc, tabLabel); // Icon if (getImage() != null) { width += getImage().getBounds().width + ICON_TEXT_GAP; } return width; } /** * Returns the font of the tab. * * @return font * @since 6.2 */ protected Font getTabFont() { final RienaDefaultLnf lnf = LnfManager.getLnf(); final Font font = lnf.getFont(LnfKeyConstants.SUB_APPLICATION_SWITCHER_FONT); return font; } /** * @since 6.2 */ protected int getHeight() { return getBounds().height - 1; } /** * @since 6.2 */ protected int getWidth() { return getBounds().width - 1; } public String getIcon() { return icon; } public void setIcon(final String icon) { this.icon = icon; setImage(ImageStore.getInstance().getImage(icon)); } /** * @since 6.2 */ protected Image getImage() { final RienaDefaultLnf lnf = LnfManager.getLnf(); if (lnf.getBooleanSetting(LnfKeyConstants.SUB_APPLICATION_SWITCHER_TAB_SHOW_ICON)) { return image; } else { return null; } } /** * @since 6.2 */ protected void setImage(final Image image) { this.image = image; } public String getLabel() { if (label == null) { label = ""; //$NON-NLS-1$ } return label; } public void setLabel(final String label) { this.label = label; } /** * @since 1.2 */ public boolean isActive() { return active; } /** * @since 1.2 */ public void setActive(final boolean active) { this.active = active; } /** * This class updates (redraws) the tab, so that the marker are also updated (redrawn). * * @since 6.2 */ protected class MarkerUpdater implements Runnable { /** * @see java.lang.Runnable#run() */ public void run() { if (!SwtUtilities.isDisposed(control)) { control.redraw(); } } } /** * Returns according to the states of the title bar the color of one of the given key.<br> * If one key is not needed, the parameter can be {@code null}. * * @param activeColorKey * @param passiveColorKey * @param disabeldColorKey * @return color * @TODO same code in EmbeddedTitlebarRenderer * @since 6.2 */ protected Color getColor(final String activeColorKey, final String passiveColorKey, final String disabeldColorKey, final String processFinishedKey) { Color color = null; String colorKey = getKey(activeColorKey, passiveColorKey, disabeldColorKey, processFinishedKey); if (colorKey == null) { colorKey = activeColorKey; } final RienaDefaultLnf lnf = LnfManager.getLnf(); color = lnf.getColor(colorKey); if (color == null) { return getDefaultColor(); } return color; } /** * @since 6.2 */ protected Color getDefaultColor() { // this was added so that the class loading no longer accesses the UIThread if (defaultColor == null) { defaultColor = new Color(null, 0, 0, 0); // black } return defaultColor; } /** * Returns according to the state of the title bar one of the given keys.<br> * If one key is not needed, the parameter can be {@code null}. * * @param activeKey * @param passiveKey * @param disabeldKey * @return key * @TODO same code in EmbeddedTitlebarRenderer Returns according to the * @since 6.2 */ protected String getKey(final String activeKey, final String passiveKey, final String disabeldKey, final String processFinishedKey) { String key = null; if (isEnabled()) { if (isActive() || isProcessFinishedMarkerVisibleWhileFlashing()) { key = activeKey; } else if (isProcessFinishedInBackground()) { key = processFinishedKey; } else { key = passiveKey; } } else { key = disabeldKey; } if (key == null) { key = activeKey; } if (key == null) { key = passiveKey; } if (key == null) { key = disabeldKey; } return key; } /** * Returns the minimum width of a tab. * * @return minimum width or 0, if no minimum width is set * @since 6.2 */ protected int getSubApplicationSwitcherTabMinWidth() { final RienaDefaultLnf lnf = LnfManager.getLnf(); int minWidth = lnf.getIntegerSetting(LnfKeyConstants.SUB_APPLICATION_SWITCHER_TAB_MIN_WIDTH, 0); minWidth = SwtUtilities.convertXToDpi(minWidth); return minWidth; } }