RepositorySearchDialog: border
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositorySearchDialog.java
blobdc3c4d882d6d38e885561d131c98ea6c26506a6c
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.UIText;
29 import org.eclipse.jface.dialogs.Dialog;
30 import org.eclipse.jface.dialogs.IDialogConstants;
31 import org.eclipse.jface.dialogs.MessageDialog;
32 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
33 import org.eclipse.jface.layout.GridDataFactory;
34 import org.eclipse.jface.operation.IRunnableWithProgress;
35 import org.eclipse.jface.viewers.BaseLabelProvider;
36 import org.eclipse.jface.viewers.CheckStateChangedEvent;
37 import org.eclipse.jface.viewers.CheckboxTableViewer;
38 import org.eclipse.jface.viewers.ICheckStateListener;
39 import org.eclipse.jface.viewers.IColorProvider;
40 import org.eclipse.jface.viewers.IStructuredContentProvider;
41 import org.eclipse.jface.viewers.ITableLabelProvider;
42 import org.eclipse.jface.viewers.Viewer;
43 import org.eclipse.jgit.lib.RepositoryCache;
44 import org.eclipse.osgi.util.NLS;
45 import org.eclipse.swt.SWT;
46 import org.eclipse.swt.events.SelectionAdapter;
47 import org.eclipse.swt.events.SelectionEvent;
48 import org.eclipse.swt.graphics.Color;
49 import org.eclipse.swt.graphics.Image;
50 import org.eclipse.swt.layout.GridData;
51 import org.eclipse.swt.layout.GridLayout;
52 import org.eclipse.swt.widgets.Button;
53 import org.eclipse.swt.widgets.Composite;
54 import org.eclipse.swt.widgets.Control;
55 import org.eclipse.swt.widgets.DirectoryDialog;
56 import org.eclipse.swt.widgets.Label;
57 import org.eclipse.swt.widgets.Shell;
58 import org.eclipse.swt.widgets.Table;
59 import org.eclipse.swt.widgets.Text;
60 import org.osgi.service.prefs.BackingStoreException;
62 /**
63 * Searches for Git directories under a path that can be selected by the user
65 public class RepositorySearchDialog extends Dialog {
67 private static final String PREF_DEEP_SEARCH = "RepositorySearchDialogDeepSearch"; //$NON-NLS-1$
69 private static final String PREF_PATH = "RepositorySearchDialogSearchPath"; //$NON-NLS-1$
71 private final Set<String> existingRepositoryDirs = new HashSet<String>();
73 private Set<String> result;
75 CheckboxTableViewer tv;
77 private Button btnToggleSelect;
79 private Table tab;
81 private final class ContentProvider implements IStructuredContentProvider {
83 @SuppressWarnings("unchecked")
84 public Object[] getElements(Object inputElement) {
85 return ((Set<String>) inputElement).toArray();
88 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
89 // nothing
92 public void dispose() {
93 // nothing
98 private final class LabelProvider extends BaseLabelProvider implements
99 ITableLabelProvider, IColorProvider {
101 public Image getColumnImage(Object element, int columnIndex) {
102 return null;
105 public String getColumnText(Object element, int columnIndex) {
106 return element.toString();
109 public Color getBackground(Object element) {
110 return null;
113 public Color getForeground(Object element) {
114 if (existingRepositoryDirs.contains(element))
115 return getShell().getDisplay().getSystemColor(SWT.COLOR_GRAY);
117 return null;
123 * @param parentShell
124 * @param existingDirs
126 protected RepositorySearchDialog(Shell parentShell,
127 Collection<String> existingDirs) {
128 super(parentShell);
129 this.existingRepositoryDirs.addAll(existingDirs);
130 setShellStyle(getShellStyle() | SWT.SHELL_TRIM);
135 * @return the directories
137 public Set<String> getDirectories() {
138 return result;
141 @Override
142 protected void configureShell(Shell newShell) {
143 super.configureShell(newShell);
144 newShell
145 .setText(UIText.RepositorySearchDialog_SearchRepositoriesHeader);
148 @Override
149 protected void okPressed() {
150 result = new HashSet<String>();
151 Object[] checked = tv.getCheckedElements();
152 for (Object o : checked) {
153 result.add((String) o);
155 super.okPressed();
158 @Override
159 protected Control createDialogArea(Composite parent) {
161 final IEclipsePreferences prefs = new InstanceScope().getNode(Activator
162 .getPluginId());
164 Composite main = new Composite(parent, SWT.NONE);
165 main.setLayout(new GridLayout(4, false));
166 main.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
168 Label dirLabel = new Label(main, SWT.NONE);
169 dirLabel.setText(UIText.RepositorySearchDialog_DirectoryLabel);
170 final Text dir = new Text(main, SWT.BORDER);
171 GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).span(2, 1).hint(300,
172 SWT.DEFAULT).applyTo(dir);
174 String initialPath = prefs.get(PREF_PATH, ResourcesPlugin
175 .getWorkspace().getRoot().getLocation().toOSString());
177 dir.setText(initialPath);
179 Button browse = new Button(main, SWT.PUSH);
180 browse.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false,
181 1, 1));
182 browse.setText(UIText.RepositorySearchDialog_BrowseButton);
183 browse.addSelectionListener(new SelectionAdapter() {
185 @Override
186 public void widgetSelected(SelectionEvent e) {
187 DirectoryDialog dd = new DirectoryDialog(getShell());
188 dd.setFilterPath(dir.getText());
189 String directory = dd.open();
190 if (directory != null) {
191 dir.setText(directory);
192 prefs.put(PREF_PATH, directory);
193 try {
194 prefs.flush();
195 } catch (BackingStoreException e1) {
196 // ignore here
203 // we fill the room under the "Directory" label
204 new Label(main, SWT.NONE);
206 final Button btnLookForNested = new Button(main, SWT.CHECK);
207 btnLookForNested.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER,
208 false, false, 2, 1));
209 btnLookForNested
210 .setSelection(prefs.getBoolean(PREF_DEEP_SEARCH, false));
211 btnLookForNested
212 .setText(UIText.RepositorySearchDialog_DeepSearch_button);
214 btnLookForNested.addSelectionListener(new SelectionAdapter() {
216 @Override
217 public void widgetSelected(SelectionEvent e) {
218 prefs.putBoolean(PREF_DEEP_SEARCH, btnLookForNested
219 .getSelection());
220 try {
221 prefs.flush();
222 } catch (BackingStoreException e1) {
223 // ignore
229 Button search = new Button(main, SWT.PUSH);
230 search.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false,
231 1, 1));
232 search.setText(UIText.RepositorySearchDialog_SearchButton);
234 tv = CheckboxTableViewer.newCheckList(main, SWT.BORDER);
235 tab = tv.getTable();
236 tab.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
237 tab.setEnabled(false);
239 btnToggleSelect = new Button(main, SWT.NONE);
240 btnToggleSelect.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false,
241 false, 1, 1));
242 btnToggleSelect
243 .setText(UIText.RepositorySearchDialog_ToggleSelection_button);
244 btnToggleSelect.setEnabled(false);
245 btnToggleSelect.addSelectionListener(new SelectionAdapter() {
247 @Override
248 public void widgetSelected(SelectionEvent e) {
250 for (int i = 0; i < tab.getItemCount(); i++) {
251 if (!existingRepositoryDirs.contains(tv.getElementAt(i)))
252 tv.setChecked(tv.getElementAt(i), !tv.getChecked(tv
253 .getElementAt(i)));
255 getButton(IDialogConstants.OK_ID).setEnabled(
256 tv.getCheckedElements().length > 0);
260 tv.addCheckStateListener(new ICheckStateListener() {
262 public void checkStateChanged(CheckStateChangedEvent event) {
263 if (existingRepositoryDirs.contains(event.getElement()))
264 event.getCheckable().setChecked(event.getElement(), false);
265 getButton(IDialogConstants.OK_ID).setEnabled(
266 tv.getCheckedElements().length > 0);
270 tv.setContentProvider(new ContentProvider());
271 tv.setLabelProvider(new LabelProvider());
273 search.addSelectionListener(new SelectionAdapter() {
275 @Override
276 public void widgetSelected(SelectionEvent e) {
277 final TreeSet<String> directories = new TreeSet<String>();
278 final File file = new File(dir.getText());
279 final boolean lookForNested = btnLookForNested.getSelection();
280 if (file.exists()) {
281 try {
282 prefs.put(PREF_PATH, file.getCanonicalPath());
283 try {
284 prefs.flush();
285 } catch (BackingStoreException e1) {
286 // ignore here
288 } catch (IOException e2) {
289 // ignore
292 IRunnableWithProgress action = new IRunnableWithProgress() {
294 public void run(IProgressMonitor monitor)
295 throws InvocationTargetException,
296 InterruptedException {
298 try {
299 findGitDirsRecursive(file, directories,
300 monitor, lookForNested);
301 } catch (Exception ex) {
302 Activator.getDefault().getLog().log(
303 new Status(IStatus.ERROR, Activator
304 .getPluginId(),
305 ex.getMessage(), ex));
309 try {
310 ProgressMonitorDialog pd = new ProgressMonitorDialog(
311 getShell());
313 .getProgressMonitor()
314 .setTaskName(
315 UIText.RepositorySearchDialog_ScanningForRepositories_message);
316 pd.run(true, true, action);
318 } catch (InvocationTargetException e1) {
319 MessageDialog.openError(getShell(),
320 UIText.RepositorySearchDialog_ErrorHeader, e1
321 .getCause().getMessage());
322 } catch (InterruptedException e1) {
323 // ignore
326 boolean foundNew = false;
328 for (String foundDir : directories) {
329 if (!existingRepositoryDirs.contains(foundDir)) {
330 foundNew = true;
331 break;
335 btnToggleSelect.setEnabled(foundNew);
336 tab.setEnabled(directories.size() > 0);
337 tv.setInput(directories);
343 return main;
346 @Override
347 protected Control createButtonBar(Composite parent) {
348 // disable the OK button until the user selects something
349 Control bar = super.createButtonBar(parent);
350 getButton(IDialogConstants.OK_ID).setEnabled(false);
351 return bar;
354 private void findGitDirsRecursive(File root, TreeSet<String> strings,
355 IProgressMonitor monitor, boolean lookForNestedRepositories) {
357 if (!root.exists() || !root.isDirectory()) {
358 return;
360 File[] children = root.listFiles();
361 // simply ignore null
362 if (children == null)
363 return;
365 for (File child : children) {
366 if (monitor.isCanceled()) {
367 return;
370 if (child.isDirectory()
371 && RepositoryCache.FileKey.isGitRepository(child)) {
372 try {
373 strings.add(child.getCanonicalPath());
374 } catch (IOException e) {
375 // ignore here
377 monitor
378 .setTaskName(NLS
379 .bind(
380 UIText.RepositorySearchDialog_RepositoriesFound_message,
381 new Integer(strings.size())));
382 if (!lookForNestedRepositories)
383 return;
384 } else if (child.isDirectory()) {
385 monitor.subTask(child.getPath());
386 findGitDirsRecursive(child, strings, monitor,
387 lookForNestedRepositories);