Fix build
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositorySearchDialog.java
blob2d1e26304af81deab1755b5a57bb7d5e049ee781
1 /*******************************************************************************
2 * Copyright (c) 2010 SAP AG.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
8 * Contributors:
9 * Mathias Kinzler (SAP AG) - initial implementation
10 *******************************************************************************/
11 package org.eclipse.egit.ui.internal.repository;
13 import java.io.File;
14 import java.io.IOException;
15 import java.lang.reflect.InvocationTargetException;
16 import java.util.Collection;
17 import java.util.HashSet;
18 import java.util.Set;
19 import java.util.TreeSet;
21 import org.eclipse.core.resources.ResourcesPlugin;
22 import org.eclipse.core.runtime.IProgressMonitor;
23 import org.eclipse.core.runtime.IStatus;
24 import org.eclipse.core.runtime.Status;
25 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
26 import org.eclipse.core.runtime.preferences.InstanceScope;
27 import org.eclipse.egit.core.Activator;
28 import org.eclipse.egit.ui.UIIcons;
29 import org.eclipse.egit.ui.UIText;
30 import org.eclipse.jface.dialogs.IMessageProvider;
31 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
32 import org.eclipse.jface.dialogs.TitleAreaDialog;
33 import org.eclipse.jface.layout.GridDataFactory;
34 import org.eclipse.jface.operation.IRunnableWithProgress;
35 import org.eclipse.jface.resource.JFaceResources;
36 import org.eclipse.jface.resource.LocalResourceManager;
37 import org.eclipse.jface.resource.ResourceManager;
38 import org.eclipse.jface.viewers.IColorProvider;
39 import org.eclipse.jface.viewers.ISelectionChangedListener;
40 import org.eclipse.jface.viewers.ITreeContentProvider;
41 import org.eclipse.jface.viewers.LabelProvider;
42 import org.eclipse.jface.viewers.SelectionChangedEvent;
43 import org.eclipse.jface.viewers.TreeViewer;
44 import org.eclipse.jface.viewers.Viewer;
45 import org.eclipse.jgit.lib.RepositoryCache;
46 import org.eclipse.jgit.util.FS;
47 import org.eclipse.osgi.util.NLS;
48 import org.eclipse.swt.SWT;
49 import org.eclipse.swt.events.ModifyEvent;
50 import org.eclipse.swt.events.ModifyListener;
51 import org.eclipse.swt.events.SelectionAdapter;
52 import org.eclipse.swt.events.SelectionEvent;
53 import org.eclipse.swt.graphics.Color;
54 import org.eclipse.swt.graphics.Image;
55 import org.eclipse.swt.layout.GridData;
56 import org.eclipse.swt.layout.GridLayout;
57 import org.eclipse.swt.widgets.Button;
58 import org.eclipse.swt.widgets.Composite;
59 import org.eclipse.swt.widgets.Control;
60 import org.eclipse.swt.widgets.DirectoryDialog;
61 import org.eclipse.swt.widgets.Display;
62 import org.eclipse.swt.widgets.Label;
63 import org.eclipse.swt.widgets.Shell;
64 import org.eclipse.swt.widgets.Text;
65 import org.eclipse.swt.widgets.TreeItem;
66 import org.eclipse.ui.dialogs.FilteredTree;
67 import org.eclipse.ui.dialogs.PatternFilter;
68 import org.osgi.service.prefs.BackingStoreException;
70 /**
71 * Searches for Git directories under a path that can be selected by the user
73 public class RepositorySearchDialog extends TitleAreaDialog {
75 private static final String PREF_DEEP_SEARCH = "RepositorySearchDialogDeepSearch"; //$NON-NLS-1$
77 private static final String PREF_PATH = "RepositorySearchDialogSearchPath"; //$NON-NLS-1$
79 private final Set<String> fExistingDirectories = new HashSet<String>();
81 private Set<String> fResult;
83 private FilteredTree fTree;
85 private TreeViewer fTreeViewer;
87 private Text dir;
89 private Button lookForNestedButton;
91 private Button fSelectAllButton;
93 private Button fDeselectAllButton;
95 private final ResourceManager fImageCache = new LocalResourceManager(
96 JFaceResources.getResources());
98 private final IEclipsePreferences prefs = new InstanceScope()
99 .getNode(Activator.getPluginId());
101 private final class ContentProvider implements ITreeContentProvider {
103 @SuppressWarnings("unchecked")
104 public Object[] getElements(Object inputElement) {
105 return ((Set<String>) inputElement).toArray();
108 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
109 // nothing
112 public void dispose() {
113 // nothing
116 public Object[] getChildren(Object parentElement) {
117 // nothing
118 return null;
121 public Object getParent(Object element) {
122 // nothing
123 return null;
126 public boolean hasChildren(Object element) {
127 // nothing
128 return false;
133 private final class RepositoryLabelProvider extends LabelProvider implements
134 IColorProvider {
136 @Override
137 public Image getImage(Object element) {
138 return fImageCache.createImage(UIIcons.REPOSITORY);
141 @Override
142 public String getText(Object element) {
143 return element.toString();
146 public Color getBackground(Object element) {
147 return null;
150 public Color getForeground(Object element) {
151 if (fExistingDirectories.contains(element))
152 return getShell().getDisplay().getSystemColor(SWT.COLOR_GRAY);
154 return null;
157 public void dispose() {
158 fImageCache.dispose();
164 * @param parentShell
165 * @param existingDirs
167 public RepositorySearchDialog(Shell parentShell,
168 Collection<String> existingDirs) {
169 super(parentShell);
170 this.fExistingDirectories.addAll(existingDirs);
171 setShellStyle(getShellStyle() | SWT.SHELL_TRIM);
172 setHelpAvailable(false);
175 @Override
176 public int open() {
177 // we start the search asynchronously so that the dialog is already open
178 // when the progress dialog is showing
179 Display.getDefault().asyncExec(new Runnable() {
180 public void run() {
181 doSearch();
184 return super.open();
189 * @return the directories
191 public Set<String> getDirectories() {
192 return fResult;
195 @Override
196 protected void configureShell(Shell newShell) {
197 super.configureShell(newShell);
198 newShell.setText(UIText.RepositorySearchDialog_searchRepositories);
199 setTitleImage(fImageCache.createImage(UIIcons.WIZBAN_IMPORT_REPO));
202 @Override
203 protected void okPressed() {
204 fResult = new HashSet<String>();
205 fResult.addAll(getCheckedItems());
206 super.okPressed();
209 @Override
210 protected Control createDialogArea(Composite parent) {
211 super.createDialogArea(parent);
212 setTitle(UIText.RepositorySearchDialog_searchRepositories);
213 setMessage(UIText.RepositorySearchDialog_searchRepositoriesMessage);
215 Composite main = new Composite(parent, SWT.NONE);
216 main.setLayout(new GridLayout(4, false));
217 main.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
219 Label dirLabel = new Label(main, SWT.NONE);
220 dirLabel.setText(UIText.RepositorySearchDialog_directory);
221 dir = new Text(main, SWT.BORDER);
222 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true,
223 false).span(2, 1).hint(300, SWT.DEFAULT).applyTo(dir);
225 String initialPath = prefs.get(PREF_PATH, ResourcesPlugin
226 .getWorkspace().getRoot().getLocation().toOSString());
228 dir.setText(initialPath);
230 Button browse = new Button(main, SWT.PUSH);
231 browse.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false,
232 1, 1));
233 browse.setText(UIText.RepositorySearchDialog_browse);
234 browse.addSelectionListener(new SelectionAdapter() {
236 @Override
237 public void widgetSelected(SelectionEvent e) {
238 DirectoryDialog dd = new DirectoryDialog(getShell());
239 dd.setFilterPath(dir.getText());
240 String directory = dd.open();
241 if (directory != null) {
242 dir.setText(directory);
243 prefs.put(PREF_PATH, directory);
244 try {
245 prefs.flush();
246 } catch (BackingStoreException e1) {
247 // ignore here
254 // TODO for 3.4 compatibility, we must use this constructor
255 fTree = new FilteredTree(main, SWT.CHECK | SWT.BORDER,
256 new PatternFilter());
257 fTreeViewer = fTree.getViewer();
258 fTreeViewer
259 .addSelectionChangedListener(new ISelectionChangedListener() {
261 public void selectionChanged(SelectionChangedEvent event) {
262 // this is used to update the OK button when the
263 // keyboard
264 // is used to toggle the check boxes
265 getButton(OK).setEnabled(hasCheckedItems());
269 GridDataFactory.fillDefaults().grab(true, true).span(4, 1).minSize(0,
270 300).applyTo(fTree);
271 fTree.setEnabled(false);
273 lookForNestedButton = new Button(main, SWT.CHECK);
274 lookForNestedButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER,
275 false, false, 2, 1));
276 lookForNestedButton.setSelection(prefs.getBoolean(PREF_DEEP_SEARCH,
277 false));
278 lookForNestedButton
279 .setText(UIText.RepositorySearchDialog_DeepSearch_button);
281 lookForNestedButton.addSelectionListener(new SelectionAdapter() {
283 @Override
284 public void widgetSelected(SelectionEvent e) {
285 prefs.putBoolean(PREF_DEEP_SEARCH, lookForNestedButton
286 .getSelection());
287 try {
288 prefs.flush();
289 } catch (BackingStoreException e1) {
290 // ignore
292 doSearch();
297 fSelectAllButton = new Button(main, SWT.NONE);
298 fSelectAllButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false,
299 false, 1, 1));
300 fSelectAllButton
301 .setText(UIText.RepositorySearchDialog_SelectAll_button);
302 fSelectAllButton.setEnabled(false);
303 fSelectAllButton.addSelectionListener(new SelectionAdapter() {
305 @Override
306 public void widgetSelected(SelectionEvent e) {
307 setAllSelected(true);
311 fDeselectAllButton = new Button(main, SWT.NONE);
312 fDeselectAllButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP,
313 false, false, 1, 1));
314 fDeselectAllButton
315 .setText(UIText.RepositorySearchDialog_DeselectAll_button);
316 fDeselectAllButton.setEnabled(false);
317 fDeselectAllButton.addSelectionListener(new SelectionAdapter() {
319 @Override
320 public void widgetSelected(SelectionEvent e) {
321 setAllSelected(false);
325 // TODO this isn't the most optimal way of handling this... ideally we
326 // should have some type of delay
327 // if we could use databinding an observeDelayedValue would totally work
328 // here
329 dir.addModifyListener(new ModifyListener() {
331 public void modifyText(ModifyEvent e) {
332 doSearch();
337 fTreeViewer.setContentProvider(new ContentProvider());
338 fTreeViewer.setLabelProvider(new RepositoryLabelProvider());
340 return main;
343 @Override
344 protected void createButtonsForButtonBar(Composite parent) {
345 super.createButtonsForButtonBar(parent);
346 getButton(OK).setEnabled(false);
349 private void findGitDirsRecursive(File root, Set<String> strings,
350 IProgressMonitor monitor, boolean lookForNestedRepositories) {
352 if (!root.exists() || !root.isDirectory()) {
353 return;
355 File[] children = root.listFiles();
356 // simply ignore null
357 if (children == null)
358 return;
360 for (File child : children) {
361 if (monitor.isCanceled()) {
362 return;
365 if (child.isDirectory()
366 && RepositoryCache.FileKey.isGitRepository(child, FS.DETECTED)) {
367 try {
368 strings.add(child.getCanonicalPath());
369 } catch (IOException e) {
370 // ignore here
372 monitor
373 .setTaskName(NLS
374 .bind(
375 UIText.RepositorySearchDialog_RepositoriesFound_message,
376 new Integer(strings.size())));
377 if (!lookForNestedRepositories)
378 return;
379 } else if (child.isDirectory()) {
380 monitor.subTask(child.getPath());
381 findGitDirsRecursive(child, strings, monitor,
382 lookForNestedRepositories);
388 private HashSet<String> getCheckedItems() {
389 HashSet<String> ret = new HashSet<String>();
390 for (TreeItem item : fTreeViewer.getTree().getItems())
391 if (item.getChecked())
392 ret.add((String) item.getData());
394 return ret;
397 private boolean hasCheckedItems() {
398 for (TreeItem item : fTreeViewer.getTree().getItems())
399 if (item.getChecked())
400 return true;
402 return false;
405 private void setAllSelected(boolean selectionState) {
406 for (TreeItem item : fTreeViewer.getTree().getItems())
407 item.setChecked(selectionState);
408 getButton(OK).setEnabled(hasCheckedItems());
411 private void doSearch() {
413 // perform the search...
414 final Set<String> directories = new HashSet<String>();
415 final File file = new File(dir.getText());
416 final boolean lookForNested = lookForNestedButton.getSelection();
417 if (file.exists()) {
418 try {
419 prefs.put(PREF_PATH, file.getCanonicalPath());
420 try {
421 prefs.flush();
422 } catch (BackingStoreException e1) {
423 // ignore here
425 } catch (IOException e2) {
426 // ignore
429 IRunnableWithProgress action = new IRunnableWithProgress() {
431 public void run(IProgressMonitor monitor)
432 throws InvocationTargetException, InterruptedException {
434 try {
435 findGitDirsRecursive(file, directories, monitor,
436 lookForNested);
437 } catch (Exception ex) {
438 Activator.getDefault().getLog().log(
439 new Status(IStatus.ERROR, Activator
440 .getPluginId(), ex.getMessage(), ex));
444 try {
445 ProgressMonitorDialog pd = new ProgressMonitorDialog(getShell());
447 .getProgressMonitor()
448 .setTaskName(
449 UIText.RepositorySearchDialog_ScanningForRepositories_message);
450 pd.run(true, true, action);
452 } catch (InvocationTargetException e1) {
453 org.eclipse.egit.ui.Activator.handleError(
454 UIText.RepositorySearchDialog_errorOccurred, e1, true);
455 } catch (InterruptedException e1) {
456 // ignore
459 boolean foundNew = false;
460 int foundOld = 0;
462 TreeSet<String> validDirs = new TreeSet<String>();
464 for (String foundDir : directories) {
465 if (!fExistingDirectories.contains(foundDir)) {
466 validDirs.add(foundDir);
467 foundNew = true;
468 } else {
469 foundOld++;
473 if (foundOld > 0) {
474 String message = NLS
475 .bind(
476 UIText.RepositorySearchDialog_SomeDirectoriesHiddenMessage,
477 Integer.valueOf(foundOld));
478 setMessage(message, IMessageProvider.INFORMATION);
479 } else
480 setMessage(null);
481 fSelectAllButton.setEnabled(foundNew);
482 fDeselectAllButton.setEnabled(foundNew);
483 fTree.setEnabled(validDirs.size() > 0);
484 fTreeViewer.setInput(validDirs);