Java tutorial
/***************************************************************************** * Copyright (c) 2008 g-Eclipse Consortium * 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 * * Initial development of the original code was made for the * g-Eclipse project founded by European Union * project number: FP6-IST-034327 http://www.geclipse.eu/ * * Contributors: * Mathias Stuempert - initial API and implementation *****************************************************************************/ package eu.geclipse.ui.dialogs; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.TitleAreaDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; 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.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.ui.PlatformUI; import eu.geclipse.core.filesystem.GEclipseURI; import eu.geclipse.core.model.GridModel; import eu.geclipse.core.model.IGridConnectionElement; import eu.geclipse.core.model.IGridContainer; import eu.geclipse.core.model.IGridElement; import eu.geclipse.core.model.IGridModelEvent; import eu.geclipse.core.model.IGridModelListener; import eu.geclipse.ui.comparators.TreeColumnComparator; import eu.geclipse.ui.internal.Activator; import eu.geclipse.ui.listeners.TreeColumnListener; import eu.geclipse.ui.providers.DecoratingGridModelLabelProvider; import eu.geclipse.ui.providers.FileStoreLabelProvider; import eu.geclipse.ui.providers.GridFileDialogContentProvider; import eu.geclipse.ui.providers.NewGridModelLabelProvider; import eu.geclipse.ui.providers.ProgressTreeNode; import eu.geclipse.ui.widgets.StoredCombo; /** * This is an implementation of a file dialog for both local * and remote files that are made available via EFS-implementations. * The remote parts make use of the Grid connections that are defined * within the user's. The dialog is highly configurable and allows to * only show the remote or local parts or both. Furthermore it can be * used to select only files or directories or both and only existing * or to specify even new files/directories. Last but not least multi- * selection may also be possible. */ public class GridFileDialog extends TitleAreaDialog { /** * Private interface used to listen to mode changes. */ private interface IModeChangeListener { /** * Invoked whenever the dialog's mode has changed. * * @param mode The new mode. */ public void modeChanged(final int mode); } /** * The <code>ModeManager</code> does manage the dialogs modes. * On the one hand it manages the activation states of the * provided {@link ToolItem}s, on the other hand it informs * listeners about mode changes. */ private static class ModeManager extends SelectionAdapter { /** * Mode constant for the remote mode. */ public static final int CONNECTION_MODE = 1; /** * Mode constant for the local mode with the user's home * as root directory. */ public static final int HOME_MODE = 2; /** * Mode constant for the local mode with the workspace * as root directory. */ public static final int WS_MODE = 3; /** * Mode constant for the local mode with the system's root * as root directory. */ public static final int ROOT_MODE = 4; /** * Identifier used to tag the {@link ToolItem}s. */ private static final String MODE_KEY = "button.mode"; //$NON-NLS-1$ /** * List of all available {@link ToolItem}s. */ private List<ToolItem> modeItems = new ArrayList<ToolItem>(); /** * List of all registered listeners. */ private List<IModeChangeListener> listeners = new ArrayList<IModeChangeListener>(); /** * Standard constructor. */ public ModeManager() { // empty implementation } /** * Add a new {@link IModeChangeListener} to the list of listeners. * * @param l The new listener. */ public void addModeChangeListener(final IModeChangeListener l) { if (!this.listeners.contains(l)) { this.listeners.add(l); } } /** * Add a new mode item to the list of items. Any formerly defined * item with the same mode will be overwritten. * * @param item The new item used to select the specified mode. * @param mode The mode that refers to the specified item. */ public void addModeItem(final ToolItem item, final int mode) { item.setData(MODE_KEY, Integer.valueOf(mode)); this.modeItems.add(item); item.addSelectionListener(this); } /** * Set the mode, i.e. update the selection states of the mode items. * * @param mode The new mode. */ public void setMode(final int mode) { for (ToolItem item : this.modeItems) { Integer m = (Integer) item.getData(MODE_KEY); item.setSelection((m != null) && (m.intValue() == mode)); } } /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(final SelectionEvent e) { for (ToolItem item : this.modeItems) { item.setSelection(item == e.widget); } Object mode = e.widget.getData(MODE_KEY); if (mode instanceof Integer) { fireModeChanged(((Integer) mode).intValue()); } } /** * Inform all registered {@link IModeChangeListener}s about a mode * changed event. * * @param mode The new mode to be reported to the listeners. */ private void fireModeChanged(final int mode) { for (IModeChangeListener l : this.listeners) { l.modeChanged(mode); } } } /** * {@link ViewerFilter} that filters out all non-folders. */ private static class FolderFilter extends ViewerFilter { /** * Standard constructor. */ public FolderFilter() { // empty implementation } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, * java.lang.Object, java.lang.Object) */ @Override public boolean select(final Viewer viewer, final Object parentElement, final Object element) { boolean result = false; if (element instanceof IGridElement) { if (element instanceof IGridConnectionElement) { result = ((IGridConnectionElement) element).isFolder(); } else if (element instanceof IGridContainer) { result = true; } } else if (element instanceof IFileStore) { IFileInfo info = ((IFileStore) element).fetchInfo(); result = info.isDirectory(); } else if (element instanceof ProgressTreeNode) { result = true; } return result; } } /** * {@link ViewerFilter} that filters out all non-remote elements. */ private static class RemoteConnectionFilter extends ViewerFilter { /** * Standard constructor. */ public RemoteConnectionFilter() { // empty implementation } @Override public boolean select(final Viewer viewer, final Object parentElement, final Object element) { boolean result = false; if (element instanceof IGridConnectionElement) { result = !((IGridConnectionElement) element).isLocal(); } else if (element instanceof ProgressTreeNode) { result = true; } return result; } } /** * {@link ViewerFilter} that filters out all files without a specific * file extension. */ private static class FileTypeFilter extends ViewerFilter { /** * Constant for the wildcard filter. */ private static final String WILDCARD = "*"; //$NON-NLS-1$ /** * Separator used to separate filename and prefix. */ private static final String PREFIX_SEPARATOR = "."; //$NON-NLS-1$ /** * The file extension for allowed files. */ private String prefix; /** * Construct a new standard filter, i.e. a filter that allows all * files (*.*). */ public FileTypeFilter() { this(null); } /** * Construct a new filter that filters all files with other extensions * than the specified. Folders are not filtered. * * @param prefix Prefix for all non-filtered files. */ public FileTypeFilter(final String prefix) { this.prefix = prefix; } /** * Get the prefix of the filtered files. * * @return The file's prefix. */ public String getPrefix() { return this.prefix == null ? WILDCARD : this.prefix; } /* (non-Javadoc) * @see org.eclipse.jface.viewers.ViewerFilter#select(org.eclipse.jface.viewers.Viewer, * java.lang.Object, java.lang.Object) */ @Override public boolean select(final Viewer viewer, final Object parentElement, final Object element) { boolean result = false; if (this.prefix != null) { if (element instanceof IFileStore) { boolean isDir = ((IFileStore) element).fetchInfo().isDirectory(); String name = ((IFileStore) element).getName(); result = isDir || name.endsWith(PREFIX_SEPARATOR + this.prefix); } else if (element instanceof IGridConnectionElement) { boolean isDir = ((IGridConnectionElement) element).isFolder(); String name = ((IGridConnectionElement) element).getName(); result = isDir || name.endsWith(PREFIX_SEPARATOR + this.prefix); } else if (element instanceof IGridContainer) { IResource resource = ((IGridContainer) element).getResource(); if ((resource != null) && (resource.getType() == IResource.FILE)) { String name = resource.getName(); result = name.endsWith(PREFIX_SEPARATOR + this.prefix); } else { result = true; } } else if (element instanceof IGridElement) { String name = ((IGridElement) element).getName(); result = name.endsWith(PREFIX_SEPARATOR + this.prefix); } else if (element instanceof ProgressTreeNode) { result = true; } } else { result = true; } return result; } } /** * Style constant for none styles. */ public static final int STYLE_NONE = 0x00; /** * Style constant for a dialog that allows only local files, * i.e. no Grid connections. */ public static final int STYLE_ALLOW_ONLY_LOCAL = 0x01; /** * Style constant for a dialog that allows Grid connections. */ public static final int STYLE_ALLOW_ONLY_CONNECTIONS = 0x02; /** * Style constant for a dialog that allows only remote files, * i.e. only Grid connections that are not referring to local files. */ public static final int STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS = 0x04; /** * Style constant for a dialog that allows only the selection of * files. */ public static final int STYLE_ALLOW_ONLY_FILES = 0x08; /** * Style constant for a dialog that allows only the selection of * directories. */ public static final int STYLE_ALLOW_ONLY_FOLDERS = 0x10; /** * Style constant for a dialog that allows only the selection of * existing files/directories. The filename and url combos can not * be edited. */ public static final int STYLE_ALLOW_ONLY_EXISTING = 0x20; /** * Style contant for a file dialog that allows multi selection. */ public static final int STYLE_MULTI_SELECTION = 0x40; /** * Empty string constant. */ private static final String EMPTY_STRING = ""; //$NON-NLS-1$ /** * Path separator. */ private static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$ /** * System property for the user's home directory. */ private static final String HOME_PROPERTY = "user.home"; //$NON-NLS-1$ /** * Root folder constant. */ private static final String ROOT_FOLDER = "/"; //$NON-NLS-1$ private static final Pattern POSIX_FILENAME = Pattern.compile("^[\\w\\.][\\w\\.-]*"); //$NON-NLS-1$ /** * The dialog's tree viewer. */ protected TreeViewer treeViewer; /** * The combo for selecting the file type. */ protected Combo filetypeCombo; /** * The combo for selecting and editing the URI directly. */ protected StoredCombo uriCombo; /** * Map of all available {@link FileTypeFilter}s. */ protected Hashtable<String, FileTypeFilter> filetypeFilters = new Hashtable<String, FileTypeFilter>(); /** * The combo for selecting and editing the filename directly. */ private StoredCombo filenameCombo; /** * {@link ModeManager} used to manage the tree viewer's mode. */ private ModeManager modeManager; /** * Listener used to listen to changes in the Grid model. */ private IGridModelListener modelListener; /** * Listener used to listen to modifications in the uri combo. */ private ModifyListener uriListener; /** * Listener used to listen to modifications in the filename combo. */ private VerifyListener filenameListener; /** * Listener used to listen to selections in the tree viewer. */ private ISelectionChangedListener selectionListener; /** * The style of the dialog. */ private int style; /** * The current selection of the tree viewer. */ private IFileStore[] currentSelection; /** * Create a new dialog with the specified style constant. * * @param parent The dialog's parent {@link Shell}. * @param style The dialog's style, i.e. a bitwise or of style * constants. */ public GridFileDialog(final Shell parent, final int style) { super(parent); this.style = style; assertStyle(); setShellStyle(SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL | SWT.RESIZE); URL imgURL = Activator.getDefault().getBundle().getResource("icons/wizban/newconn_wiz.gif"); //$NON-NLS-1$ ImageDescriptor imgDesc = ImageDescriptor.createFromURL(imgURL); setTitleImage(imgDesc.createImage()); } /** * Convenience method to open a file dialog. If filters have to be * specified this method may not be used. * * @param parent The dialog's parent {@link Shell}. * @param style The dialog's style, i.e. a bitwise or of style * constants. * @return Array containing the {@link URI}s of all selected elements * or <code>null</code> if no selection is available. */ public static URI[] openFileDialog(final Shell parent, final int style) { URI[] result = null; GridFileDialog dialog = new GridFileDialog(parent, style); if (dialog.open() == Window.OK) { result = dialog.getSelectedURIs(); } return result; } /** * Add a file type filter to this dialog. The filter will appear in * the file type combo. * * @param prefix The prefix of files that will be shown. * @param description A description of the filter. This description * will be shown in the file type combo. */ public void addFileTypeFilter(final String prefix, final String description) { FileTypeFilter filter = new FileTypeFilter(prefix); addFileTypeFilter(filter, description); } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.TrayDialog#close() */ @Override public boolean close() { if (this.modelListener != null) { GridModel.getRoot().removeGridModelListener(this.modelListener); } return super.close(); } /** * Get all currently selected {@link IFileStore}s that meet the style of * this dialog. * * @return Array containing the {@link IFileStore}s of all selected elements * or <code>null</code> if no selection is available. */ public IFileStore[] getSelectedFileStores() { IFileStore[] result = null; if ((this.currentSelection != null) && (this.currentSelection.length > 0)) { result = new IFileStore[this.currentSelection.length]; System.arraycopy(this.currentSelection, 0, result, 0, result.length); } return result; } /** * Get all currently selected {@link URI}s. * * @return Array containing the {@link URI}s of all selected elements * or <code>null</code> if no selection is available. */ public URI[] getSelectedURIs() { URI[] result = null; IFileStore[] stores = getSelectedFileStores(); if ((stores != null) && (stores.length > 0)) { result = new URI[stores.length]; for (int i = 0; i < stores.length; i++) { URI uri = stores[i].toURI(); GEclipseURI gUri = new GEclipseURI(uri); result[i] = gUri.toSlaveURI(); } } return result; } /** * Configure the tree viewer's filters according to the dialog's style. */ protected void configureViewerFilters() { if (this.treeViewer != null) { this.treeViewer.resetFilters(); if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) { this.treeViewer.addFilter(new FolderFilter()); } if (hasStyle(STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) { this.treeViewer.addFilter(new RemoteConnectionFilter()); } if (this.filetypeCombo != null) { if (this.filetypeCombo.getItemCount() != this.filetypeFilters.size()) { initializeFileTypeCombo(); } String key = this.filetypeCombo.getText(); FileTypeFilter filter = this.filetypeFilters.get(key); if (filter != null) { this.treeViewer.addFilter(filter); } } } } /* (non-Javadoc) * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite) */ @Override protected Control createDialogArea(final Composite parent) { this.modeManager = new ModeManager(); this.modeManager.addModeChangeListener(new IModeChangeListener() { public void modeChanged(final int mode) { setMode(mode); } }); GridData gData; Label topRule = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR); topRule.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); Composite mainComp = new Composite(parent, SWT.NONE); mainComp.setLayout(new GridLayout(2, false)); gData = new GridData(GridData.FILL_BOTH); gData.grabExcessHorizontalSpace = true; gData.grabExcessVerticalSpace = true; gData.widthHint = 500; gData.heightHint = 400; mainComp.setLayoutData(gData); Label bottomRule = new Label(parent, SWT.HORIZONTAL | SWT.SEPARATOR); bottomRule.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if (!hasStyle(STYLE_MULTI_SELECTION)) { Composite uriComp = new Composite(mainComp, SWT.NONE); uriComp.setLayout(new GridLayout(2, false)); gData = new GridData(GridData.FILL_HORIZONTAL); gData.grabExcessHorizontalSpace = true; gData.horizontalSpan = 2; uriComp.setLayoutData(gData); Label uriLabel = new Label(uriComp, SWT.NONE); uriLabel.setText(Messages.getString("GridFileDialog.label_URI")); //$NON-NLS-1$ gData = new GridData(); uriLabel.setLayoutData(gData); this.uriCombo = new StoredCombo(uriComp, SWT.NONE); gData = new GridData(GridData.FILL_HORIZONTAL); gData.grabExcessHorizontalSpace = true; this.uriCombo.setLayoutData(gData); this.uriCombo.setEnabled(!hasStyle(STYLE_ALLOW_ONLY_EXISTING)); } if (!hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS | STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) { ToolBar modeBar = new ToolBar(mainComp, SWT.VERTICAL | SWT.BORDER); modeBar.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_WHITE)); gData = new GridData(GridData.FILL_VERTICAL); gData.grabExcessVerticalSpace = true; modeBar.setLayoutData(gData); if (!hasStyle(STYLE_ALLOW_ONLY_LOCAL)) { URL connURL = Activator.getDefault().getBundle() .getResource("icons/extras/grid_file_dialog_conn_mode.gif"); //$NON-NLS-1$ ImageDescriptor connDesc = ImageDescriptor.createFromURL(connURL); ToolItem connItem = new ToolItem(modeBar, SWT.CHECK); connItem.setImage(connDesc.createImage()); connItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_connections")); //$NON-NLS-1$ this.modeManager.addModeItem(connItem, ModeManager.CONNECTION_MODE); } if (!hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS | STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) { URL wsURL = Activator.getDefault().getBundle() .getResource("icons/extras/grid_file_dialog_ws_mode.gif"); //$NON-NLS-1$ ImageDescriptor wsDesc = ImageDescriptor.createFromURL(wsURL); ToolItem wsItem = new ToolItem(modeBar, SWT.CHECK); wsItem.setImage(wsDesc.createImage()); wsItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_workspace")); //$NON-NLS-1$ this.modeManager.addModeItem(wsItem, ModeManager.WS_MODE); URL homeURL = Activator.getDefault().getBundle() .getResource("icons/extras/grid_file_dialog_home_mode.gif"); //$NON-NLS-1$ ImageDescriptor homeDesc = ImageDescriptor.createFromURL(homeURL); ToolItem homeItem = new ToolItem(modeBar, SWT.CHECK); homeItem.setImage(homeDesc.createImage()); homeItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_home")); //$NON-NLS-1$ this.modeManager.addModeItem(homeItem, ModeManager.HOME_MODE); URL rootURL = Activator.getDefault().getBundle() .getResource("icons/extras/grid_file_dialog_root_mode.gif"); //$NON-NLS-1$ ImageDescriptor rootDesc = ImageDescriptor.createFromURL(rootURL); ToolItem rootItem = new ToolItem(modeBar, SWT.CHECK); rootItem.setImage(rootDesc.createImage()); rootItem.setToolTipText(Messages.getString("GridFileDialog.switch_to_root")); //$NON-NLS-1$ this.modeManager.addModeItem(rootItem, ModeManager.ROOT_MODE); } } Composite browserComp = new Composite(mainComp, SWT.NONE); GridLayout browserLayout = new GridLayout(1, false); browserLayout.marginWidth = 0; browserLayout.marginHeight = 0; browserComp.setLayout(browserLayout); gData = new GridData(GridData.FILL_BOTH); gData.grabExcessHorizontalSpace = true; gData.grabExcessVerticalSpace = true; browserComp.setLayoutData(gData); int treeStyle = SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER; if (hasStyle(STYLE_MULTI_SELECTION)) { treeStyle |= SWT.MULTI; } else { treeStyle |= SWT.SINGLE; } this.treeViewer = new TreeViewer(browserComp, treeStyle); this.treeViewer.setContentProvider(new GridFileDialogContentProvider()); NewGridModelLabelProvider lProvider = new NewGridModelLabelProvider(); lProvider.addColumn(0, FileStoreLabelProvider.COLUMN_TYPE_NAME); lProvider.addColumn(1, FileStoreLabelProvider.COLUMN_TYPE_SIZE); lProvider.addColumn(2, FileStoreLabelProvider.COLUMN_TYPE_MOD_DATE); ILabelDecorator decorator = PlatformUI.getWorkbench().getDecoratorManager().getLabelDecorator(); DecoratingGridModelLabelProvider dProvider = new DecoratingGridModelLabelProvider(lProvider, decorator); this.treeViewer.setLabelProvider(dProvider); Tree tree = this.treeViewer.getTree(); tree.setHeaderVisible(true); gData = new GridData(GridData.FILL_BOTH); gData.grabExcessHorizontalSpace = true; gData.grabExcessVerticalSpace = true; tree.setLayoutData(gData); TreeColumn nameColumn = new TreeColumn(tree, SWT.NONE); nameColumn.setText(Messages.getString("GridFileDialog.column_title_name")); //$NON-NLS-1$ nameColumn.setAlignment(SWT.LEFT); nameColumn.setWidth(300); TreeColumn sizeColumn = new TreeColumn(tree, SWT.NONE); sizeColumn.setText(Messages.getString("GridFileDialog.column_title_size")); //$NON-NLS-1$ sizeColumn.setAlignment(SWT.RIGHT); sizeColumn.setWidth(100); TreeColumn modColumn = new TreeColumn(tree, SWT.NONE); modColumn.setText(Messages.getString("GridFileDialog.column_title_last_modification")); //$NON-NLS-1$ modColumn.setAlignment(SWT.CENTER); modColumn.setWidth(200); TreeColumnListener columnListener = new TreeColumnListener(this.treeViewer); for (TreeColumn column : tree.getColumns()) { column.addSelectionListener(columnListener); } tree.setSortColumn(nameColumn); tree.setSortDirection(SWT.UP); this.treeViewer.setComparator(new TreeColumnComparator(nameColumn)); Composite fileComp = new Composite(browserComp, SWT.NONE); fileComp.setLayout(new GridLayout(2, false)); gData = new GridData(GridData.FILL_HORIZONTAL); gData.grabExcessHorizontalSpace = true; fileComp.setLayoutData(gData); if (!hasStyle(STYLE_MULTI_SELECTION)) { Label filenameLabel = new Label(fileComp, SWT.NONE); filenameLabel.setText( hasStyle(STYLE_ALLOW_ONLY_FOLDERS) ? Messages.getString("GridFileDialog.label_foldername") //$NON-NLS-1$ : hasStyle(STYLE_ALLOW_ONLY_FILES) ? Messages.getString("GridFileDialog.label_filename") //$NON-NLS-1$ : Messages.getString("GridFileDialog.label_name") //$NON-NLS-1$ ); gData = new GridData(); gData.horizontalAlignment = GridData.BEGINNING; filenameLabel.setLayoutData(gData); this.filenameCombo = new StoredCombo(fileComp, SWT.BORDER); gData = new GridData(GridData.FILL_HORIZONTAL); gData.grabExcessHorizontalSpace = true; this.filenameCombo.setLayoutData(gData); this.filenameCombo.setEnabled(!hasStyle(STYLE_ALLOW_ONLY_EXISTING)); } if (!hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) { Label filetypeLabel = new Label(fileComp, SWT.NONE); filetypeLabel.setText(Messages.getString("GridFileDialog.label_filetype")); //$NON-NLS-1$ gData = new GridData(); gData.horizontalAlignment = GridData.BEGINNING; filetypeLabel.setLayoutData(gData); this.filetypeCombo = new Combo(fileComp, SWT.BORDER | SWT.READ_ONLY); gData = new GridData(GridData.FILL_HORIZONTAL); gData.grabExcessHorizontalSpace = true; this.filetypeCombo.setLayoutData(gData); } int mode = !hasStyle(STYLE_ALLOW_ONLY_LOCAL) ? ModeManager.CONNECTION_MODE : ModeManager.WS_MODE; this.modeManager.setMode(mode); setMode(mode); this.treeViewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(final DoubleClickEvent event) { handleDoubleClick(); } }); this.selectionListener = new ISelectionChangedListener() { public void selectionChanged(final SelectionChangedEvent event) { setNotificationEnabled(false); handleSelectionChanged(); setNotificationEnabled(true); } }; this.treeViewer.addSelectionChangedListener(this.selectionListener); if (this.uriCombo != null) { this.uriListener = new ModifyListener() { public void modifyText(final ModifyEvent e) { setNotificationEnabled(false); handleUriChanged(); setNotificationEnabled(true); } }; this.uriCombo.addModifyListener(this.uriListener); } if (this.filenameCombo != null) { this.filenameListener = new VerifyListener() { public void verifyText(final VerifyEvent e) { setNotificationEnabled(false); handleFilenameChanged(e); setNotificationEnabled(true); } }; this.filenameCombo.addVerifyListener(this.filenameListener); } if (this.filetypeCombo != null) { this.filetypeCombo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { configureViewerFilters(); } }); } this.modelListener = new IGridModelListener() { public void gridModelChanged(final IGridModelEvent event) { if ((event.getType() == IGridModelEvent.ELEMENTS_ADDED) || (event.getType() == IGridModelEvent.ELEMENTS_REMOVED)) { refreshViewer(event.getSource()); } } }; GridModel.getRoot().addGridModelListener(this.modelListener); Shell shell = getShell(); if (hasStyle(STYLE_ALLOW_ONLY_FILES)) { if (hasStyle(STYLE_MULTI_SELECTION)) { shell.setText(Messages.getString("GridFileDialog.shell_title_files")); //$NON-NLS-1$ } else { shell.setText(Messages.getString("GridFileDialog.shell_title_file")); //$NON-NLS-1$ } } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) { if (hasStyle(STYLE_MULTI_SELECTION)) { shell.setText(Messages.getString("GridFileDialog.shell_title_folders")); //$NON-NLS-1$ } else { shell.setText(Messages.getString("GridFileDialog.shell_title_folder")); //$NON-NLS-1$ } } else { if (hasStyle(STYLE_MULTI_SELECTION)) { shell.setText(Messages.getString("GridFileDialog.shell_title_files_folders")); //$NON-NLS-1$ } else { shell.setText(Messages.getString("GridFileDialog.shell_title_file_folder")); //$NON-NLS-1$ } } setTitle(Messages.getString("GridFileDialog.title")); //$NON-NLS-1$ if (hasStyle(STYLE_ALLOW_ONLY_FILES)) { if (hasStyle(STYLE_MULTI_SELECTION)) { setMessage(Messages.getString("GridFileDialog.title_files")); //$NON-NLS-1$ } else { setMessage(Messages.getString("GridFileDialog.title_file")); //$NON-NLS-1$ } } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) { if (hasStyle(STYLE_MULTI_SELECTION)) { setMessage(Messages.getString("GridFileDialog.title_folders")); //$NON-NLS-1$ } else { setMessage(Messages.getString("GridFileDialog.title_folder")); //$NON-NLS-1$ } } else { if (hasStyle(STYLE_MULTI_SELECTION)) { setMessage(Messages.getString("GridFileDialog.title_files_folders")); //$NON-NLS-1$ } else { setMessage(Messages.getString("GridFileDialog.title_file_folder")); //$NON-NLS-1$ } } addFileTypeFilter(new FileTypeFilter(), Messages.getString("GridFileDialog.label_all_files")); //$NON-NLS-1$ if (this.filenameCombo != null && this.filenameCombo.getItemCount() != 0) { this.filetypeCombo.select(0); } return mainComp; } /** * Handler method for handling double clicks in the {@link TreeViewer}. This * handler expands or collapses tree nodes if the associated * element is a folder or selects an element and * closes the dialog if the associated element is a file. */ protected void handleDoubleClick() { IStructuredSelection selection = (IStructuredSelection) this.treeViewer.getSelection(); Object object = selection.getFirstElement(); if (this.treeViewer.isExpandable(object)) { boolean state = this.treeViewer.getExpandedState(object); this.treeViewer.setExpandedState(object, !state); } else { setReturnCode(IDialogConstants.OK_ID); close(); } } /** * Handler that handles changes in the filename combo. */ protected void handleFilenameChanged(final VerifyEvent e) { String errMsg = null; URI uri = getURI(); if (uri != null) { IPath path = new Path(uri.getPath()); String lastSegment = path.lastSegment(); String filename = getFilename(); if (filename == null) { filename = EMPTY_STRING; } if ((lastSegment != null) && lastSegment.equals(filename)) { path = path.removeLastSegments(1); } String newFilename = filename.substring(0, e.start) + e.text + filename.substring(e.end); if ((newFilename.length() > 0) && (!validateFilename(newFilename))) { e.doit = false; } else { path = path.append(newFilename); String spath = path.toString(); if (!spath.startsWith(PATH_SEPARATOR)) { spath = PATH_SEPARATOR + spath; } try { uri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), spath, uri.getQuery(), uri.getFragment()); setURI(uri); try { IFileStore fileStore = EFS.getStore(uri); IFileInfo fileInfo = fileStore.fetchInfo(); if (hasStyle(STYLE_ALLOW_ONLY_FILES) && fileInfo.isDirectory()) { errMsg = Messages.getString("GridFileDialog.file_only_mode_warning"); //$NON-NLS-1$ } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS) && !fileInfo.isDirectory()) { errMsg = Messages.getString("GridFileDialog.folder_only_mode_warning"); //$NON-NLS-1$ } else { setCurrentSelection(new IFileStore[] { fileStore }); } } catch (CoreException cExc) { errMsg = String.format(Messages.getString("GridFileDialog.create_file_store_error"), //$NON-NLS-1$ cExc.getLocalizedMessage()); } } catch (URISyntaxException uriExc) { errMsg = String.format(Messages.getString("GridFileDialog.invalid_file_name_error"), //$NON-NLS-1$ newFilename); } } } if (errMsg != null) { setCurrentSelection(null); } setErrorMessage(errMsg); } protected void handleSelectionChanged() { String errMsg = null; IFileStore[] selection = getSelection(); if ((selection == null) || (selection.length == 0)) { setFilename(null); setURI(null); } else if (hasStyle(STYLE_ALLOW_ONLY_FILES) || hasStyle(STYLE_ALLOW_ONLY_FOLDERS)) { for (IFileStore store : selection) { IFileInfo info = store.fetchInfo(); if (hasStyle(STYLE_ALLOW_ONLY_FILES) && info.isDirectory()) { errMsg = Messages.getString("GridFileDialog.file_only_mode_warning"); //$NON-NLS-1$ break; } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS) && !info.isDirectory()) { errMsg = Messages.getString("GridFileDialog.folder_only_mode_warning"); //$NON-NLS-1$ break; } } } if ((selection != null) && (selection.length == 1)) { setURI(selection[0].toURI()); if (errMsg == null) { setFilename(selection[0].getName()); } else { setFilename(null); } } else { setURI(null); setFilename(null); } if (errMsg == null) { setCurrentSelection(selection); } else { setCurrentSelection(null); } setErrorMessage(errMsg); } /** * Handler that handles changes in the uri combo. */ protected void handleUriChanged() { URI uri = getURI(); if (uri != null) { String errMsg = null; try { IFileStore fileStore = EFS.getStore(uri); IFileInfo fileInfo = fileStore.fetchInfo(); if (hasStyle(STYLE_ALLOW_ONLY_FILES) && fileInfo.isDirectory()) { errMsg = Messages.getString("GridFileDialog.file_only_mode_warning"); //$NON-NLS-1$ } else if (hasStyle(STYLE_ALLOW_ONLY_FOLDERS) && !fileInfo.isDirectory()) { errMsg = Messages.getString("GridFileDialog.folder_only_mode_warning"); //$NON-NLS-1$ } else { setFilename(fileInfo.getName()); setCurrentSelection(new IFileStore[] { fileStore }); } } catch (CoreException cExc) { errMsg = String.format(Messages.getString("GridFileDialog.create_file_store_error"), //$NON-NLS-1$ cExc.getLocalizedMessage()); } if (errMsg != null) { setFilename(null); setCurrentSelection(null); } setErrorMessage(errMsg); } else { setFilename(null); setCurrentSelection(null); } } protected void setMode(final int mode) { switch (mode) { case ModeManager.CONNECTION_MODE: this.treeViewer.setInput(GridModel.getConnectionManager()); break; case ModeManager.ROOT_MODE: this.treeViewer.setInput(EFS.getLocalFileSystem().getStore(new Path(ROOT_FOLDER))); break; case ModeManager.HOME_MODE: String home = System.getProperty(HOME_PROPERTY); this.treeViewer.setInput(EFS.getLocalFileSystem().getStore(new Path(home))); break; case ModeManager.WS_MODE: this.treeViewer.setInput(GridModel.getRoot()); break; } } /** * Refreshes the {@link TreeViewer} starting with the specified element. If * the element is <code>null</code> the whole {@link TreeViewer} will be * refreshed. * * @param element The {@link IGridElement} that will be refreshed. This also * includes the element's children. */ protected void refreshViewer(final IGridElement element) { Control control = this.treeViewer.getControl(); if (!control.isDisposed()) { Display display = control.getDisplay(); display.asyncExec(new Runnable() { public void run() { if (!GridFileDialog.this.treeViewer.getControl().isDisposed()) { if (element == null) { GridFileDialog.this.treeViewer.refresh(false); } else { if (element instanceof IGridContainer) { IGridContainer container = (IGridContainer) element; if (container.isLazy() && container.isDirty()) { GridFileDialog.this.treeViewer.setChildCount(container, container.getChildCount()); } } GridFileDialog.this.treeViewer.refresh(element, false); } } } }); } } /** * Add the specified {@link FileTypeFilter} to this dialog's filters. * * @param filter The filter to be added. * @param description A description of the filter set as refering text * in the type filter combo. */ private void addFileTypeFilter(final FileTypeFilter filter, final String description) { this.filetypeFilters.put(description, filter); configureViewerFilters(); } /** * Helper method to determine if this dialog was constructed with * the specified style. * * @param bit One of the style constants. * @return True if the specified style constant was specified for * this dialog. */ private boolean hasStyle(final int bit) { return (this.style & bit) != 0; } /** * Initialize the file type combo with the available file type filters. */ private void initializeFileTypeCombo() { if (this.filetypeCombo != null) { this.filetypeCombo.removeAll(); Set<String> keySet = this.filetypeFilters.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray, new Comparator<String>() { public int compare(final String s1, final String s2) { String p1 = GridFileDialog.this.filetypeFilters.get(s1).getPrefix(); String p2 = GridFileDialog.this.filetypeFilters.get(s2).getPrefix(); return p1.compareToIgnoreCase(p2); } }); this.filetypeCombo.setItems(keyArray); this.filetypeCombo.select(0); } } /** * Check if there are not ambiguities in the dialog's style. */ private void assertStyle() { Assert.isTrue( !((hasStyle(STYLE_ALLOW_ONLY_LOCAL) && hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS)) || (hasStyle(STYLE_ALLOW_ONLY_LOCAL) && hasStyle(STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS)) || (hasStyle(STYLE_ALLOW_ONLY_CONNECTIONS) && hasStyle(STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS))), "Only one of STYLE_ALLOW_ONLY_LOCAL, STYLE_ALLOW_ONLY_CONNECTIONS" //$NON-NLS-1$ + " and STYLE_ALLOW_ONLY_REMOTE_CONNECTIONS is allowed" //$NON-NLS-1$ ); Assert.isTrue(!(hasStyle(STYLE_ALLOW_ONLY_FILES) && hasStyle(STYLE_ALLOW_ONLY_FOLDERS)), "Only one of STYLE_ALLOW_ONLY_FILES and STYLE_ALLOW_ONLY_FOLDERS is allowed" //$NON-NLS-1$ ); } private String getFilename() { String result = null; if ((this.filenameCombo != null) && !this.filenameCombo.isDisposed()) { result = this.filenameCombo.getText(); } return result; } private IFileStore[] getSelection() { IFileStore[] result = null; if ((this.treeViewer != null) && !this.treeViewer.getTree().isDisposed()) { List<IFileStore> list = new ArrayList<IFileStore>(); IStructuredSelection selection = (IStructuredSelection) this.treeViewer.getSelection(); Iterator<?> iterator = selection.iterator(); while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof IGridConnectionElement) { try { o = ((IGridConnectionElement) o).getConnectionFileStore(); } catch (CoreException cExc) { // Silently ignored } } else if (o instanceof IGridElement) { o = ((IGridElement) o).getFileStore(); } if (o instanceof IFileStore) { list.add((IFileStore) o); } } if (!list.isEmpty()) { result = list.toArray(new IFileStore[list.size()]); } } return result; } private URI getURI() { URI result = null; setErrorMessage(null); if ((this.uriCombo != null) && !this.uriCombo.isDisposed()) { String text = this.uriCombo.getText(); try { result = new URI(text); } catch (URISyntaxException uriExc) { setErrorMessage(String.format(Messages.getString("GridFileDialog.invalid_uri_error"), //$NON-NLS-1$ uriExc.getLocalizedMessage())); } } return result; } private void setCurrentSelection(final IFileStore[] selection) { if ((selection != null) && (selection.length > 0)) { this.currentSelection = new IFileStore[selection.length]; System.arraycopy(selection, 0, this.currentSelection, 0, selection.length); getButton(IDialogConstants.OK_ID).setEnabled(true); } else { this.currentSelection = null; getButton(IDialogConstants.OK_ID).setEnabled(false); } } private void setFilename(final String filename) { if ((this.filenameCombo != null) && !this.filenameCombo.isDisposed()) { if (filename == null) { this.filenameCombo.setText(EMPTY_STRING); } else { this.filenameCombo.setText(filename); } } } private void setURI(final URI uri) { if ((this.uriCombo != null) && !this.uriCombo.isDisposed()) { if (uri == null) { this.uriCombo.setText(EMPTY_STRING); } else { GEclipseURI geclURI = new GEclipseURI(uri); this.uriCombo.setText(geclURI.toSlaveURI().toString()); } } } /** * Enables or disables notifications about modifications of the uri * and filename combos and changes in the selection of the tree viewer. * * @param b If <code>true</code> the notifications will be switched * on, if <code>false</code> they will be switched of. */ protected void setNotificationEnabled(final boolean b) { if (b) { if (this.uriCombo != null) { this.uriCombo.addModifyListener(this.uriListener); } if (this.filenameCombo != null) { this.filenameCombo.addVerifyListener(this.filenameListener); } this.treeViewer.addSelectionChangedListener(this.selectionListener); } else { if (this.uriCombo != null) { this.uriCombo.removeModifyListener(this.uriListener); } if (this.filenameCombo != null) { this.filenameCombo.removeVerifyListener(this.filenameListener); } this.treeViewer.removeSelectionChangedListener(this.selectionListener); } } private boolean validateFilename(final String filename) { return POSIX_FILENAME.matcher(filename).matches(); } }