/*******************************************************************************
* Copyright (c) 2009 QNX Software Systems 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:
* QNX Software Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.workingsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.cdt.ui.CUIPlugin;
/**
* The view controller for the working set configurations pane in the dialog. It takes care of coordinating
* the user gestures in the pane with the working set configuration model, and vice-versa. It also implements
* the handling of the action buttons.
*
* @author Christian W. Damus (cdamus)
*
* @since 6.0
*
*/
class WorkingSetConfigsController implements SelectionListener, ISelectionChangedListener {
private TreeViewer tree;
private Button addButton;
private Button removeButton;
private Button renameButton;
private Button activateButton;
private Button buildButton;
private IWorkingSetProxy.ISnapshot initialWorkingSet;
private IWorkingSetProxy.ISnapshot currentWorkingSet;
private IWorkingSetConfiguration.ISnapshot currentConfig;
private final WorkspaceSnapshot workspace;
private ProjectConfigsController projectsController;
/**
* Initializes me we an initial working set to select.
*/
WorkingSetConfigsController(WorkspaceSnapshot workspace, IWorkingSetProxy.ISnapshot initialWorkingSet) {
this.workspace = workspace;
if (initialWorkingSet != null) {
this.initialWorkingSet = initialWorkingSet;
} else {
IWorkingSet[] recent = WorkingSetConfigurationManager.WS_MGR.getRecentWorkingSets();
if ((recent != null) && (recent.length > 0)) {
this.initialWorkingSet = workspace.getWorkingSet(recent[0].getName());
}
}
}
/**
* Initializes me.
*/
WorkingSetConfigsController(WorkspaceSnapshot workspace) {
this(workspace, null);
}
/**
* Assigns the tree viewer that I control.
*
* @param tree
* my tree viewer
*/
void setTreeViewer(final TreeViewer tree) {
if (this.tree != null) {
this.tree.removeSelectionChangedListener(this);
}
this.tree = tree;
if (this.tree != null) {
this.tree.addSelectionChangedListener(this);
this.tree.setInput(workspace.getWorkingSets());
if (!workspace.getWorkingSets().isEmpty()) {
tree.getTree().getDisplay().asyncExec(new Runnable() {
public void run() {
Object initialSelection;
ITreeContentProvider content = (ITreeContentProvider) tree.getContentProvider();
if ((initialWorkingSet != null)
&& Arrays.asList(content.getElements(tree.getInput())).contains(
initialWorkingSet)) {
initialSelection = initialWorkingSet;
} else {
// we have a most-recently-used working set. Just
// take the first in tree order
initialSelection = tree.getTree().getItem(0).getData();
}
Object[] children = content.getChildren(initialSelection);
IStructuredSelection sel;
if ((children == null) || (children.length == 0)) {
// Shouldn't happen: there should at least be the
// read-only config.
// Can only select the initial working set
sel = new StructuredSelection(initialSelection);
} else {
Object[] toSort = new Object[children.length];
System.arraycopy(children, 0, toSort, 0, children.length);
tree.getComparator().sort(tree, toSort);
sel = new StructuredSelection(toSort[0]);
}
// make the selection
tree.setSelection(sel, true);
}
});
}
}
}
private Button updateButton(Button oldButton, Button newButton) {
if (oldButton != null) {
oldButton.removeSelectionListener(this);
}
if (newButton != null) {
newButton.addSelectionListener(this);
}
return newButton;
}
/**
* Assigns me my "Add..." button.
*
* @param addButton
* my add button
*/
void setAddButton(Button addButton) {
this.addButton = updateButton(this.addButton, addButton);
}
/**
* Assigns me my "Remove" button.
*
* @param removeButton
* my remove button
*/
void setRemoveButton(Button removeButton) {
this.removeButton = updateButton(this.removeButton, removeButton);
}
/**
* Assigns me my "Rename..." button.
*
* @param renameButton
* my rename button
*/
void setRenameButton(Button renameButton) {
this.renameButton = updateButton(this.renameButton, renameButton);
}
/**
* Assigns me my "Activate" button.
*
* @param activateButton
* my activate button
*/
void setActivateButton(Button activateButton) {
this.activateButton = updateButton(this.activateButton, activateButton);
}
/**
* Assigns me my "Build" button.
*
* @param buildButton
* my build button
*/
void setBuildButton(Button buildButton) {
this.buildButton = updateButton(this.buildButton, buildButton);
}
/**
* Connects me to the controller for the project configurations pane, into which I inject the currently
* selected working set configuration.
*
* @param controller
* my project configurations controller
*/
void setProjectConfigsController(ProjectConfigsController controller) {
this.projectsController = controller;
if (controller != null) {
controller.setWorkingSetConfiguration(currentConfig);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
// not interesting
}
/**
* Handles button presses in the working set configurations pane.
*/
public void widgetSelected(SelectionEvent e) {
// handle button press
if (e.widget == addButton) {
addConfig();
} else if (e.widget == removeButton) {
removeConfig();
} else if (e.widget == renameButton) {
renameConfig();
} else if (e.widget == activateButton) {
activateConfig();
} else if (e.widget == buildButton) {
buildConfig();
}
}
/**
* Handles selection of working sets and their configurations. Among potentially other actions, this
* injects the working-set configuration selection into the project configurations controller and updates
* the enablement of the buttons.
*/
public void selectionChanged(SelectionChangedEvent event) {
currentConfig = null;
currentWorkingSet = null;
if (event.getSelection() instanceof IStructuredSelection) {
IStructuredSelection sel = (IStructuredSelection) event.getSelection();
if (!sel.isEmpty()) {
Object first = sel.getFirstElement();
if (first instanceof IWorkingSetConfiguration) {
currentConfig = (IWorkingSetConfiguration.ISnapshot) first;
currentWorkingSet = currentConfig.getWorkingSet();
} else if (first instanceof IWorkingSetProxy) {
currentWorkingSet = (IWorkingSetProxy.ISnapshot) first;
}
}
}
if (projectsController != null) {
// tell the project controller
projectsController.setWorkingSetConfiguration(currentConfig);
}
updateButtons();
}
/**
* Handler for the "Add..." button.
*/
private void addConfig() {
InputDialog dlg = new InputDialog(tree.getTree().getShell(),
WorkingSetMessages.WSConfigsController_addDlg_title,
WorkingSetMessages.WSConfigsController_addDlg_msg,
WorkingSetMessages.WSConfigsController_addDlg_defaultName, new IInputValidator() {
public String isValid(String newText) {
if (currentWorkingSet.getConfiguration(newText) != null) {
return WorkingSetMessages.WSConfigsController_addDlg_nameExists;
}
if (newText.length() == 0) {
return WorkingSetMessages.WSConfigsController_addDlg_emptyName;
}
return null;
}
});
if (dlg.open() == IDialogConstants.OK_ID) {
IWorkingSetConfiguration.ISnapshot newConfig = currentWorkingSet.createConfiguration(dlg
.getValue());
tree.refresh(currentWorkingSet);
tree.setSelection(new StructuredSelection(newConfig), true);
currentConfig = newConfig;
currentWorkingSet = currentConfig.getWorkingSet();
// this is a "recently used" working set
IWorkingSet ws = currentWorkingSet.resolve();
if (ws != null) {
WorkingSetConfigurationManager.WS_MGR.addRecentWorkingSet(ws);
}
}
}
/**
* Handler for the "Remove" button.
*/
private void removeConfig() {
currentWorkingSet.removeConfiguration(currentConfig);
tree.refresh(currentWorkingSet);
}
/**
* Handler for the "Rename..." button.
*/
private void renameConfig() {
InputDialog dlg = new InputDialog(tree.getTree().getShell(),
WorkingSetMessages.WSConfigsController_renameDlg_title,
WorkingSetMessages.WSConfigsController_renameDlg_msg, currentConfig.getName(),
new IInputValidator() {
public String isValid(String newText) {
if (newText.equals(currentConfig.getName())) {
return ""; //$NON-NLS-1$
}
if (currentWorkingSet.getConfiguration(newText) != null) {
return WorkingSetMessages.WSConfigsController_addDlg_nameExists;
}
if (newText.length() == 0) {
return WorkingSetMessages.WSConfigsController_addDlg_emptyName;
}
return null;
}
});
if (dlg.open() == IDialogConstants.OK_ID) {
currentConfig.setName(dlg.getValue());
tree.refresh(currentWorkingSet);
}
}
/**
* Handler for the "Activate" button.
*/
private void activateConfig() {
currentConfig.activate();
projectsController.update();
updateForActivation();
}
/**
* Updates the display to reflect potential changes in project activation and the resulting changes in
* working-set config activation, if any.
*/
private void updateForActivation() {
// update all working-set configs that intersect this config
Collection<IWorkingSetProxy.ISnapshot> unaffectedWorkingSets = new java.util.HashSet<IWorkingSetProxy.ISnapshot>(
workspace.getWorkingSets());
for (IProject project : currentConfig.getWorkingSet().resolveProjects()) {
for (Iterator<IWorkingSetProxy.ISnapshot> iter = unaffectedWorkingSets.iterator(); iter.hasNext();) {
IWorkingSetProxy.ISnapshot next = iter.next();
if (next.resolveProjects().contains(project)) {
iter.remove();
if (next.updateActiveConfigurations()) {
// major change. Refresh it altogether
tree.refresh(next);
} else {
// lighter-weight updates of its configs
for (IWorkingSetConfiguration config : next.getConfigurations()) {
tree.update(config, null);
}
}
}
}
}
updateButtons();
}
/**
* Handler for the "Build" button.
*/
private void buildConfig() {
final IStatus[] problem = new IStatus[1];
try {
ProgressMonitorDialog dlg = new ProgressMonitorDialog(tree.getControl().getShell());
dlg.run(true, true, new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) {
IStatus status = currentConfig.build(monitor);
if (status.matches(IStatus.WARNING | IStatus.ERROR)) {
problem[0] = status;
}
}
});
} catch (Exception e) {
CUIPlugin.log(WorkingSetMessages.WSConfigsController_buildFailedLog, e);
}
// it is possible that some project configurations had to applied in
// order to effect a build. Refresh to handle that case
updateForActivation();
if (problem[0] != null) {
// show the problem
ErrorDialog.openError(tree.getControl().getShell(),
WorkingSetMessages.WSConfigsController_buildFailedDlgTitle,
WorkingSetMessages.WSConfigsController_buildFailedDlgMsg, problem[0]);
}
}
/**
* Updates the enablement state of the action buttons according to the current selection.
*/
private void updateButtons() {
if (addButton != null) {
addButton.setEnabled(currentWorkingSet != null);
}
if (removeButton != null) {
removeButton.setEnabled((currentConfig != null) && !currentConfig.isReadOnly());
}
if (renameButton != null) {
renameButton.setEnabled((currentConfig != null) && !currentConfig.isReadOnly());
}
if (activateButton != null) {
activateButton.setEnabled((currentConfig != null) && !currentConfig.isActive());
}
if (buildButton != null) {
buildButton.setEnabled(currentConfig != null);
}
}
/**
* Notification that the selection of configuration(s) in some project in the current working set
* configuration has changed. I accordingly update the visuals of the working-set configuration to
* indicate whether it is active or not.
*
* @param project
* the project configuration whose active configuration selections have changed
*/
void projectSelectionsChanged(IWorkingSetProjectConfiguration project) {
tree.update(currentConfig, null);
updateButtons(); // depends on whether the ws config is active
}
}