RepositorySearchDialog: String Externalization
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositorySearchDialog.java
blob7820d32628e48b71b1fb074c0874e95dc4ab678e
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.osgi.util.NLS;
47 import org.eclipse.swt.SWT;
48 import org.eclipse.swt.events.ModifyEvent;
49 import org.eclipse.swt.events.ModifyListener;
50 import org.eclipse.swt.events.SelectionAdapter;
51 import org.eclipse.swt.events.SelectionEvent;
52 import org.eclipse.swt.graphics.Color;
53 import org.eclipse.swt.graphics.Image;
54 import org.eclipse.swt.layout.GridData;
55 import org.eclipse.swt.layout.GridLayout;
56 import org.eclipse.swt.widgets.Button;
57 import org.eclipse.swt.widgets.Composite;
58 import org.eclipse.swt.widgets.Control;
59 import org.eclipse.swt.widgets.DirectoryDialog;
60 import org.eclipse.swt.widgets.Display;
61 import org.eclipse.swt.widgets.Label;
62 import org.eclipse.swt.widgets.Shell;
63 import org.eclipse.swt.widgets.Text;
64 import org.eclipse.swt.widgets.TreeItem;
65 import org.eclipse.ui.dialogs.FilteredTree;
66 import org.eclipse.ui.dialogs.PatternFilter;
67 import org.osgi.service.prefs.BackingStoreException;
69 /**
70 * Searches for Git directories under a path that can be selected by the user
72 public class RepositorySearchDialog extends TitleAreaDialog {
74 private static final String PREF_DEEP_SEARCH = "RepositorySearchDialogDeepSearch"; //$NON-NLS-1$
76 private static final String PREF_PATH = "RepositorySearchDialogSearchPath"; //$NON-NLS-1$
78 private final Set<String> fExistingDirectories = new HashSet<String>();
80 private Set<String> fResult;
82 private FilteredTree fTree;
84 private TreeViewer fTreeViewer;
86 private Text dir;
88 private Button lookForNestedButton;
90 private Button fSelectAllButton;
92 private Button fDeselectAllButton;
94 private final ResourceManager fImageCache = new LocalResourceManager(
95 JFaceResources.getResources());
97 private final IEclipsePreferences prefs = new InstanceScope()
98 .getNode(Activator.getPluginId());
100 private final class ContentProvider implements ITreeContentProvider {
102 @SuppressWarnings("unchecked")
103 public Object[] getElements(Object inputElement) {
104 return ((Set<String>) inputElement).toArray();
107 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
108 // nothing
111 public void dispose() {
112 // nothing
115 public Object[] getChildren(Object parentElement) {
116 // nothing
117 return null;
120 public Object getParent(Object element) {
121 // nothing
122 return null;
125 public boolean hasChildren(Object element) {
126 // nothing
127 return false;
132 private final class RepositoryLabelProvider extends LabelProvider implements
133 IColorProvider {
135 @Override
136 public Image getImage(Object element) {
137 return fImageCache.createImage(UIIcons.REPOSITORY);
140 @Override
141 public String getText(Object element) {
142 return element.toString();
145 public Color getBackground(Object element) {
146 return null;
149 public Color getForeground(Object element) {
150 if (fExistingDirectories.contains(element))
151 return getShell().getDisplay().getSystemColor(SWT.COLOR_GRAY);
153 return null;
156 public void dispose() {
157 fImageCache.dispose();
163 * @param parentShell
164 * @param existingDirs
166 public RepositorySearchDialog(Shell parentShell,
167 Collection<String> existingDirs) {
168 super(parentShell);
169 this.fExistingDirectories.addAll(existingDirs);
170 setShellStyle(getShellStyle() | SWT.SHELL_TRIM);
171 setHelpAvailable(false);
174 @Override
175 public int open() {
176 // we start the search asynchronously so that the dialog is already open
177 // when the progress dialog is showing
178 Display.getDefault().asyncExec(new Runnable() {
179 public void run() {
180 doSearch();
183 return super.open();
188 * @return the directories
190 public Set<String> getDirectories() {
191 return fResult;
194 @Override
195 protected void configureShell(Shell newShell) {
196 super.configureShell(newShell);
197 newShell.setText(UIText.RepositorySearchDialog_searchRepositories);
198 setTitleImage(fImageCache.createImage(UIIcons.WIZBAN_IMPORT_REPO));
201 @Override
202 protected void okPressed() {
203 fResult = new HashSet<String>();
204 fResult.addAll(getCheckedItems());
205 super.okPressed();
208 @Override
209 protected Control createDialogArea(Composite parent) {
210 super.createDialogArea(parent);
211 setTitle(UIText.RepositorySearchDialog_searchRepositories);
212 setMessage(UIText.RepositorySearchDialog_searchRepositoriesMessage);
214 Composite main = new Composite(parent, SWT.NONE);
215 main.setLayout(new GridLayout(4, false));
216 main.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
218 Label dirLabel = new Label(main, SWT.NONE);
219 dirLabel.setText(UIText.RepositorySearchDialog_directory);
220 dir = new Text(main, SWT.BORDER);
221 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true,
222 false).span(2, 1).hint(300, SWT.DEFAULT).applyTo(dir);
224 String initialPath = prefs.get(PREF_PATH, ResourcesPlugin
225 .getWorkspace().getRoot().getLocation().toOSString());
227 dir.setText(initialPath);
229 Button browse = new Button(main, SWT.PUSH);
230 browse.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false,
231 1, 1));
232 browse.setText(UIText.RepositorySearchDialog_browse);
233 browse.addSelectionListener(new SelectionAdapter() {
235 @Override
236 public void widgetSelected(SelectionEvent e) {
237 DirectoryDialog dd = new DirectoryDialog(getShell());
238 dd.setFilterPath(dir.getText());
239 String directory = dd.open();
240 if (directory != null) {
241 dir.setText(directory);
242 prefs.put(PREF_PATH, directory);
243 try {
244 prefs.flush();
245 } catch (BackingStoreException e1) {
246 // ignore here
253 // TODO for 3.4 compatibility, we must use this constructor
254 fTree = new FilteredTree(main, SWT.CHECK | SWT.BORDER,
255 new PatternFilter());
256 fTreeViewer = fTree.getViewer();
257 fTreeViewer
258 .addSelectionChangedListener(new ISelectionChangedListener() {
260 public void selectionChanged(SelectionChangedEvent event) {
261 // this is used to update the OK button when the
262 // keyboard
263 // is used to toggle the check boxes
264 getButton(OK).setEnabled(hasCheckedItems());
268 GridDataFactory.fillDefaults().grab(true, true).span(4, 1).minSize(0,
269 300).applyTo(fTree);
270 fTree.setEnabled(false);
272 lookForNestedButton = new Button(main, SWT.CHECK);
273 lookForNestedButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER,
274 false, false, 2, 1));
275 lookForNestedButton.setSelection(prefs.getBoolean(PREF_DEEP_SEARCH,
276 false));
277 lookForNestedButton
278 .setText(UIText.RepositorySearchDialog_DeepSearch_button);
280 lookForNestedButton.addSelectionListener(new SelectionAdapter() {
282 @Override
283 public void widgetSelected(SelectionEvent e) {
284 prefs.putBoolean(PREF_DEEP_SEARCH, lookForNestedButton
285 .getSelection());
286 try {
287 prefs.flush();
288 } catch (BackingStoreException e1) {
289 // ignore
291 doSearch();
296 fSelectAllButton = new Button(main, SWT.NONE);
297 fSelectAllButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false,
298 false, 1, 1));
299 fSelectAllButton
300 .setText(UIText.RepositorySearchDialog_SelectAll_button);
301 fSelectAllButton.setEnabled(false);
302 fSelectAllButton.addSelectionListener(new SelectionAdapter() {
304 @Override
305 public void widgetSelected(SelectionEvent e) {
306 setAllSelected(true);
310 fDeselectAllButton = new Button(main, SWT.NONE);
311 fDeselectAllButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP,
312 false, false, 1, 1));
313 fDeselectAllButton
314 .setText(UIText.RepositorySearchDialog_DeselectAll_button);
315 fDeselectAllButton.setEnabled(false);
316 fDeselectAllButton.addSelectionListener(new SelectionAdapter() {
318 @Override
319 public void widgetSelected(SelectionEvent e) {
320 setAllSelected(false);
324 // TODO this isn't the most optimal way of handling this... ideally we
325 // should have some type of delay
326 // if we could use databinding an observeDelayedValue would totally work
327 // here
328 dir.addModifyListener(new ModifyListener() {
330 public void modifyText(ModifyEvent e) {
331 doSearch();
336 fTreeViewer.setContentProvider(new ContentProvider());
337 fTreeViewer.setLabelProvider(new RepositoryLabelProvider());
339 return main;
342 @Override
343 protected void createButtonsForButtonBar(Composite parent) {
344 super.createButtonsForButtonBar(parent);
345 getButton(OK).setEnabled(false);
348 private void findGitDirsRecursive(File root, Set<String> strings,
349 IProgressMonitor monitor, boolean lookForNestedRepositories) {
351 if (!root.exists() || !root.isDirectory()) {
352 return;
354 File[] children = root.listFiles();
355 // simply ignore null
356 if (children == null)
357 return;
359 for (File child : children) {
360 if (monitor.isCanceled()) {
361 return;
364 if (child.isDirectory()
365 && RepositoryCache.FileKey.isGitRepository(child)) {
366 try {
367 strings.add(child.getCanonicalPath());
368 } catch (IOException e) {
369 // ignore here
371 monitor
372 .setTaskName(NLS
373 .bind(
374 UIText.RepositorySearchDialog_RepositoriesFound_message,
375 new Integer(strings.size())));
376 if (!lookForNestedRepositories)
377 return;
378 } else if (child.isDirectory()) {
379 monitor.subTask(child.getPath());
380 findGitDirsRecursive(child, strings, monitor,
381 lookForNestedRepositories);
387 private HashSet<String> getCheckedItems() {
388 HashSet<String> ret = new HashSet<String>();
389 for (TreeItem item : fTreeViewer.getTree().getItems())
390 if (item.getChecked())
391 ret.add((String) item.getData());
393 return ret;
396 private boolean hasCheckedItems() {
397 for (TreeItem item : fTreeViewer.getTree().getItems())
398 if (item.getChecked())
399 return true;
401 return false;
404 private void setAllSelected(boolean selectionState) {
405 for (TreeItem item : fTreeViewer.getTree().getItems())
406 item.setChecked(selectionState);
407 getButton(OK).setEnabled(hasCheckedItems());
410 private void doSearch() {
412 // perform the search...
413 final Set<String> directories = new HashSet<String>();
414 final File file = new File(dir.getText());
415 final boolean lookForNested = lookForNestedButton.getSelection();
416 if (file.exists()) {
417 try {
418 prefs.put(PREF_PATH, file.getCanonicalPath());
419 try {
420 prefs.flush();
421 } catch (BackingStoreException e1) {
422 // ignore here
424 } catch (IOException e2) {
425 // ignore
428 IRunnableWithProgress action = new IRunnableWithProgress() {
430 public void run(IProgressMonitor monitor)
431 throws InvocationTargetException, InterruptedException {
433 try {
434 findGitDirsRecursive(file, directories, monitor,
435 lookForNested);
436 } catch (Exception ex) {
437 Activator.getDefault().getLog().log(
438 new Status(IStatus.ERROR, Activator
439 .getPluginId(), ex.getMessage(), ex));
443 try {
444 ProgressMonitorDialog pd = new ProgressMonitorDialog(getShell());
446 .getProgressMonitor()
447 .setTaskName(
448 UIText.RepositorySearchDialog_ScanningForRepositories_message);
449 pd.run(true, true, action);
451 } catch (InvocationTargetException e1) {
452 org.eclipse.egit.ui.Activator.handleError(
453 UIText.RepositorySearchDialog_errorOccurred, e1, true);
454 } catch (InterruptedException e1) {
455 // ignore
458 boolean foundNew = false;
459 int foundOld = 0;
461 TreeSet<String> validDirs = new TreeSet<String>();
463 for (String foundDir : directories) {
464 if (!fExistingDirectories.contains(foundDir)) {
465 validDirs.add(foundDir);
466 foundNew = true;
467 } else {
468 foundOld++;
472 if (foundOld > 0) {
473 String message = NLS
474 .bind(
475 UIText.RepositorySearchDialog_SomeDirectoriesHiddenMessage,
476 Integer.valueOf(foundOld));
477 setMessage(message, IMessageProvider.WARNING);
478 } else
479 setMessage(null);
480 fSelectAllButton.setEnabled(foundNew);
481 fDeselectAllButton.setEnabled(foundNew);
482 fTree.setEnabled(validDirs.size() > 0);
483 fTreeViewer.setInput(validDirs);