1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.ui.internal.editors.text;
15 
16 import java.util.ArrayList;
17 import java.util.Arrays;
18 import java.util.HashSet;
19 import java.util.Hashtable;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Map;
23 
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.custom.BusyIndicator;
26 import org.eclipse.swt.events.SelectionAdapter;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.events.SelectionListener;
29 import org.eclipse.swt.graphics.Font;
30 import org.eclipse.swt.layout.GridData;
31 import org.eclipse.swt.widgets.Button;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Control;
34 import org.eclipse.swt.widgets.Label;
35 import org.eclipse.swt.widgets.Shell;
36 
37 import org.eclipse.core.runtime.CoreException;
38 
39 import org.eclipse.core.resources.IContainer;
40 import org.eclipse.core.resources.IResource;
41 import org.eclipse.core.resources.IWorkspaceRoot;
42 import org.eclipse.core.resources.ResourcesPlugin;
43 
44 import org.eclipse.jface.dialogs.Dialog;
45 import org.eclipse.jface.dialogs.IDialogConstants;
46 import org.eclipse.jface.layout.GridLayoutFactory;
47 import org.eclipse.jface.viewers.ITreeContentProvider;
48 
49 import org.eclipse.ui.dialogs.TypeFilteringDialog;
50 import org.eclipse.ui.model.WorkbenchContentProvider;
51 import org.eclipse.ui.model.WorkbenchLabelProvider;
52 
53 
54 /**
55  * Dialog for selecting resources.
56  *
57  * @since 3.1
58  */
59 class SelectResourcesDialog extends Dialog {
60 
61 	interface IFilter {
accept(IResource resource)62 		boolean accept(IResource resource);
63 	}
64 
65 	private SelectResourcesBlock fResourceGroup;
66 	private List<Object> fAcceptedFileTypes = new ArrayList<>();
67 	private IResource[] fInput;
68 	private String fTitle;
69 	private String fInstruction;
70 	private Label fCountIndication;
71 	private IFilter fAcceptableLocationsFilter;
72 
73 
SelectResourcesDialog(Shell parentShell, String title, String instruction, IFilter acceptableLocationsFilter)74 	public SelectResourcesDialog(Shell parentShell, String title, String instruction, IFilter acceptableLocationsFilter) {
75 		super(parentShell);
76 		fTitle= title;
77 		fInstruction= instruction;
78 		fAcceptableLocationsFilter= acceptableLocationsFilter;
79 	}
80 
81 	@Override
isResizable()82 	protected boolean isResizable() {
83 		return true;
84 	}
85 
setInput(IResource[] input)86 	public void setInput(IResource[] input) {
87 		fInput= input;
88 	}
89 
refresh()90 	public void refresh() {
91 		fResourceGroup.refresh();
92 		setSelection(fInput, fAcceptableLocationsFilter);
93 	}
94 
getSelectedResources()95 	public IResource[] getSelectedResources() {
96 		List<Object> items= fResourceGroup.getAllCheckedListItems();
97 		return items.toArray(new IResource[items.size()]);
98 	}
99 
100 	@Override
configureShell(Shell newShell)101 	protected void configureShell(Shell newShell) {
102 		super.configureShell(newShell);
103 
104 		if (fTitle != null)
105 			newShell.setText(fTitle);
106 	}
107 
108 	@Override
createDialogArea(Composite parent)109 	protected Control createDialogArea(Composite parent) {
110 		Composite composite= (Composite) super.createDialogArea(parent);
111 		Label label= new Label(composite, SWT.LEFT);
112 		label.setText(fInstruction);
113 		label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
114 
115 		fResourceGroup= new SelectResourcesBlock(composite, ResourcesPlugin.getWorkspace().getRoot(), getResourceProvider(IResource.FOLDER | IResource.PROJECT), WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider(), getResourceProvider(IResource.FILE), WorkbenchLabelProvider.getDecoratingWorkbenchLabelProvider(), SWT.NONE, useHeightHint(parent));
116 		fResourceGroup.addCheckStateListener(event -> updateSelectionCount());
117 
118 		fCountIndication= new Label(composite, SWT.LEFT);
119 		fCountIndication.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
120 
121 		createSelectionButtonGroup(composite);
122 
123 		setInitialSelection();
124 		return composite;
125 	}
126 
useHeightHint(Composite parent)127 	private boolean useHeightHint(Composite parent) {
128 		int fontHeight= (parent.getFont().getFontData())[0].getHeight();
129 		int displayHeight= parent.getDisplay().getClientArea().height;
130 		return (displayHeight / fontHeight) > 50;
131 	}
132 
getResourceProvider(final int resourceType)133 	private ITreeContentProvider getResourceProvider(final int resourceType) {
134 		return new WorkbenchContentProvider() {
135 			@Override
136 			public Object[] getChildren(Object o) {
137 				if (o instanceof IWorkspaceRoot) {
138 					HashSet<IResource> projects= new HashSet<>();
139 					for (IResource f : fInput) {
140 						IResource project = f.getProject();
141 						if ((project.getType() & resourceType) > 0)
142 							projects.add(project);
143 					}
144 					return projects.toArray();
145 				}
146 
147 				if (o instanceof IContainer) {
148 					IResource[] members = null;
149 					try {
150 						members = ((IContainer) o).members();
151 					} catch (CoreException e) {
152 						//just return an empty set of children
153 						return new Object[0];
154 					}
155 
156 					//filter out the desired resource types
157 					ArrayList<IResource> results = new ArrayList<>();
158 					for (IResource member : members) {
159 						//And the test bits with the resource types to see if they are what we want
160 						if ((member.getType() & resourceType) > 0 && (resourceType != IResource.FILE || fAcceptableLocationsFilter == null || fAcceptableLocationsFilter.accept(member))) {
161 							results.add(member);
162 						}
163 					}
164 					return results.toArray();
165 				}
166 
167 				//input element case
168 				if (o instanceof ArrayList)
169 					return ((ArrayList<?>) o).toArray();
170 
171 				return new Object[0];
172 			}
173 		};
174 	}
175 
176 	protected Composite createSelectionButtonGroup(Composite parent) {
177 
178 		Font font= parent.getFont();
179 
180 		// top level group
181 		Composite buttonComposite= new Composite(parent, SWT.NONE);
182 		buttonComposite.setFont(parent.getFont());
183 
184 		buttonComposite.setLayout(GridLayoutFactory.fillDefaults().numColumns(0).equalWidth(true).create());
185 
186 		Button selectButton= createButton(buttonComposite, IDialogConstants.SELECT_ALL_ID, TextEditorMessages.SelectResourcesDialog_selectAll, false);
187 
188 		SelectionListener listener= new SelectionAdapter() {
189 			@Override
190 			public void widgetSelected(SelectionEvent e) {
191 				fResourceGroup.setAllSelections(true);
192 				updateSelectionCount();
193 			}
194 		};
195 		selectButton.addSelectionListener(listener);
196 		selectButton.setFont(font);
197 		setButtonLayoutData(selectButton);
198 
199 		Button deselectButton= createButton(buttonComposite, IDialogConstants.DESELECT_ALL_ID, TextEditorMessages.SelectResourcesDialog_deselectAll, false);
200 
201 		listener= new SelectionAdapter() {
202 			@Override
203 			public void widgetSelected(SelectionEvent e) {
204 				fResourceGroup.setAllSelections(false);
205 				updateSelectionCount();
206 			}
207 		};
208 		deselectButton.addSelectionListener(listener);
209 		deselectButton.setFont(font);
210 		setButtonLayoutData(deselectButton);
211 
212 		// types edit button
213 		Button selectTypesButton= createButton(buttonComposite, IDialogConstants.SELECT_TYPES_ID, TextEditorMessages.SelectResourcesDialog_filterSelection, false);
214 
215 		listener= new SelectionAdapter() {
216 			@Override
217 			public void widgetSelected(SelectionEvent e) {
218 				handleSelectFileTypes();
219 			}
220 		};
221 		selectTypesButton.addSelectionListener(listener);
222 		selectTypesButton.setFont(font);
223 		setButtonLayoutData(selectTypesButton);
224 
225 		return buttonComposite;
226 	}
227 
228 	protected void handleSelectFileTypes() {
229 		Object[] acceptedFileTypes= queryFileTypes();
230 		if (acceptedFileTypes != null) {
231 			fAcceptedFileTypes= Arrays.asList(acceptedFileTypes);
232 			filterSelection();
233 		}
234 	}
235 
236 	protected Object[] queryFileTypes() {
237 		TypeFilteringDialog dialog= new TypeFilteringDialog(getShell(), fAcceptedFileTypes);
238 		dialog.open();
239 		return dialog.getResult();
240 	}
241 
242 	private void filterSelection() {
243 
244 		final IFilter filter= this::hasAcceptedFileType;
245 
246 		List<Object> list= fResourceGroup.getAllWhiteCheckedItems();
247 		final IResource[] resources= list.toArray(new IResource[list.size()]);
248 
249 		Runnable runnable= () -> setSelection(resources, filter);
250 
251 		BusyIndicator.showWhile(getShell().getDisplay(), runnable);
252 	}
253 
254 	protected boolean hasAcceptedFileType(IResource resource) {
255 		if (fAcceptedFileTypes == null)
256 			return true;
257 
258 		String resourceName= resource.getName();
259 		int separatorIndex= resourceName.lastIndexOf("."); //$NON-NLS-1$
260 		if (separatorIndex == -1)
261 			return false;
262 
263 		String extension= resourceName.substring(separatorIndex + 1);
264 
265 		Iterator<Object> e= fAcceptedFileTypes.iterator();
266 		while (e.hasNext()) {
267 			if (extension.equalsIgnoreCase((String) e.next()))
268 				return true;
269 		}
270 
271 		return false;
272 	}
273 
274 	protected void setInitialSelection() {
275 		setSelection(fInput, fAcceptableLocationsFilter);
276 		selectAndReveal(fInput[0]);
277 	}
278 
279 	protected void setSelection(IResource[] input, IFilter filter) {
280 		Map<IContainer, List<Object>> selectionMap= new Hashtable<>();
281 		for (IResource resource : input) {
282 			if ((resource.getType() & IResource.FILE) > 0) {
283 				if (filter.accept(resource)) {
284 					List<Object> files= null;
285 					IContainer parent= resource.getParent();
286 					if (selectionMap.containsKey(parent))
287 						files= selectionMap.get(parent);
288 					else
289 						files= new ArrayList<>();
290 
291 					files.add(resource);
292 					selectionMap.put(parent, files);
293 				}
294 			} else
295 				setSelection(selectionMap, (IContainer) resource, filter);
296 		}
297 		fResourceGroup.updateSelections(selectionMap);
298 		updateSelectionCount();
299 	}
300 
301 	private void setSelection(Map<IContainer, List<Object>> selectionMap, IContainer parent, IFilter filter) {
302 		try {
303 
304 			IResource[] resources= parent.members();
305 			List<Object> selections= new ArrayList<>();
306 
307 			for (IResource resource : resources) {
308 				if ((resource.getType() & IResource.FILE) > 0) {
309 					if (filter.accept(resource))
310 						selections.add(resource);
311 				} else {
312 					setSelection(selectionMap, (IContainer) resource, filter);
313 				}
314 			}
315 
316 			if (!selections.isEmpty())
317 				selectionMap.put(parent, selections);
318 
319 		} catch (CoreException x) {
320 			//Just return if we can't get any info
321 			return;
322 		}
323 	}
324 
325 	private void selectAndReveal(IResource resource) {
326 		IContainer container= null;
327 		if ((IResource.FILE & resource.getType()) > 0)
328 			container= resource.getParent();
329 		else
330 			container= (IContainer) resource;
331 		fResourceGroup.selectAndReveal(container);
332 	}
333 
334 	private void updateSelectionCount() {
335 		List<Object> listItems= fResourceGroup.getAllCheckedListItems();
336 		int checkedFiles= listItems.size();
337 		StringBuilder buffer= new StringBuilder();
338 		switch (checkedFiles) {
339 			case 0:
340 				buffer.append(TextEditorMessages.SelectResourcesDialog_noFilesSelected);
341 				break;
342 			case 1:
343 				buffer.append(TextEditorMessages.SelectResourcesDialog_oneFileSelected);
344 				break;
345 			default:
346 				buffer.append(NLSUtility.format(TextEditorMessages.SelectResourcesDialog_nFilesSelected, Integer.valueOf(checkedFiles)));
347 		}
348 		fCountIndication.setText(buffer.toString());
349 
350 		Button okButton= getButton(IDialogConstants.OK_ID);
351 		if (okButton != null)
352 			okButton.setEnabled(checkedFiles > 0);
353 	}
354 }
355