/******************************************************************************* * Copyright (c) 2009 Johannes Utzig. * 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: * Johannes Utzig - initial API and implementation *******************************************************************************/ package org.eclipse.buckminster.ui.dialogs; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.eclipse.buckminster.core.common.model.Documentation; import org.eclipse.buckminster.core.cspec.model.Attribute; import org.eclipse.buckminster.core.cspec.model.CSpec; import org.eclipse.buckminster.core.cspec.model.Group; import org.eclipse.buckminster.runtime.Trivial; import org.eclipse.buckminster.ui.AbstractCSpecAction; import org.eclipse.buckminster.ui.Messages; import org.eclipse.buckminster.ui.UiPlugin; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.DialogSettings; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CCombo; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; 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.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IMemento; import org.eclipse.ui.dialogs.ElementTreeSelectionDialog; import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog; import org.eclipse.ui.model.WorkbenchContentProvider; import org.eclipse.ui.model.WorkbenchLabelProvider; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * Displays a selection dialog to allow the user to select one or more * attributes from a cspec. * <p> * The choices of the user are made available in * {@link InvokeActionDialog#isForceRebuild()}, * {@link InvokeActionDialog#getSelectedAttributes()} and * {@link InvokeActionDialog#getPropertiesFile()}. * * @author Johannes Utzig * */ public class InvokeActionDialog extends FilteredItemsSelectionDialog { private class AttributeFilter extends ItemsFilter { public final boolean finalOnlyPublic = InvokeActionDialog.this.onlyPublic; @Override public boolean equalsFilter(ItemsFilter itemsFilter) { AttributeFilter filter = (AttributeFilter) itemsFilter; if (isOnlyPublic() != filter.isOnlyPublic()) return false; return super.equalsFilter(filter); } @Override public String getPattern() { // without at least one character the dialog does not fill the // content provider return super.getPattern() + "*"; //$NON-NLS-1$ } @Override public boolean isConsistentItem(Object item) { return attributes.contains(item); } public boolean isOnlyPublic() { return finalOnlyPublic; } @Override public boolean isSubFilter(ItemsFilter itemsFilter) { AttributeFilter filter = (AttributeFilter) itemsFilter; if (isOnlyPublic() != filter.isOnlyPublic()) return false; return super.isSubFilter(filter); } @Override public boolean matchItem(Object item) { Attribute attrib = (Attribute) item; boolean isPrivate = !attrib.isPublic(); if (isPrivate && isOnlyPublic()) return false; return matches(attrib.getName()); } } private class AttributeSelectionHistory extends SelectionHistory { @Override protected Object restoreItemFromMemento(IMemento element) { String attributeName = element.getString("attribute"); //$NON-NLS-1$ if (attributeName == null || attributes == null) return null; for (Attribute attribute : attributes) { if (attributeName.equals(attribute.getQualifiedName())) return attribute; } return null; } @Override protected void storeItemToMemento(Object item, IMemento element) { element.putString("attribute", item.toString()); //$NON-NLS-1$ } } private class DetailsLabelProvider extends LabelProvider { @Override public String getText(Object element) { if (element instanceof Attribute) { Attribute attribute = (Attribute) element; Documentation documentation = attribute.getDocumentation(); if (documentation == null) return attribute.getName(); return documentation.toString(); } return element.toString(); } } private class ForceRebuildAction extends Action { private static final String FORCE_REBUILD = "force.rebuild"; //$NON-NLS-1$ public ForceRebuildAction() { super(Messages.force_complete_rebuild, IAction.AS_CHECK_BOX); forceRebuild = getDialogSettings().getBoolean(FORCE_REBUILD); setChecked(forceRebuild); } @Override public void run() { getDialogSettings().put(FORCE_REBUILD, isChecked()); forceRebuild = isChecked(); } } private class LabelProvider extends org.eclipse.jface.viewers.LabelProvider implements ILabelProvider { private Image privateGroup, group, privateAction, action; @Override public void dispose() { super.dispose(); if (privateAction != null) privateAction.dispose(); if (privateGroup != null) privateGroup.dispose(); if (action != null) action.dispose(); if (group != null) group.dispose(); } @Override public Image getImage(Object element) { if (element instanceof Attribute) { Attribute attribute = (Attribute) element; if (attribute.isPublic()) { if (attribute instanceof Group) { if (group == null) { group = AbstractUIPlugin.imageDescriptorFromPlugin(UiPlugin.getID(), "icons/group.gif").createImage(); //$NON-NLS-1$ } return group; } if (action == null) { action = AbstractUIPlugin.imageDescriptorFromPlugin(UiPlugin.getID(), "icons/action.gif").createImage(); //$NON-NLS-1$ } return action; } if (attribute instanceof Group) { if (privateGroup == null) { privateGroup = AbstractUIPlugin.imageDescriptorFromPlugin(UiPlugin.getID(), "icons/private_group.gif").createImage(); //$NON-NLS-1$ } return privateGroup; } if (privateAction == null) { privateAction = AbstractUIPlugin.imageDescriptorFromPlugin(UiPlugin.getID(), "icons/private_action.gif").createImage(); //$NON-NLS-1$ } return privateAction; } return null; } @Override public String getText(Object element) { if (element == null) return ""; //$NON-NLS-1$ Attribute attribute = (Attribute) element; return attribute.getName(); } } private class ShowPrivateAttributesAction extends Action { private static final String SHOW_PRIVATE = "show.private"; //$NON-NLS-1$ public ShowPrivateAttributesAction() { super(Messages.show_private_attributes, IAction.AS_CHECK_BOX); boolean showPrivate = getDialogSettings().getBoolean(SHOW_PRIVATE); onlyPublic = !showPrivate; setChecked(showPrivate); } @Override public void run() { getDialogSettings().put(SHOW_PRIVATE, isChecked()); onlyPublic = !isChecked(); applyFilter(); } } private boolean forceRebuild; public boolean onlyPublic; private String propertiesFile; private static Comparator<Attribute> attributeComparator = new Comparator<Attribute>() { @Override public int compare(Attribute o1, Attribute o2) { return o1.getName().compareTo(o2.getName()); } }; private static final String DIALOG_SETTINGS = "org.eclipse.buckminster.ui.dialogs.InvokeActionDialog"; //$NON-NLS-1$ private static final String LAST_PROPERTIES = "last.properties"; //$NON-NLS-1$ private Collection<Attribute> attributes; private AbstractCSpecAction cspecAction; public InvokeActionDialog(Shell shell, AbstractCSpecAction action) { this(shell, "", null); //$NON-NLS-1$ cspecAction = action; } public InvokeActionDialog(Shell shell, String title, Collection<Attribute> viableAttributes) { super(shell, true); this.attributes = viableAttributes; setListLabelProvider(new LabelProvider()); setTitle(title); setMessage(Messages.select_actions_to_perform); setSeparatorLabel(Messages.available_attributes); setSelectionHistory(new AttributeSelectionHistory()); setDetailsLabelProvider(new DetailsLabelProvider()); } @Override public String getElementName(Object item) { Attribute attribute = (Attribute) item; return attribute.getQualifiedName(); } /** * * @return the properties file choosen by the user or <code>null</code> if * the dialog was canceled or the user didn't select a properties * file */ public File getPropertiesFile() { if (propertiesFile == null || propertiesFile.trim().length() == 0) return null; return new File(propertiesFile); } /** * * @return the attributes selected by the user or <code>null</code> if * nothing was selected */ public List<Attribute> getSelectedAttributes() { Object[] selection = getResult(); if (selection == null) return null; List<Attribute> selectedAttributes = new ArrayList<Attribute>(selection.length); for (int i = 0; i < selection.length; i++) { selectedAttributes.add((Attribute) selection[i]); } return selectedAttributes; } public boolean isForceRebuild() { return forceRebuild; } @Override protected Control createExtendedContentArea(Composite parent) { Composite composite = new Composite(parent, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(composite); composite.setLayout(new GridLayout(4, false)); Label label = new Label(composite, SWT.WRAP); label.setText(Messages.properties_file_with_colon); final CCombo combo = new CCombo(composite, SWT.NONE); GridDataFactory.fillDefaults().grab(true, false).applyTo(combo); String[] lastProperties = getDialogSettings().getArray(LAST_PROPERTIES); if (lastProperties != null) { List<String> storedPropertyFiles = new ArrayList<String>(Arrays.asList(lastProperties)); Iterator<String> it = storedPropertyFiles.iterator(); while (it.hasNext()) { String propertyFile = it.next(); if (propertyFile == null) { // remove corrupt dialog setting it.remove(); } } if (!storedPropertyFiles.contains("")) //$NON-NLS-1$ { // add an empty string for a 'no properties option' if it // doesn't exist storedPropertyFiles.add(""); //$NON-NLS-1$ } String[] temp = new String[storedPropertyFiles.size()]; storedPropertyFiles.toArray(temp); lastProperties = temp; } else { lastProperties = new String[0]; } combo.setItems(lastProperties); combo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { propertiesFile = Trivial.trim(combo.getText()); } }); combo.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { propertiesFile = combo.getText(); updateStatus(validateItem(propertiesFile)); } }); if (combo.getItemCount() > 0) { combo.select(0); } Button browse = new Button(composite, SWT.PUSH); browse.setText(Messages.filesystem); browse.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { FileDialog dialog = new FileDialog(getShell(), SWT.OPEN); String fileName = dialog.open(); if (fileName == null) return; int index = index(fileName); if (index < 0) { combo.add(fileName); index = combo.getItemCount() - 1; } combo.select(index); } private int index(String fileName) { String[] items = combo.getItems(); for (int i = 0; i < items.length; i++) { String item = items[i]; if (fileName.equals(item)) return i; } return -1; } }); Button browseWS = new Button(composite, SWT.PUSH); browseWS.setText(Messages.workspace); browseWS.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String fileName = null; ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(), new WorkbenchLabelProvider(), new WorkbenchContentProvider()); dialog.setValidator(new FileValidator()); dialog.setAllowMultiple(true); dialog.setTitle(Messages.action_properties_file_selection); dialog.setMessage(Messages.select_action_properties_file); dialog.addFilter(new FileExtensionFilter("properties")); //$NON-NLS-1$ dialog.setInput(ResourcesPlugin.getWorkspace().getRoot()); dialog.create(); if (dialog.open() == Window.OK) { Object[] files = dialog.getResult(); if (files.length > 0) fileName = ((IFile) files[0]).getLocation().toOSString(); } if (fileName == null) return; int index = index(fileName); if (index < 0) { combo.add(fileName); index = combo.getItemCount() - 1; } combo.select(index); } private int index(String fileName) { String[] items = combo.getItems(); for (int i = 0; i < items.length; i++) { String item = items[i]; if (fileName.equals(item)) return i; } return -1; } }); return composite; } @Override protected ItemsFilter createFilter() { return new AttributeFilter(); } @Override protected void fillContentProvider(AbstractContentProvider contentProvider, ItemsFilter itemsFilter, IProgressMonitor progressMonitor) throws CoreException { // progressMonitor.beginTask(Messages.collecting_actions, 100); if (attributes == null) { final CSpec cspec = cspecAction.fetchCSpec(progressMonitor); if (cspec != null) { attributes = cspec.getAttributes().values(); getShell().getDisplay().asyncExec(new Runnable() { @Override public void run() { getShell().setText(Messages.actions_of + cspec.getName()); } }); } } if (attributes != null) { for (Attribute attribute : attributes) { if (itemsFilter.matchItem(attribute)) { contentProvider.add(attribute, itemsFilter); } } } // progressMonitor.done(); } @Override protected void fillViewMenu(IMenuManager menuManager) { menuManager.add(new ShowPrivateAttributesAction()); menuManager.add(new ForceRebuildAction()); super.fillViewMenu(menuManager); } @Override protected IDialogSettings getDialogSettings() { IDialogSettings settings = UiPlugin.getDefault().getDialogSettings().getSection(DIALOG_SETTINGS); if (settings == null) { settings = UiPlugin.getDefault().getDialogSettings().addNewSection(DIALOG_SETTINGS); } return settings; } @Override protected Comparator<?> getItemsComparator() { return attributeComparator; } @Override protected void okPressed() { storeDialogPreferences(); super.okPressed(); } @Override protected IStatus validateItem(Object item) { if (propertiesFile == null || propertiesFile.trim().length() == 0) return Status.OK_STATUS; File file = new File(propertiesFile); if (!file.exists()) { return new Status(IStatus.ERROR, UiPlugin.getID(), Messages.properties_file_does_not_exist); } if (!file.isFile()) { return new Status(IStatus.ERROR, UiPlugin.getID(), Messages.not_a_valid_file); } return Status.OK_STATUS; } /** * adds the dialog preferences to the {@link DialogSettings}. * <p> * the list of the last 5 choosen properties gets persisted and ordered * according to their last usage. * * @see InvokeActionDialog#getDialogSettings() */ private void storeDialogPreferences() { String[] lastProperties = getDialogSettings().getArray(LAST_PROPERTIES); List<String> propertyList; if (lastProperties != null) { propertyList = new ArrayList<String>(Arrays.asList(lastProperties)); } else { propertyList = new ArrayList<String>(); } if (propertyList.contains(propertiesFile)) { propertyList.remove(propertiesFile); } if (propertiesFile == null) { // null values are not allowed in the combo propertyList.remove(""); //$NON-NLS-1$ propertyList.add(0, ""); //$NON-NLS-1$ } else { propertyList.add(0, propertiesFile); } lastProperties = propertyList.subList(0, Math.min(5, propertyList.size())).toArray(new String[Math.min(5, propertyList.size())]); getDialogSettings().put(LAST_PROPERTIES, lastProperties); } }