Here's the code if someone is interested:
package at.bestsolution.jface.viewers;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Shell;
public abstract class EmulatedNativeCheckBoxLabelProvider
extends ColumnLabelProvider
{
private static final String CHECKED_KEY = "CHECKED";
private static final String UNCHECK_KEY = "UNCHECKED";
public EmulatedNativeCheckBoxLabelProvider(ColumnViewer viewer) {
if( JFaceResources.getImageRegistry().getDescriptor(CHECKED_KEY) == null ) {
JFaceResources.getImageRegistry().put(UNCHECK_KEY,
makeShot(viewer.getControl().getShell(),false));
JFaceResources.getImageRegistry().put(CHECKED_KEY,
makeShot(viewer.getControl().getShell(),true));
}
}
private Image makeShot(Shell shell, boolean type) {
Shell s = new Shell(shell,SWT.NO_TRIM);
Button b = new Button(s,SWT.CHECK);
b.setSelection(type);
Point bsize = b.computeSize(SWT.DEFAULT, SWT.DEFAULT);
b.setSize(bsize);
b.setLocation(0, 0);
s.setSize(bsize);
s.open();
GC gc = new GC(b);
Image image = new Image(shell.getDisplay(), bsize.x, bsize.y);
gc.copyArea(image, 0, 0);
gc.dispose();
s.close();
return image;
}
public Image getImage(Object element) {
if( isChecked(element) ) {
return JFaceResources.getImageRegistry().
getDescriptor(CHECKED_KEY).createImage();
} else {
return JFaceResources.getImageRegistry().
getDescriptor(UNCHECK_KEY).createImage();
}
}
protected abstract boolean isChecked(Object element);
}
I haven't really tested this (currently only on WinXP) but I suppose it's working on all platforms. You can also get the code from my svn-repository which holds some other interesting utilities and viewer classes.
15 comments:
neat stuff! thanks!
I catched a lot of your work, lately! :-)
That is a ludicrous hack. I LOVE IT!!!
However, you are able to add native (not native like) check boxes on the Tree/Table widget, which TreeViewer /TableViewer wraps.
Why can't the SWT.CHECK flag be pushed somehow into the underlying widget?
back with some more light on the mater. Here's a bit of code from the last JFace:
/**
* Creates a tree viewer on a newly-created tree control under the given
* parent. The tree control is created using the given SWT style bits. The
* viewer has no input, no content provider, a default label provider, no
* sorter, and no filters.
*
* @param parent
* the parent control
* @param style
* the SWT style bits used to create the tree.
*/
public TreeViewer(Composite parent, int style) {
this(new Tree(parent, style));
}
The style you specify here is passed unchanged to the inner Tree widget.
This worked perfectly for me:
TreeViewer viewer = new TreeViewer(viewerParent, SWT.CHECK);
I know SWT.CHECK but the problem is that with this you can only have a checkbox in the first column of your Table/Tree. This solution works in any column of a Viewer :-)
private Image makeShot(Control control, boolean type) {
Shell s = new Shell(control.getShell(), SWT.NO_TRIM);
// otherwise we have a default gray color
Color backgroundColor = control.getBackground();
s.setBackground(backgroundColor);
Button b = new Button(s, SWT.CHECK);
b.setBackground(backgroundColor);
b.setSelection(type);
// otherwise an image is located in a corner
b.setLocation(1, 1);
Point bsize = b.computeSize(SWT.DEFAULT, SWT.DEFAULT);
// otherwise an image is stretched by width
bsize.x=Math.max(bsize.x, bsize.y);
bsize.y=Math.max(bsize.x, bsize.y);
b.setSize(bsize);
s.setSize(bsize);
s.open();
GC gc = new GC(s);
Image image = new Image(control.getDisplay(), bsize.x, bsize.y);
gc.copyArea(image, 0, 0);
gc.dispose();
s.close();
return image;
}
here the fully tested and working code. improvements:
* checkbox is painted at the right position
* the correct beackground-color is used
* bugfix: the previous getImage() created a new image at every getImage()-call. this led to a NoMoreHandles-Exception
package de.fhmracing.glasseye.canexplorer.gui.transmit;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
public abstract class EmulatedNativeCheckBoxLabelProvider extends ColumnLabelProvider
{
private static final String CHECKED_KEY = "CHECKED";
private static final String UNCHECK_KEY = "UNCHECKED";
public EmulatedNativeCheckBoxLabelProvider(ColumnViewer viewer)
{
if (JFaceResources.getImageRegistry().getDescriptor(CHECKED_KEY) == null)
{
JFaceResources.getImageRegistry().put(UNCHECK_KEY, makeShot(viewer.getControl(), false));
JFaceResources.getImageRegistry().put(CHECKED_KEY, makeShot(viewer.getControl(), true));
}
}
private Image makeShot(Control control, boolean type)
{
Shell shell = new Shell(control.getShell(), SWT.NO_TRIM);
// otherwise we have a default gray color
Color backgroundColor = control.getBackground();
shell.setBackground(backgroundColor);
Button button = new Button(shell, SWT.CHECK);
button.setBackground(backgroundColor);
button.setSelection(type);
// otherwise an image is located in a corner
button.setLocation(1, 1);
Point bsize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
// otherwise an image is stretched by width
bsize.x = Math.max(bsize.x-1, bsize.y-1);
bsize.y = Math.max(bsize.x-1, bsize.y-1);
button.setSize(bsize);
shell.setSize(bsize);
shell.open();
GC gc = new GC(shell);
Image image = new Image(control.getDisplay(), bsize.x, bsize.y);
gc.copyArea(image, 0, 0);
gc.dispose();
shell.close();
return image;
}
public Image getImage(Object element)
{
if (isChecked(element))
{
return JFaceResources.getImageRegistry().get(CHECKED_KEY);
}
else
{
return JFaceResources.getImageRegistry().get(UNCHECK_KEY);
}
}
protected abstract boolean isChecked(Object element);
}
here the fully tested and working code. improvements:
* checkbox is painted at the right position
* the correct beackground-color is used
* bugfix: the previous getImage() created a new image at every getImage()-call. this led to a NoMoreHandles-Exception
package de.fhmracing.glasseye.canexplorer.gui.transmit;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
public abstract class EmulatedNativeCheckBoxLabelProvider extends ColumnLabelProvider
{
private static final String CHECKED_KEY = "CHECKED";
private static final String UNCHECK_KEY = "UNCHECKED";
public EmulatedNativeCheckBoxLabelProvider(ColumnViewer viewer)
{
if (JFaceResources.getImageRegistry().getDescriptor(CHECKED_KEY) == null)
{
JFaceResources.getImageRegistry().put(UNCHECK_KEY, makeShot(viewer.getControl(), false));
JFaceResources.getImageRegistry().put(CHECKED_KEY, makeShot(viewer.getControl(), true));
}
}
private Image makeShot(Control control, boolean type)
{
Shell shell = new Shell(control.getShell(), SWT.NO_TRIM);
// otherwise we have a default gray color
Color backgroundColor = control.getBackground();
shell.setBackground(backgroundColor);
Button button = new Button(shell, SWT.CHECK);
button.setBackground(backgroundColor);
button.setSelection(type);
// otherwise an image is located in a corner
button.setLocation(1, 1);
Point bsize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
// otherwise an image is stretched by width
bsize.x = Math.max(bsize.x-1, bsize.y-1);
bsize.y = Math.max(bsize.x-1, bsize.y-1);
button.setSize(bsize);
shell.setSize(bsize);
shell.open();
GC gc = new GC(shell);
Image image = new Image(control.getDisplay(), bsize.x, bsize.y);
gc.copyArea(image, 0, 0);
gc.dispose();
shell.close();
return image;
}
public Image getImage(Object element)
{
if (isChecked(element))
{
return JFaceResources.getImageRegistry().get(CHECKED_KEY);
}
else
{
return JFaceResources.getImageRegistry().get(UNCHECK_KEY);
}
}
protected abstract boolean isChecked(Object element);
}
Nice hack! Unfortunately the checkbox gets a grey background on OS X instead of the blue or white table row background color.
I came up with a a workaround that works for me on the Mac. I haven't tested it on Windows but I assume it'd work there too.
package de.fhmracing.glasseye.canexplorer.gui.transmit;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
public abstract class EmulatedNativeCheckBoxLabelProvider extends
ColumnLabelProvider {
private static final String CHECKED_KEY = "CHECKED";
private static final String UNCHECK_KEY = "UNCHECKED";
public EmulatedNativeCheckBoxLabelProvider(ColumnViewer viewer) {
if (JFaceResources.getImageRegistry().getDescriptor(CHECKED_KEY) == null) {
JFaceResources.getImageRegistry().put(UNCHECK_KEY,
makeShot(viewer.getControl(), false));
JFaceResources.getImageRegistry().put(CHECKED_KEY,
makeShot(viewer.getControl(), true));
}
}
private Image makeShot(Control control, boolean type)
{
// Hopefully no platform uses exactly this color because we'll make
// it transparent in the image.
Color greenScreen = new Color(control.getDisplay(), 222, 223, 224);
Shell shell = new Shell(control.getShell(), SWT.NO_TRIM);
// otherwise we have a default gray color
shell.setBackground(greenScreen);
Button button = new Button(shell, SWT.CHECK);
button.setBackground(greenScreen);
button.setSelection(type);
// otherwise an image is located in a corner
button.setLocation(1, 1);
Point bsize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
// otherwise an image is stretched by width
bsize.x = Math.max(bsize.x - 1, bsize.y - 1);
bsize.y = Math.max(bsize.x - 1, bsize.y - 1);
button.setSize(bsize);
shell.setSize(bsize);
shell.open();
GC gc = new GC(shell);
Image image = new Image(control.getDisplay(), bsize.x, bsize.y);
gc.copyArea(image, 0, 0);
gc.dispose();
shell.close();
ImageData imageData = image.getImageData();
imageData.transparentPixel = imageData.palette.getPixel(greenScreen
.getRGB());
return new Image(control.getDisplay(), imageData);
}
public Image getImage(Object element) {
if (isChecked(element)) {
return JFaceResources.getImageRegistry().get(CHECKED_KEY);
} else {
return JFaceResources.getImageRegistry().get(UNCHECK_KEY);
}
}
protected abstract boolean isChecked(Object element);
}
one more thing i guess - if you set the background of the viewer a little border with that color will be visible when the respective row is selected. Setting
SWT.NO_BACKGROUND
flag on the Shell and Buttons eliminates the problem:
Shell shell = new Shell(control.getShell(), SWT.NO_TRIM | SWT.NO_BACKGROUND);
Button button = new Button(shell, SWT.CHECK | SWT.NO_BACKGROUND);
It works ok. :-)
Thanks for sharing this trick.
I have published an updated version that uses the Control::print API available since SWT 3.4, and also supporting the grayed state of checkboxes.
See tkilla.ch/eclipse for the update source.
Would you like to make this part of our Snippet collection [*]http://wiki.eclipse.org/JFaceSnippets#Snippet061FakedNativeCellEditor.
Then please file a bugzilla and attach patch to it.
Excellent... very useful information. Thanks for sharing this code. I would like to see more unique update from you.
Regards
Alexa
Post a Comment