Commit 2f8fa12c authored by Simon Barner's avatar Simon Barner
Browse files

Unify implementation of generic boolean checkbox table editor and annotation...

Unify implementation of generic boolean checkbox table editor and annotation view-specific implementation
  - Eliminate org.fortiss.tooling.base.ui.annotation.editingsupport.BooleanCellEditor since instead of showning an editor, the icon visualizing the checkbox state is toggled.
  - Rename org.fortiss.tooling.base.ui.annotation.editingsupport.EmulatedNativeCheckBoxLabelProvider -> CheckBoxLabelProviderInsteadSubclassing. It is based on static utility methods provided by org.fortiss.tooling.base.ui.tablecell.CheckBoxLabelProvider (subclassing is not possible due to conflicting required base classes).
refs 2268
parent 57336370
/**
* <copyright>
*
* Copyright (c) 2008 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 - Initial API and implementation
* Alexander Mark Diewald, fortiss GmbH (diewald@fortiss.org) - Minor adoptions and documentation
*
* </copyright>
*
* $Id$
*/
package org.fortiss.tooling.base.ui.annotation.editingsupport;
import static org.eclipse.core.runtime.Platform.getBundle;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
/**
* {@link CellEditor} for manipulating boolean values within a table. Unlike the
* {@link CheckboxCellEditor}, this class actually provides a checkbox for manipulating the boolean
* value, instead of just toggling <code>true</code> and <code>false</code>.
*
* @author diewald
* @author $Author$
* @version $Rev$
* @ConQAT.Rating YELLOW Hash: EFD0302B4B0E12BEC467B3A359C4DFA7
*/
public class BooleanCellEditor extends CellEditor {
/** The checkbox button. */
private Button button;
/** Default constructor for {@link BooleanCellEditor}. */
public BooleanCellEditor(Composite parent) {
super(parent);
}
/** Constructs a new {@link BooleanCellEditor} using a given style. */
public BooleanCellEditor(Composite parent, int style) {
super(parent, style);
}
/** {@inheritDoc} */
@Override
protected Control createControl(Composite parent) {
Font font = parent.getFont();
Color bg = parent.getBackground();
button = new Button(parent, getStyle() | SWT.CHECK);
button.setFont(font);
button.setBackground(bg);
return button;
}
/** {@inheritDoc} */
@Override
protected Object doGetValue() {
return button.getSelection();
}
/** {@inheritDoc} */
@Override
protected void doSetValue(Object value) {
boolean selection = Boolean.TRUE.equals(value);
button.setSelection(selection);
}
/** {@inheritDoc} */
@Override
protected void doSetFocus() {
if(button != null) {
// Toggle value on first click
boolean value = Boolean.TRUE.equals(doGetValue());
doSetValue(!(value));
// Set focus to button
button.setFocus();
// Update view, i.e. remove last visible traces of the icon
// shown by the corresponding EmulatedNativeCheckBoxLabelProvider
// (which as already been removed at the time when this method is
// called).
button.getParent().layout();
// For GTK-environments, the layout call is not sufficient to hide
// the cell editor. Here, it is required to unset the focus. This
// results in a NPE in windows, so restrict to gtk environments.
if(getBundle("org.eclipse.swt.gtk.linux.x86_64") != null ||
getBundle("org.eclipse.swt.gtk.linux.x86") != null) {
focusLost();
}
// TODO (see #2268): This BooleanCellEditor widget is temporarily shown at a wrong
// position (seen best when holding mouse button down for a longer time
// when clicking the cell).
}
}
}
......@@ -18,9 +18,10 @@ $Id$
package org.fortiss.tooling.base.ui.annotation.editingsupport;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.jface.viewers.TableViewer;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
......@@ -30,21 +31,18 @@ import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
* @author diewald
* @author $Author$
* @version $Rev$
* @ConQAT.Rating GREEN Hash: C42FE8EEE22E0569F39C33ACBFD27962
* @ConQAT.Rating YELLOW Hash: 70DB817065BF424F1C51A85A018C02D1
*/
public class CheckBoxEditingSupport extends AnnotationEditingSupportBase {
/** Contains the {@link BooleanCellEditor} that is displayed. */
private BooleanCellEditor checkBoxEditor;
/** Flag if this {@link BooleanCellEditor} is editable. */
private boolean isEditable;
/** Placeholder for the boolean value editor (in fact, only an icon is toggled). */
private CheckboxCellEditor checkBoxEditor;
/** Constructs a new {@link CheckBoxEditingSupport}. */
public CheckBoxEditingSupport(ColumnViewer viewer,
Class<? extends IAnnotatedSpecification> specClass, String instanceKey) {
super(viewer, specClass, instanceKey);
checkBoxEditor = new BooleanCellEditor((Composite)getViewer().getControl());
checkBoxEditor = new CheckboxCellEditor(((TableViewer)viewer).getTable());
}
/** {@inheritDoc} */
......@@ -56,25 +54,4 @@ public class CheckBoxEditingSupport extends AnnotationEditingSupportBase {
return checkBoxEditor;
}
/** {@inheritDoc} */
@Override
protected void setValue(Object element, Object box) {
if(isEditable) {
super.setValue(element, checkBoxEditor.getValue());
}
}
/** {@inheritDoc} */
@Override
protected Object getValue(Object element) {
Object value = super.getValue(element);
if(value != null) {
isEditable = ((AnnotationEntry)element).canEdit(specClass);
checkBoxEditor.setValue(value);
}
return value;
}
}
/*******************************************************************************
* Copyright (c) 2006, 2014 Tom Schindl 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:
* Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
* Florian Potschka <signalrauschen@gmail.com> - Bug 260061
* Alexander Ljungberg <siker@norwinter.com> - Bug 260061
* Jeanderson Candido <http://jeandersonbc.github.io> - Bug 414565
* Alexander Mark Diewald <diewald@fortiss.org>
*******************************************************************************/
package org.fortiss.tooling.base.ui.annotation.labelprovider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.graphics.Image;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.ui.annotation.view.IAnnotationViewPart;
/**
* <p>
* A {@link ColumnLabelProvider} for annotations that displays checkboxes indicating the value of
* annotations that contain boolean values.
* </p>
* <p>
* This implementation uses 'screenshot' {@link Image}s of the checkboxes, since {@link TableViewer}
* is not able to display SWT widgets when a cell is not edited. Since the screenshots are made
* during runtime, the resulting images fit the underlying OS.
* </p>
*
* @author diewald
* @author $Author$
* @version $Rev$
* @ConQAT.Rating YELLOW Hash: 6A850011E144844D374AD1283AC48CC0
*/
public class CheckBoxLabelProvider extends AnnotationLabelProvider {
/** Constructor. */
public CheckBoxLabelProvider(Class<? extends IAnnotatedSpecification> clazz,
IAnnotationViewPart viewPart, String instanceKey, EditingSupport editingSupport) {
super(clazz, viewPart, instanceKey, editingSupport);
}
/** {@inheritDoc} */
@Override
public String getText(Object element) {
return "";
}
/** {@inheritDoc} */
@Override
public Image getImage(Object element) {
if(element instanceof AnnotationEntry) {
boolean enabled = ((AnnotationEntry)element).canEdit(clazz);
return org.fortiss.tooling.base.ui.tablecell.CheckBoxLabelProvider.getImage(
isChecked(element), enabled);
}
return null;
}
/**
* Determines whether the boolean value is 'true' or 'false'. Used to determine the 'checked'
* status of the checkbox.
*/
protected boolean isChecked(Object element) {
if(element instanceof AnnotationEntry) {
AnnotationEntry annotationEntry = (AnnotationEntry)element;
Object value = null;
try {
value = annotationEntry.getSpecificationValue(clazz, instanceKey);
} catch(Exception e) {
return false;
}
if(value != null) {
// It is already known that the value is a boolean.
return (Boolean)value;
}
}
return false;
}
}
/*******************************************************************************
* Copyright (c) 2006, 2014 Tom Schindl 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:
* Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
* Florian Potschka <signalrauschen@gmail.com> - Bug 260061
* Alexander Ljungberg <siker@norwinter.com> - Bug 260061
* Jeanderson Candido <http://jeandersonbc.github.io> - Bug 414565
* Alexander Mark Diewald <diewald@fortiss.org>
*******************************************************************************/
package org.fortiss.tooling.base.ui.annotation.labelprovider;
import static org.eclipse.core.runtime.Platform.getBundle;
import static org.eclipse.jface.resource.JFaceResources.getImageRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.viewers.ViewerRow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.fortiss.tooling.base.annotation.AnnotationEntry;
import org.fortiss.tooling.base.model.element.IAnnotatedSpecification;
import org.fortiss.tooling.base.ui.annotation.view.IAnnotationViewPart;
/**
* <p>
* A {@link ColumnLabelProvider} for annotations that displays checkboxes indicating the value of
* annotations that contain boolean values.
* </p>
* <p>
* This is a workaround using 'screenshots' of the checkboxes, since {@link TableViewer} is not able
* to display SWT widgets when a cell is not edited. This concretely means that, when the cell is
* not being edited (e.g., if no or another cell is edited), then the checkbox would simply
* disappear and the default text-based label provider would be shown.
* </p>
*
* <p>
* Since the screenshots are made during runtime, the resulting images fit the underlying OS.
* </p>
*
*
* @author diewald
* @author $Author$
* @version $Rev$
* @ConQAT.Rating YELLOW Hash: BF686162C5BC9CEDC6DBBD199131F8C4
*/
public class EmulatedNativeCheckBoxLabelProvider extends AnnotationLabelProvider {
/** Key for the 'checked' checkbox. */
private static final String CHECKED_KEY = "CHECKED";
/** Key for the 'unchecked' checkbox. */
private static final String UNCHECKED_KEY = "UNCHECKED";
/** Currently referenced row. */
ViewerRow row;
/** Constructs a new {@link EmulatedNativeCheckBoxLabelProvider}. */
public EmulatedNativeCheckBoxLabelProvider(Class<? extends IAnnotatedSpecification> specClass,
IAnnotationViewPart viewPart, String instanceKey, ColumnViewer viewer) {
super(specClass, viewPart, instanceKey, null);
if(getImageRegistry().getDescriptor(CHECKED_KEY) == null) {
getImageRegistry().put(UNCHECKED_KEY, makeShot(viewer.getControl(), false));
getImageRegistry().put(CHECKED_KEY, makeShot(viewer.getControl(), true));
}
}
/** {@inheritDoc} */
@Override
public void update(final ViewerCell cell) {
super.update(cell);
if(cell != null && cell.getViewerRow() != null) {
// On the first invocation, register mouse listener to
// cell's control (i.e., the table) in order to remove the
// icon when the actual widget is to be shown.
//
// This cannot be achieved using a FocusListener since it
// does not provide a way to determine the cell which has been
// clicked (source and widget of the event refer to the Table itself).
if(row == null) {
cell.getControl().addMouseListener(new MouseListener() {
@Override
public void mouseDoubleClick(MouseEvent e) {
// Nothing to do
}
@Override
public void mouseDown(MouseEvent e) {
Point pt = new Point(e.x, e.y);
// Registration of mouseListener ensures that row is non-null
TableItem item = ((Table)row.getControl()).getItem(pt);
// Hide image showing the icon emulating the native check box since the
// user as clicked this cell and the check box widget is about to be
// shown.
//
// Otherwise, both the icon and the widget would be visible in parallel.
//
// After the user has finished editing, the framework will call
// getImage() in order to restore the icon emulating the native check
// box.
if(item != null) {
// Source and widget of the event 'e' refer to the Table itself, so
// we need to iterate over all columns of the current row and check the
// coordinates to find the actual cell.
for(int col = 0; col < ((Table)row.getControl()).getColumnCount(); col++) {
Rectangle rect = item.getBounds(col);
if(rect.contains(pt)) {
row.setImage(col, null);
}
}
}
}
@Override
public void mouseUp(MouseEvent e) {
// Nothing to do
}
});
}
// Later on, just update the referenced row.
row = cell.getViewerRow();
}
super.update(cell);
}
/** Takes a 'screenshot' of the checkbox control an stores it in the image registry. */
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();
// Work around a gtk display error that causes the widgets in the
// shell not being "visible". Opening a second Shell modifies some
// internal properties of the existing shell, such that the widgets
// become visible.
if(getBundle("org.eclipse.swt.gtk.linux.x86_64") != null ||
getBundle("org.eclipse.swt.gtk.linux.x86") != null) {
Shell shell2 = new Shell(control.getShell(), SWT.NO_TRIM);
shell2.open();
shell2.close();
}
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());
image.dispose();
greenScreen.dispose();
return new Image(control.getDisplay(), imageData);
}
/** {@inheritDoc} */
@Override
public String getText(Object element) {
return "";
}
/** {@inheritDoc} */
@Override
public Image getImage(Object element) {
if(element instanceof AnnotationEntry && ((AnnotationEntry)element).canEdit(clazz)) {
if(isChecked(element)) {
return JFaceResources.getImageRegistry().get(CHECKED_KEY);
}
return JFaceResources.getImageRegistry().get(UNCHECKED_KEY);
}
return null;
}
/**
* Determines whether the boolean value is 'true' or 'false'. Used to determine the 'checked'
* status of the checkbox.
*/
protected boolean isChecked(Object element) {
if(element instanceof AnnotationEntry) {
AnnotationEntry annotationEntry = (AnnotationEntry)element;
Object value = null;
try {
value = annotationEntry.getSpecificationValue(clazz, instanceKey);
} catch(Exception e) {
return false;
}
if(value != null) {
// It is already known that the value is a boolean.
return (Boolean)value;
}
}
return false;
}
}
......@@ -32,7 +32,7 @@ import org.fortiss.tooling.base.ui.annotation.view.generic.GenericAnnotationView
* @author barner
* @author $Author$
* @version $Rev$
* @ConQAT.Rating GREEN Hash: BA975FC0E3ECD78ED2D3FC42B64CB7BA
* @ConQAT.Rating YELLOW Hash: 8611517FF616B8E77B42DE4D986456F1
*/
public class LabelProviderFactory {
......@@ -63,8 +63,8 @@ public class LabelProviderFactory {
throws Exception {
if(valueProvider.getAnnotationValue(specification, instanceKey) instanceof Boolean) {
return new EmulatedNativeCheckBoxLabelProvider(clazz, viewPart, instanceKey,
columnViewer);
return new CheckBoxLabelProvider(clazz, viewPart, instanceKey,
editingSupport);
}
return new AnnotationLabelProvider(clazz, viewPart, instanceKey, editingSupport);
}
......
......@@ -17,28 +17,30 @@ $Id$
+--------------------------------------------------------------------------*/
package org.fortiss.tooling.base.ui.tablecell;
import static org.eclipse.core.runtime.Platform.getBundle;
import static org.eclipse.jface.resource.JFaceResources.getImageRegistry;
import static org.eclipse.swt.SWT.CHECK;
import static org.eclipse.swt.SWT.DEFAULT;
import static org.eclipse.swt.SWT.NO_TRIM;
import org.eclipse.jface.resource.ImageRegistry;
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.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Button;