/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.ide.dialogs; import java.util.TreeSet; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.preference.FieldEditor; import org.eclipse.jface.preference.IntegerFieldEditor; import org.eclipse.jface.preference.PreferencePage; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Font; 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.Label; import org.eclipse.swt.widgets.List; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPreferencePage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.GlobalBuildAction; import org.eclipse.ui.dialogs.ListSelectionDialog; import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; import org.eclipse.ui.internal.ide.IIDEHelpContextIds; import org.eclipse.ui.internal.util.PrefUtil; /** * Page used to determine what order projects will be built in * by the workspace. */ public class BuildOrderPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { private IWorkbench workbench; private Button defaultOrderButton; private Label buildLabel; private List buildList; private Composite buttonComposite; private IntegerFieldEditor maxItersField; private String[] defaultBuildOrder; private String[] customBuildOrder; //Boolean to indicate if we have looked it up private boolean notCheckedBuildOrder = true; private final String UP_LABEL = IDEWorkbenchMessages.BuildOrderPreference_up; private final String DOWN_LABEL = IDEWorkbenchMessages.BuildOrderPreference_down; private final String ADD_LABEL = IDEWorkbenchMessages.BuildOrderPreference_add; private final String REMOVE_LABEL = IDEWorkbenchMessages.BuildOrderPreference_remove; private final String PROJECT_SELECTION_MESSAGE = IDEWorkbenchMessages.BuildOrderPreference_selectOtherProjects; private final String DEFAULTS_LABEL = IDEWorkbenchMessages.BuildOrderPreference_useDefaults; private final String LIST_LABEL = IDEWorkbenchMessages.BuildOrderPreference_projectBuildOrder; private final String NOTE_LABEL = IDEWorkbenchMessages.Preference_note; private final String REMOVE_MESSAGE = IDEWorkbenchMessages.BuildOrderPreference_removeNote; // whether or not the use defaults option was selected when Apply (or OK) was last pressed // (or when the preference page was opened). This represents the most recent applied state. private boolean defaultOrderInitiallySelected; private IPropertyChangeListener validityChangeListener = new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { if (event.getProperty().equals(FieldEditor.IS_VALID)) { updateValidState(); } } }; /** * Add another project to the list at the end. */ private void addProject() { String[] currentItems = this.buildList.getItems(); IProject[] allProjects = getWorkspace().getRoot().getProjects(); ILabelProvider labelProvider = new LabelProvider() { public String getText(Object element) { return (String) element; } }; SimpleListContentProvider contentsProvider = new SimpleListContentProvider(); contentsProvider .setElements(sortedDifference(allProjects, currentItems)); ListSelectionDialog dialog = new ListSelectionDialog(this.getShell(), this, contentsProvider, labelProvider, PROJECT_SELECTION_MESSAGE) { protected int getShellStyle() { return super.getShellStyle() | SWT.SHEET; } }; if (dialog.open() != Window.OK) { return; } Object[] result = dialog.getResult(); int currentItemsLength = currentItems.length; int resultLength = result.length; String[] newItems = new String[currentItemsLength + resultLength]; System.arraycopy(currentItems, 0, newItems, 0, currentItemsLength); System .arraycopy(result, 0, newItems, currentItemsLength, result.length); this.buildList.setItems(newItems); } /** * Updates the valid state of the page. */ private void updateValidState() { setValid(maxItersField.isValid()); } /** * Create the list of build paths. If the current build order is empty make the list empty * and disable it. * @param composite - the parent to create the list in * @param enabled - the boolean that indcates if the list will be sensitive initially or not */ private void createBuildOrderList(Composite composite, boolean enabled) { Font font = composite.getFont(); this.buildLabel = new Label(composite, SWT.NONE); this.buildLabel.setText(LIST_LABEL); this.buildLabel.setEnabled(enabled); GridData gridData = new GridData(); gridData.horizontalAlignment = GridData.FILL; gridData.horizontalSpan = 2; this.buildLabel.setLayoutData(gridData); this.buildLabel.setFont(font); this.buildList = new List(composite, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); this.buildList.setEnabled(enabled); GridData data = new GridData(); //Set heightHint with a small value so the list size will be defined by //the space available in the dialog instead of resizing the dialog to //fit all the items in the list. data.heightHint = buildList.getItemHeight(); data.verticalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL; data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = true; this.buildList.setLayoutData(data); this.buildList.setFont(font); } /** * Create the widgets that are used to determine the build order. * * @param parent the parent composite * @return the new control */ protected Control createContents(Composite parent) { PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, IIDEHelpContextIds.BUILD_ORDER_PREFERENCE_PAGE); Font font = parent.getFont(); //The main composite Composite composite = new Composite(parent, SWT.NULL); GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginWidth = 0; layout.marginHeight = 0; composite.setLayout(layout); GridData data = new GridData(); data.verticalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL; composite.setLayoutData(data); composite.setFont(font); String[] buildOrder = getCurrentBuildOrder(); boolean useDefault = (buildOrder == null); createDefaultPathButton(composite, useDefault); // List always enabled so user can scroll list. // Only the buttons need to be disabled. createBuildOrderList(composite, true); createListButtons(composite, !useDefault); Composite noteComposite = createNoteComposite(font, composite, NOTE_LABEL, REMOVE_MESSAGE); GridData noteData = new GridData(); noteData.horizontalSpan = 2; noteComposite.setLayoutData(noteData); createSpacer(composite); createMaxIterationsField(composite); createSpacer(composite); if (useDefault) { this.buildList.setItems(getDefaultProjectOrder()); } else { this.buildList.setItems(buildOrder); } return composite; } /** * Adds in a spacer. * * @param composite the parent composite */ private void createSpacer(Composite composite) { Label spacer = new Label(composite, SWT.NONE); GridData spacerData = new GridData(); spacerData.horizontalSpan = 2; spacer.setLayoutData(spacerData); } /** * Create the default path button. Set it to selected based on the current workspace * build path. * @param composite org.eclipse.swt.widgets.Composite * @param selected - the boolean that indicates the buttons initial state */ private void createDefaultPathButton(Composite composite, boolean selected) { defaultOrderInitiallySelected = selected; this.defaultOrderButton = new Button(composite, SWT.LEFT | SWT.CHECK); this.defaultOrderButton.setSelection(selected); this.defaultOrderButton.setText(DEFAULTS_LABEL); SelectionListener listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { defaultsButtonSelected(defaultOrderButton.getSelection()); } }; this.defaultOrderButton.addSelectionListener(listener); GridData gridData = new GridData(); gridData.horizontalAlignment = GridData.FILL; gridData.horizontalSpan = 2; this.defaultOrderButton.setLayoutData(gridData); this.defaultOrderButton.setFont(composite.getFont()); } /** * Create the buttons used to manipulate the list. These Add, Remove and Move Up or Down * the list items. * @param composite the parent of the buttons * @param enableComposite - boolean that indicates if a composite should be enabled */ private void createListButtons(Composite composite, boolean enableComposite) { Font font = composite.getFont(); //Create an intermeditate composite to keep the buttons in the same column this.buttonComposite = new Composite(composite, SWT.RIGHT); GridLayout layout = new GridLayout(); layout.marginWidth = 0; layout.marginHeight = 0; this.buttonComposite.setLayout(layout); GridData gridData = new GridData(); gridData.verticalAlignment = GridData.FILL; gridData.horizontalAlignment = GridData.FILL; this.buttonComposite.setLayoutData(gridData); this.buttonComposite.setFont(font); Button upButton = new Button(this.buttonComposite, SWT.CENTER | SWT.PUSH); upButton.setText(UP_LABEL); upButton.setEnabled(enableComposite); upButton.setFont(font); setButtonLayoutData(upButton); SelectionListener listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { moveSelectionUp(); } }; upButton.addSelectionListener(listener); Button downButton = new Button(this.buttonComposite, SWT.CENTER | SWT.PUSH); downButton.setText(DOWN_LABEL); downButton.setEnabled(enableComposite); listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { moveSelectionDown(); } }; downButton.addSelectionListener(listener); downButton.setFont(font); setButtonLayoutData(downButton); Button addButton = new Button(this.buttonComposite, SWT.CENTER | SWT.PUSH); addButton.setText(ADD_LABEL); listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { addProject(); } }; addButton.addSelectionListener(listener); addButton.setEnabled(enableComposite); addButton.setFont(font); setButtonLayoutData(addButton); Button removeButton = new Button(this.buttonComposite, SWT.CENTER | SWT.PUSH); removeButton.setText(REMOVE_LABEL); listener = new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { removeSelection(); } }; removeButton.addSelectionListener(listener); removeButton.setEnabled(enableComposite); removeButton.setFont(font); setButtonLayoutData(removeButton); } /** * Create the field for the maximum number of iterations in the presence * of cycles. */ private void createMaxIterationsField(Composite composite) { Composite maxItersComposite = new Composite(composite, SWT.NONE); GridData gd = new GridData(GridData.FILL_HORIZONTAL); maxItersComposite.setLayoutData(gd); maxItersComposite.setFont(composite.getFont()); maxItersField = new IntegerFieldEditor( "", IDEWorkbenchMessages.BuildOrderPreference_maxIterationsLabel, maxItersComposite) { //$NON-NLS-1$ protected void doLoad() { Text text = getTextControl(); if (text != null) { int value = getWorkspace().getDescription() .getMaxBuildIterations(); text.setText(Integer.toString(value)); } } protected void doLoadDefault() { Text text = getTextControl(); if (text != null) { int value = ResourcesPlugin.getPlugin() .getPluginPreferences().getDefaultInt( ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS); text.setText(Integer.toString(value)); } valueChanged(); } protected void doStore() { // handled specially in performOK() throw new UnsupportedOperationException(); } }; maxItersField.setValidRange(1, Integer.MAX_VALUE); maxItersField.setPage(this); maxItersField.setPreferenceStore(getPreferenceStore()); maxItersField.setPropertyChangeListener(validityChangeListener); maxItersField.load(); } /** * The defaults button has been selected - update the other widgets as required. * @param selected - whether or not the defaults button got selected */ private void defaultsButtonSelected(boolean selected) { if (selected) { setBuildOrderWidgetsEnablement(false); //Cache the current value as the custom order customBuildOrder = buildList.getItems(); buildList.setItems(getDefaultProjectOrder()); } else { setBuildOrderWidgetsEnablement(true); String[] buildOrder = getCurrentBuildOrder(); if (buildOrder == null) { buildList.setItems(getDefaultProjectOrder()); } else { buildList.setItems(buildOrder); } } } /** * Get the project names for the current custom build * order stored in the workspace description. * * @return java.lang.String[] or null if there is no setting */ private String[] getCurrentBuildOrder() { if (notCheckedBuildOrder) { customBuildOrder = getWorkspace().getDescription().getBuildOrder(); notCheckedBuildOrder = false; } return customBuildOrder; } /** * Get the project names in the default build order * based on the current Workspace settings. * * @return java.lang.String[] */ private String[] getDefaultProjectOrder() { if (defaultBuildOrder == null) { IWorkspace workspace = getWorkspace(); IWorkspace.ProjectOrder projectOrder = getWorkspace() .computeProjectOrder(workspace.getRoot().getProjects()); IProject[] foundProjects = projectOrder.projects; defaultBuildOrder = new String[foundProjects.length]; int foundSize = foundProjects.length; for (int i = 0; i < foundSize; i++) { defaultBuildOrder[i] = foundProjects[i].getName(); } } return defaultBuildOrder; } /** * Return the Workspace the build order is from. * @return org.eclipse.core.resources.IWorkspace */ private IWorkspace getWorkspace() { return ResourcesPlugin.getWorkspace(); } /** * Return whether or not searchElement is in testArray. */ private boolean includes(String[] testArray, String searchElement) { for (int i = 0; i < testArray.length; i++) { if (searchElement.equals(testArray[i])) { return true; } } return false; } /** * See IWorkbenchPreferencePage. This class does nothing with he Workbench. */ public void init(IWorkbench workbench) { this.workbench = workbench; setPreferenceStore(PrefUtil.getInternalPreferenceStore()); } /** * Move the current selection in the build list down. */ private void moveSelectionDown() { //Only do this operation on a single selection if (this.buildList.getSelectionCount() == 1) { int currentIndex = this.buildList.getSelectionIndex(); if (currentIndex < this.buildList.getItemCount() - 1) { String elementToMove = this.buildList.getItem(currentIndex); this.buildList.remove(currentIndex); this.buildList.add(elementToMove, currentIndex + 1); this.buildList.select(currentIndex + 1); } } } /** * Move the current selection in the build list up. */ private void moveSelectionUp() { int currentIndex = this.buildList.getSelectionIndex(); //Only do this operation on a single selection if (currentIndex > 0 && this.buildList.getSelectionCount() == 1) { String elementToMove = this.buildList.getItem(currentIndex); this.buildList.remove(currentIndex); this.buildList.add(elementToMove, currentIndex - 1); this.buildList.select(currentIndex - 1); } } /** * Performs special processing when this page's Defaults button has been pressed. * In this case change the defaultOrderButton to have it's selection set to true. */ protected void performDefaults() { this.defaultOrderButton.setSelection(true); defaultsButtonSelected(true); maxItersField.loadDefault(); super.performDefaults(); } /** * OK has been pressed. If the defualt button is pressed then reset the build order to false; * otherwise set it to the contents of the list. */ public boolean performOk() { String[] buildOrder = null; boolean useDefault = defaultOrderButton.getSelection(); // if use defaults is turned off if (!useDefault) { buildOrder = buildList.getItems(); } //Get a copy of the description from the workspace, set the build order and then //apply it to the workspace. IWorkspaceDescription description = getWorkspace().getDescription(); description.setBuildOrder(buildOrder); description.setMaxBuildIterations(maxItersField.getIntValue()); try { getWorkspace().setDescription(description); } catch (CoreException exception) { //failed - return false return false; } // Perform auto-build if use default is off (because // order could have changed) or if use default setting // was changed. if (!useDefault || (useDefault != defaultOrderInitiallySelected)) { defaultOrderInitiallySelected = useDefault; // If auto build is turned on, then do a global incremental // build on all the projects. if (ResourcesPlugin.getWorkspace().isAutoBuilding()) { GlobalBuildAction action = new GlobalBuildAction(workbench .getActiveWorkbenchWindow(), IncrementalProjectBuilder.INCREMENTAL_BUILD); action.doBuild(); } } // Clear the custom build order cache customBuildOrder = null; return true; } /** * Remove the current selection in the build list. */ private void removeSelection() { this.buildList.remove(this.buildList.getSelectionIndices()); } /** * Set the widgets that select build order to be enabled or diabled. * @param value boolean */ private void setBuildOrderWidgetsEnablement(boolean value) { // Only change enablement of buttons. Leave list alone // because you can't scroll it when disabled. Control[] children = this.buttonComposite.getChildren(); for (int i = 0; i < children.length; i++) { children[i].setEnabled(value); } } /** * Return a sorted array of the names of the projects that are already in the currently * displayed names. * @return String[] * @param allProjects - all of the projects in the workspace * @param currentlyDisplayed - the names of the projects already being displayed */ private String[] sortedDifference(IProject[] allProjects, String[] currentlyDisplayed) { TreeSet difference = new TreeSet(); for (int i = 0; i < allProjects.length; i++) { if (!includes(currentlyDisplayed, allProjects[i].getName())) { difference.add(allProjects[i].getName()); } } String[] returnValue = new String[difference.size()]; difference.toArray(returnValue); return returnValue; } }