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.HashSet;
013: import java.util.Iterator;
014: import java.util.Map;
015: import java.util.Set;
016: import java.util.SortedMap;
017: import java.util.TreeMap;
018:
019: import org.eclipse.core.filesystem.IFileInfo;
020: import org.eclipse.core.resources.IPathVariableManager;
021: import org.eclipse.core.resources.IResource;
022: import org.eclipse.core.resources.ResourcesPlugin;
023: import org.eclipse.core.runtime.CoreException;
024: import org.eclipse.core.runtime.IPath;
025: import org.eclipse.core.runtime.Path;
026: import org.eclipse.jface.dialogs.Dialog;
027: import org.eclipse.jface.dialogs.ErrorDialog;
028: import org.eclipse.jface.dialogs.IDialogConstants;
029: import org.eclipse.jface.resource.ImageDescriptor;
030: import org.eclipse.jface.window.Window;
031: import org.eclipse.swt.SWT;
032: import org.eclipse.swt.events.SelectionAdapter;
033: import org.eclipse.swt.events.SelectionEvent;
034: import org.eclipse.swt.graphics.Font;
035: import org.eclipse.swt.graphics.FontMetrics;
036: import org.eclipse.swt.graphics.GC;
037: import org.eclipse.swt.graphics.Image;
038: import org.eclipse.swt.layout.GridData;
039: import org.eclipse.swt.layout.GridLayout;
040: import org.eclipse.swt.widgets.Button;
041: import org.eclipse.swt.widgets.Composite;
042: import org.eclipse.swt.widgets.Control;
043: import org.eclipse.swt.widgets.Event;
044: import org.eclipse.swt.widgets.Label;
045: import org.eclipse.swt.widgets.Listener;
046: import org.eclipse.swt.widgets.Shell;
047: import org.eclipse.swt.widgets.Table;
048: import org.eclipse.swt.widgets.TableItem;
049: import org.eclipse.ui.ISharedImages;
050: import org.eclipse.ui.PlatformUI;
051: import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
052: import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
053: import org.eclipse.ui.plugin.AbstractUIPlugin;
054:
055: /**
056: * A widget group that displays path variables.
057: * Includes buttons to edit, remove existing variables and create new ones.
058: *
059: * @since 2.1
060: */
061: public class PathVariablesGroup {
062: /**
063: * Simple data structure that holds a path variable name/value pair.
064: */
065: public static class PathVariableElement {
066: /**
067: * The name of the element.
068: */
069: public String name;
070:
071: /**
072: * The path of the element.
073: */
074: public IPath path;
075: }
076:
077: // sizing constants
078: private static final int SIZING_SELECTION_PANE_WIDTH = 400;
079:
080: // parent shell
081: private Shell shell;
082:
083: private Label variableLabel;
084:
085: private Table variableTable;
086:
087: private Button addButton;
088:
089: private Button editButton;
090:
091: private Button removeButton;
092:
093: // used to compute layout sizes
094: private FontMetrics fontMetrics;
095:
096: // create a multi select table
097: private boolean multiSelect;
098:
099: // IResource.FILE and/or IResource.FOLDER
100: private int variableType;
101:
102: // External listener called when the table selection changes
103: private Listener selectionListener;
104:
105: // temporary collection for keeping currently defined variables
106: private SortedMap tempPathVariables;
107:
108: // set of removed variables' names
109: private Set removedVariableNames;
110:
111: // reference to the workspace's path variable manager
112: private IPathVariableManager pathVariableManager;
113:
114: // file image
115: private final Image FILE_IMG = PlatformUI.getWorkbench()
116: .getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);
117:
118: // folder image
119: private final Image FOLDER_IMG = PlatformUI.getWorkbench()
120: .getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
121:
122: // unknown (non-existent) image. created locally, dispose locally
123: private Image imageUnkown;
124:
125: /**
126: * Creates a new PathVariablesGroup.
127: *
128: * @param multiSelect create a multi select tree
129: * @param variableType the type of variables that are displayed in
130: * the widget group. <code>IResource.FILE</code> and/or <code>IResource.FOLDER</code>
131: * logically ORed together.
132: */
133: public PathVariablesGroup(boolean multiSelect, int variableType) {
134: this .multiSelect = multiSelect;
135: this .variableType = variableType;
136: pathVariableManager = ResourcesPlugin.getWorkspace()
137: .getPathVariableManager();
138: removedVariableNames = new HashSet();
139: tempPathVariables = new TreeMap();
140: // initialize internal model
141: initTemporaryState();
142: }
143:
144: /**
145: * Creates a new PathVariablesGroup.
146: *
147: * @param multiSelect create a multi select tree
148: * @param variableType the type of variables that are displayed in
149: * the widget group. <code>IResource.FILE</code> and/or <code>IResource.FOLDER</code>
150: * logically ORed together.
151: * @param selectionListener listener notified when the selection changes
152: * in the variables list.
153: */
154: public PathVariablesGroup(boolean multiSelect, int variableType,
155: Listener selectionListener) {
156: this (multiSelect, variableType);
157: this .selectionListener = selectionListener;
158: }
159:
160: /**
161: * Opens a dialog for creating a new variable.
162: */
163: private void addNewVariable() {
164: // constructs a dialog for editing the new variable's current name and value
165: PathVariableDialog dialog = new PathVariableDialog(shell,
166: PathVariableDialog.NEW_VARIABLE, variableType,
167: pathVariableManager, tempPathVariables.keySet());
168:
169: // opens the dialog - just returns if the user cancels it
170: if (dialog.open() == Window.CANCEL) {
171: return;
172: }
173:
174: // otherwise, adds the new variable (or updates an existing one) in the
175: // temporary collection of currently defined variables
176: String newVariableName = dialog.getVariableName();
177: IPath newVariableValue = new Path(dialog.getVariableValue());
178: tempPathVariables.put(newVariableName, newVariableValue);
179:
180: // the UI must be updated
181: updateWidgetState(newVariableName);
182: }
183:
184: /**
185: * Creates the widget group.
186: * Callers must call <code>dispose</code> when the group is no
187: * longer needed.
188: *
189: * @param parent the widget parent
190: * @return container of the widgets
191: */
192: public Control createContents(Composite parent) {
193: Font font = parent.getFont();
194:
195: if (imageUnkown == null) {
196: ImageDescriptor descriptor = AbstractUIPlugin
197: .imageDescriptorFromPlugin(
198: IDEWorkbenchPlugin.IDE_WORKBENCH,
199: "$nl$/icons/full/obj16/warning.gif"); //$NON-NLS-1$
200: imageUnkown = descriptor.createImage();
201: }
202: initializeDialogUnits(parent);
203: shell = parent.getShell();
204:
205: // define container & its layout
206: Composite pageComponent = new Composite(parent, SWT.NULL);
207: GridLayout layout = new GridLayout();
208: layout.numColumns = 2;
209: layout.marginWidth = 0;
210: layout.marginHeight = 0;
211: pageComponent.setLayout(layout);
212: GridData data = new GridData(GridData.FILL_BOTH);
213: data.widthHint = SIZING_SELECTION_PANE_WIDTH;
214: pageComponent.setLayoutData(data);
215: pageComponent.setFont(font);
216:
217: // layout the table & its buttons
218: variableLabel = new Label(pageComponent, SWT.LEFT);
219: variableLabel
220: .setText(IDEWorkbenchMessages.PathVariablesBlock_variablesLabel);
221: data = new GridData();
222: data.horizontalAlignment = GridData.FILL;
223: data.horizontalSpan = 2;
224: variableLabel.setLayoutData(data);
225: variableLabel.setFont(font);
226:
227: int tableStyle = SWT.BORDER | SWT.FULL_SELECTION;
228: if (multiSelect) {
229: tableStyle |= SWT.MULTI;
230: }
231: variableTable = new Table(pageComponent, tableStyle);
232: variableTable.addSelectionListener(new SelectionAdapter() {
233: public void widgetSelected(SelectionEvent e) {
234: updateEnabledState();
235: if (selectionListener != null) {
236: selectionListener.handleEvent(new Event());
237: }
238: }
239: });
240: data = new GridData(GridData.FILL_BOTH);
241: data.heightHint = variableTable.getItemHeight() * 7;
242: variableTable.setLayoutData(data);
243: variableTable.setFont(font);
244:
245: createButtonGroup(pageComponent);
246: // populate table with current internal state and set buttons' initial state
247: updateWidgetState(null);
248:
249: return pageComponent;
250: }
251:
252: /**
253: * Disposes the group's resources.
254: */
255: public void dispose() {
256: if (imageUnkown != null) {
257: imageUnkown.dispose();
258: imageUnkown = null;
259: }
260: }
261:
262: /**
263: * Opens a dialog for editing an existing variable.
264: *
265: * @see PathVariableDialog
266: */
267: private void editSelectedVariable() {
268: // retrieves the name and value for the currently selected variable
269: TableItem item = variableTable.getItem(variableTable
270: .getSelectionIndex());
271: String variableName = (String) item.getData();
272: IPath variableValue = (IPath) tempPathVariables
273: .get(variableName);
274:
275: // constructs a dialog for editing the variable's current name and value
276: PathVariableDialog dialog = new PathVariableDialog(shell,
277: PathVariableDialog.EXISTING_VARIABLE, variableType,
278: pathVariableManager, tempPathVariables.keySet());
279: dialog.setVariableName(variableName);
280: dialog.setVariableValue(variableValue.toOSString());
281:
282: // opens the dialog - just returns if the user cancels it
283: if (dialog.open() == Window.CANCEL) {
284: return;
285: }
286:
287: // the name can be changed, so we remove the current variable definition...
288: removedVariableNames.add(variableName);
289: tempPathVariables.remove(variableName);
290:
291: String newVariableName = dialog.getVariableName();
292: IPath newVariableValue = new Path(dialog.getVariableValue());
293:
294: // and add it again (maybe with a different name)
295: tempPathVariables.put(newVariableName, newVariableValue);
296:
297: // now we must refresh the UI state
298: updateWidgetState(newVariableName);
299:
300: }
301:
302: /**
303: * Returns the enabled state of the group's widgets.
304: * Returns <code>true</code> if called prior to calling
305: * <code>createContents</code>.
306: *
307: * @return boolean the enabled state of the group's widgets.
308: * <code>true</code> if called prior to calling <code>createContents</code>.
309: */
310: public boolean getEnabled() {
311: if (variableTable != null && !variableTable.isDisposed()) {
312: return variableTable.getEnabled();
313: }
314: return true;
315: }
316:
317: /**
318: * Returns the selected variables.
319: *
320: * @return the selected variables. Returns an empty array if
321: * the widget group has not been created yet by calling
322: * <code>createContents</code>
323: */
324: public PathVariableElement[] getSelection() {
325: if (variableTable == null) {
326: return new PathVariableElement[0];
327: }
328: TableItem[] items = variableTable.getSelection();
329: PathVariableElement[] selection = new PathVariableElement[items.length];
330:
331: for (int i = 0; i < items.length; i++) {
332: String name = (String) items[i].getData();
333: selection[i] = new PathVariableElement();
334: selection[i].name = name;
335: selection[i].path = (IPath) tempPathVariables.get(name);
336: }
337: return selection;
338: }
339:
340: /**
341: * Creates the add/edit/remove buttons
342: *
343: * @param parent the widget parent
344: */
345: private void createButtonGroup(Composite parent) {
346: Font font = parent.getFont();
347: Composite groupComponent = new Composite(parent, SWT.NULL);
348: GridLayout groupLayout = new GridLayout();
349: groupLayout.marginWidth = 0;
350: groupLayout.marginHeight = 0;
351: groupComponent.setLayout(groupLayout);
352: GridData data = new GridData();
353: data.verticalAlignment = GridData.FILL;
354: data.horizontalAlignment = GridData.FILL;
355: groupComponent.setLayoutData(data);
356: groupComponent.setFont(font);
357:
358: addButton = new Button(groupComponent, SWT.PUSH);
359: addButton
360: .setText(IDEWorkbenchMessages.PathVariablesBlock_addVariableButton);
361: addButton.addSelectionListener(new SelectionAdapter() {
362: public void widgetSelected(SelectionEvent e) {
363: addNewVariable();
364: }
365: });
366: addButton.setFont(font);
367: setButtonLayoutData(addButton);
368:
369: editButton = new Button(groupComponent, SWT.PUSH);
370: editButton
371: .setText(IDEWorkbenchMessages.PathVariablesBlock_editVariableButton);
372: editButton.addSelectionListener(new SelectionAdapter() {
373: public void widgetSelected(SelectionEvent e) {
374: editSelectedVariable();
375: }
376: });
377: editButton.setFont(font);
378: setButtonLayoutData(editButton);
379:
380: removeButton = new Button(groupComponent, SWT.PUSH);
381: removeButton
382: .setText(IDEWorkbenchMessages.PathVariablesBlock_removeVariableButton);
383: removeButton.addSelectionListener(new SelectionAdapter() {
384: public void widgetSelected(SelectionEvent e) {
385: removeSelectedVariables();
386: }
387: });
388: removeButton.setFont(font);
389: setButtonLayoutData(removeButton);
390: }
391:
392: /**
393: * Initializes the computation of horizontal and vertical dialog units
394: * based on the size of current font.
395: * <p>
396: * This method must be called before <code>setButtonLayoutData</code>
397: * is called.
398: * </p>
399: *
400: * @param control a control from which to obtain the current font
401: */
402: protected void initializeDialogUnits(Control control) {
403: // Compute and store a font metric
404: GC gc = new GC(control);
405: gc.setFont(control.getFont());
406: fontMetrics = gc.getFontMetrics();
407: gc.dispose();
408: }
409:
410: /**
411: * (Re-)Initialize collections used to mantain temporary variable state.
412: */
413: private void initTemporaryState() {
414: String[] varNames = pathVariableManager.getPathVariableNames();
415:
416: tempPathVariables.clear();
417: for (int i = 0; i < varNames.length; i++) {
418: IPath value = pathVariableManager.getValue(varNames[i]);
419:
420: // the value may not exist any more
421: if (value != null) {
422: boolean isFile = value.toFile().isFile();
423: if ((isFile && (variableType & IResource.FILE) != 0)
424: || (isFile == false && (variableType & IResource.FOLDER) != 0)) {
425:
426: tempPathVariables.put(varNames[i], value);
427: }
428: }
429: }
430: removedVariableNames.clear();
431: }
432:
433: /**
434: * Updates button enabled state, depending on the number of currently selected
435: * variables in the table.
436: */
437: private void updateEnabledState() {
438: int itemsSelectedCount = variableTable.getSelectionCount();
439: editButton.setEnabled(itemsSelectedCount == 1);
440: removeButton.setEnabled(itemsSelectedCount > 0);
441: }
442:
443: /**
444: * Rebuilds table widget state with the current list of variables (reflecting
445: * any changes, additions and removals), and selects the item corresponding to
446: * the given variable name. If the variable name is <code>null</code>, the
447: * first item (if any) will be selected.
448: *
449: * @param selectedVarName the name for the variable to be selected (may be
450: * <code>null</code>)
451: * @see IPathVariableManager#getPathVariableNames()
452: * @see IPathVariableManager#getValue(String)
453: */
454: private void updateVariableTable(String selectedVarName) {
455: variableTable.removeAll();
456: int selectedVarIndex = 0;
457: for (Iterator varNames = tempPathVariables.keySet().iterator(); varNames
458: .hasNext();) {
459: TableItem item = new TableItem(variableTable, SWT.NONE);
460: String varName = (String) varNames.next();
461: IPath value = (IPath) tempPathVariables.get(varName);
462: IFileInfo file = IDEResourceInfoUtils.getFileInfo(value);
463:
464: item.setText(varName + " - " + value.toOSString()); //$NON-NLS-1$
465: // the corresponding variable name is stored in each table widget item
466: item.setData(varName);
467: item
468: .setImage(file.exists() ? (file.isDirectory() ? FOLDER_IMG
469: : FILE_IMG)
470: : imageUnkown);
471: if (varName.equals(selectedVarName)) {
472: selectedVarIndex = variableTable.getItemCount() - 1;
473: }
474: }
475: if (variableTable.getItemCount() > selectedVarIndex) {
476: variableTable.setSelection(selectedVarIndex);
477: if (selectionListener != null) {
478: selectionListener.handleEvent(new Event());
479: }
480: } else if (variableTable.getItemCount() == 0
481: && selectionListener != null) {
482: selectionListener.handleEvent(new Event());
483: }
484: }
485:
486: /**
487: * Commits the temporary state to the path variable manager in response to user
488: * confirmation.
489: * @return boolean <code>true</code> if there were no problems.
490: * @see IPathVariableManager#setValue(String, IPath)
491: */
492: public boolean performOk() {
493: try {
494: // first process removed variables
495: for (Iterator removed = removedVariableNames.iterator(); removed
496: .hasNext();) {
497: String removedVariableName = (String) removed.next();
498: // only removes variables that have not been added again
499: if (!tempPathVariables.containsKey(removedVariableName)) {
500: pathVariableManager.setValue(removedVariableName,
501: null);
502: }
503: }
504:
505: // then process the current collection of variables, adding/updating them
506: for (Iterator current = tempPathVariables.entrySet()
507: .iterator(); current.hasNext();) {
508: Map.Entry entry = (Map.Entry) current.next();
509: String variableName = (String) entry.getKey();
510: IPath variableValue = (IPath) entry.getValue();
511: pathVariableManager.setValue(variableName,
512: variableValue);
513: }
514: // re-initialize temporary state
515: initTemporaryState();
516:
517: // performOk accepted
518: return true;
519: } catch (CoreException ce) {
520: ErrorDialog.openError(shell, null, null, ce.getStatus());
521: }
522: return false;
523: }
524:
525: /**
526: * Removes the currently selected variables.
527: */
528: private void removeSelectedVariables() {
529: // remove each selected element
530: int[] selectedIndices = variableTable.getSelectionIndices();
531: for (int i = 0; i < selectedIndices.length; i++) {
532: TableItem selectedItem = variableTable
533: .getItem(selectedIndices[i]);
534: String varName = (String) selectedItem.getData();
535: removedVariableNames.add(varName);
536: tempPathVariables.remove(varName);
537: }
538: updateWidgetState(null);
539: }
540:
541: /**
542: * Sets the <code>GridData</code> on the specified button to
543: * be one that is spaced for the current dialog page units. The
544: * method <code>initializeDialogUnits</code> must be called once
545: * before calling this method for the first time.
546: *
547: * @param button the button to set the <code>GridData</code>
548: * @return the <code>GridData</code> set on the specified button
549: */
550: private GridData setButtonLayoutData(Button button) {
551: GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
552: int widthHint = Dialog.convertHorizontalDLUsToPixels(
553: fontMetrics, IDialogConstants.BUTTON_WIDTH);
554: data.widthHint = Math.max(widthHint, button.computeSize(
555: SWT.DEFAULT, SWT.DEFAULT, true).x);
556: button.setLayoutData(data);
557: return data;
558: }
559:
560: /**
561: * Sets the enabled state of the group's widgets.
562: * Does nothing if called prior to calling <code>createContents</code>.
563: *
564: * @param enabled the new enabled state of the group's widgets
565: */
566: public void setEnabled(boolean enabled) {
567: if (variableTable != null && !variableTable.isDisposed()) {
568: variableLabel.setEnabled(enabled);
569: variableTable.setEnabled(enabled);
570: addButton.setEnabled(enabled);
571: if (enabled) {
572: updateEnabledState();
573: } else {
574: editButton.setEnabled(enabled);
575: removeButton.setEnabled(enabled);
576: }
577: }
578: }
579:
580: /**
581: * Updates the widget's current state: refreshes the table with the current
582: * defined variables, selects the item corresponding to the given variable
583: * (selects the first item if <code>null</code> is provided) and updates
584: * the enabled state for the Add/Remove/Edit buttons.
585: *
586: * @param selectedVarName the name of the variable to be selected (may be null)
587: */
588: private void updateWidgetState(String selectedVarName) {
589: updateVariableTable(selectedVarName);
590: updateEnabledState();
591: }
592: }
|