blob: 7d9ae224cad80b38b8356a06db3bde9f01d59e59 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
/**
* Instances of this class allow the user to select a color
* from a predefined set of available colors.
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>(none)</dd>
* <dt><b>Events:</b></dt>
* <dd>(none)</dd>
* </dl>
* <p>
* IMPORTANT: This class is intended to be subclassed <em>only</em>
* within the SWT implementation.
* </p>
*/
public class ColorDialog extends Dialog {
private static final int COLORSWATCH_SIZE_DEPTH4 = 40;
private static final int COLORSWATCH_SIZE_DEPTH8 = 15;
private static final int COLORSWATCH_SIZE_DEPTH16 = 10;
private static final int COLORSWATCH_BORDER = 1; // border between each color pad
private Shell shell; // the dialog shell
private Canvas colorsCanvas;
private Label sampleLabel, selectionLabel;
private Canvas sampleCanvas, selectionCanvas;
private Button okButton, cancelButton;
private int colorChooserWidth, colorChooserHeight;
private boolean okSelected;
private RGB rgb;
private int colorDepth; // color depth of the display
private int colorSwatchExtent; // the size of each color square
private Color colorGrid[][]; // the colors displayed in the dialog
/**
* Constructs a new instance of this class given only its parent.
*
* @param parent a composite control which will be the parent of the new instance
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*
* @see SWT
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public ColorDialog(Shell parent) {
this(parent, SWT.NULL);
}
/**
* Constructs a new instance of this class given its parent
* and a style value describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in
* class <code>SWT</code> which is applicable to instances of this
* class, or must be built by <em>bitwise OR</em>'ing together
* (that is, using the <code>int</code> "|" operator) two or more
* of those <code>SWT</code> style constants. The class description
* lists the style constants that are applicable to the class.
* Style bits are also inherited from superclasses.
* </p>
*
* @param parent a composite control which will be the parent of the new instance (cannot be null)
* @param style the style of control to construct
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
* <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
* </ul>
*
* @see SWT
* @see Widget#checkSubclass
* @see Widget#getStyle
*/
public ColorDialog(Shell parent, int style) {
super(parent, style | SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL);
checkSubclass ();
}
void createChildren() {
Shell dialog = shell;
GridLayout layout = new GridLayout (2, false);
dialog.setLayout(layout);
colorChooserWidth = colorSwatchExtent * colorGrid.length - 1;
colorChooserHeight = colorSwatchExtent * colorGrid[0].length - 1;
colorsCanvas = new Canvas(dialog, SWT.BORDER);
GridData data = new GridData ();
data.widthHint = colorChooserWidth;
data.heightHint = colorChooserHeight;
colorsCanvas.setLayoutData(data);
Composite buttonsGroup = new Composite (dialog, SWT.NONE);
buttonsGroup.setLayout(new GridLayout());
buttonsGroup.setLayoutData(new GridData(GridData.BEGINNING));
createOkCancel(buttonsGroup);
Composite bottomGroup = new Composite (dialog,SWT.NONE);
layout = new GridLayout(2, true);
layout.marginHeight = 0;
layout.marginWidth = 0;
bottomGroup.setLayout(layout);
bottomGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
createSampleGroup(bottomGroup);
createSelectionGroup(bottomGroup);
}
void createOkCancel(Composite parent) {
okButton = new Button(parent, SWT.PUSH);
okButton.setText(SWT.getMessage("SWT_OK"));
shell.setDefaultButton(okButton);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
okButton.setLayoutData(data);
cancelButton = new Button(parent, SWT.PUSH);
cancelButton.setText(SWT.getMessage("SWT_Cancel"));
data = new GridData(GridData.FILL_HORIZONTAL);
cancelButton.setLayoutData(data);
}
void createSampleGroup(Composite parent) {
Group sampleGroup = new Group(parent, SWT.NULL);
GridData data = new GridData(GridData.FILL_BOTH);
data.grabExcessHorizontalSpace = true;
sampleGroup.setLayout(new GridLayout());
sampleGroup.setLayoutData(data);
sampleGroup.setText(SWT.getMessage("SWT_Sample"));
sampleLabel = new Label(sampleGroup, SWT.CENTER | SWT.BORDER);
sampleLabel.setAlignment(SWT.CENTER);
sampleLabel.setText(SWT.getMessage("SWT_A_Sample_Text"));
data = new GridData(GridData.FILL_HORIZONTAL);
sampleLabel.setLayoutData(data);
sampleCanvas = new Canvas(sampleGroup, SWT.BORDER);
data = new GridData(GridData.FILL_HORIZONTAL);
data.heightHint = 15;
sampleCanvas.setLayoutData(data);
}
void createSelectionGroup(Composite parent) {
Group selectionGroup = new Group(parent, SWT.NULL);
GridData data = new GridData(GridData.FILL_BOTH);
data.grabExcessHorizontalSpace = true;
selectionGroup.setLayout(new GridLayout());
selectionGroup.setLayoutData(data);
selectionGroup.setText(SWT.getMessage("SWT_Selection"));
selectionLabel = new Label(selectionGroup, SWT.CENTER | SWT.BORDER);
selectionLabel.setAlignment(SWT.CENTER);
selectionLabel.setText(SWT.getMessage("SWT_Current_Selection"));
data = new GridData(GridData.FILL_HORIZONTAL);
data.grabExcessHorizontalSpace = true;
selectionLabel.setLayoutData(data);
selectionCanvas = new Canvas(selectionGroup, SWT.BORDER);
data = new GridData(GridData.FILL_HORIZONTAL);
data.grabExcessHorizontalSpace = true;
data.heightHint = 15;
selectionCanvas.setLayoutData(data);
}
void disposeColors() {
for (int row = 0; row < colorGrid.length; row++) {
for (int column = 0; column < colorGrid[row].length; column++) {
colorGrid[row][column].dispose();
}
}
}
void drawColor(int xIndex, int yIndex, Color color, GC gc) {
int colorExtent = colorSwatchExtent - COLORSWATCH_BORDER;
gc.setBackground(color);
gc.fillRectangle(
xIndex * colorSwatchExtent,
yIndex * colorSwatchExtent,
colorExtent, colorExtent);
}
/**
* Returns the currently selected color in the receiver.
*
* @return the RGB value for the selected color, may be null
*
* @see PaletteData#getRGBs
*/
public RGB getRGB() {
return rgb;
}
void handleEvents(Event event) {
if (event.type == SWT.Paint) {
paint(event);
}
else
if (event.type == SWT.MouseDown) {
mouseDown(event);
}
else
if (event.type == SWT.MouseMove) {
mouseMove(event);
}
else
if (event.type == SWT.Selection) {
if (event.widget == okButton) {
okSelected = true;
shell.setVisible(false);
}
else
if (event.widget == cancelButton) {
okSelected = false;
shell.setVisible(false);
}
}
}
void initialize4BitColors() {
Display display = shell.display;
colorGrid[0][0] = new Color(display, 0, 0, 0);
colorGrid[0][1] = new Color(display, 255, 255, 255);
colorGrid[1][0] = new Color(display, 128, 128, 128);
colorGrid[1][1] = new Color(display, 192, 192, 192);
colorGrid[2][0] = new Color(display, 0, 0, 128);
colorGrid[2][1] = new Color(display, 0, 0, 255);
colorGrid[3][0] = new Color(display, 0, 128, 128);
colorGrid[3][1] = new Color(display, 0, 255, 255);
colorGrid[4][0] = new Color(display, 0, 128, 0);
colorGrid[4][1] = new Color(display, 0, 255, 0);
colorGrid[5][0] = new Color(display, 128, 128, 0);
colorGrid[5][1] = new Color(display, 255, 255, 0);
colorGrid[6][0] = new Color(display, 128, 0, 0);
colorGrid[6][1] = new Color(display, 255, 0, 0);
colorGrid[7][0] = new Color(display, 128, 0, 128);
colorGrid[7][1] = new Color(display, 255, 0, 255);
}
void initialize8BitColors() {
Display display = shell.display;
int numRows = colorGrid[0].length;
int iterationStep = 64;
int row = 0, column = 0;
int red, green, blue;
// run the loops from 0 to 256 inclusive since this is easiest for the step
// size, then adjust the 256 case to the proper 255 value when needed
for (red = 0; red <= 256; red += iterationStep) {
for (blue = 0; blue <= 256; blue += iterationStep) {
for (green = 0; green <= 256; green += iterationStep) {
if (row == numRows) {
row = 0;
column++;
}
if (red == 256) red = 255;
if (blue == 256) blue = 255;
if (green == 256) green = 255;
colorGrid[column][row++] = new Color(display, red, green, blue);
}
}
}
}
void initialize16BitColors() {
Display display = shell.display;
int numRows = colorGrid[0].length;
int iterationStep = 51;
int row = 0, column = 0;
int red, green, blue;
for (red = 0; red <= 255; red += iterationStep) {
for (blue = 0; blue <= 255; blue += iterationStep) {
if (blue == iterationStep && column < 20) { // hack to evenly distribute 256 colors on 32 columns
blue += iterationStep;
}
for (green = 0; green <= 255; green += iterationStep) {
if (row == 2 || row == 5) { // hack to evenly distribute 256 colors on 8 rows
colorGrid[column][row++] = new Color(display, red, green - iterationStep / 2, blue);
}
if (row == numRows) {
row = 0;
column++;
}
colorGrid[column][row++] = new Color(display, red, green, blue);
}
}
}
}
void initializeWidgets() {
Display display = shell.display;
if (rgb != null) {
Color selectionColor = new Color(display, rgb);
selectionCanvas.setBackground(selectionColor);
selectionLabel.setBackground(selectionColor);
selectionColor.dispose();
}
}
void installListeners() {
Listener listener = new Listener() {
public void handleEvent(Event event) {handleEvents(event);}
};
okButton.addListener(SWT.Selection, listener);
cancelButton.addListener(SWT.Selection, listener);
colorsCanvas.addListener(SWT.Paint, listener);
colorsCanvas.addListener(SWT.MouseDown, listener);
colorsCanvas.addListener(SWT.MouseMove, listener);
}
void mouseDown(Event event) {
int swatchExtent = colorSwatchExtent;
Color color = colorGrid[event.x / swatchExtent][event.y / swatchExtent];
selectionCanvas.setBackground(color);
selectionLabel.setBackground(color);
}
void mouseMove(Event event) {
int swatchExtent = colorSwatchExtent;
// adjust for events received from moving over the Canvas' border
if (!(0 <= event.x && event.x <= colorChooserWidth)) return;
if (!(0 <= event.y && event.y <= colorChooserHeight)) return;
int xgrid = Math.min(colorGrid.length - 1, event.x / swatchExtent);
int ygrid = Math.min(colorGrid[0].length - 1, event.y / swatchExtent);
Color color = colorGrid[xgrid][ygrid];
sampleCanvas.setBackground(color);
sampleLabel.setBackground(color);
}
/**
* Makes the receiver visible and brings it to the front
* of the display.
*
* @return the selected color, or null if the dialog was
* cancelled, no color was selected, or an error
* occurred
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
public RGB open() {
shell = new Shell(parent, getStyle() | SWT.TITLE | SWT.BORDER | SWT.APPLICATION_MODAL);
Display display = shell.display;
setColorDepth(display.getDepth());
createChildren();
installListeners();
openModal();
if (okSelected) {
Color selectionColor = selectionCanvas.getBackground();
rgb = new RGB(
selectionColor.getRed(),
selectionColor.getGreen(),
selectionColor.getBlue());
}
disposeColors();
if (shell.isDisposed() == false) {
shell.dispose();
}
if (!okSelected) return null;
return rgb;
}
/**
* Open the receiver and set its size to the size calculated by
* the layout manager.
*/
void openDialog() {
Shell dialog = shell;
// Start everything off by setting the shell size to its computed size.
Point pt = dialog.computeSize(-1, -1, false);
// Ensure that the width of the shell fits the display.
Display display = dialog.display;
Rectangle displayRect = display.getBounds();
int widthLimit = displayRect.width * 7 / 8;
int heightLimit = displayRect.height * 7 / 8;
if (pt.x > widthLimit) {
pt = dialog.computeSize (widthLimit, -1, false);
}
// centre the dialog on its parent, and ensure that the
// whole dialog appears within the screen bounds
Rectangle parentBounds = getParent ().getBounds ();
int originX = (parentBounds.width - pt.x) / 2 + parentBounds.x;
originX = Math.max (originX, 0);
originX = Math.min (originX, widthLimit - pt.x);
int originY = (parentBounds.height - pt.y) / 2 + parentBounds.y;
originY = Math.max (originY, 0);
originY = Math.min (originY, heightLimit - pt.y);
dialog.setBounds (originX, originY, pt.x, pt.y);
String title = getText ();
if (title.length () == 0) title = SWT.getMessage ("SWT_ColorDialog_Title");
shell.setText(title);
dialog.open();
}
void openModal() {
Display display = shell.display;
initializeWidgets();
openDialog();
while (shell.isDisposed() == false && shell.getVisible() == true) {
if (display.readAndDispatch() == false) {
display.sleep();
}
}
}
void paint(Event event) {
for (int column = 0; column < colorGrid.length; column++) {
for (int row = 0; row < colorGrid[0].length; row++) {
drawColor(column, row, colorGrid[column][row], event.gc);
}
}
}
void setColorDepth(int bits) {
colorDepth = bits;
if (bits == 4) {
colorSwatchExtent = COLORSWATCH_SIZE_DEPTH4;
colorGrid = new Color[8][2];
initialize4BitColors();
return;
}
if (bits == 8) {
colorSwatchExtent = COLORSWATCH_SIZE_DEPTH8;
colorGrid = new Color[25][5];
initialize8BitColors();
return;
}
// default case: 16, 24 or 32 bits
colorSwatchExtent = COLORSWATCH_SIZE_DEPTH16;
colorGrid = new Color[32][8];
initialize16BitColors();
}
/**
* Sets the receiver's selected color to be the argument.
*
* @param rgb the new RGB value for the selected color, may be
* null to let the platform select a default when
* open() is called
* @see PaletteData#getRGBs
*/
public void setRGB(RGB rgb) {
this.rgb = rgb;
}
}