001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.ide.dialogs;
011:
012: import java.util.TreeSet;
013:
014: import org.eclipse.core.resources.IProject;
015: import org.eclipse.core.resources.IWorkspace;
016: import org.eclipse.core.resources.IWorkspaceDescription;
017: import org.eclipse.core.resources.IncrementalProjectBuilder;
018: import org.eclipse.core.resources.ResourcesPlugin;
019: import org.eclipse.core.runtime.CoreException;
020: import org.eclipse.jface.preference.FieldEditor;
021: import org.eclipse.jface.preference.IntegerFieldEditor;
022: import org.eclipse.jface.preference.PreferencePage;
023: import org.eclipse.jface.util.IPropertyChangeListener;
024: import org.eclipse.jface.util.PropertyChangeEvent;
025: import org.eclipse.jface.viewers.ILabelProvider;
026: import org.eclipse.jface.viewers.LabelProvider;
027: import org.eclipse.jface.window.Window;
028: import org.eclipse.swt.SWT;
029: import org.eclipse.swt.events.SelectionAdapter;
030: import org.eclipse.swt.events.SelectionEvent;
031: import org.eclipse.swt.events.SelectionListener;
032: import org.eclipse.swt.graphics.Font;
033: import org.eclipse.swt.layout.GridData;
034: import org.eclipse.swt.layout.GridLayout;
035: import org.eclipse.swt.widgets.Button;
036: import org.eclipse.swt.widgets.Composite;
037: import org.eclipse.swt.widgets.Control;
038: import org.eclipse.swt.widgets.Label;
039: import org.eclipse.swt.widgets.List;
040: import org.eclipse.swt.widgets.Text;
041: import org.eclipse.ui.IWorkbench;
042: import org.eclipse.ui.IWorkbenchPreferencePage;
043: import org.eclipse.ui.PlatformUI;
044: import org.eclipse.ui.actions.GlobalBuildAction;
045: import org.eclipse.ui.dialogs.ListSelectionDialog;
046: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
047: import org.eclipse.ui.internal.ide.IIDEHelpContextIds;
048: import org.eclipse.ui.internal.util.PrefUtil;
049:
050: /**
051: * Page used to determine what order projects will be built in
052: * by the workspace.
053: */
054: public class BuildOrderPreferencePage extends PreferencePage implements
055: IWorkbenchPreferencePage {
056:
057: private IWorkbench workbench;
058:
059: private Button defaultOrderButton;
060:
061: private Label buildLabel;
062:
063: private List buildList;
064:
065: private Composite buttonComposite;
066:
067: private IntegerFieldEditor maxItersField;
068:
069: private String[] defaultBuildOrder;
070:
071: private String[] customBuildOrder;
072:
073: //Boolean to indicate if we have looked it up
074: private boolean notCheckedBuildOrder = true;
075:
076: private final String UP_LABEL = IDEWorkbenchMessages.BuildOrderPreference_up;
077:
078: private final String DOWN_LABEL = IDEWorkbenchMessages.BuildOrderPreference_down;
079:
080: private final String ADD_LABEL = IDEWorkbenchMessages.BuildOrderPreference_add;
081:
082: private final String REMOVE_LABEL = IDEWorkbenchMessages.BuildOrderPreference_remove;
083:
084: private final String PROJECT_SELECTION_MESSAGE = IDEWorkbenchMessages.BuildOrderPreference_selectOtherProjects;
085:
086: private final String DEFAULTS_LABEL = IDEWorkbenchMessages.BuildOrderPreference_useDefaults;
087:
088: private final String LIST_LABEL = IDEWorkbenchMessages.BuildOrderPreference_projectBuildOrder;
089:
090: private final String NOTE_LABEL = IDEWorkbenchMessages.Preference_note;
091:
092: private final String REMOVE_MESSAGE = IDEWorkbenchMessages.BuildOrderPreference_removeNote;
093:
094: // whether or not the use defaults option was selected when Apply (or OK) was last pressed
095: // (or when the preference page was opened). This represents the most recent applied state.
096: private boolean defaultOrderInitiallySelected;
097:
098: private IPropertyChangeListener validityChangeListener = new IPropertyChangeListener() {
099: public void propertyChange(PropertyChangeEvent event) {
100: if (event.getProperty().equals(FieldEditor.IS_VALID)) {
101: updateValidState();
102: }
103: }
104: };
105:
106: /**
107: * Add another project to the list at the end.
108: */
109: private void addProject() {
110:
111: String[] currentItems = this .buildList.getItems();
112:
113: IProject[] allProjects = getWorkspace().getRoot().getProjects();
114:
115: ILabelProvider labelProvider = new LabelProvider() {
116: public String getText(Object element) {
117: return (String) element;
118: }
119: };
120:
121: SimpleListContentProvider contentsProvider = new SimpleListContentProvider();
122: contentsProvider.setElements(sortedDifference(allProjects,
123: currentItems));
124:
125: ListSelectionDialog dialog = new ListSelectionDialog(this
126: .getShell(), this , contentsProvider, labelProvider,
127: PROJECT_SELECTION_MESSAGE);
128:
129: if (dialog.open() != Window.OK) {
130: return;
131: }
132:
133: Object[] result = dialog.getResult();
134:
135: int currentItemsLength = currentItems.length;
136: int resultLength = result.length;
137: String[] newItems = new String[currentItemsLength
138: + resultLength];
139:
140: System.arraycopy(currentItems, 0, newItems, 0,
141: currentItemsLength);
142: System.arraycopy(result, 0, newItems, currentItemsLength,
143: result.length);
144: this .buildList.setItems(newItems);
145: }
146:
147: /**
148: * Updates the valid state of the page.
149: */
150: private void updateValidState() {
151: setValid(maxItersField.isValid());
152: }
153:
154: /**
155: * Create the list of build paths. If the current build order is empty make the list empty
156: * and disable it.
157: * @param composite - the parent to create the list in
158: * @param enabled - the boolean that indcates if the list will be sensitive initially or not
159: */
160: private void createBuildOrderList(Composite composite,
161: boolean enabled) {
162:
163: Font font = composite.getFont();
164:
165: this .buildLabel = new Label(composite, SWT.NONE);
166: this .buildLabel.setText(LIST_LABEL);
167: this .buildLabel.setEnabled(enabled);
168: GridData gridData = new GridData();
169: gridData.horizontalAlignment = GridData.FILL;
170: gridData.horizontalSpan = 2;
171: this .buildLabel.setLayoutData(gridData);
172: this .buildLabel.setFont(font);
173:
174: this .buildList = new List(composite, SWT.BORDER | SWT.MULTI
175: | SWT.H_SCROLL | SWT.V_SCROLL);
176: this .buildList.setEnabled(enabled);
177: GridData data = new GridData();
178: //Set heightHint with a small value so the list size will be defined by
179: //the space available in the dialog instead of resizing the dialog to
180: //fit all the items in the list.
181: data.heightHint = buildList.getItemHeight();
182: data.verticalAlignment = GridData.FILL;
183: data.horizontalAlignment = GridData.FILL;
184: data.grabExcessHorizontalSpace = true;
185: data.grabExcessVerticalSpace = true;
186: this .buildList.setLayoutData(data);
187: this .buildList.setFont(font);
188: }
189:
190: /**
191: * Create the widgets that are used to determine the build order.
192: *
193: * @param parent the parent composite
194: * @return the new control
195: */
196: protected Control createContents(Composite parent) {
197:
198: PlatformUI.getWorkbench().getHelpSystem().setHelp(parent,
199: IIDEHelpContextIds.BUILD_ORDER_PREFERENCE_PAGE);
200:
201: Font font = parent.getFont();
202:
203: //The main composite
204: Composite composite = new Composite(parent, SWT.NULL);
205: GridLayout layout = new GridLayout();
206: layout.numColumns = 2;
207: layout.marginWidth = 0;
208: layout.marginHeight = 0;
209: composite.setLayout(layout);
210: GridData data = new GridData();
211: data.verticalAlignment = GridData.FILL;
212: data.horizontalAlignment = GridData.FILL;
213: composite.setLayoutData(data);
214: composite.setFont(font);
215:
216: String[] buildOrder = getCurrentBuildOrder();
217: boolean useDefault = (buildOrder == null);
218:
219: createDefaultPathButton(composite, useDefault);
220: // List always enabled so user can scroll list.
221: // Only the buttons need to be disabled.
222: createBuildOrderList(composite, true);
223: createListButtons(composite, !useDefault);
224:
225: Composite noteComposite = createNoteComposite(font, composite,
226: NOTE_LABEL, REMOVE_MESSAGE);
227: GridData noteData = new GridData();
228: noteData.horizontalSpan = 2;
229: noteComposite.setLayoutData(noteData);
230:
231: createSpacer(composite);
232:
233: createMaxIterationsField(composite);
234:
235: createSpacer(composite);
236:
237: if (useDefault) {
238: this .buildList.setItems(getDefaultProjectOrder());
239: } else {
240: this .buildList.setItems(buildOrder);
241: }
242:
243: return composite;
244:
245: }
246:
247: /**
248: * Adds in a spacer.
249: *
250: * @param composite the parent composite
251: */
252: private void createSpacer(Composite composite) {
253: Label spacer = new Label(composite, SWT.NONE);
254: GridData spacerData = new GridData();
255: spacerData.horizontalSpan = 2;
256: spacer.setLayoutData(spacerData);
257: }
258:
259: /**
260: * Create the default path button. Set it to selected based on the current workspace
261: * build path.
262: * @param composite org.eclipse.swt.widgets.Composite
263: * @param selected - the boolean that indicates the buttons initial state
264: */
265: private void createDefaultPathButton(Composite composite,
266: boolean selected) {
267:
268: defaultOrderInitiallySelected = selected;
269:
270: this .defaultOrderButton = new Button(composite, SWT.LEFT
271: | SWT.CHECK);
272: this .defaultOrderButton.setSelection(selected);
273: this .defaultOrderButton.setText(DEFAULTS_LABEL);
274: SelectionListener listener = new SelectionAdapter() {
275: public void widgetSelected(SelectionEvent e) {
276: defaultsButtonSelected(defaultOrderButton
277: .getSelection());
278: }
279: };
280: this .defaultOrderButton.addSelectionListener(listener);
281:
282: GridData gridData = new GridData();
283: gridData.horizontalAlignment = GridData.FILL;
284: gridData.horizontalSpan = 2;
285: this .defaultOrderButton.setLayoutData(gridData);
286: this .defaultOrderButton.setFont(composite.getFont());
287: }
288:
289: /**
290: * Create the buttons used to manipulate the list. These Add, Remove and Move Up or Down
291: * the list items.
292: * @param composite the parent of the buttons
293: * @param enableComposite - boolean that indicates if a composite should be enabled
294: */
295: private void createListButtons(Composite composite,
296: boolean enableComposite) {
297:
298: Font font = composite.getFont();
299:
300: //Create an intermeditate composite to keep the buttons in the same column
301: this .buttonComposite = new Composite(composite, SWT.RIGHT);
302: GridLayout layout = new GridLayout();
303: layout.marginWidth = 0;
304: layout.marginHeight = 0;
305: this .buttonComposite.setLayout(layout);
306: GridData gridData = new GridData();
307: gridData.verticalAlignment = GridData.FILL;
308: gridData.horizontalAlignment = GridData.FILL;
309: this .buttonComposite.setLayoutData(gridData);
310: this .buttonComposite.setFont(font);
311:
312: Button upButton = new Button(this .buttonComposite, SWT.CENTER
313: | SWT.PUSH);
314: upButton.setText(UP_LABEL);
315: upButton.setEnabled(enableComposite);
316: upButton.setFont(font);
317: setButtonLayoutData(upButton);
318:
319: SelectionListener listener = new SelectionAdapter() {
320: public void widgetSelected(SelectionEvent e) {
321: moveSelectionUp();
322: }
323: };
324: upButton.addSelectionListener(listener);
325:
326: Button downButton = new Button(this .buttonComposite, SWT.CENTER
327: | SWT.PUSH);
328: downButton.setText(DOWN_LABEL);
329: downButton.setEnabled(enableComposite);
330: listener = new SelectionAdapter() {
331: public void widgetSelected(SelectionEvent e) {
332: moveSelectionDown();
333: }
334: };
335: downButton.addSelectionListener(listener);
336: downButton.setFont(font);
337: setButtonLayoutData(downButton);
338:
339: Button addButton = new Button(this .buttonComposite, SWT.CENTER
340: | SWT.PUSH);
341: addButton.setText(ADD_LABEL);
342: listener = new SelectionAdapter() {
343: public void widgetSelected(SelectionEvent e) {
344: addProject();
345: }
346: };
347: addButton.addSelectionListener(listener);
348: addButton.setEnabled(enableComposite);
349: addButton.setFont(font);
350: setButtonLayoutData(addButton);
351:
352: Button removeButton = new Button(this .buttonComposite,
353: SWT.CENTER | SWT.PUSH);
354: removeButton.setText(REMOVE_LABEL);
355: listener = new SelectionAdapter() {
356: public void widgetSelected(SelectionEvent e) {
357: removeSelection();
358: }
359: };
360: removeButton.addSelectionListener(listener);
361: removeButton.setEnabled(enableComposite);
362: removeButton.setFont(font);
363: setButtonLayoutData(removeButton);
364:
365: }
366:
367: /**
368: * Create the field for the maximum number of iterations in the presence
369: * of cycles.
370: */
371: private void createMaxIterationsField(Composite composite) {
372: Composite maxItersComposite = new Composite(composite, SWT.NONE);
373: GridData gd = new GridData(GridData.FILL_HORIZONTAL);
374: maxItersComposite.setLayoutData(gd);
375: maxItersComposite.setFont(composite.getFont());
376:
377: maxItersField = new IntegerFieldEditor(
378: "", IDEWorkbenchMessages.BuildOrderPreference_maxIterationsLabel, maxItersComposite) { //$NON-NLS-1$
379: protected void doLoad() {
380: Text text = getTextControl();
381: if (text != null) {
382: int value = getWorkspace().getDescription()
383: .getMaxBuildIterations();
384: text.setText(Integer.toString(value));
385: }
386: }
387:
388: protected void doLoadDefault() {
389: Text text = getTextControl();
390: if (text != null) {
391: int value = ResourcesPlugin
392: .getPlugin()
393: .getPluginPreferences()
394: .getDefaultInt(
395: ResourcesPlugin.PREF_MAX_BUILD_ITERATIONS);
396: text.setText(Integer.toString(value));
397: }
398: valueChanged();
399: }
400:
401: protected void doStore() {
402: // handled specially in performOK()
403: throw new UnsupportedOperationException();
404: }
405: };
406: maxItersField.setValidRange(1, Integer.MAX_VALUE);
407: maxItersField.setPage(this );
408: maxItersField.setPreferenceStore(getPreferenceStore());
409: maxItersField.setPropertyChangeListener(validityChangeListener);
410: maxItersField.load();
411: }
412:
413: /**
414: * The defaults button has been selected - update the other widgets as required.
415: * @param selected - whether or not the defaults button got selected
416: */
417: private void defaultsButtonSelected(boolean selected) {
418: if (selected) {
419: setBuildOrderWidgetsEnablement(false);
420: //Cache the current value as the custom order
421: customBuildOrder = buildList.getItems();
422: buildList.setItems(getDefaultProjectOrder());
423:
424: } else {
425: setBuildOrderWidgetsEnablement(true);
426: String[] buildOrder = getCurrentBuildOrder();
427: if (buildOrder == null) {
428: buildList.setItems(getDefaultProjectOrder());
429: } else {
430: buildList.setItems(buildOrder);
431: }
432: }
433: }
434:
435: /**
436: * Get the project names for the current custom build
437: * order stored in the workspace description.
438: *
439: * @return java.lang.String[] or null if there is no setting
440: */
441: private String[] getCurrentBuildOrder() {
442: if (notCheckedBuildOrder) {
443: customBuildOrder = getWorkspace().getDescription()
444: .getBuildOrder();
445: notCheckedBuildOrder = false;
446: }
447:
448: return customBuildOrder;
449: }
450:
451: /**
452: * Get the project names in the default build order
453: * based on the current Workspace settings.
454: *
455: * @return java.lang.String[]
456: */
457: private String[] getDefaultProjectOrder() {
458: if (defaultBuildOrder == null) {
459: IWorkspace workspace = getWorkspace();
460: IWorkspace.ProjectOrder projectOrder = getWorkspace()
461: .computeProjectOrder(
462: workspace.getRoot().getProjects());
463: IProject[] foundProjects = projectOrder.projects;
464: defaultBuildOrder = new String[foundProjects.length];
465: int foundSize = foundProjects.length;
466: for (int i = 0; i < foundSize; i++) {
467: defaultBuildOrder[i] = foundProjects[i].getName();
468: }
469: }
470:
471: return defaultBuildOrder;
472: }
473:
474: /**
475: * Return the Workspace the build order is from.
476: * @return org.eclipse.core.resources.IWorkspace
477: */
478: private IWorkspace getWorkspace() {
479: return ResourcesPlugin.getWorkspace();
480: }
481:
482: /**
483: * Return whether or not searchElement is in testArray.
484: */
485: private boolean includes(String[] testArray, String searchElement) {
486:
487: for (int i = 0; i < testArray.length; i++) {
488: if (searchElement.equals(testArray[i])) {
489: return true;
490: }
491: }
492: return false;
493:
494: }
495:
496: /**
497: * See IWorkbenchPreferencePage. This class does nothing with he Workbench.
498: */
499: public void init(IWorkbench workbench) {
500: this .workbench = workbench;
501: setPreferenceStore(PrefUtil.getInternalPreferenceStore());
502: }
503:
504: /**
505: * Move the current selection in the build list down.
506: */
507: private void moveSelectionDown() {
508:
509: //Only do this operation on a single selection
510: if (this .buildList.getSelectionCount() == 1) {
511: int currentIndex = this .buildList.getSelectionIndex();
512: if (currentIndex < this .buildList.getItemCount() - 1) {
513: String elementToMove = this .buildList
514: .getItem(currentIndex);
515: this .buildList.remove(currentIndex);
516: this .buildList.add(elementToMove, currentIndex + 1);
517: this .buildList.select(currentIndex + 1);
518: }
519: }
520: }
521:
522: /**
523: * Move the current selection in the build list up.
524: */
525: private void moveSelectionUp() {
526:
527: int currentIndex = this .buildList.getSelectionIndex();
528:
529: //Only do this operation on a single selection
530: if (currentIndex > 0 && this .buildList.getSelectionCount() == 1) {
531: String elementToMove = this .buildList.getItem(currentIndex);
532: this .buildList.remove(currentIndex);
533: this .buildList.add(elementToMove, currentIndex - 1);
534: this .buildList.select(currentIndex - 1);
535: }
536: }
537:
538: /**
539: * Performs special processing when this page's Defaults button has been pressed.
540: * In this case change the defaultOrderButton to have it's selection set to true.
541: */
542: protected void performDefaults() {
543: this .defaultOrderButton.setSelection(true);
544: defaultsButtonSelected(true);
545: maxItersField.loadDefault();
546: super .performDefaults();
547: }
548:
549: /**
550: * OK has been pressed. If the defualt button is pressed then reset the build order to false;
551: * otherwise set it to the contents of the list.
552: */
553: public boolean performOk() {
554:
555: String[] buildOrder = null;
556: boolean useDefault = defaultOrderButton.getSelection();
557:
558: // if use defaults is turned off
559: if (!useDefault) {
560: buildOrder = buildList.getItems();
561: }
562:
563: //Get a copy of the description from the workspace, set the build order and then
564: //apply it to the workspace.
565: IWorkspaceDescription description = getWorkspace()
566: .getDescription();
567: description.setBuildOrder(buildOrder);
568: description.setMaxBuildIterations(maxItersField.getIntValue());
569: try {
570: getWorkspace().setDescription(description);
571: } catch (CoreException exception) {
572: //failed - return false
573: return false;
574: }
575:
576: // Perform auto-build if use default is off (because
577: // order could have changed) or if use default setting
578: // was changed.
579: if (!useDefault
580: || (useDefault != defaultOrderInitiallySelected)) {
581: defaultOrderInitiallySelected = useDefault;
582: // If auto build is turned on, then do a global incremental
583: // build on all the projects.
584: if (ResourcesPlugin.getWorkspace().isAutoBuilding()) {
585: GlobalBuildAction action = new GlobalBuildAction(
586: workbench.getActiveWorkbenchWindow(),
587: IncrementalProjectBuilder.INCREMENTAL_BUILD);
588: action.doBuild();
589: }
590: }
591:
592: // Clear the custom build order cache
593: customBuildOrder = null;
594:
595: return true;
596: }
597:
598: /**
599: * Remove the current selection in the build list.
600: */
601: private void removeSelection() {
602:
603: this .buildList.remove(this .buildList.getSelectionIndices());
604: }
605:
606: /**
607: * Set the widgets that select build order to be enabled or diabled.
608: * @param value boolean
609: */
610: private void setBuildOrderWidgetsEnablement(boolean value) {
611:
612: // Only change enablement of buttons. Leave list alone
613: // because you can't scroll it when disabled.
614: Control[] children = this .buttonComposite.getChildren();
615: for (int i = 0; i < children.length; i++) {
616: children[i].setEnabled(value);
617: }
618: }
619:
620: /**
621: * Return a sorted array of the names of the projects that are already in the currently
622: * displayed names.
623: * @return String[]
624: * @param allProjects - all of the projects in the workspace
625: * @param currentlyDisplayed - the names of the projects already being displayed
626: */
627: private String[] sortedDifference(IProject[] allProjects,
628: String[] currentlyDisplayed) {
629:
630: TreeSet difference = new TreeSet();
631:
632: for (int i = 0; i < allProjects.length; i++) {
633: if (!includes(currentlyDisplayed, allProjects[i].getName())) {
634: difference.add(allProjects[i].getName());
635: }
636: }
637:
638: String[] returnValue = new String[difference.size()];
639: difference.toArray(returnValue);
640: return returnValue;
641: }
642: }
|