/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG 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:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.ui.ridgets.swt.views;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.riena.ui.ridgets.IRidget;
import org.eclipse.riena.ui.ridgets.IRidgetContainer;
import org.eclipse.riena.ui.ridgets.controller.AbstractWindowController;
import org.eclipse.riena.ui.swt.GrabCorner;
import org.eclipse.riena.ui.swt.IStatusLineContentFactory;
import org.eclipse.riena.ui.swt.RienaWindowRenderer;
import org.eclipse.riena.ui.swt.Statusline;
import org.eclipse.riena.ui.swt.StatuslineSpacer;
import org.eclipse.riena.ui.swt.layout.DpiGridLayoutFactory;
import org.eclipse.riena.ui.swt.lnf.LnFUpdater;
import org.eclipse.riena.ui.swt.lnf.LnfKeyConstants;
import org.eclipse.riena.ui.swt.lnf.LnfManager;
import org.eclipse.riena.ui.swt.utils.SWTControlFinder;
import org.eclipse.riena.ui.swt.utils.ShellHelper;
import org.eclipse.riena.ui.swt.utils.UIControlsFactory;
/**
* Base class for Riena Dialogs. This class enhances JFace dialogs by adding: (a) theming capabilities based on the current Look-and-Feel, (b) providing View /
* Controller separation, (c) binding the View's widgets to the Controller's ridgets.
* <p>
* Implementors have to subclass this class and provide these methods:
* <ol>
* <li>createController() - returns the Controller for this dialog</li>
* <li>buildView() - creates the UI for this dialog. This includes creating the appropriate buttons, such as Ok and Cancel.</li>
* </ol>
* This subclass can then be used as any other JFace dialog: create a new instance and invoke dialog.open() to show the dialog. Open() blocks as long as the
* dialog is open. It returns an integer code. By default this is {@link Window#OK} ({@value Window#OK}). You can change the return code via
* {@link AbstractWindowController}{@link #setReturnCode(int)}.
* <p>
* <b>How to migrate from DialogView</b>
* <p>
* If you have been using DialogView and want to use this class instead you should:
* <ol>
* <li>createContentView() does not need to call super anymore</li>
* <li>onClose() becomes close() - remember to invoke super.close() when overriding</li>
* <li>build() is deprecated - invoke open() in client code instead</li>
* </ol>
*/
public abstract class AbstractDialogView extends Dialog {
private static final LnFUpdater LNF_UPDATER = LnFUpdater.getInstance();
/**
* @since 5.0
* @deprecated use {@link AbstractWindowController#RIDGET_ID_STATUSLINE}
*/
@Deprecated
public static final String STATUSLINE_BINDING_ID = "dlg_statusline"; //$NON-NLS-1$
private final RienaWindowRenderer dlgRenderer;
private final ControlledView controlledView;
private String title;
private boolean isClosing;
private final boolean statusLine;
private final BlockHelper blockHelper;
private static Shell getShellByGuessing() {
if (PlatformUI.isWorkbenchRunning()) {
final IWorkbench workbench = PlatformUI.getWorkbench();
if (workbench != null) {
final IWorkbenchWindow activeWorkbenchWindow = workbench.getActiveWorkbenchWindow();
if (activeWorkbenchWindow != null) {
return activeWorkbenchWindow.getShell();
}
}
} else if (Display.getCurrent() != null) {
return Display.getCurrent().getActiveShell();
}
// may return null, but that is ok; Dialog does not require a parent shell
return null;
}
/**
* Create a new instance of this class.
*
* @param parentShell
* the parent Shell. It is recommended to supply one. If you use {@code null}, this class will try to guess the most appropriate parent shell.
* @param statusLine
* {@code true} to display a status line at the bottom of this dialog; otherwise {@code false}
* @throws RuntimeException
* if no shell instance could be obtained - this can only happen when parentShell the value {@code null} and the class failed to obtain an
* appropriate shell.
* @since 5.0
*/
protected AbstractDialogView(final Shell parentShell, final boolean statusLine) {
super(parentShell != null ? parentShell : getShellByGuessing());
this.statusLine = statusLine;
title = ""; //$NON-NLS-1$
dlgRenderer = new RienaWindowRenderer(this, isPaintTitlebar());
controlledView = new ControlledView();
final AbstractWindowController controller = createController();
blockHelper = new BlockHelper() {
@Override
protected boolean shouldRestoreFocus() {
return true;
}
@Override
protected Control getParentComposite() {
return getContents();
}
@Override
protected IRidget getFocusRidget() {
return null;
}
@Override
protected IRidgetContainer getController() {
return AbstractDialogView.this.getController();
}
};
controller.setBlocker(blockHelper);
controlledView.setController(controller);
}
protected AbstractDialogView(final Shell parentShell) {
this(parentShell, false);
}
/**
* @since 3.0
*/
protected boolean isPaintTitlebar() {
return true;
}
@Override
public void create() {
// compute the 'styled' shell style, before creating the shell
setShellStyle(dlgRenderer.computeShellStyle());
super.create();
applyTitle(getShell());
addUIControls(getShell());
LNF_UPDATER.updateUIControlColors(getShell());
bindController();
LNF_UPDATER.updateUIControls(getShell(), true);
// after binding the controller it is necessary to calculate the bounds of the dialog again
// because the controller can add some data that influences the size of some widgets (e.g. ChoiceComposite)
initializeBounds();
ShellHelper.center(getShell());
getShell().addDisposeListener(new DisposeListener() {
public void widgetDisposed(final DisposeEvent e) {
if (!isClosing) {
close();
} else {
final AbstractWindowController controller = getController();
controller.setReturnCode(CANCEL);
setReturnCode(controller.getReturnCode());
}
isClosing = false;
}
});
getShell().setMinimumSize(100, 50);
}
@Override
public boolean close() {
if (blockHelper.isBlocked()) {
return false;
}
final AbstractWindowController controller = getController();
isClosing = true;
setReturnCode(controller.getReturnCode());
controlledView.unbind(controller);
return super.close();
}
/**
* Returns the controller instance for this dialog.
*
* @return an AbstractWindowController; never null
*/
public final AbstractWindowController getController() {
return controlledView.getController();
}
/**
* Sets the default button for the dialog.
*
* @param defaultButton
* the button that should be "focused" by default.
* @since 3.0
*/
public void setDefaultButton(final Button defaultButton) {
getShell().setDefaultButton(defaultButton);
}
/**
* Sets the title of this dialog (convenience method).
* <p>
* Implementation note: if you set the title both from the view (here) and the controller (via windowRidget.setTitle(...)), the value used in the controller
* will prevail.
*
* @param title
* the title; never null.
*/
public final void setTitle(final String title) {
Assert.isNotNull(title);
this.title = title;
}
/**
* @deprecated use {@link #open()}
*/
@Deprecated
public final void build() {
open();
}
// protected methods
////////////////////
/**
* Add a control to the list of 'bound' controls. These controls will be bound to ridgets by the framework.
*
* @param uiControl
* the UI control to bind; never null
* @param bindingId
* a non-empty non-null binding id for the control. Must be unique within this composite
*/
protected final void addUIControl(final Object uiControl, final String bindingId) {
controlledView.addUIControl(uiControl, bindingId);
}
@Override
protected final Control createButtonBar(final Composite parent) {
return dlgRenderer.createButtonBar(parent);
}
@Override
protected final Control createContents(final Composite parent) {
final Control result = dlgRenderer.createContents(parent);
super.createContents(dlgRenderer.getCenterComposite());
return result;
}
@Override
protected final Control createDialogArea(final Composite parent) {
final Composite mainComposite = createMainComposite(parent);
int lineRightOffset = 0;
if (isResizable() && isHideOsBorder() && isPaintTitlebar()) {
final GrabCorner grabCorner = new GrabCorner(mainComposite, mainComposite.getStyle());
lineRightOffset = grabCorner.getGrabCornerSize().x;
}
if (statusLine) {
final Statusline line = new Statusline(mainComposite, SWT.None, StatuslineSpacer.class, getStatusLineContentFacroty());
final FormData lineFormData = new FormData();
lineFormData.height = getStatuslineHeight();
lineFormData.bottom = new FormAttachment(100, 0);
lineFormData.right = new FormAttachment(100, -lineRightOffset);
lineFormData.left = new FormAttachment(0, 0);
line.setLayoutData(lineFormData);
addUIControl(line, AbstractWindowController.RIDGET_ID_STATUSLINE);
}
createContentComposite(mainComposite);
blockHelper.registerOnContentComposite(parent);
return mainComposite;
}
private Integer getStatuslineHeight() {
if (statusLine) {
return LnfManager.getLnf().getIntegerSetting(LnfKeyConstants.DIALOG_STATUSLINE_HEIGHT);
} else {
return 0;
}
}
/**
* @since 3.0
*/
protected void createOkCancelButtons(final Composite parent) {
// do nothing by default
}
private Composite createMainComposite(final Composite parent) {
final Composite mainComposite = UIControlsFactory.createComposite(parent, parent.getStyle());
mainComposite.setBackground(parent.getBackground());
mainComposite.setLayout(new FormLayout());
GridDataFactory.fillDefaults().grab(true, true).applyTo(mainComposite);
return mainComposite;
}
private void createContentComposite(final Composite parent) {
final Composite mainContentComposite = UIControlsFactory.createComposite(parent, parent.getStyle());
DpiGridLayoutFactory.fillDefaults().applyTo(mainContentComposite);
final Composite contentComposite = UIControlsFactory.createComposite(mainContentComposite, parent.getStyle());
GridDataFactory.fillDefaults().grab(true, true).align(SWT.FILL, SWT.FILL).applyTo(contentComposite);
DpiGridLayoutFactory.fillDefaults().applyTo(contentComposite);
buildView(contentComposite);
addUIControl(getShell(), AbstractWindowController.RIDGET_ID_WINDOW);
final FormData resultFormData = new FormData();
resultFormData.top = new FormAttachment(0, 0);
resultFormData.left = new FormAttachment(0, 0);
resultFormData.right = new FormAttachment(100, 0);
resultFormData.bottom = new FormAttachment(100, -getStatuslineHeight());
mainContentComposite.setLayoutData(resultFormData);
createOkCancelButtons(mainContentComposite);
}
@Override
protected boolean isResizable() {
return (getShellStyle() & SWT.RESIZE) == SWT.RESIZE;
}
private boolean isHideOsBorder() {
return LnfManager.getLnf().getBooleanSetting(LnfKeyConstants.DIALOG_HIDE_OS_BORDER);
}
/**
* Returns the factory that creates the content of the status line.
*
* @return factory to create the content of the status line
* @since 5.0
*/
protected IStatusLineContentFactory getStatusLineContentFacroty() {
return new DialogStatuslineContentFactory();
}
/**
* Creates the UI for this dialog. This includes creating the appropriate buttons, such as Ok and Cancel.
*
* @wbp.parser.entryPoint
*/
protected abstract Control buildView(Composite parent);
/**
* Create the controller for this dialog.
*
* @return a subclass of AbstractWindowController; never null
*/
protected abstract AbstractWindowController createController();
// helping methods
//////////////////
private void applyTitle(final Shell shell) {
if (shell.getText().length() == 0) {
shell.setText(title);
}
}
private void addUIControls(final Composite composite) {
final SWTControlFinder finder = new SWTControlFinder(composite) {
@Override
public void handleBoundControl(final Control control, final String bindingProperty) {
addUIControl(control, bindingProperty);
}
};
finder.run();
}
private void bindController() {
controlledView.initialize(getController());
controlledView.bind(getController());
}
private static final class ControlledView extends AbstractControlledView<AbstractWindowController> {
@Override
protected void addUIControl(final Object uiControl, final String propertyName) {
super.addUIControl(uiControl, propertyName);
}
@Override
protected void setController(final AbstractWindowController controller) {
super.setController(controller);
}
}
}