/*******************************************************************************
* Copyright (c) 2012 Arapiki Solutions 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:
* "Peter Smith <psmith@arapiki.com>" - initial API and
* implementation and/or initial documentation
*******************************************************************************/
package com.buildml.eclipse.utils;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
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.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import com.buildml.eclipse.utils.errors.FatalError;
import com.buildml.model.IBuildStore;
import com.buildml.model.IPackageMemberMgr;
import com.buildml.model.IPackageMgr;
import com.buildml.model.types.PackageSet;
/**
* A Dialog allowing the user to select (with checkboxes) a subset of all the
* BuildStore's packages. This class is used in the Eclipse plugin, wherever
* it's necessary to select a combination of packages.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
public class PackageFilterDialog extends BmlTitleAreaDialog {
/*=====================================================================================*
* FIELDS/TYPES
*=====================================================================================*/
/**
* The input PackageSet that this Dialog will display. This remains unmodified since
* we create a clone of this object before modifying it.
*/
private PackageSet pkgSet;
/**
* We need one widget for each line of the table.
*/
private PkgSelectWidget lineWidgets[];
/** Default width of the dialog box. */
private int dialogWidth;
/** Default height of the dialog box. */
private int dialogHeight;
/** Should the dialog box show scope names? */
private boolean showScopes;
/*=====================================================================================*
* NESTED CLASS - PkgSelectWidget
*=====================================================================================*/
/**
* A nested class representing a single line in the PackageFilterDialog window.
* Each PkgSelectWidget contains the package's name, and a checkbox for each of
* the scopes.
*
* @author "Peter Smith <psmith@arapiki.com>"
*/
private class PkgSelectWidget extends Composite {
/*---------------------------------------------------------------------------------*/
/** Each scope name (except for None) has its own checkbox widget. */
private Button checkBoxes[] = null;
/** This ID of the package that this widget represents. */
int pkgId;
/*---------------------------------------------------------------------------------*/
/**
* Create a new instance of the PkgSelectWidget class.
* @param parent The PackageFilterDialog composite that this widget is pat of.
* @param pkgName The name of the package to be displayed.
*/
public PkgSelectWidget(
Composite parent,
String pkgName)
{
super(parent, 0);
final IPackageMgr pkgMgr = pkgSet.getBuildStore().getPackageMgr();
final IPackageMemberMgr pkgMemberMgr = pkgSet.getBuildStore().getPackageMemberMgr();
/* get the scope name, and prepare for a checkbox for each */
checkBoxes = new Button[IPackageMemberMgr.SCOPE_MAX];
/* we'll use the package's internal ID when accessing the package Set */
pkgId = pkgMgr.getId(pkgName);
/* make sure this widget is stretched to the full width of the shell */
this.setLayoutData(new GridData(SWT.FILL, SWT.NONE, true, false));
/*
* Each line will contain a section for the package name,
* and a section for each of the scopes (excluding the None scope).
*/
GridLayout layout = new GridLayout();
layout.numColumns = 1 + getNumScopes();
layout.marginHeight = 0;
setLayout(layout);
/* The package name takes as much of the left side as possible */
Label label1 = new Label(this, 0);
label1.setText(pkgName);
label1.setLayoutData(new GridData(SWT.LEFT, SWT.NONE, true, false));
/*
* On the right side, create a new checkbox button for each scope
* (except for "None"). We take note of each widget (in the checkBoxes)
* array so we can set/refresh it later.
*/
int numScopes = getNumScopes();
for (int i = 0; i < numScopes; i++) {
/* the scope check boxes are on the right side */
final Button newButton = new Button(this, SWT.CHECK);
if (showScopes) {
newButton.setText(pkgMemberMgr.getScopeName(i + 1));
} else {
newButton.setText("Show");
}
newButton.setLayoutData(new GridData());
/*
* An an event listener that updates the current state of
* the PackageSet whenever a checkbox is selected.
*/
newButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
Button button = (Button)(e.getSource());
String buttonText = button.getText();
int scopeId;
if (showScopes) {
scopeId = pkgMemberMgr.getScopeId(buttonText);
} else {
scopeId = IPackageMemberMgr.SCOPE_PUBLIC;
}
if (button.getSelection()) {
pkgSet.add(pkgId, scopeId);
} else {
pkgSet.remove(pkgId, scopeId);
}
}
});
checkBoxes[i] = newButton;
}
/* set all the checkboxes to their initial values. */
refresh();
}
/*---------------------------------------------------------------------------------*/
/**
* Refresh this widget with update content from the pkgSet.
*/
public void refresh()
{
int numScopes = getNumScopes();
for (int i = 0; i != numScopes; i++) {
Button thisCheck = checkBoxes[i];
if (showScopes) {
thisCheck.setSelection(pkgSet.isMember(pkgId, i + 1));
} else {
thisCheck.setSelection(pkgSet.isMember(pkgId, IPackageMemberMgr.SCOPE_PUBLIC));
}
}
}
/*---------------------------------------------------------------------------------*/
/**
* @return The number of scope names that should be displayed (e.g. "Public" and
* "Private"), or just "Show".
*/
private int getNumScopes() {
if (showScopes) {
return IPackageMemberMgr.SCOPE_MAX;
} else {
return 1;
}
}
/*---------------------------------------------------------------------------------*/
}
/*=====================================================================================*
* CONSTRUCTOR
*=====================================================================================*/
/**
* Create a new PackageFilterDialog object, with the specified set of packages
* shown as being selected.
* @param initialPkgs The packages that will initially be selected.
* @param showScopes True if our dialog box should show the package scope names (public
* and private), or just allow the whole package to be selected.
*/
public PackageFilterDialog(PackageSet initialPkgs, boolean showScopes) {
super(new Shell(), 0, 0, 0, 0.5);
this.showScopes = showScopes;
/*
* Make a copy of the PackageSet, so we can mess with it as much as we like
* without disturbing the original copy (if the user hits "Cancel", we want the
* original to be untouched).
*/
try {
pkgSet = (PackageSet)initialPkgs.clone();
} catch (CloneNotSupportedException e) {
throw new FatalError("Cloning not supported on PackageSet objects");
}
}
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/**
* @return The PackageSet representing the current combination
* of components/scopes that are selected in the dialog.
*/
public PackageSet getPackageSet() {
return pkgSet;
}
/*-------------------------------------------------------------------------------------*/
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
@Override
protected Control createDialogArea(Composite parent) {
final IPackageMgr pkgMgr = pkgSet.getBuildStore().getPackageMgr();
setTitle("Select the packages you wish to view:");
setHelpAvailable(false);
/* create and format the top-level composite of this dialog */
Composite composite = (Composite) super.createDialogArea(parent);
/*
* Add a box containing all the packages, and their selectable state. We put
* everything inside a ScrolledComposite, so that the scrollbar is managed for us.
*/
ScrolledComposite scrolledComposite = new ScrolledComposite(composite, SWT.BORDER | SWT.V_SCROLL);
scrolledComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
scrolledComposite.setLayout(new GridLayout());
scrolledComposite.setAlwaysShowScrollBars(true);
/* Add another composite (within the ScrolledComposite) that will contain the list of package names */
Composite listComposite = new Composite(scrolledComposite, SWT.NONE);
listComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
GridLayout layout = new GridLayout();
layout.numColumns = 1;
listComposite.setLayout(layout);
/* Add a new line for each package name */
String packageNames[] = pkgMgr.getPackages();
lineWidgets = new PkgSelectWidget[packageNames.length];
for (int i = 0; i != packageNames.length; i++) {
String pkgName = packageNames[i];
lineWidgets[i] = new PkgSelectWidget(listComposite, pkgName);
}
/* tell the ScrolledComposite what it's managing, and how big it should be. */
scrolledComposite.setContent(listComposite);
scrolledComposite.setExpandHorizontal(true);
scrolledComposite.setMinWidth(0);
listComposite.setSize(listComposite.computeSize(dialogWidth, SWT.DEFAULT));
return composite;
}
/*-------------------------------------------------------------------------------------*/
/*
* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
/* add the standard buttons */
super.createButtonsForButtonBar(parent);
createButton(parent, IDialogConstants.SELECT_ALL_ID,
"Select All", false);
createButton(parent, IDialogConstants.DESELECT_ALL_ID,
"Deselect All", false);
}
/*-------------------------------------------------------------------------------------*/
/*
* (non-Javadoc)
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
*/
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
/* centre the dialog in the middle of the screen */
Rectangle parentBounds = Display.getCurrent().getBounds();
dialogHeight = parentBounds.height / 2;
dialogWidth = parentBounds.width / 4;
newShell.setBounds(
parentBounds.width / 2 - (dialogWidth / 2),
parentBounds.height / 2 - (dialogHeight / 2), dialogWidth, dialogHeight);
newShell.setText("Select Packages to View");
}
/*-------------------------------------------------------------------------------------*/
/*
* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
*/
@Override
protected void buttonPressed(int buttonId) {
/* handle standard buttons */
super.buttonPressed(buttonId);
/*
* Handle the "Select all" or "Deselect All" buttons.
*/
if ((buttonId == IDialogConstants.SELECT_ALL_ID) ||
(buttonId == IDialogConstants.DESELECT_ALL_ID)) {
/*
* Create a new (replacement) PackageSet with no content, but
* a default of false (deselect all) or true (select all).
*/
IBuildStore bs = pkgSet.getBuildStore();
pkgSet = new PackageSet(bs);
if (buttonId == IDialogConstants.SELECT_ALL_ID) {
pkgSet.setDefault(true);
}
/*
* Refresh all the widgets, using this new PackageSet.
*/
for (int i = 0; i < lineWidgets.length; i++) {
lineWidgets[i].refresh();
}
}
}
/*-------------------------------------------------------------------------------------*/
}