001: /*******************************************************************************
002: * Copyright (c) 2007 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: * Remy Chi Jian Suen <remy.suen@gmail.com> - bug 201661
011: *******************************************************************************/package org.eclipse.ui.dialogs;
012:
013: import java.util.ArrayList;
014: import java.util.Arrays;
015: import java.util.Collection;
016: import java.util.Comparator;
017: import java.util.HashSet;
018: import java.util.Iterator;
019: import java.util.List;
020:
021: import org.eclipse.core.runtime.Assert;
022: import org.eclipse.jface.dialogs.Dialog;
023: import org.eclipse.jface.dialogs.IDialogConstants;
024: import org.eclipse.jface.dialogs.IDialogSettings;
025: import org.eclipse.jface.resource.JFaceResources;
026: import org.eclipse.jface.viewers.IStructuredSelection;
027: import org.eclipse.jface.window.Window;
028: import org.eclipse.jface.wizard.IWizardPage;
029: import org.eclipse.swt.SWT;
030: import org.eclipse.swt.events.SelectionAdapter;
031: import org.eclipse.swt.events.SelectionEvent;
032: import org.eclipse.swt.graphics.FontMetrics;
033: import org.eclipse.swt.graphics.GC;
034: import org.eclipse.swt.graphics.Point;
035: import org.eclipse.swt.layout.GridData;
036: import org.eclipse.swt.layout.GridLayout;
037: import org.eclipse.swt.widgets.Button;
038: import org.eclipse.swt.widgets.Combo;
039: import org.eclipse.swt.widgets.Composite;
040: import org.eclipse.swt.widgets.Label;
041: import org.eclipse.ui.IWorkingSet;
042: import org.eclipse.ui.IWorkingSetManager;
043: import org.eclipse.ui.PlatformUI;
044: import org.eclipse.ui.internal.WorkbenchMessages;
045: import org.eclipse.ui.internal.dialogs.SimpleWorkingSetSelectionDialog;
046:
047: import com.ibm.icu.text.Collator;
048:
049: /**
050: * Instances of this class provide a reusable composite with controls that allow
051: * the selection of working sets. This class is most useful in
052: * {@link IWizardPage} instances that wish to create resources and pre-install
053: * them into particular working sets.
054: *
055: * <strong>Please note that this API is experimental and may change before 3.4
056: * ships.</strong>
057: *
058: * @since 3.4
059: *
060: */
061: public class WorkingSetConfigurationBlock {
062:
063: /**
064: * Filters the given working sets such that the following is true: for each
065: * IWorkingSet s in result: s.getId() is element of workingSetIds
066: *
067: * @param workingSets
068: * the array to filter
069: * @param workingSetIds
070: * the acceptable working set ids
071: * @return the filtered elements
072: */
073: public static IWorkingSet[] filter(IWorkingSet[] workingSets,
074: String[] workingSetIds) {
075:
076: // create a copy so we can sort the array without mucking it up for clients.
077: String[] workingSetIdsCopy = new String[workingSetIds.length];
078: System.arraycopy(workingSetIds, 0, workingSetIdsCopy, 0,
079: workingSetIds.length);
080: Arrays.sort(workingSetIdsCopy);
081:
082: ArrayList result = new ArrayList();
083:
084: for (int i = 0; i < workingSets.length; i++) {
085: if (Arrays.binarySearch(workingSetIdsCopy, workingSets[i]
086: .getId()) >= 0)
087: result.add(workingSets[i]);
088: }
089:
090: return (IWorkingSet[]) result.toArray(new IWorkingSet[result
091: .size()]);
092: }
093:
094: /**
095: * Empty working set array constant.
096: */
097: private static final IWorkingSet[] EMPTY_WORKING_SET_ARRAY = new IWorkingSet[0];
098:
099: private static final String WORKINGSET_SELECTION_HISTORY = "workingset_selection_history"; //$NON-NLS-1$
100: private static final int MAX_HISTORY_SIZE = 5;
101:
102: private Label workingSetLabel;
103: private Combo workingSetCombo;
104: private Button selectButton;
105: private Button enableButton;
106:
107: private IWorkingSet[] selectedWorkingSets;
108: private ArrayList selectionHistory;
109: private final IDialogSettings dialogSettings;
110: private final String[] workingSetTypeIds;
111:
112: private final String selectLabel;
113:
114: private final String comboLabel;
115:
116: private final String addButtonLabel;
117:
118: /**
119: * Create a new instance of this working set block using default labels.
120: *
121: * @param workingSetIds
122: * working set ids from which the user can choose
123: * @param settings
124: * to store/load the selection history
125: */
126: public WorkingSetConfigurationBlock(String[] workingSetIds,
127: IDialogSettings settings) {
128: this (workingSetIds, settings, null, null, null);
129: }
130:
131: /**
132: * Create a new instance of this working set block using custom labels.
133: *
134: * @param workingSetIds
135: * working set ids from which the user can choose
136: * @param settings
137: * to store/load the selection history
138: * @param addButtonLabel
139: * the label to use for the checkable enablement button. May be
140: * <code>null</code> to use the default value.
141: * @param comboLabel
142: * the label to use for the recent working set combo. May be
143: * <code>null</code> to use the default value.
144: * @param selectLabel
145: * the label to use for the select button. May be
146: * <code>null</code> to use the default value.
147: */
148: public WorkingSetConfigurationBlock(String[] workingSetIds,
149: IDialogSettings settings, String addButtonLabel,
150: String comboLabel, String selectLabel) {
151: Assert.isNotNull(workingSetIds);
152: Assert.isNotNull(settings);
153:
154: workingSetTypeIds = workingSetIds;
155: Arrays.sort(workingSetIds); // we'll be performing some searches with these later - presort them
156: selectedWorkingSets = EMPTY_WORKING_SET_ARRAY;
157: dialogSettings = settings;
158: selectionHistory = loadSelectionHistory(settings, workingSetIds);
159:
160: this .addButtonLabel = addButtonLabel == null ? WorkbenchMessages.WorkingSetGroup_EnableWorkingSet_button
161: : addButtonLabel;
162: this .comboLabel = comboLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_WorkingSetText_name
163: : comboLabel;
164: this .selectLabel = selectLabel == null ? WorkbenchMessages.WorkingSetConfigurationBlock_SelectWorkingSet_button
165: : selectLabel;
166: }
167:
168: /**
169: * Set the current selection in the workbench.
170: *
171: * @param selection
172: * the selection to present in the UI or <b>null</b>
173: * @deprecated use
174: * {@link #setWorkingSets(IWorkingSet[])} and {@link #findApplicableWorkingSets(IStructuredSelection)}
175: * instead. This method will be removed before 3.4 ships.
176: */
177: public void setSelection(IStructuredSelection selection) {
178: selectedWorkingSets = findApplicableWorkingSets(selection);
179:
180: if (workingSetCombo != null)
181: updateSelectedWorkingSets();
182: }
183:
184: /**
185: * Set the current selection of working sets. This array will be filtered to
186: * contain only working sets that are applicable to this instance.
187: *
188: * @param workingSets
189: * the working sets
190: */
191: public void setWorkingSets(IWorkingSet[] workingSets) {
192: selectedWorkingSets = filterWorkingSets(Arrays
193: .asList(workingSets));
194: if (workingSetCombo != null)
195: updateSelectedWorkingSets();
196: }
197:
198: /**
199: * Retrieves a working set from the given <code>selection</code> or an
200: * empty array if no working set could be retrieved. This selection is
201: * filtered based on the criteria used to construct this instance.
202: *
203: * @param selection
204: * the selection to retrieve the working set from
205: * @return the selected working set or an empty array
206: */
207: public IWorkingSet[] findApplicableWorkingSets(
208: IStructuredSelection selection) {
209: if (selection == null)
210: return EMPTY_WORKING_SET_ARRAY;
211:
212: return filterWorkingSets(selection.toList());
213: }
214:
215: /**
216: * Prune a list of working sets such that they all match the criteria set
217: * out by this block.
218: *
219: * @param elements
220: * the elements to filter
221: * @return the filtered elements
222: */
223: private IWorkingSet[] filterWorkingSets(Collection elements) {
224: ArrayList result = new ArrayList();
225: for (Iterator iterator = elements.iterator(); iterator
226: .hasNext();) {
227: Object element = iterator.next();
228: if (element instanceof IWorkingSet
229: && verifyWorkingSet((IWorkingSet) element)) {
230: result.add(element);
231: }
232: }
233: return (IWorkingSet[]) result.toArray(new IWorkingSet[result
234: .size()]);
235: }
236:
237: /**
238: * Verifies that the given working set is suitable for selection in this
239: * block.
240: *
241: * @param workingSetCandidate
242: * the candidate to test
243: * @return whether it is suitable
244: */
245: private boolean verifyWorkingSet(IWorkingSet workingSetCandidate) {
246: return !workingSetCandidate.isAggregateWorkingSet()
247: && Arrays.binarySearch(workingSetTypeIds,
248: workingSetCandidate.getId()) >= 0;
249: }
250:
251: /**
252: * Return the currently selected working sets. If the controls representing
253: * this block are disabled this array will be empty regardless of the
254: * currently selected values.
255: *
256: * @return the selected working sets
257: */
258: public IWorkingSet[] getSelectedWorkingSets() {
259: if (enableButton.getSelection()) {
260: return selectedWorkingSets;
261: }
262: return EMPTY_WORKING_SET_ARRAY;
263: }
264:
265: /**
266: * Add this block to the <code>parent</parent>
267: *
268: * @param parent the parent to add the block to
269: */
270: public void createContent(final Composite parent) {
271: int numColumn = 3;
272:
273: Composite composite = new Composite(parent, SWT.NONE);
274: composite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true,
275: false));
276: composite.setLayout(new GridLayout(numColumn, false));
277:
278: enableButton = new Button(composite, SWT.CHECK);
279: enableButton.setText(addButtonLabel);
280: GridData enableData = new GridData(SWT.FILL, SWT.CENTER, true,
281: false);
282: enableData.horizontalSpan = numColumn;
283: enableButton.setLayoutData(enableData);
284: enableButton.setSelection(selectedWorkingSets.length > 0);
285:
286: workingSetLabel = new Label(composite, SWT.NONE);
287: workingSetLabel.setText(comboLabel);
288:
289: workingSetCombo = new Combo(composite, SWT.READ_ONLY
290: | SWT.BORDER);
291: GridData textData = new GridData(SWT.FILL, SWT.CENTER, true,
292: false);
293: textData.horizontalSpan = numColumn - 2;
294: textData.horizontalIndent = 0;
295: workingSetCombo.setLayoutData(textData);
296:
297: selectButton = new Button(composite, SWT.PUSH);
298: selectButton.setText(selectLabel);
299: setButtonLayoutData(selectButton);
300: selectButton.addSelectionListener(new SelectionAdapter() {
301:
302: public void widgetSelected(SelectionEvent e) {
303: SimpleWorkingSetSelectionDialog dialog = new SimpleWorkingSetSelectionDialog(
304: parent.getShell(), workingSetTypeIds,
305: selectedWorkingSets, false);
306: dialog
307: .setMessage(WorkbenchMessages.WorkingSetGroup_WorkingSetSelection_message);
308:
309: if (dialog.open() == Window.OK) {
310: IWorkingSet[] result = dialog.getSelection();
311: if (result != null && result.length > 0) {
312: selectedWorkingSets = result;
313: PlatformUI.getWorkbench()
314: .getWorkingSetManager()
315: .addRecentWorkingSet(result[0]);
316: } else {
317: selectedWorkingSets = EMPTY_WORKING_SET_ARRAY;
318: }
319: updateWorkingSetSelection();
320: }
321: }
322: });
323:
324: enableButton.addSelectionListener(new SelectionAdapter() {
325: public void widgetSelected(SelectionEvent e) {
326: updateEnableState(enableButton.getSelection());
327: }
328: });
329: updateEnableState(enableButton.getSelection());
330:
331: workingSetCombo.addSelectionListener(new SelectionAdapter() {
332: public void widgetSelected(SelectionEvent e) {
333: updateSelectedWorkingSets();
334: }
335: });
336:
337: workingSetCombo.setItems(getHistoryEntries());
338: if (selectedWorkingSets.length == 0
339: && selectionHistory.size() > 0) {
340: workingSetCombo
341: .select(historyIndex((String) selectionHistory
342: .get(0)));
343: updateSelectedWorkingSets();
344: } else {
345: updateWorkingSetSelection();
346: }
347: }
348:
349: private void updateEnableState(boolean enabled) {
350: workingSetLabel.setEnabled(enabled);
351: workingSetCombo
352: .setEnabled(enabled
353: && (selectedWorkingSets.length > 0 || getHistoryEntries().length > 0));
354: selectButton.setEnabled(enabled);
355: }
356:
357: private void updateWorkingSetSelection() {
358: if (selectedWorkingSets.length > 0) {
359: workingSetCombo.setEnabled(true);
360: StringBuffer buf = new StringBuffer();
361:
362: buf.append(selectedWorkingSets[0].getLabel());
363: for (int i = 1; i < selectedWorkingSets.length; i++) {
364: IWorkingSet ws = selectedWorkingSets[i];
365: buf.append(',').append(' ');
366: buf.append(ws.getLabel());
367: }
368:
369: String currentSelection = buf.toString();
370: int index = historyIndex(currentSelection);
371: historyInsert(currentSelection);
372: if (index >= 0) {
373: workingSetCombo.select(index);
374: } else {
375: workingSetCombo.setItems(getHistoryEntries());
376: workingSetCombo.select(historyIndex(currentSelection));
377: }
378: } else {
379: enableButton.setSelection(false);
380: updateEnableState(false);
381: }
382: }
383:
384: private String[] getHistoryEntries() {
385: String[] history = (String[]) selectionHistory
386: .toArray(new String[selectionHistory.size()]);
387: Arrays.sort(history, new Comparator() {
388: public int compare(Object o1, Object o2) {
389: return Collator.getInstance().compare(o1, o2);
390: }
391: });
392: return history;
393: }
394:
395: private void historyInsert(String entry) {
396: selectionHistory.remove(entry);
397: selectionHistory.add(0, entry);
398: storeSelectionHistory(dialogSettings);
399: }
400:
401: private int historyIndex(String entry) {
402: for (int i = 0; i < workingSetCombo.getItemCount(); i++) {
403: if (workingSetCombo.getItem(i).equals(entry))
404: return i;
405: }
406:
407: return -1;
408: }
409:
410: private void updateSelectedWorkingSets() {
411: String item = workingSetCombo.getItem(workingSetCombo
412: .getSelectionIndex());
413: String[] workingSetNames = item.split(", "); //$NON-NLS-1$
414:
415: IWorkingSetManager workingSetManager = PlatformUI
416: .getWorkbench().getWorkingSetManager();
417: selectedWorkingSets = new IWorkingSet[workingSetNames.length];
418: for (int i = 0; i < workingSetNames.length; i++) {
419: IWorkingSet set = workingSetManager
420: .getWorkingSet(workingSetNames[i]);
421: Assert.isNotNull(set);
422: selectedWorkingSets[i] = set;
423: }
424: }
425:
426: private void storeSelectionHistory(IDialogSettings settings) {
427: String[] history;
428: if (selectionHistory.size() > MAX_HISTORY_SIZE) {
429: List subList = selectionHistory
430: .subList(0, MAX_HISTORY_SIZE);
431: history = (String[]) subList.toArray(new String[subList
432: .size()]);
433: } else {
434: history = (String[]) selectionHistory
435: .toArray(new String[selectionHistory.size()]);
436: }
437: settings.put(WORKINGSET_SELECTION_HISTORY, history);
438: }
439:
440: private ArrayList loadSelectionHistory(IDialogSettings settings,
441: String[] workingSetIds) {
442: String[] strings = settings
443: .getArray(WORKINGSET_SELECTION_HISTORY);
444: if (strings == null || strings.length == 0)
445: return new ArrayList();
446:
447: ArrayList result = new ArrayList();
448:
449: HashSet workingSetIdsSet = new HashSet(Arrays
450: .asList(workingSetIds));
451:
452: IWorkingSetManager workingSetManager = PlatformUI
453: .getWorkbench().getWorkingSetManager();
454: for (int i = 0; i < strings.length; i++) {
455: String[] workingSetNames = strings[i].split(", "); //$NON-NLS-1$
456: boolean valid = true;
457: for (int j = 0; j < workingSetNames.length && valid; j++) {
458: IWorkingSet workingSet = workingSetManager
459: .getWorkingSet(workingSetNames[j]);
460: if (workingSet == null) {
461: valid = false;
462: } else {
463: if (!workingSetIdsSet.contains(workingSet.getId()))
464: valid = false;
465: }
466: }
467: if (valid) {
468: result.add(strings[i]);
469: }
470: }
471:
472: return result;
473: }
474:
475: /*
476: * Copy from DialogPage with changes to accomodate the lack of a Dialog context.
477: */
478: private GridData setButtonLayoutData(Button button) {
479: button.setFont(JFaceResources.getDialogFont());
480:
481: GC gc = new GC(button);
482: gc.setFont(button.getFont());
483: FontMetrics fontMetrics = gc.getFontMetrics();
484: gc.dispose();
485:
486: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
487: int widthHint = Dialog.convertHorizontalDLUsToPixels(
488: fontMetrics, IDialogConstants.BUTTON_WIDTH);
489: Point minSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT,
490: true);
491: data.widthHint = Math.max(widthHint, minSize.x);
492: button.setLayoutData(data);
493: return data;
494: }
495: }
|