blob: eb61a2a51ce9d3712452f0d015f125c51d733f60 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BSI Business Systems Integration AG.
* 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 tribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.ui.swt.basic.table.celleditor;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.scout.commons.annotations.Order;
import org.eclipse.scout.commons.annotations.OrderedCollection;
import org.eclipse.scout.commons.exception.ProcessingException;
import org.eclipse.scout.commons.holders.Holder;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.rt.client.ui.form.AbstractForm;
import org.eclipse.scout.rt.client.ui.form.AbstractFormHandler;
import org.eclipse.scout.rt.client.ui.form.IForm;
import org.eclipse.scout.rt.client.ui.form.fields.IFormField;
import org.eclipse.scout.rt.client.ui.form.fields.groupbox.AbstractGroupBox;
import org.eclipse.scout.rt.ui.swt.basic.SwtScoutComposite;
import org.eclipse.scout.rt.ui.swt.form.fields.ISwtScoutFormField;
import org.eclipse.scout.rt.ui.swt.window.popup.SwtScoutDropDownPopup;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
/**
* <p>
* Wraps a {@link IFormField} to be displayed as popup cell editor.
* </p>
* Thereby, {@link SwtScoutComposite} is constructed as following:
* <ul>
* <li>{@link SwtScoutComposite#getSwtField()}: references the the UI representation of the FormField that is displayed
* within the popup {@link Shell}; is lazy bound the time the {@link Shell} is painted for the first time</li>
* <li>{@link SwtScoutComposite#getSwtContainer()}: references the owner control the Shell is bound to</li>
* </ul>
*/
public class SwtScoutFormFieldPopup extends SwtScoutComposite<IFormField> {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwtScoutFormFieldPopup.class);
private SwtScoutDropDownPopup m_swtScoutPopup;
private Composite m_owner;
private int m_minWidth;
private int m_prefWidth;
private int m_minHeight;
private int m_prefHeight;
private int m_style;
private final Set<IFormFieldPopupListener> m_listeners = Collections.synchronizedSet(new HashSet<IFormFieldPopupListener>());
public SwtScoutFormFieldPopup(Composite owner) {
m_owner = owner;
m_style = SWT.NO_TRIM; // no trim area on the popup Shell.
}
@Override
protected void initializeSwt(Composite parent) {
super.initializeSwt(parent);
// Create the Popup-Shell but do not make it visible yet; This is done the time the owner component is painted.
m_swtScoutPopup = new SwtScoutDropDownPopup(getEnvironment(), m_owner, true, m_style);
m_swtScoutPopup.setPopupOnField(true);
m_swtScoutPopup.setHeightHint(m_prefHeight);
m_swtScoutPopup.setWidthHint(m_prefWidth);
m_swtScoutPopup.getShell().setMinimumSize(m_minWidth, m_minHeight);
// Listener to be notified about traversal events and keystrokes occurred on the popup.
final TraverseListener traverseListener = new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
switch (e.detail) {
case SWT.TRAVERSE_TAB_NEXT: {
e.doit = false;
notifyListeners(IFormFieldPopupListener.TYPE_OK | IFormFieldPopupListener.TYPE_FOCUS_NEXT);
break;
}
case SWT.TRAVERSE_TAB_PREVIOUS: {
e.doit = false;
notifyListeners(IFormFieldPopupListener.TYPE_OK | IFormFieldPopupListener.TYPE_FOCUS_BACK);
break;
}
case SWT.TRAVERSE_ESCAPE: {
e.doit = false;
notifyListeners(IFormFieldPopupListener.TYPE_CANCEL);
break;
}
case SWT.TRAVERSE_RETURN: {
e.doit = false;
notifyListeners(IFormFieldPopupListener.TYPE_OK);
break;
}
}
}
};
// Listener to handle Shell deactivation events.
m_swtScoutPopup.addShellListener(new ShellAdapter() {
@Override
public void shellDeactivated(ShellEvent e) {
notifyListeners(IFormFieldPopupListener.TYPE_OK);
}
});
// Defer opening the Shell to be positioned at the location of the owner component.
m_owner.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (m_owner.isDisposed()) {
return; // do not open the Shell if the owner is already disposed.
}
m_owner.removePaintListener(this);
// Create the Form to contain the form-field.
final IForm form = createForm();
if (form != null) {
m_swtScoutPopup.showForm(form);
final Control swtFormField = findSwtFormField(m_swtScoutPopup.getUiForm().getSwtField(), getScoutObject());
if (swtFormField == null) {
LOG.warn("UI-FormField could not be found in UI-Form");
}
setSwtField(swtFormField);
// Install listener to be notified about traversal events.
installTraverseListener(m_swtScoutPopup.getShell(), traverseListener);
}
else {
LOG.error("Failed to create the Form for the form-field.");
}
}
});
setSwtContainer(m_owner);
}
public void setMinWidth(int minWidth) {
m_minWidth = minWidth;
}
public void setPrefWidth(int prefWidth) {
m_prefWidth = prefWidth;
}
public void setMinHeight(int minHeight) {
m_minHeight = minHeight;
}
public void setPrefHeight(int prefHeight) {
m_prefHeight = prefHeight;
}
/**
* Closes the popup.
*/
public void closePopup() {
m_swtScoutPopup.closePart();
}
public void addListener(IFormFieldPopupListener listener) {
m_listeners.add(listener);
}
public void removeListener(IFormFieldPopupListener listener) {
m_listeners.remove(listener);
}
private void notifyListeners(int eventType) {
for (IFormFieldPopupListener listener : m_listeners) {
listener.handleEvent(eventType);
}
}
/**
* Installs the given traverse listener on the given control and its child controls.
*/
private void installTraverseListener(Control control, TraverseListener listener) {
control.addTraverseListener(listener);
if (control instanceof Composite) {
Composite composite = (Composite) control;
for (Control child : composite.getChildren()) {
installTraverseListener(child, listener);
}
}
}
/**
* @return creates the {@link IForm} that contains the {@link IFormField}.
*/
private IForm createForm() {
final Holder<IForm> result = new Holder<IForm>();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
P_Form form = new P_Form(getScoutObject());
form.setAutoAddRemoveOnDesktop(false);
form.startForm();
result.setValue(form);
}
catch (Exception e) {
LOG.error("Failed to create and start popup form.", e);
}
}
};
try {
getEnvironment().invokeScoutLater(runnable, 2345).join(2345);
}
catch (InterruptedException e) {
LOG.warn("Interrupted while waiting for the popup form to be started.", e);
}
return result.getValue();
}
/**
* Finds the <code>FormField</code> {@link Control} that represents the {@link IFormField} in the hierarchy of the
* given {@link Control}.
*
* @param control
* container.
* @param formField
* {@link IFormField}.
* @return {@link Control} or <code>null</code> if not found.
*/
private static Control findSwtFormField(Control control, IFormField formField) {
Object o = control.getData(ISwtScoutFormField.CLIENT_PROPERTY_SCOUT_OBJECT);
if (o == formField) {
return control;
}
if (control instanceof Composite) {
for (Control child : ((Composite) control).getChildren()) {
Control candiate = findSwtFormField(child, formField);
if (candiate != null) {
return candiate;
}
}
}
return null;
}
/**
* {@link IForm} to contain the {@link IFormField}.
*/
private static class P_Form extends AbstractForm {
private final IFormField m_formField;
public P_Form(IFormField formField) throws ProcessingException {
super(false);
m_formField = formField;
callInitializer();
}
@Override
protected boolean getConfiguredModal() {
return false;
}
@Override
protected int getConfiguredDisplayHint() {
return DISPLAY_HINT_VIEW;
}
@Override
public String getDisplayViewId() {
return IForm.VIEW_ID_CENTER;
}
@Override
protected boolean getConfiguredAskIfNeedSave() {
return false;
}
public void startForm() throws ProcessingException {
startInternal(new FormHandler());
}
public MainBox getMainBox() {
return (MainBox) getRootGroupBox();
}
@Order(10)
public class MainBox extends AbstractGroupBox {
@Override
protected void injectFieldsInternal(OrderedCollection<IFormField> fields) {
fields.addOrdered(m_formField);
}
@Override
protected boolean getConfiguredBorderVisible() {
return false;
}
@Override
protected int getConfiguredGridColumnCount() {
return 1;
}
@Override
protected boolean getConfiguredGridUseUiWidth() {
return true;
}
@Override
protected boolean getConfiguredGridUseUiHeight() {
return true;
}
}
private class FormHandler extends AbstractFormHandler {
}
}
}