/******************************************************************************* * Copyright (c) 2000, 2007 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 Corporation - initial API and implementation *******************************************************************************/ package org.openlca.app.components; import java.text.MessageFormat; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.CellEditor; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; 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.Label; import org.eclipse.swt.widgets.Layout; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.Hyperlink; import org.openlca.app.M; import org.openlca.app.util.Colors; /** * An abstract cell editor that uses a dialog. Dialog cell editors usually have * a label control on the left and a button on the right. Pressing the button * opens a dialog window (for example, a color dialog or a file dialog) to * change the cell editor's value. The cell editor's value is the value of the * dialog. * <p> * Subclasses may override the following methods: * <ul> * <li><code>createButton</code>: creates the cell editor's button control</li> * <li><code>createContents</code>: creates the cell editor's 'display value' * control</li> * <li><code>updateContents</code>: updates the cell editor's 'display value' * control after its value has changed</li> * <li><code>openDialogBox</code>: opens the dialog box when the end user * presses the button</li> * </ul> * </p> */ public abstract class DialogCellEditor extends CellEditor { /** * Image registry key for three dot image (value * <code>"cell_editor_dots_button_image"</code>). */ public static final String CELL_EDITOR_IMG_DOTS_BUTTON = "cell_editor_dots_button_image"; /** * The editor control. */ private Composite editor; /** * The current contents. */ private Control contents; /** * The label that gets reused by <code>updateLabel</code>. */ private Label defaultLabel; /** * The button. */ private Hyperlink hyperlink; /** * Listens for 'focusLost' events and fires the 'apply' event as long as the * focus wasn't lost because the dialog was opened. */ private FocusListener buttonFocusListener; /** * The value of this cell editor; initially <code>null</code>. */ private Object value = null; static { ImageRegistry reg = JFaceResources.getImageRegistry(); reg.put(CELL_EDITOR_IMG_DOTS_BUTTON, ImageDescriptor.createFromFile( DialogCellEditor.class, "images/dots_button.gif")); } /** * Internal class for laying out the dialog. */ private class DialogCellLayout extends Layout { @Override public void layout(Composite editor, boolean force) { Rectangle bounds = editor.getClientArea(); Point size = hyperlink.computeSize(SWT.DEFAULT, SWT.DEFAULT, force); if (contents != null) { contents.setBounds(0, 0, bounds.width - size.x, bounds.height); } hyperlink .setBounds(bounds.width - size.x, 0, size.x, bounds.height); } @Override public Point computeSize(Composite editor, int wHint, int hHint, boolean force) { if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) { return new Point(wHint, hHint); } Point contentsSize = contents.computeSize(SWT.DEFAULT, SWT.DEFAULT, force); Point buttonSize = hyperlink.computeSize(SWT.DEFAULT, SWT.DEFAULT, force); // Just return the button width to ensure the button is not clipped // if the label is long. // The label will just use whatever extra width there is Point result = new Point(buttonSize.x, Math.max(contentsSize.y, buttonSize.y)); return result; } } /** * Default DialogCellEditor style */ private static final int defaultStyle = SWT.NONE; /** * Creates a new dialog cell editor with no control * * @since 2.1 */ public DialogCellEditor() { setStyle(defaultStyle); } /** * Creates a new dialog cell editor parented under the given control. The * cell editor value is <code>null</code> initially, and has no validator. * * @param parent * the parent control */ protected DialogCellEditor(Composite parent) { this(parent, defaultStyle); } /** * Creates a new dialog cell editor parented under the given control. The * cell editor value is <code>null</code> initially, and has no validator. * * @param parent * the parent control * @param style * the style bits * @since 2.1 */ protected DialogCellEditor(Composite parent, int style) { super(parent, style); } protected Hyperlink createLink(Composite parent) { Hyperlink link = new Hyperlink(parent, SWT.NONE); link.setText(M.Edit); link.setBackground(Colors.white()); link.setForeground(Colors.linkBlue()); return link; } /** * Creates the controls used to show the value of this cell editor. * <p> * The default implementation of this framework method creates a label * widget, using the same font and background color as the parent control. * </p> * <p> * Subclasses may reimplement. If you reimplement this method, you should * also reimplement <code>updateContents</code>. * </p> * * @param cell * the control for this cell editor * @return the underlying control */ protected Control createContents(Composite cell) { defaultLabel = new Label(cell, SWT.LEFT); defaultLabel.setFont(cell.getFont()); defaultLabel.setBackground(cell.getBackground()); return defaultLabel; } /* * (non-Javadoc) Method declared on CellEditor. */ @Override protected Control createControl(Composite parent) { Font font = parent.getFont(); Color bg = parent.getBackground(); editor = new Composite(parent, getStyle()); editor.setFont(font); editor.setBackground(bg); editor.setLayout(new DialogCellLayout()); contents = createContents(editor); updateContents(value); hyperlink = createLink(editor); hyperlink.setFont(font); hyperlink.addKeyListener(new KeyAdapter() { /* * (non-Javadoc) * * @see * org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt * .events.KeyEvent) */ @Override public void keyReleased(KeyEvent e) { if (e.character == '\u001b') { // Escape fireCancelEditor(); } } }); hyperlink.addFocusListener(getButtonFocusListener()); hyperlink.addHyperlinkListener(new HyperlinkAdapter() { /* * (non-Javadoc) * * @see * org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse * .swt.events.SelectionEvent) */ @Override public void linkActivated(HyperlinkEvent e) { // Remove the button's focus listener since it's guaranteed // to lose focus when the dialog opens hyperlink.removeFocusListener(getButtonFocusListener()); Object newValue = openDialogBox(editor); // Re-add the listener once the dialog closes hyperlink.addFocusListener(getButtonFocusListener()); if (newValue != null) { boolean newValidState = isCorrect(newValue); if (newValidState) { markDirty(); doSetValue(newValue); } else { // try to insert the current value into the error // message. setErrorMessage(MessageFormat.format(getErrorMessage(), new Object[] { newValue.toString() })); } fireApplyEditorValue(); } } }); setValueValid(true); return editor; } /* * (non-Javadoc) * * Override in order to remove the button's focus listener if the celleditor * is deactivating. * * @see org.eclipse.jface.viewers.CellEditor#deactivate() */ @Override public void deactivate() { if (hyperlink != null && !hyperlink.isDisposed()) { hyperlink.removeFocusListener(getButtonFocusListener()); } super.deactivate(); } /* * (non-Javadoc) Method declared on CellEditor. */ @Override protected Object doGetValue() { return value; } /* * (non-Javadoc) Method declared on CellEditor. The focus is set to the cell * editor's button. */ @Override protected void doSetFocus() { hyperlink.setFocus(); // add a FocusListener to the button hyperlink.addFocusListener(getButtonFocusListener()); } /** * Return a listener for button focus. * * @return FocusListener */ private FocusListener getButtonFocusListener() { if (buttonFocusListener == null) { buttonFocusListener = new FocusListener() { /* * (non-Javadoc) * * @see * org.eclipse.swt.events.FocusListener#focusGained(org.eclipse * .swt.events.FocusEvent) */ @Override public void focusGained(FocusEvent e) { // Do nothing } /* * (non-Javadoc) * * @see * org.eclipse.swt.events.FocusListener#focusLost(org.eclipse * .swt.events.FocusEvent) */ @Override public void focusLost(FocusEvent e) { DialogCellEditor.this.focusLost(); } }; } return buttonFocusListener; } /* * (non-Javadoc) Method declared on CellEditor. */ @Override protected void doSetValue(Object value) { this.value = value; updateContents(value); } /** * Returns the default label widget created by <code>createContents</code>. * * @return the default label widget */ protected Label getDefaultLabel() { return defaultLabel; } /** * Opens a dialog box under the given parent control and returns the * dialog's value when it closes, or <code>null</code> if the dialog was * canceled or no selection was made in the dialog. * <p> * This framework method must be implemented by concrete subclasses. It is * called when the user has pressed the button and the dialog box must pop * up. * </p> * * @param cellEditorWindow * the parent control cell editor's window so that a subclass can * adjust the dialog box accordingly * @return the selected value, or <code>null</code> if the dialog was * canceled or no selection was made in the dialog */ protected abstract Object openDialogBox(Control cellEditorWindow); /** * Updates the controls showing the value of this cell editor. * <p> * The default implementation of this framework method just converts the * passed object to a string using <code>toString</code> and sets this as * the text of the label widget. * </p> * <p> * Subclasses may reimplement. If you reimplement this method, you should * also reimplement <code>createContents</code>. * </p> * * @param value * the new value of this cell editor */ protected void updateContents(Object value) { if (defaultLabel == null) { return; } String text = ""; if (value != null) { text = value.toString(); } defaultLabel.setText(text); } }