package edu.ualberta.med.biobank.gui.common.dialogs;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.jface.dialogs.DialogMessageArea;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import edu.ualberta.med.biobank.gui.common.BgcPlugin;
/**
* Took same structure than PreferenceDialog. Difference is that the left panel
* is a list and not a tree. The list is composed of BgcDialogPage objects (see
* createPages method).
*
* @see org.eclipse.jface.preference.PreferenceDialog
* @author delphine
*
*/
public abstract class BgcDialogWithPages extends BgcBaseDialog {
private ScrolledComposite scrolled;
private Composite pageContainer;
private BgcDialogPage currentPage;
private List<BgcDialogPage> pages;
private Point lastShellSize;
private Composite titleArea;
private DialogMessageArea messageArea;
private Composite formTitleComposite;
private boolean showingError;
private ListViewer listViewer;
public BgcDialogWithPages(Shell parentShell) {
super(parentShell);
}
protected List<BgcDialogPage> getPages() {
if (pages == null)
pages = createPages();
return pages;
}
/**
* @return Pages that are listed inside the left list.
*/
protected abstract List<BgcDialogPage> createPages();
@Override
protected Control createContents(final Composite parent) {
Control control = super.createContents(parent);
BgcDialogPage defaultSelection = getDefaultSelection();
if (defaultSelection != null)
// select first node when dialog open
listViewer.setSelection(new StructuredSelection(defaultSelection));
return control;
}
/**
* @return selection when dialog opens
*/
protected abstract BgcDialogPage getDefaultSelection();
@Override
protected void createDialogAreaInternal(Composite parent) throws Exception {
// remove default spaces from parent
GridLayout l = (GridLayout) parent.getLayout();
l.marginHeight = 0;
l.marginWidth = 0;
l.horizontalSpacing = 0;
l.verticalSpacing = 0;
Composite content = new Composite(parent, SWT.NONE);
GridLayout gl = new GridLayout(3, false);
gl.horizontalSpacing = 0;
gl.verticalSpacing = 0;
gl.marginWidth = 0;
gl.marginHeight = 0;
content.setLayout(gl);
content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
// left list
createListWidget(content);
// show a separator between the list and the right container
Label versep = new Label(content, SWT.SEPARATOR | SWT.VERTICAL);
GridData verGd = new GridData(GridData.FILL_VERTICAL
| GridData.GRAB_VERTICAL);
versep.setLayoutData(verGd);
versep.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true));
// right container. Display the current page.
Composite pageAreaComposite = new Composite(content, SWT.NONE);
pageAreaComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
GridLayout layout = new GridLayout(1, true);
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 0;
pageAreaComposite.setLayout(layout);
formTitleComposite = new Composite(pageAreaComposite, SWT.NONE);
FormLayout titleLayout = new FormLayout();
titleLayout.marginWidth = 0;
titleLayout.marginHeight = 0;
formTitleComposite.setLayout(titleLayout);
GridData titleGridData = new GridData(GridData.FILL_HORIZONTAL);
titleGridData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
formTitleComposite.setLayoutData(titleGridData);
// Build the title area
Composite titleComposite = new Composite(formTitleComposite, SWT.NONE);
layout = new GridLayout(2, false);
layout.marginBottom = 5;
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.horizontalSpacing = 0;
titleComposite.setLayout(layout);
FormData titleFormData = new FormData();
titleFormData.top = new FormAttachment(0, 0);
titleFormData.left = new FormAttachment(0, 0);
titleFormData.right = new FormAttachment(100, 0);
titleFormData.bottom = new FormAttachment(100, 0);
titleComposite.setLayoutData(titleFormData);
createTitleArea(titleComposite);
ToolBar tbar = new ToolBar(titleComposite, SWT.FLAT | SWT.HORIZONTAL);
ToolItem titem = new ToolItem(tbar, SWT.NULL);
titem.setImage(BgcPlugin.getDefault().getImageRegistry()
.get(BgcPlugin.IMG_ADD));
titem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
getSelection().runAddAction();
}
});
// separator between title and page container
Label separator2 = new Label(pageAreaComposite, SWT.HORIZONTAL
| SWT.SEPARATOR);
separator2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
| GridData.GRAB_HORIZONTAL));
// Build the Page container
pageContainer = createPageContainer(pageAreaComposite);
GridData pageContainerData = new GridData(GridData.FILL_BOTH);
pageContainerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
pageContainer.setLayoutData(pageContainerData);
}
protected BgcDialogPage getSelection() {
return (BgcDialogPage) ((IStructuredSelection) listViewer
.getSelection()).getFirstElement();
}
private void createListWidget(Composite content) {
listViewer = new ListViewer(content);
GridData gd = new GridData(SWT.FILL, SWT.FILL, false, true);
gd.verticalIndent = 0;
gd.horizontalIndent = 0;
gd.widthHint = 150;
listViewer.getControl().setLayoutData(gd);
listViewer.setContentProvider(new ArrayContentProvider());
listViewer.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
if ((element != null) && (element instanceof BgcDialogPage))
return ((BgcDialogPage) element).getTitle();
return ""; //$NON-NLS-1$
}
});
listViewer.setInput(getPages());
listViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
showPage(getSelection());
}
});
}
/**
* Create the title area which will contain a title, message, and image.
*/
protected Composite createTitleArea(Composite parent) {
int margins = 2;
titleArea = new Composite(parent, SWT.NONE);
FormLayout layout = new FormLayout();
layout.marginHeight = 0;
layout.marginWidth = margins;
titleArea.setLayout(layout);
GridData layoutData = new GridData(GridData.FILL_HORIZONTAL);
layoutData.verticalAlignment = SWT.TOP;
titleArea.setLayoutData(layoutData);
// Message label
messageArea = new DialogMessageArea();
messageArea.createContents(titleArea);
titleArea.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
updateMessage();
}
});
final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
if (JFaceResources.BANNER_FONT.equals(event.getProperty())) {
updateMessage();
}
if (JFaceResources.DIALOG_FONT.equals(event.getProperty())) {
updateMessage();
Font dialogFont = JFaceResources.getDialogFont();
// updateTreeFont(dialogFont);
Control[] children = ((Composite) buttonBar).getChildren();
for (int i = 0; i < children.length; i++) {
children[i].setFont(dialogFont);
}
}
}
};
titleArea.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent event) {
JFaceResources.getFontRegistry().removeListener(fontListener);
}
});
JFaceResources.getFontRegistry().addListener(fontListener);
messageArea.setTitleLayoutData(createMessageAreaData());
messageArea.setMessageLayoutData(createMessageAreaData());
return titleArea;
}
private FormData createMessageAreaData() {
FormData messageData = new FormData();
messageData.top = new FormAttachment(0);
messageData.bottom = new FormAttachment(100);
messageData.right = new FormAttachment(100);
messageData.left = new FormAttachment(0);
return messageData;
}
/**
* Create main page container
*/
protected Composite createPageContainer(Composite parent) {
Composite outer = new Composite(parent, SWT.NONE);
GridData outerData = new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
outerData.horizontalIndent = IDialogConstants.HORIZONTAL_MARGIN;
GridLayout outerLayout = new GridLayout();
outerLayout = new GridLayout();
outerLayout.marginBottom = 0;
outerLayout.marginHeight = 0;
outerLayout.marginWidth = 0;
outerLayout.horizontalSpacing = 0;
outer.setLayout(outerLayout);
outer.setLayoutData(outerData);
// Create an outer composite for spacing
scrolled = new ScrolledComposite(outer, SWT.V_SCROLL | SWT.H_SCROLL);
// always show the focus control
scrolled.setShowFocusedControl(true);
scrolled.setExpandHorizontal(true);
scrolled.setExpandVertical(true);
GridData scrolledData = new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
scrolled.setLayoutData(scrolledData);
Composite result = new Composite(scrolled, SWT.NONE);
GridLayout resLayout = new GridLayout(1, false);
resLayout = new GridLayout();
resLayout.marginBottom = 0;
resLayout.marginHeight = 0;
resLayout.marginWidth = 0;
resLayout.horizontalSpacing = 0;
result.setLayout(resLayout);
result.setLayoutData(new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
scrolled.setContent(result);
return result;
}
/**
* Display the selection
*/
protected boolean showPage(BgcDialogPage newPage) {
if (newPage == currentPage) {
return true;
}
BgcDialogPage oldPage = currentPage;
currentPage = newPage;
// Ensure that the page control has been created
// (this allows lazy page control creation)
if ((currentPage != null) && (currentPage.getControl() == null)) {
final boolean[] failed = { false };
SafeRunnable.run(new ISafeRunnable() {
@Override
public void handleException(Throwable e) {
failed[0] = true;
}
@Override
public void run() {
currentPage.createControl(pageContainer);
}
});
if (failed[0]) {
return false;
}
// the page is responsible for ensuring the created control is
// accessible via getControl.
Assert.isNotNull(currentPage.getControl());
}
// Force calculation of the page's description label because
// label can be wrapped.
final Point[] size = new Point[1];
final Point failed = new Point(-1, -1);
SafeRunnable.run(new ISafeRunnable() {
@Override
public void handleException(Throwable e) {
size[0] = failed;
}
@Override
public void run() {
size[0] = currentPage.computeSize();
}
});
if (size[0].equals(failed)) {
return false;
}
Point contentSize = size[0];
// Do we need resizing. Computation not needed if the
// first page is inserted since computing the dialog's
// size is done by calling dialog.open().
// Also prevent auto resize if the user has manually resized
Shell shell = getShell();
Point shellSize = shell.getSize();
if (oldPage != null) {
Rectangle rect = pageContainer.getClientArea();
Point containerSize = new Point(rect.width, rect.height);
int hdiff = contentSize.x - containerSize.x;
int vdiff = contentSize.y - containerSize.y;
if (((hdiff > 0) || (vdiff > 0)) && shellSize.equals(lastShellSize)) {
hdiff = Math.max(0, hdiff);
vdiff = Math.max(0, vdiff);
setShellSize(shellSize.x + hdiff, shellSize.y + vdiff);
lastShellSize = shell.getSize();
if (currentPage.getControl().getSize().x == 0) {
currentPage.getControl().setSize(containerSize);
}
} else {
currentPage.setSize(containerSize);
}
}
scrolled.setMinSize(contentSize);
// Ensure that all other pages are invisible
// (including ones that triggered an exception during
// their creation).
Control[] children = pageContainer.getChildren();
Control currentControl = currentPage.getControl();
for (int i = 0; i < children.length; i++) {
if (children[i] != currentControl) {
children[i].setVisible(false);
}
}
// Make the new page visible
currentPage.setVisible(true);
if (oldPage != null) {
oldPage.setVisible(false);
}
// update the dialog controls
update();
return true;
}
private void setShellSize(int width, int height) {
Rectangle preferred = getShell().getBounds();
preferred.width = width;
preferred.height = height;
getShell().setBounds(getConstrainedShellBounds(preferred));
}
/**
* Updates this dialog's controls to reflect the current page.
*/
protected void update() {
// Update the title bar
updateTitle();
// Update the message line
updateMessage();
}
public void updateTitle() {
if (currentPage == null) {
return;
}
messageArea.showTitle(currentPage.getTitle(), currentPage.getImage());
}
public void updateMessage() {
String message = null;
String errorMessage = null;
if (currentPage != null) {
message = currentPage.getMessage();
errorMessage = currentPage.getErrorMessage();
}
int messageType = IMessageProvider.NONE;
if ((message != null) && (currentPage != null)) {
messageType = ((IMessageProvider) currentPage).getMessageType();
}
if (errorMessage == null) {
if (showingError) {
// we were previously showing an error
showingError = false;
}
} else {
message = errorMessage;
messageType = IMessageProvider.ERROR;
if (!showingError) {
// we were not previously showing an error
showingError = true;
}
}
messageArea.updateText(message, messageType);
}
@Override
protected boolean isResizable() {
// DD: couldn't find out why the display was wrong after the dialog
// was resized, so forbid it for now.
return false;
}
}