blob: 384415e36afd147ad9737dc8eb519af47e18a158 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2007 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.forms;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
/**
* Manages colors that will be applied to forms and form widgets. The colors are
* chosen to make the widgets look correct in the editor area. If a different
* set of colors is needed, subclass this class and override 'initialize' and/or
* 'initializeColors'.
*
* @since 3.0
*/
public class FormColors {
/**
* Key for the form title foreground color.
*
* @deprecated use <code>IFormColors.TITLE</code>.
*/
public static final String TITLE = IFormColors.TITLE;
/**
* Key for the tree/table border color.
*
* @deprecated use <code>IFormColors.BORDER</code>
*/
public static final String BORDER = IFormColors.BORDER;
/**
* Key for the section separator color.
*
* @deprecated use <code>IFormColors.SEPARATOR</code>.
*/
public static final String SEPARATOR = IFormColors.SEPARATOR;
/**
* Key for the section title bar background.
*
* @deprecated use <code>IFormColors.TB_BG
*/
public static final String TB_BG = IFormColors.TB_BG;
/**
* Key for the section title bar foreground.
*
* @deprecated use <code>IFormColors.TB_FG</code>
*/
public static final String TB_FG = IFormColors.TB_FG;
/**
* Key for the section title bar gradient.
*
* @deprecated use <code>IFormColors.TB_GBG</code>
*/
public static final String TB_GBG = IFormColors.TB_GBG;
/**
* Key for the section title bar border.
*
* @deprecated use <code>IFormColors.TB_BORDER</code>.
*/
public static final String TB_BORDER = IFormColors.TB_BORDER;
/**
* Key for the section toggle color. Since 3.1, this color is used for all
* section styles.
*
* @deprecated use <code>IFormColors.TB_TOGGLE</code>.
*/
public static final String TB_TOGGLE = IFormColors.TB_TOGGLE;
/**
* Key for the section toggle hover color.
*
* @since 3.1
* @deprecated use <code>IFormColors.TB_TOGGLE_HOVER</code>.
*/
public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER;
protected Map colorRegistry = new HashMap(10);
protected Color background;
protected Color foreground;
private boolean shared;
protected Display display;
protected Color border;
/**
* Creates form colors using the provided display.
*
* @param display
* the display to use
*/
public FormColors(Display display) {
this.display = display;
initialize();
}
/**
* Returns the display used to create colors.
*
* @return the display
*/
public Display getDisplay() {
return display;
}
/**
* Initializes the colors. Subclasses can override this method to change the
* way colors are created. Alternatively, only the color table can be
* modified by overriding <code>initializeColorTable()</code>.
*
* @see #initializeColorTable
*/
protected void initialize() {
background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
initializeColorTable();
updateBorderColor();
}
/**
* Allocates colors for the following keys: BORDER, SEPARATOR and
* TITLE. Subclasses can override to allocate these colors differently.
*/
protected void initializeColorTable() {
createTitleColor();
createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB());
RGB black = getSystemColor(SWT.COLOR_BLACK);
RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
createColor(IFormColors.BORDER, blend(borderRGB, black, 80));
}
/**
* Allocates colors for the section tool bar (all the keys that start with
* TB). Since these colors are only needed when TITLE_BAR style is used with
* the Section widget, they are not needed all the time and are allocated on
* demand. Consequently, this method will do nothing if the colors have been
* already initialized. Call this method prior to using colors with the TB
* keys to ensure they are available.
*/
public void initializeSectionToolBarColors() {
if (colorRegistry.containsKey(IFormColors.TB_BG))
return;
createTitleBarGradientColors();
createTitleBarOutlineColors();
createTwistieColors();
}
/**
* Allocates additional colors for the form header, namely background
* gradients, bottom separator keylines and DND highlights. Since these
* colors are only needed for clients that want to use these particular
* style of header rendering, they are not needed all the time and are
* allocated on demand. Consequently, this method will do nothing if the
* colors have been already initialized. Call this method prior to using
* color keys with the H_ prefix to ensure they are available.
*
* @since 3.3
*/
protected void initializeFormHeaderColors() {
if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2))
return;
createFormHeaderColors();
}
/**
* Returns the RGB value of the system color represented by the code
* argument, as defined in <code>SWT</code> class.
*
* @param code
* the system color constant as defined in <code>SWT</code>
* class.
* @return the RGB value of the system color
*/
public RGB getSystemColor(int code) {
return getDisplay().getSystemColor(code).getRGB();
}
/**
* Creates the color for the specified key using the provided RGB object.
* The color object will be returned and also put into the registry. When
* the class is disposed, the color will be disposed with it.
*
* @param key
* the unique color key
* @param rgb
* the RGB object
* @return the allocated color object
*/
public Color createColor(String key, RGB rgb) {
return createColor(key, rgb.red, rgb.green, rgb.blue);
}
/**
* Creates a color that can be used for areas of the form that is inactive.
* These areas can contain images, links, controls and other content but are
* considered auxilliary to the main content area.
*
* <p>
* The color should not be disposed because it is managed by this class.
*
* @return the inactive form color
* @since 3.1
*/
public Color getInactiveBackground() {
String key = "__ncbg__"; //$NON-NLS-1$
Color color = getColor(key);
if (color == null) {
RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION);
// a blend of 95% white and 5% list selection system color
RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5);
color = createColor(key, ncbg);
}
return color;
}
/**
* Creates the color for the specified key using the provided RGB values.
* The color object will be returned and also put into the registry. If
* there is already another color object under the same key in the registry,
* the existing object will be disposed. When the class is disposed, the
* color will be disposed with it.
*
* @param key
* the unique color key
* @param r
* red value
* @param g
* green value
* @param b
* blue value
* @return the allocated color object
*/
public Color createColor(String key, int r, int g, int b) {
Color c = new Color(display, r, g, b);
Color prevC = (Color) colorRegistry.get(key);
if (prevC != null)
prevC.dispose();
colorRegistry.put(key, c);
return c;
}
/**
* Computes the border color relative to the background. Allocated border
* color is designed to work well with white. Otherwise, stanard widget
* background color will be used.
*/
protected void updateBorderColor() {
if (isWhiteBackground())
border = getColor(IFormColors.BORDER);
else {
border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
Color bg = getImpliedBackground();
if (border.getRed() == bg.getRed()
&& border.getGreen() == bg.getGreen()
&& border.getBlue() == bg.getBlue())
border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
}
}
/**
* Sets the background color. All the toolkits that use this class will
* share the same background.
*
* @param bg
* background color
*/
public void setBackground(Color bg) {
this.background = bg;
updateBorderColor();
updateFormHeaderColors();
}
/**
* Sets the foreground color. All the toolkits that use this class will
* share the same foreground.
*
* @param fg
* foreground color
*/
public void setForeground(Color fg) {
this.foreground = fg;
}
/**
* Returns the current background color.
*
* @return the background color
*/
public Color getBackground() {
return background;
}
/**
* Returns the current foreground color.
*
* @return the foreground color
*/
public Color getForeground() {
return foreground;
}
/**
* Returns the computed border color. Border color depends on the background
* and is recomputed whenever the background changes.
*
* @return the current border color
*/
public Color getBorderColor() {
return border;
}
/**
* Tests if the background is white. White background has RGB value
* 255,255,255.
*
* @return <samp>true</samp> if background is white, <samp>false</samp>
* otherwise.
*/
public boolean isWhiteBackground() {
Color bg = getImpliedBackground();
return bg.getRed() == 255 && bg.getGreen() == 255
&& bg.getBlue() == 255;
}
/**
* Returns the color object for the provided key or <samp>null </samp> if
* not in the registry.
*
* @param key
* the color key
* @return color object if found, or <samp>null </samp> if not.
*/
public Color getColor(String key) {
if (key.startsWith(IFormColors.TB_PREFIX))
initializeSectionToolBarColors();
else if (key.startsWith(IFormColors.H_PREFIX))
initializeFormHeaderColors();
return (Color) colorRegistry.get(key);
}
/**
* Disposes all the colors in the registry.
*/
public void dispose() {
Iterator e = colorRegistry.values().iterator();
while (e.hasNext())
((Color) e.next()).dispose();
colorRegistry = null;
}
/**
* Marks the colors shared. This prevents toolkits that share this object
* from disposing it.
*/
public void markShared() {
this.shared = true;
}
/**
* Tests if the colors are shared.
*
* @return <code>true</code> if shared, <code>false</code> otherwise.
*/
public boolean isShared() {
return shared;
}
/**
* Blends c1 and c2 based in the provided ratio.
*
* @param c1
* first color
* @param c2
* second color
* @param ratio
* percentage of the first color in the blend (0-100)
* @return the RGB value of the blended color
* @since 3.1
*/
public 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);
}
/**
* Tests the source RGB for range.
*
* @param rgb
* the tested RGB
* @param from
* range start (excluding the value itself)
* @param to
* range end (excluding the value itself)
* @return <code>true</code> if at least one of the primary colors in the
* source RGB are within the provided range, <code>false</code>
* otherwise.
* @since 3.1
*/
public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) {
if (testPrimaryColor(rgb.red, from, to))
return true;
if (testPrimaryColor(rgb.green, from, to))
return true;
if (testPrimaryColor(rgb.blue, from, to))
return true;
return false;
}
/**
* Tests the source RGB for range.
*
* @param rgb
* the tested RGB
* @param from
* range start (excluding the value itself)
* @param to
* tange end (excluding the value itself)
* @return <code>true</code> if at least two of the primary colors in the
* source RGB are within the provided range, <code>false</code>
* otherwise.
* @since 3.1
*/
public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) {
int total = 0;
if (testPrimaryColor(rgb.red, from, to))
total++;
if (testPrimaryColor(rgb.green, from, to))
total++;
if (testPrimaryColor(rgb.blue, from, to))
total++;
return total >= 2;
}
/**
* Blends two primary color components based on the provided ratio.
*
* @param v1
* first component
* @param v2
* second component
* @param ratio
* percentage of the first component in the blend
* @return
*/
private static int blend(int v1, int v2, int ratio) {
int b = (ratio * v1 + (100 - ratio) * v2) / 100;
return Math.min(255, b);
}
private Color getImpliedBackground() {
if (getBackground() != null)
return getBackground();
return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
}
private static boolean testPrimaryColor(int value, int from, int to) {
return value > from && value < to;
}
private void createTitleColor() {
/*
* RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light
* if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80);
* else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK,
* 50); createColor(TITLE, rgb);
*/
RGB bg = getImpliedBackground().getRGB();
RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION);
RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND);
RGB rgb = listSelection;
// Group 1
// Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or
// between 0 and 120, then use 100% LIST_SELECTION as it is (no
// additions)
// Examples: XP Default, Win Classic Standard, Win High Con White, Win
// Classic Marine
if (testTwoPrimaryColors(listSelection, -1, 121))
rgb = listSelection;
// Group 2
// When LIST_BACKGROUND = white (255, 255, 255) or not black, text
// colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over
// LIST_BACKGROUND
// Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or
// between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION
// foreground colour
// Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX
// Aqua, OSX Graphite, Linux GTK
else if (testTwoPrimaryColors(listSelection, 120, 256)
|| (bg.red == 0 && bg.green == 0 && bg.blue == 0))
rgb = blend(listSelection, listForeground, 50);
// Group 3
// When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION
// @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND
// Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to
// LIST_SELECTION foreground colour
// Examples: Win High Con Black, Win High Con #1, Win High Con #2
// (covered in the second part of the OR clause above)
createColor(IFormColors.TITLE, rgb);
}
private void createTwistieColors() {
RGB rgb = getColor(IFormColors.TITLE).getRGB();
RGB white = getSystemColor(SWT.COLOR_WHITE);
createColor(TB_TOGGLE, rgb);
rgb = blend(rgb, white, 60);
createColor(TB_TOGGLE_HOVER, rgb);
}
private void createTitleBarGradientColors() {
RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
RGB bg = getImpliedBackground().getRGB();
// Group 1
// Rule: If at least 2 of the RGB values are equal to or between 180 and
// 255, then apply specified opacity for Group 1
// Examples: Vista, XP Silver, Wn High Con #2
// Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
if (testTwoPrimaryColors(tbBg, 179, 256))
tbBg = blend(tbBg, bg, 30);
// Group 2
// Rule: If at least 2 of the RGB values are equal to or between 121 and
// 179, then apply specified opacity for Group 2
// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
// Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
else if (testTwoPrimaryColors(tbBg, 120, 180))
tbBg = blend(tbBg, bg, 20);
// Group 3
// Rule: Everything else
// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
// Aqua, Wn High Con White, Wn High Con #1
// Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
else {
tbBg = blend(tbBg, bg, 10);
}
createColor(IFormColors.TB_BG, tbBg);
// for backward compatibility
createColor(TB_GBG, tbBg);
}
private void createTitleBarOutlineColors() {
// title bar outline - border color
RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
RGB bg = getImpliedBackground().getRGB();
// Group 1
// Rule: If at least 2 of the RGB values are equal to or between 180 and
// 255, then apply specified opacity for Group 1
// Examples: Vista, XP Silver, Wn High Con #2
// Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
if (testTwoPrimaryColors(tbBorder, 179, 256))
tbBorder = blend(tbBorder, bg, 70);
// Group 2
// Rule: If at least 2 of the RGB values are equal to or between 121 and
// 179, then apply specified opacity for Group 2
// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
// Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND
else if (testTwoPrimaryColors(tbBorder, 120, 180))
tbBorder = blend(tbBorder, bg, 50);
// Group 3
// Rule: Everything else
// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
// Aqua, Wn High Con White, Wn High Con #1
// Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
else {
tbBorder = blend(tbBorder, bg, 30);
}
createColor(FormColors.TB_BORDER, tbBorder);
}
private void updateFormHeaderColors() {
if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) {
disposeIfFound(IFormColors.H_GRADIENT_END);
disposeIfFound(IFormColors.H_GRADIENT_START);
disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1);
disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2);
disposeIfFound(IFormColors.H_HOVER_LIGHT);
disposeIfFound(IFormColors.H_HOVER_FULL);
initializeFormHeaderColors();
}
}
private void disposeIfFound(String key) {
Color color = getColor(key);
if (color != null) {
colorRegistry.remove(key);
color.dispose();
}
}
private void createFormHeaderColors() {
createFormHeaderGradientColors();
createFormHeaderKeylineColors();
createFormHeaderDNDColors();
}
private void createFormHeaderGradientColors() {
RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
Color bgColor = getImpliedBackground();
RGB bg = bgColor.getRGB();
RGB bottom, top;
// Group 1
// Rule: If at least 2 of the RGB values are equal to or between 180 and
// 255, then apply specified opacity for Group 1
// Examples: Vista, XP Silver, Wn High Con #2
// Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
if (testTwoPrimaryColors(titleBg, 179, 256)) {
bottom = blend(titleBg, bg, 30);
top = bg;
}
// Group 2
// Rule: If at least 2 of the RGB values are equal to or between 121 and
// 179, then apply specified opacity for Group 2
// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
// Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
else if (testTwoPrimaryColors(titleBg, 120, 180)) {
bottom = blend(titleBg, bg, 20);
top = bg;
}
// Group 3
// Rule: If at least 2 of the RGB values are equal to or between 0 and
// 120, then apply specified opacity for Group 3
// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
// Aqua, Wn High Con White, Wn High Con #1
// Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
else {
bottom = blend(titleBg, bg, 10);
top = bg;
}
createColor(IFormColors.H_GRADIENT_END, top);
createColor(IFormColors.H_GRADIENT_START, bottom);
}
private void createFormHeaderKeylineColors() {
RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
Color bgColor = getImpliedBackground();
RGB bg = bgColor.getRGB();
RGB keyline2;
// H_BOTTOM_KEYLINE1
createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255));
// H_BOTTOM_KEYLINE2
// Group 1
// Rule: If at least 2 of the RGB values are equal to or between 180 and
// 255, then apply specified opacity for Group 1
// Examples: Vista, XP Silver, Wn High Con #2
// Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
if (testTwoPrimaryColors(titleBg, 179, 256))
keyline2 = blend(titleBg, bg, 70);
// Group 2
// Rule: If at least 2 of the RGB values are equal to or between 121 and
// 179, then apply specified opacity for Group 2
// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
// Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND
else if (testTwoPrimaryColors(titleBg, 120, 180))
keyline2 = blend(titleBg, bg, 50);
// Group 3
// Rule: If at least 2 of the RGB values are equal to or between 0 and
// 120, then apply specified opacity for Group 3
// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
// Aqua, Wn High Con White, Wn High Con #1
// Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
else
keyline2 = blend(titleBg, bg, 30);
// H_BOTTOM_KEYLINE2
createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2);
}
private void createFormHeaderDNDColors() {
RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT);
Color bgColor = getImpliedBackground();
RGB bg = bgColor.getRGB();
RGB light, full;
// ALL Themes
//
// Light Highlight
// When *near* the 'hot' area
// Rule: If near the title in the 'hot' area, show background highlight
// TITLE_BACKGROUND_GRADIENT @ 40%
light = blend(titleBg, bg, 40);
// Full Highlight
// When *on* the title area (regions 1 and 2)
// Rule: If near the title in the 'hot' area, show background highlight
// TITLE_BACKGROUND_GRADIENT @ 60%
full = blend(titleBg, bg, 60);
// H_DND_LIGHT
// H_DND_FULL
createColor(IFormColors.H_HOVER_LIGHT, light);
createColor(IFormColors.H_HOVER_FULL, full);
}
}