/*******************************************************************************
* Copyright (c) 2013 GoPivotal, Inc.
* 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:
* GoPivotal, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.wizard.guides;
import java.util.HashSet;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.springsource.ide.eclipse.commons.livexp.core.LiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.UIValueListener;
import org.springsource.ide.eclipse.commons.livexp.core.ValidationResult;
import org.springsource.ide.eclipse.commons.livexp.core.Validator;
import org.springsource.ide.eclipse.commons.livexp.ui.CommentSection;
import org.springsource.ide.eclipse.commons.livexp.ui.IPageWithSections;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageSection;
import org.springsource.ide.eclipse.commons.livexp.ui.WizardPageWithSections;
/**
* @author Kris De Volder
*/
public class CodeSetCheckBoxesSection extends WizardPageSection {
public static class CheckBox extends WizardPageSection {
private final String name;
private final MultiSelectionModel<String> model;
private Button cb;
public CheckBox(IPageWithSections owner, String name, MultiSelectionModel<String> model) {
super(owner);
this.name = name;
this.model = model;
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return Validator.OK;
}
@Override
public void createContents(Composite page) {
if (page!=null && !page.isDisposed()) {
this.cb = new Button(page, SWT.CHECK);
cb.setText(name);
cb.setSelection(model.selecteds.contains(name));
GridDataFactory.fillDefaults().grab(true, false).applyTo(cb);
cb.addSelectionListener(new SelectionListener() {
//@Override
public void widgetSelected(SelectionEvent e) {
handleSelection();
}
//@Override
public void widgetDefaultSelected(SelectionEvent e) {
handleSelection();
}
private void handleSelection() {
boolean add = cb.getSelection();
if (add) {
model.selecteds.add(name);
} else {
model.selecteds.remove(name);
}
}
});
}
}
@Override
public void dispose() {
if (cb!=null && !cb.isDisposed()) {
cb.dispose();
cb = null;
}
}
}
private final LiveExpression<String[]> options;
private final MultiSelectionModel<String> model;
private Group group;
public CodeSetCheckBoxesSection(WizardPageWithSections owner, LiveExpression<String[]> options, MultiSelectionModel<String> model) {
super(owner);
this.model = model;
this.options = options;
}
private WizardPageSection[] subsections;
protected GridLayout createLayout() {
return new GridLayout(2, true);
}
@Override
public LiveExpression<ValidationResult> getValidator() {
return model.validator;
}
@Override
public void createContents(Composite page) {
this.group = new Group(page, SWT.NONE);
this.group.setText("Code Sets");
GridLayout layout = createLayout();
group.setLayout(layout);
GridDataFactory.fillDefaults().grab(true, false).applyTo(group);
//This section is a bit special in that the contents of the group is only
// filled in dynamically in response to events from the 'options' LiveExpression.
options.addListener(new UIValueListener<String[]>() {
@Override
public void uiGotValue(LiveExpression<String[]> exp, String[] names) {
if (group==null || group.isDisposed()) {
//Don't bother. The UI is already gone.
options.removeListener(this);
return;
}
if (names==null) {
names = new String[0];
}
//Dispose the checkboxes and create new ones.
if (subsections!=null) {
for (WizardPageSection subsection : subsections) {
subsection.dispose();
}
}
subsections = new WizardPageSection[Math.max(1, names.length)];
// GridData gd = (GridData) group.getLayoutData();
// boolean visible = checkboxes.length>0;
// gd.exclude = !visible;
if (names.length==0) {
//don't leave section empty it looks ugly
subsections[0] = new CommentSection(owner, "No codesets");
subsections[0].createContents(group);
}
GridLayout newLayout = createLayout();
newLayout.numColumns = names.length>2 ? 3 : 2;
group.setLayout(newLayout);
for (int i = 0; i < names.length; i++) {
subsections[i] = new CheckBox(owner, names[i], model);
if (isNewName(names[i])) {
model.selecteds.add(names[i]);
}
subsections[i].createContents(group);
}
//Note: code below removes invalid names from selection model.
// Code has been commented out. We assume that any code
// using the 'model.selecteds' will just ignore invalid
// codeset names. That way we can leave the selecteds as is.
// The benefit is that if one switches away from a guide
// and then back again, then selected items will be 'preserved' even if
// they weren't valid in between. (If the code below is reactivated invalid
// names will be automatically cleared and the user will have to reselect them
// when they return to the original guide).
// HashSet<String> validNameSet = new HashSet<String>(Arrays.asList(names));
// for (String selectedName : model.selecteds.getValues()) {
// if (!validNameSet.contains(selectedName)) {
// model.selecteds.remove(selectedName);
// }
// };
group.getParent().layout(true, true);
}
private final HashSet<String> namesSeen = new HashSet<String>();
/**
* Tracks codeset names that have (not) been seen before.
* This method returns true the first time it is called with a given
* name. Subsequent calls with the same name will return false
*/
private boolean isNewName(String name) {
boolean seen = namesSeen.contains(name);
if (!seen) {
//Seen it now...next time its not new anymore
namesSeen.add(name);
}
return !seen;
}
});
}
private void clear(Group group) {
Control[] children = group.getChildren();
for (Control c : children) {
c.dispose();
}
}
}