blob: f655c128162bca53407536ff657ef66f57cb93a0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.ui.internal.widgets;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
/**
* This <code>TriStateCheckBox</code> is responsible to handle three states:
* unchecked, checked and partially selected. Either from a mouse selection,
* keyboard selection or programmatically, the selection state is using a
* <code>Boolean</code> value where a <code>null</code> value means partially
* selected.
* <p>
* The order of state state is: unchecked -> partially selected -> checked.
*
* @version 2.0
* @since 2.0
*/
@SuppressWarnings("nls")
public final class TriStateCheckBox {
/**
* Flag used to prevent the selection listener from changing the selection
* state when the mouse is changing it since the selection state is called
* after the mouse down event and before the mouse up event.
*/
private boolean handledByMouseEvent;
/**
* The current selection state.
*/
private TriState state;
/**
* A tri-state check box can only be used within a tree or table, we used
* here the table widget.
*/
private Table table;
/**
* Creates a new <code>TriStateCheckBox</code>.
*
* @param parent The parent composite
*/
public TriStateCheckBox(Composite parent) {
super();
this.state = TriState.UNCHECKED;
this.buildWidgets(parent);
}
/**
* @see org.eclipse.swt.widgets.Widget#addDisposeListener(DisposeListener)
*/
public void addDisposeListener(DisposeListener disposeListener) {
this.table.addDisposeListener(disposeListener);
}
/**
* @see Table#addSelectionListener(SelectionListener)
*/
public void addSelectionListener(SelectionListener selectionListener) {
this.table.addSelectionListener(selectionListener);
}
private MouseAdapter buildMouseListener() {
return new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
handledByMouseEvent = true;
TriStateCheckBox.this.changeTriState();
TriStateCheckBox.this.updateCheckBox();
}
@Override
public void mouseUp(MouseEvent e) {
TriStateCheckBox.this.updateCheckBox();
}
};
}
private SelectionAdapter buildSelectionListener() {
return new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (handledByMouseEvent) {
handledByMouseEvent = false;
}
else {
TriStateCheckBox.this.changeTriState();
TriStateCheckBox.this.updateCheckBox();
}
}
};
}
private TriState buildState(Boolean selection) {
if (selection == null) {
return TriState.PARTIALLY_CHECKED;
}
return selection.booleanValue() ? TriState.CHECKED : TriState.UNCHECKED;
}
private Layout buildTableLayout() {
return new Layout() {
@Override
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
Rectangle bounds = TriStateCheckBox.this.getCheckBox().getBounds();
return new Point(bounds.x + bounds.width, bounds.y + bounds.height);
}
private int indentation() {
if (Platform.OS_WIN32.equals(Platform.getOS())) {
try {
String version = System.getProperty("os.version");
// Under Vista, the check box has to be indented by -6
// but under XP, it needs to remain 0
if (Double.parseDouble(version) >= 6) {
return 6;
}
}
catch (Exception e) {
// Ignore and return 0
}
}
return 0;
}
@Override
protected void layout(Composite composite, boolean flushCache) {
Rectangle bounds = TriStateCheckBox.this.getCheckBox().getBounds();
int indentation = indentation();
TriStateCheckBox.this.table.setBounds(
-indentation,
0,
bounds.x + bounds.width + indentation,
bounds.y + bounds.height
);
}
};
}
private void buildWidgets(Composite parent) {
parent = new Composite(parent, SWT.NULL);
parent.setLayout(this.buildTableLayout());
this.table = new Table(parent, SWT.CHECK | SWT.SINGLE | SWT.TRANSPARENT);
this.table.addMouseListener(buildMouseListener());
this.table.addSelectionListener(buildSelectionListener());
this.table.getHorizontalBar().setVisible(false);
this.table.getVerticalBar().setVisible(false);
new TableItem(this.table, SWT.CHECK);
}
private void changeTriState() {
if (this.state == TriState.UNCHECKED) {
this.state = TriState.PARTIALLY_CHECKED;
}
else if (this.state == TriState.CHECKED) {
this.state = TriState.UNCHECKED;
}
else {
this.state = TriState.CHECKED;
}
}
/**
* Returns the actual <code>TableItem</code> used to show a tri-state check
* box.
*
* @return The unique item of the table that handles tri-state selection
*/
public TableItem getCheckBox() {
return this.table.getItem(0);
}
/**
* Returns the main widget of the tri-state check box.
*
* @return The main composite used to display the tri-state check box
*/
public Control getControl() {
return this.table.getParent();
}
/**
* Returns the check box's image.
*
* @return The image of the check box
*/
public Image getImage() {
return this.getCheckBox().getImage();
}
/**
* Returns the selection state of the check box.
*
* @return Either <code>true</code> or <code>false</code> for checked and
* unchecked; or <code>null</code> for partially selected
*/
public Boolean getSelection() {
return (this.state == TriState.PARTIALLY_CHECKED) ? null : (this.state == TriState.CHECKED);
}
/**
* Returns the check box's text.
*
* @return The text of the check box
*/
public String getText() {
return this.getCheckBox().getText();
}
/**
* Determines whether the check box is enabled or not.
*
* @return <code>true</code> if the check box is enabled; <code>false</code>
* otherwise
*/
public boolean isEnabled() {
return this.table.isEnabled();
}
/**
* @see org.eclipse.swt.widgets.Widget#removeDisposeListener(DisposeListener)
*/
public void removeDisposeListener(DisposeListener disposeListener) {
this.table.removeDisposeListener(disposeListener);
}
/**
* @see Table#removeSelectionListener(SelectionListener)
*/
public void removeSelectionListener(SelectionListener selectionListener) {
this.table.removeSelectionListener(selectionListener);
}
/**
* Changes the enablement state of the widgets of this pane.
*
* @param enabled <code>true</code> to enable the widgets or <code>false</code>
* to disable them
*/
public void setEnabled(boolean enabled) {
this.table.setEnabled(enabled);
}
/**
* Sets the check box's image.
*
* @param image The new image of the check box
*/
public void setImage(Image image) {
// TODO: Not sure this will update the layout, if that is the case,
// then copy the code from LabeledTableItem.updateTableItem()
this.getCheckBox().setImage(image);
}
/**
* Changes the selection state of the check box.
*
* @param selection Either <code>true</code> or <code>false</code> for
* checked and unchecked; or <code>null</code> for partially selected
*/
public void setSelection(Boolean selection) {
TriState oldState = this.state;
this.state = this.buildState(selection);
if (oldState != this.state) {
this.updateCheckBox();
}
}
/**
* Sets the check box's text.
*
* @param text The new text of the check box
*/
public void setText(String text) {
// TODO: Not sure this will update the layout, if that is the case,
// then copy the code from LabeledTableItem.updateTableItem()
this.getCheckBox().setText(text);
}
/**
* Updates the selection state of the of the check box based on the tri-state
* value.
*/
private void updateCheckBox() {
TableItem checkBox = this.getCheckBox();
if (this.state == TriState.PARTIALLY_CHECKED) {
checkBox.setChecked(true);
checkBox.setGrayed(true);
}
else {
checkBox.setGrayed(false);
checkBox.setChecked(this.state == TriState.CHECKED);
}
}
/**
* An enum containing the possible selection.
*/
public enum TriState {
CHECKED,
PARTIALLY_CHECKED,
UNCHECKED
}
}