View Javadoc
1   package org.argeo.cms.ui.eclipse.forms;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import org.eclipse.jface.resource.JFaceResources;
7   import org.eclipse.jface.resource.LocalResourceManager;
8   import org.eclipse.swt.SWT;
9   import org.eclipse.swt.graphics.Color;
10  import org.eclipse.swt.graphics.RGB;
11  //import org.eclipse.swt.internal.graphics.Graphics;
12  import org.eclipse.swt.widgets.Display;
13  
14  /**
15   * Manages colors that will be applied to forms and form widgets. The colors are
16   * chosen to make the widgets look correct in the editor area. If a different
17   * set of colors is needed, subclass this class and override 'initialize' and/or
18   * 'initializeColors'.
19   * 
20   * @since 1.0
21   */
22  public class FormColors {
23  	/**
24  	 * Key for the form title foreground color.
25  	 * 
26  	 * @deprecated use <code>IFormColors.TITLE</code>.
27  	 */
28  	public static final String TITLE = IFormColors.TITLE;
29  
30  	/**
31  	 * Key for the tree/table border color.
32  	 * 
33  	 * @deprecated use <code>IFormColors.BORDER</code>
34  	 */
35  	public static final String BORDER = IFormColors.BORDER;
36  
37  	/**
38  	 * Key for the section separator color.
39  	 * 
40  	 * @deprecated use <code>IFormColors.SEPARATOR</code>.
41  	 */
42  	public static final String SEPARATOR = IFormColors.SEPARATOR;
43  
44  	/**
45  	 * Key for the section title bar background.
46  	 * 
47  	 * @deprecated use <code>IFormColors.TB_BG
48  	 */
49  	public static final String TB_BG = IFormColors.TB_BG;
50  
51  	/**
52  	 * Key for the section title bar foreground.
53  	 * 
54  	 * @deprecated use <code>IFormColors.TB_FG</code>
55  	 */
56  	public static final String TB_FG = IFormColors.TB_FG;
57  
58  	/**
59  	 * Key for the section title bar gradient.
60  	 * 
61  	 * @deprecated use <code>IFormColors.TB_GBG</code>
62  	 */
63  	public static final String TB_GBG = IFormColors.TB_GBG;
64  
65  	/**
66  	 * Key for the section title bar border.
67  	 * 
68  	 * @deprecated use <code>IFormColors.TB_BORDER</code>.
69  	 */
70  	public static final String TB_BORDER = IFormColors.TB_BORDER;
71  
72  	/**
73  	 * Key for the section toggle color. Since 3.1, this color is used for all
74  	 * section styles.
75  	 * 
76  	 * @deprecated use <code>IFormColors.TB_TOGGLE</code>.
77  	 */
78  	public static final String TB_TOGGLE = IFormColors.TB_TOGGLE;
79  
80  	/**
81  	 * Key for the section toggle hover color.
82  	 * 
83  	 * @deprecated use <code>IFormColors.TB_TOGGLE_HOVER</code>.
84  	 */
85  	public static final String TB_TOGGLE_HOVER = IFormColors.TB_TOGGLE_HOVER;
86  
87  	protected Map colorRegistry = new HashMap(10);
88  
89  	private LocalResourceManager resources;
90  
91  	protected Color background;
92  
93  	protected Color foreground;
94  
95  	private boolean shared;
96  
97  	protected Display display;
98  
99  	protected Color border;
100 
101 	/**
102 	 * Creates form colors using the provided display.
103 	 * 
104 	 * @param display
105 	 *            the display to use
106 	 */
107 	public FormColors(Display display) {
108 		this.display = display;
109 		initialize();
110 	}
111 
112 	/**
113 	 * Returns the display used to create colors.
114 	 * 
115 	 * @return the display
116 	 */
117 	public Display getDisplay() {
118 		return display;
119 	}
120 
121 	/**
122 	 * Initializes the colors. Subclasses can override this method to change the
123 	 * way colors are created. Alternatively, only the color table can be
124 	 * modified by overriding <code>initializeColorTable()</code>.
125 	 * 
126 	 * @see #initializeColorTable
127 	 */
128 	protected void initialize() {
129 		background = display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
130 		foreground = display.getSystemColor(SWT.COLOR_LIST_FOREGROUND);
131 		initializeColorTable();
132 		updateBorderColor();
133 	}
134 
135 	/**
136 	 * Allocates colors for the following keys: BORDER, SEPARATOR and
137 	 * TITLE. Subclasses can override to allocate these colors differently.
138 	 */
139 	protected void initializeColorTable() {
140 		createTitleColor();
141 		createColor(IFormColors.SEPARATOR, getColor(IFormColors.TITLE).getRGB());
142 		RGB black = getSystemColor(SWT.COLOR_BLACK);
143 		RGB borderRGB = getSystemColor(SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT);
144 		createColor(IFormColors.BORDER, blend(borderRGB, black, 80));
145 	}
146 
147 	/**
148 	 * Allocates colors for the section tool bar (all the keys that start with
149 	 * TB). Since these colors are only needed when TITLE_BAR style is used with
150 	 * the Section widget, they are not needed all the time and are allocated on
151 	 * demand. Consequently, this method will do nothing if the colors have been
152 	 * already initialized. Call this method prior to using colors with the TB
153 	 * keys to ensure they are available.
154 	 */
155 	public void initializeSectionToolBarColors() {
156 		if (colorRegistry.containsKey(IFormColors.TB_BG))
157 			return;
158 		createTitleBarGradientColors();
159 		createTitleBarOutlineColors();
160 		createTwistieColors();
161 	}
162 
163 	/**
164 	 * Allocates additional colors for the form header, namely background
165 	 * gradients, bottom separator keylines and DND highlights. Since these
166 	 * colors are only needed for clients that want to use these particular
167 	 * style of header rendering, they are not needed all the time and are
168 	 * allocated on demand. Consequently, this method will do nothing if the
169 	 * colors have been already initialized. Call this method prior to using
170 	 * color keys with the H_ prefix to ensure they are available.
171 	 */
172 	protected void initializeFormHeaderColors() {
173 		if (colorRegistry.containsKey(IFormColors.H_BOTTOM_KEYLINE2))
174 			return;
175 		createFormHeaderColors();
176 	}
177 
178 	/**
179 	 * Returns the RGB value of the system color represented by the code
180 	 * argument, as defined in <code>SWT</code> class.
181 	 * 
182 	 * @param code
183 	 *            the system color constant as defined in <code>SWT</code>
184 	 *            class.
185 	 * @return the RGB value of the system color
186 	 */
187 	public RGB getSystemColor(int code) {
188 		return getDisplay().getSystemColor(code).getRGB();
189 	}
190 
191 	/**
192 	 * Creates the color for the specified key using the provided RGB object.
193 	 * The color object will be returned and also put into the registry. When
194 	 * the class is disposed, the color will be disposed with it.
195 	 * 
196 	 * @param key
197 	 *            the unique color key
198 	 * @param rgb
199 	 *            the RGB object
200 	 * @return the allocated color object
201 	 */
202 	public Color createColor(String key, RGB rgb) {
203 		// RAP [rh] changes due to missing Color constructor
204 //		Color c = getResourceManager().createColor(rgb);
205 //		Color prevC = (Color) colorRegistry.get(key);
206 //		if (prevC != null && !prevC.isDisposed())
207 //			getResourceManager().destroyColor(prevC.getRGB());
208 //	  	Color c = Graphics.getColor(rgb);
209 		Color c = new Color(display, rgb);
210 		colorRegistry.put(key, c);	  
211 		return c;
212 	}
213 
214 	/**
215 	 * Creates a color that can be used for areas of the form that is inactive.
216 	 * These areas can contain images, links, controls and other content but are
217 	 * considered auxilliary to the main content area.
218 	 * 
219 	 * <p>
220 	 * The color should not be disposed because it is managed by this class.
221 	 * 
222 	 * @return the inactive form color
223 	 */
224 	public Color getInactiveBackground() {
225 		String key = "__ncbg__"; //$NON-NLS-1$
226 		Color color = getColor(key);
227 		if (color == null) {
228 			RGB sel = getSystemColor(SWT.COLOR_LIST_SELECTION);
229 			// a blend of 95% white and 5% list selection system color
230 			RGB ncbg = blend(sel, getSystemColor(SWT.COLOR_WHITE), 5);
231 			color = createColor(key, ncbg);
232 		}
233 		return color;
234 	}
235 
236 	/**
237 	 * Creates the color for the specified key using the provided RGB values.
238 	 * The color object will be returned and also put into the registry. If
239 	 * there is already another color object under the same key in the registry,
240 	 * the existing object will be disposed. When the class is disposed, the
241 	 * color will be disposed with it.
242 	 * 
243 	 * @param key
244 	 *            the unique color key
245 	 * @param r
246 	 *            red value
247 	 * @param g
248 	 *            green value
249 	 * @param b
250 	 *            blue value
251 	 * @return the allocated color object
252 	 */
253 	public Color createColor(String key, int r, int g, int b) {
254 		return createColor(key, new RGB(r,g,b));
255 	}
256 
257 	/**
258 	 * Computes the border color relative to the background. Allocated border
259 	 * color is designed to work well with white. Otherwise, stanard widget
260 	 * background color will be used.
261 	 */
262 	protected void updateBorderColor() {
263 		if (isWhiteBackground())
264 			border = getColor(IFormColors.BORDER);
265 		else {
266 			border = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
267 			Color bg = getImpliedBackground();
268 			if (border.getRed() == bg.getRed()
269 					&& border.getGreen() == bg.getGreen()
270 					&& border.getBlue() == bg.getBlue())
271 				border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
272 		}
273 	}
274 
275 	/**
276 	 * Sets the background color. All the toolkits that use this class will
277 	 * share the same background.
278 	 * 
279 	 * @param bg
280 	 *            background color
281 	 */
282 	public void setBackground(Color bg) {
283 		this.background = bg;
284 		updateBorderColor();
285 		updateFormHeaderColors();
286 	}
287 
288 	/**
289 	 * Sets the foreground color. All the toolkits that use this class will
290 	 * share the same foreground.
291 	 * 
292 	 * @param fg
293 	 *            foreground color
294 	 */
295 	public void setForeground(Color fg) {
296 		this.foreground = fg;
297 	}
298 
299 	/**
300 	 * Returns the current background color.
301 	 * 
302 	 * @return the background color
303 	 */
304 	public Color getBackground() {
305 		return background;
306 	}
307 
308 	/**
309 	 * Returns the current foreground color.
310 	 * 
311 	 * @return the foreground color
312 	 */
313 	public Color getForeground() {
314 		return foreground;
315 	}
316 
317 	/**
318 	 * Returns the computed border color. Border color depends on the background
319 	 * and is recomputed whenever the background changes.
320 	 * 
321 	 * @return the current border color
322 	 */
323 	public Color getBorderColor() {
324 		return border;
325 	}
326 
327 	/**
328 	 * Tests if the background is white. White background has RGB value
329 	 * 255,255,255.
330 	 * 
331 	 * @return <samp>true</samp> if background is white, <samp>false</samp>
332 	 *         otherwise.
333 	 */
334 	public boolean isWhiteBackground() {
335 		Color bg = getImpliedBackground();
336 		return bg.getRed() == 255 && bg.getGreen() == 255
337 				&& bg.getBlue() == 255;
338 	}
339 
340 	/**
341 	 * Returns the color object for the provided key or <samp>null </samp> if
342 	 * not in the registry.
343 	 * 
344 	 * @param key
345 	 *            the color key
346 	 * @return color object if found, or <samp>null </samp> if not.
347 	 */
348 	public Color getColor(String key) {
349 		if (key.startsWith(IFormColors.TB_PREFIX))
350 			initializeSectionToolBarColors();
351 		else if (key.startsWith(IFormColors.H_PREFIX))
352 			initializeFormHeaderColors();
353 		return (Color) colorRegistry.get(key);
354 	}
355 
356 	/**
357 	 * Disposes all the colors in the registry.
358 	 */
359 	public void dispose() {
360 		if (resources != null)
361 			resources.dispose();
362 		resources = null;
363 		colorRegistry = null;
364 	}
365 
366 	/**
367 	 * Marks the colors shared. This prevents toolkits that share this object
368 	 * from disposing it.
369 	 */
370 	public void markShared() {
371 		this.shared = true;
372 	}
373 
374 	/**
375 	 * Tests if the colors are shared.
376 	 * 
377 	 * @return <code>true</code> if shared, <code>false</code> otherwise.
378 	 */
379 	public boolean isShared() {
380 		return shared;
381 	}
382 
383 	/**
384 	 * Blends c1 and c2 based in the provided ratio.
385 	 * 
386 	 * @param c1
387 	 *            first color
388 	 * @param c2
389 	 *            second color
390 	 * @param ratio
391 	 *            percentage of the first color in the blend (0-100)
392 	 * @return the RGB value of the blended color
393 	 */
394 	public static RGB blend(RGB c1, RGB c2, int ratio) {
395 		int r = blend(c1.red, c2.red, ratio);
396 		int g = blend(c1.green, c2.green, ratio);
397 		int b = blend(c1.blue, c2.blue, ratio);
398 		return new RGB(r, g, b);
399 	}
400 
401 	/**
402 	 * Tests the source RGB for range.
403 	 * 
404 	 * @param rgb
405 	 *            the tested RGB
406 	 * @param from
407 	 *            range start (excluding the value itself)
408 	 * @param to
409 	 *            range end (excluding the value itself)
410 	 * @return <code>true</code> if at least one of the primary colors in the
411 	 *         source RGB are within the provided range, <code>false</code>
412 	 *         otherwise.
413 	 */
414 	public static boolean testAnyPrimaryColor(RGB rgb, int from, int to) {
415 		if (testPrimaryColor(rgb.red, from, to))
416 			return true;
417 		if (testPrimaryColor(rgb.green, from, to))
418 			return true;
419 		if (testPrimaryColor(rgb.blue, from, to))
420 			return true;
421 		return false;
422 	}
423 
424 	/**
425 	 * Tests the source RGB for range.
426 	 * 
427 	 * @param rgb
428 	 *            the tested RGB
429 	 * @param from
430 	 *            range start (excluding the value itself)
431 	 * @param to
432 	 *            tange end (excluding the value itself)
433 	 * @return <code>true</code> if at least two of the primary colors in the
434 	 *         source RGB are within the provided range, <code>false</code>
435 	 *         otherwise.
436 	 */
437 	public static boolean testTwoPrimaryColors(RGB rgb, int from, int to) {
438 		int total = 0;
439 		if (testPrimaryColor(rgb.red, from, to))
440 			total++;
441 		if (testPrimaryColor(rgb.green, from, to))
442 			total++;
443 		if (testPrimaryColor(rgb.blue, from, to))
444 			total++;
445 		return total >= 2;
446 	}
447 
448 	/**
449 	 * Blends two primary color components based on the provided ratio.
450 	 * 
451 	 * @param v1
452 	 *            first component
453 	 * @param v2
454 	 *            second component
455 	 * @param ratio
456 	 *            percentage of the first component in the blend
457 	 * @return
458 	 */
459 	private static int blend(int v1, int v2, int ratio) {
460 		int b = (ratio * v1 + (100 - ratio) * v2) / 100;
461 		return Math.min(255, b);
462 	}
463 
464 	private Color getImpliedBackground() {
465 		if (getBackground() != null)
466 			return getBackground();
467 		return getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
468 	}
469 
470 	private static boolean testPrimaryColor(int value, int from, int to) {
471 		return value > from && value < to;
472 	}
473 
474 	private void createTitleColor() {
475 		/*
476 		 * RGB rgb = getSystemColor(SWT.COLOR_LIST_SELECTION); // test too light
477 		 * if (testTwoPrimaryColors(rgb, 120, 151)) rgb = blend(rgb, BLACK, 80);
478 		 * else if (testTwoPrimaryColors(rgb, 150, 256)) rgb = blend(rgb, BLACK,
479 		 * 50); createColor(TITLE, rgb);
480 		 */
481 		RGB bg = getImpliedBackground().getRGB();
482 		RGB listSelection = getSystemColor(SWT.COLOR_LIST_SELECTION);
483 		RGB listForeground = getSystemColor(SWT.COLOR_LIST_FOREGROUND);
484 		RGB rgb = listSelection;
485 
486 		// Group 1
487 		// Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or
488 		// between 0 and 120, then use 100% LIST_SELECTION as it is (no
489 		// additions)
490 		// Examples: XP Default, Win Classic Standard, Win High Con White, Win
491 		// Classic Marine
492 		if (testTwoPrimaryColors(listSelection, -1, 121))
493 			rgb = listSelection;
494 		// Group 2
495 		// When LIST_BACKGROUND = white (255, 255, 255) or not black, text
496 		// colour = LIST_SELECTION @ 100% Opacity + 50% LIST_FOREGROUND over
497 		// LIST_BACKGROUND
498 		// Rule: If at least 2 of the LIST_SELECTION RGB values are equal to or
499 		// between 121 and 255, then add 50% LIST_FOREGROUND to LIST_SELECTION
500 		// foreground colour
501 		// Examples: Win Vista, XP Silver, XP Olive , Win Classic Plum, OSX
502 		// Aqua, OSX Graphite, Linux GTK
503 		else if (testTwoPrimaryColors(listSelection, 120, 256)
504 				|| (bg.red == 0 && bg.green == 0 && bg.blue == 0))
505 			rgb = blend(listSelection, listForeground, 50);
506 		// Group 3
507 		// When LIST_BACKGROUND = black (0, 0, 0), text colour = LIST_SELECTION
508 		// @ 100% Opacity + 50% LIST_FOREGROUND over LIST_BACKGROUND
509 		// Rule: If LIST_BACKGROUND = 0, 0, 0, then add 50% LIST_FOREGROUND to
510 		// LIST_SELECTION foreground colour
511 		// Examples: Win High Con Black, Win High Con #1, Win High Con #2
512 		// (covered in the second part of the OR clause above)
513 		createColor(IFormColors.TITLE, rgb);
514 	}
515 
516 	private void createTwistieColors() {
517 		RGB rgb = getColor(IFormColors.TITLE).getRGB();
518 		RGB white = getSystemColor(SWT.COLOR_WHITE);
519 		createColor(TB_TOGGLE, rgb);
520 		rgb = blend(rgb, white, 60);
521 		createColor(TB_TOGGLE_HOVER, rgb);
522 	}
523 
524 	private void createTitleBarGradientColors() {
525 		RGB tbBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
526 		RGB bg = getImpliedBackground().getRGB();
527 
528 		// Group 1
529 		// Rule: If at least 2 of the RGB values are equal to or between 180 and
530 		// 255, then apply specified opacity for Group 1
531 		// Examples: Vista, XP Silver, Wn High Con #2
532 		// Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
533 		// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
534 		if (testTwoPrimaryColors(tbBg, 179, 256))
535 			tbBg = blend(tbBg, bg, 30);
536 
537 		// Group 2
538 		// Rule: If at least 2 of the RGB values are equal to or between 121 and
539 		// 179, then apply specified opacity for Group 2
540 		// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
541 		// Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
542 		// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
543 		else if (testTwoPrimaryColors(tbBg, 120, 180))
544 			tbBg = blend(tbBg, bg, 20);
545 
546 		// Group 3
547 		// Rule: Everything else
548 		// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
549 		// Aqua, Wn High Con White, Wn High Con #1
550 		// Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
551 		// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
552 		else {
553 			tbBg = blend(tbBg, bg, 10);
554 		}
555 
556 		createColor(IFormColors.TB_BG, tbBg);
557 		
558 		// for backward compatibility
559 		createColor(TB_GBG, tbBg);
560 	}
561 
562 	private void createTitleBarOutlineColors() {
563 		// title bar outline - border color
564 		RGB tbBorder = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
565 		RGB bg = getImpliedBackground().getRGB();
566 		// Group 1
567 		// Rule: If at least 2 of the RGB values are equal to or between 180 and
568 		// 255, then apply specified opacity for Group 1
569 		// Examples: Vista, XP Silver, Wn High Con #2
570 		// Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
571 		if (testTwoPrimaryColors(tbBorder, 179, 256))
572 			tbBorder = blend(tbBorder, bg, 70);
573 
574 		// Group 2
575 		// Rule: If at least 2 of the RGB values are equal to or between 121 and
576 		// 179, then apply specified opacity for Group 2
577 		// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
578 
579 		// Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND
580 		else if (testTwoPrimaryColors(tbBorder, 120, 180))
581 			tbBorder = blend(tbBorder, bg, 50);
582 
583 		// Group 3
584 		// Rule: Everything else
585 		// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
586 		// Aqua, Wn High Con White, Wn High Con #1
587 
588 		// Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
589 		else {
590 			tbBorder = blend(tbBorder, bg, 30);
591 		}
592 		createColor(FormColors.TB_BORDER, tbBorder);
593 	}
594 
595 	private void updateFormHeaderColors() {
596 		if (colorRegistry.containsKey(IFormColors.H_GRADIENT_END)) {
597 			disposeIfFound(IFormColors.H_GRADIENT_END);
598 			disposeIfFound(IFormColors.H_GRADIENT_START);
599 			disposeIfFound(IFormColors.H_BOTTOM_KEYLINE1);
600 			disposeIfFound(IFormColors.H_BOTTOM_KEYLINE2);
601 			disposeIfFound(IFormColors.H_HOVER_LIGHT);
602 			disposeIfFound(IFormColors.H_HOVER_FULL);
603 			initializeFormHeaderColors();
604 		}
605 	}
606 
607 	private void disposeIfFound(String key) {
608 		Color color = getColor(key);
609 		if (color != null) {
610 			colorRegistry.remove(key);
611 		// RAP [rh] changes due to missing Color#dispose()			
612 //			color.dispose();
613 		}
614 	}
615 
616 	private void createFormHeaderColors() {
617 		createFormHeaderGradientColors();
618 		createFormHeaderKeylineColors();
619 		createFormHeaderDNDColors();
620 	}
621 
622 	private void createFormHeaderGradientColors() {
623 		RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
624 		Color bgColor = getImpliedBackground();
625 		RGB bg = bgColor.getRGB();
626 		RGB bottom, top;
627 		// Group 1
628 		// Rule: If at least 2 of the RGB values are equal to or between 180 and
629 		// 255, then apply specified opacity for Group 1
630 		// Examples: Vista, XP Silver, Wn High Con #2
631 		// Gradient Bottom = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
632 		// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
633 		if (testTwoPrimaryColors(titleBg, 179, 256)) {
634 			bottom = blend(titleBg, bg, 30);
635 			top = bg;
636 		}
637 
638 		// Group 2
639 		// Rule: If at least 2 of the RGB values are equal to or between 121 and
640 		// 179, then apply specified opacity for Group 2
641 		// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
642 		// Gradient Bottom = TITLE_BACKGROUND @ 20% Opacity over LIST_BACKGROUND
643 		// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
644 		else if (testTwoPrimaryColors(titleBg, 120, 180)) {
645 			bottom = blend(titleBg, bg, 20);
646 			top = bg;
647 		}
648 
649 		// Group 3
650 		// Rule: If at least 2 of the RGB values are equal to or between 0 and
651 		// 120, then apply specified opacity for Group 3
652 		// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
653 		// Aqua, Wn High Con White, Wn High Con #1
654 		// Gradient Bottom = TITLE_BACKGROUND @ 10% Opacity over LIST_BACKGROUND
655 		// Gradient Top = TITLE BACKGROUND @ 0% Opacity over LIST_BACKGROUND
656 		else {
657 			bottom = blend(titleBg, bg, 10);
658 			top = bg;
659 		}
660 		createColor(IFormColors.H_GRADIENT_END, top);
661 		createColor(IFormColors.H_GRADIENT_START, bottom);
662 	}
663 
664 	private void createFormHeaderKeylineColors() {
665 		RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND);
666 		Color bgColor = getImpliedBackground();
667 		RGB bg = bgColor.getRGB();
668 		RGB keyline2;
669 		// H_BOTTOM_KEYLINE1
670 		createColor(IFormColors.H_BOTTOM_KEYLINE1, new RGB(255, 255, 255));
671 
672 		// H_BOTTOM_KEYLINE2
673 		// Group 1
674 		// Rule: If at least 2 of the RGB values are equal to or between 180 and
675 		// 255, then apply specified opacity for Group 1
676 		// Examples: Vista, XP Silver, Wn High Con #2
677 		// Keyline = TITLE_BACKGROUND @ 70% Opacity over LIST_BACKGROUND
678 		if (testTwoPrimaryColors(titleBg, 179, 256))
679 			keyline2 = blend(titleBg, bg, 70);
680 
681 		// Group 2
682 		// Rule: If at least 2 of the RGB values are equal to or between 121 and
683 		// 179, then apply specified opacity for Group 2
684 		// Examples: XP Olive, OSX Graphite, Linux GTK, Wn High Con Black
685 		// Keyline = TITLE_BACKGROUND @ 50% Opacity over LIST_BACKGROUND
686 		else if (testTwoPrimaryColors(titleBg, 120, 180))
687 			keyline2 = blend(titleBg, bg, 50);
688 
689 		// Group 3
690 		// Rule: If at least 2 of the RGB values are equal to or between 0 and
691 		// 120, then apply specified opacity for Group 3
692 		// Examples: XP Default, Wn Classic Standard, Wn Marine, Wn Plum, OSX
693 		// Aqua, Wn High Con White, Wn High Con #1
694 
695 		// Keyline = TITLE_BACKGROUND @ 30% Opacity over LIST_BACKGROUND
696 		else
697 			keyline2 = blend(titleBg, bg, 30);
698 		// H_BOTTOM_KEYLINE2
699 		createColor(IFormColors.H_BOTTOM_KEYLINE2, keyline2);
700 	}
701 
702 	private void createFormHeaderDNDColors() {
703 		RGB titleBg = getSystemColor(SWT.COLOR_TITLE_BACKGROUND_GRADIENT);
704 		Color bgColor = getImpliedBackground();
705 		RGB bg = bgColor.getRGB();
706 		RGB light, full;
707 		// ALL Themes
708 		//
709 		// Light Highlight
710 		// When *near* the 'hot' area
711 		// Rule: If near the title in the 'hot' area, show background highlight
712 		// TITLE_BACKGROUND_GRADIENT @ 40%
713 		light = blend(titleBg, bg, 40);
714 		// Full Highlight
715 		// When *on* the title area (regions 1 and 2)
716 		// Rule: If near the title in the 'hot' area, show background highlight
717 		// TITLE_BACKGROUND_GRADIENT @ 60%
718 		full = blend(titleBg, bg, 60);
719 		// H_DND_LIGHT
720 		// H_DND_FULL
721 		createColor(IFormColors.H_HOVER_LIGHT, light);
722 		createColor(IFormColors.H_HOVER_FULL, full);
723 	}
724 	
725 	private LocalResourceManager getResourceManager() {
726 		if (resources == null)
727 			resources = new LocalResourceManager(JFaceResources.getResources());
728 		return resources;
729 	}
730 }