RepositoriesView: use the GitImportProject wizard
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositoriesView.java
blobed3fc59e40b359c0ccf38d1c42e6eaba7f63e732
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.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Set;
21 import java.util.StringTokenizer;
22 import java.util.TreeSet;
24 import org.eclipse.core.filesystem.EFS;
25 import org.eclipse.core.filesystem.IFileStore;
26 import org.eclipse.core.resources.IProject;
27 import org.eclipse.core.resources.IProjectDescription;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.resources.IWorkspace;
30 import org.eclipse.core.resources.IWorkspaceRunnable;
31 import org.eclipse.core.resources.ResourcesPlugin;
32 import org.eclipse.core.runtime.CoreException;
33 import org.eclipse.core.runtime.IAdaptable;
34 import org.eclipse.core.runtime.IPath;
35 import org.eclipse.core.runtime.IProgressMonitor;
36 import org.eclipse.core.runtime.IStatus;
37 import org.eclipse.core.runtime.NullProgressMonitor;
38 import org.eclipse.core.runtime.Path;
39 import org.eclipse.core.runtime.Status;
40 import org.eclipse.core.runtime.SubProgressMonitor;
41 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
42 import org.eclipse.core.runtime.jobs.Job;
43 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
44 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
45 import org.eclipse.core.runtime.preferences.InstanceScope;
46 import org.eclipse.egit.core.op.BranchOperation;
47 import org.eclipse.egit.core.op.ConnectProviderOperation;
48 import org.eclipse.egit.core.project.RepositoryMapping;
49 import org.eclipse.egit.ui.Activator;
50 import org.eclipse.egit.ui.UIIcons;
51 import org.eclipse.egit.ui.UIText;
52 import org.eclipse.egit.ui.internal.clone.GitCloneWizard;
53 import org.eclipse.egit.ui.internal.clone.GitImportProjectsWizard;
54 import org.eclipse.egit.ui.internal.repository.RepositoryTreeNode.RepositoryTreeNodeType;
55 import org.eclipse.jface.action.Action;
56 import org.eclipse.jface.action.IAction;
57 import org.eclipse.jface.dialogs.MessageDialog;
58 import org.eclipse.jface.viewers.ISelection;
59 import org.eclipse.jface.viewers.ISelectionChangedListener;
60 import org.eclipse.jface.viewers.ISelectionProvider;
61 import org.eclipse.jface.viewers.IStructuredSelection;
62 import org.eclipse.jface.viewers.SelectionChangedEvent;
63 import org.eclipse.jface.viewers.StructuredSelection;
64 import org.eclipse.jface.viewers.TreeViewer;
65 import org.eclipse.jface.window.Window;
66 import org.eclipse.jface.wizard.Wizard;
67 import org.eclipse.jface.wizard.WizardDialog;
68 import org.eclipse.jgit.lib.Ref;
69 import org.eclipse.jgit.lib.Repository;
70 import org.eclipse.jgit.lib.RepositoryConfig;
71 import org.eclipse.osgi.util.NLS;
72 import org.eclipse.swt.SWT;
73 import org.eclipse.swt.events.MenuDetectEvent;
74 import org.eclipse.swt.events.MenuDetectListener;
75 import org.eclipse.swt.events.SelectionAdapter;
76 import org.eclipse.swt.events.SelectionEvent;
77 import org.eclipse.swt.graphics.Point;
78 import org.eclipse.swt.widgets.Composite;
79 import org.eclipse.swt.widgets.Display;
80 import org.eclipse.swt.widgets.Menu;
81 import org.eclipse.swt.widgets.MenuItem;
82 import org.eclipse.swt.widgets.TreeItem;
83 import org.eclipse.ui.IEditorInput;
84 import org.eclipse.ui.IEditorPart;
85 import org.eclipse.ui.IFileEditorInput;
86 import org.eclipse.ui.IPageLayout;
87 import org.eclipse.ui.ISelectionListener;
88 import org.eclipse.ui.ISelectionService;
89 import org.eclipse.ui.IViewPart;
90 import org.eclipse.ui.IWorkbenchPart;
91 import org.eclipse.ui.PartInitException;
92 import org.eclipse.ui.PlatformUI;
93 import org.eclipse.ui.editors.text.EditorsUI;
94 import org.eclipse.ui.ide.FileStoreEditorInput;
95 import org.eclipse.ui.ide.IDE;
96 import org.eclipse.ui.part.ViewPart;
97 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
98 import org.eclipse.ui.views.properties.IPropertySheetPage;
99 import org.eclipse.ui.views.properties.PropertySheet;
100 import org.eclipse.ui.views.properties.PropertySheetPage;
101 import org.osgi.service.prefs.BackingStoreException;
105 * The Git Repositories view.
106 * <p>
107 * This keeps track of a bunch of local directory names each of which represent
108 * a Git Repository. This list is stored in some Preferences object and used to
109 * build the tree in the view.
110 * <p>
111 * Implements {@link ISelectionProvider} in order to integrate with the
112 * Properties view.
113 * <p>
114 * TODO
115 * <li>Clarification whether to show projects, perhaps configurable switch</li>
118 public class RepositoriesView extends ViewPart implements ISelectionProvider {
120 /** The view ID */
121 public static final String VIEW_ID = "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
123 // TODO central constants? RemoteConfig ones are private
124 static final String REMOTE = "remote"; //$NON-NLS-1$
126 static final String URL = "url"; //$NON-NLS-1$
128 static final String PUSHURL = "pushurl"; //$NON-NLS-1$
130 static final String FETCH = "fetch"; //$NON-NLS-1$
132 static final String PUSH = "push"; //$NON-NLS-1$
134 private static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
136 private static final String PREFS_SYNCED = "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
138 private final List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
140 private ISelection currentSelection = new StructuredSelection();
142 private Job scheduledJob;
144 private TreeViewer tv;
146 private IAction importAction;
148 private IAction addAction;
150 private IAction refreshAction;
152 private IAction linkWithSelectionAction;
154 private static List<String> getDirs() {
155 List<String> resultStrings = new ArrayList<String>();
156 String dirs = getPrefs().get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
157 if (dirs != null && dirs.length() > 0) {
158 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
159 while (tok.hasMoreTokens()) {
160 String dirName = tok.nextToken();
161 File testFile = new File(dirName);
162 if (testFile.exists()) {
163 resultStrings.add(dirName);
167 Collections.sort(resultStrings);
168 return resultStrings;
171 private static void removeDir(File file) {
173 String dir;
174 try {
175 dir = file.getCanonicalPath();
176 } catch (IOException e1) {
177 dir = file.getAbsolutePath();
180 IEclipsePreferences prefs = getPrefs();
182 TreeSet<String> resultStrings = new TreeSet<String>();
183 String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
184 if (dirs != null && dirs.length() > 0) {
185 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
186 while (tok.hasMoreTokens()) {
187 String dirName = tok.nextToken();
188 File testFile = new File(dirName);
189 if (testFile.exists()) {
190 try {
191 resultStrings.add(testFile.getCanonicalPath());
192 } catch (IOException e) {
193 resultStrings.add(testFile.getAbsolutePath());
199 if (resultStrings.remove(dir)) {
200 StringBuilder sb = new StringBuilder();
201 for (String gitDirString : resultStrings) {
202 sb.append(gitDirString);
203 sb.append(File.pathSeparatorChar);
206 prefs.put(PREFS_DIRECTORIES, sb.toString());
207 try {
208 prefs.flush();
209 } catch (BackingStoreException e) {
210 IStatus error = new Status(IStatus.ERROR, Activator
211 .getPluginId(), e.getMessage(), e);
212 Activator.getDefault().getLog().log(error);
218 @Override
219 public Object getAdapter(Class adapter) {
220 // integrate with Properties view
221 if (adapter == IPropertySheetPage.class) {
222 PropertySheetPage page = new PropertySheetPage();
223 page
224 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
225 page));
226 return page;
229 return super.getAdapter(adapter);
232 @Override
233 public void createPartControl(Composite parent) {
234 tv = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
235 tv.setContentProvider(new RepositoriesViewContentProvider());
236 // the label provider registers itself
237 new RepositoriesViewLabelProvider(tv);
239 getSite().setSelectionProvider(this);
241 tv.addSelectionChangedListener(new ISelectionChangedListener() {
243 public void selectionChanged(SelectionChangedEvent event) {
245 IStructuredSelection ssel = (IStructuredSelection) event
246 .getSelection();
247 if (ssel.size() == 1) {
248 setSelection(new StructuredSelection(ssel.getFirstElement()));
249 } else {
250 setSelection(new StructuredSelection());
256 addContextMenu();
258 addActionsToToolbar();
260 scheduleRefresh();
262 ISelectionService srv = (ISelectionService) getSite().getService(
263 ISelectionService.class);
264 srv.addPostSelectionListener(new ISelectionListener() {
266 public void selectionChanged(IWorkbenchPart part,
267 ISelection selection) {
269 // if the "link with selection" toggle is off, we're done
270 if (linkWithSelectionAction == null
271 || !linkWithSelectionAction.isChecked())
272 return;
274 // this may happen if we switch between editors
275 if (part instanceof IEditorPart) {
276 IEditorInput input = ((IEditorPart) part).getEditorInput();
277 if (input instanceof IFileEditorInput)
278 reactOnSelection(new StructuredSelection(
279 ((IFileEditorInput) input).getFile()));
281 } else {
282 reactOnSelection(selection);
289 private void reactOnSelection(ISelection selection) {
290 if (selection instanceof StructuredSelection) {
291 StructuredSelection ssel = (StructuredSelection) selection;
292 if (ssel.size() != 1)
293 return;
294 if (ssel.getFirstElement() instanceof IResource) {
295 showResource((IResource) ssel.getFirstElement());
297 if (ssel.getFirstElement() instanceof IAdaptable) {
298 IResource adapted = (IResource) ((IAdaptable) ssel
299 .getFirstElement()).getAdapter(IResource.class);
300 if (adapted != null)
301 showResource(adapted);
306 private void addContextMenu() {
307 tv.getTree().addMenuDetectListener(new MenuDetectListener() {
309 public void menuDetected(MenuDetectEvent e) {
311 tv.getTree().setMenu(null);
312 Menu men = new Menu(tv.getTree());
314 TreeItem testItem = tv.getTree().getItem(
315 tv.getTree().toControl(new Point(e.x, e.y)));
316 if (testItem == null) {
317 addMenuItemsForPanel(men);
318 } else {
319 addMenuItemsForTreeSelection(men);
322 tv.getTree().setMenu(men);
327 private void addMenuItemsForPanel(Menu men) {
329 MenuItem importItem = new MenuItem(men, SWT.PUSH);
330 importItem.setText(UIText.RepositoriesView_ImportRepository_MenuItem);
331 importItem.addSelectionListener(new SelectionAdapter() {
333 @Override
334 public void widgetSelected(SelectionEvent e) {
335 importAction.run();
340 MenuItem addItem = new MenuItem(men, SWT.PUSH);
341 addItem.setText(UIText.RepositoriesView_AddRepository_MenuItem);
342 addItem.addSelectionListener(new SelectionAdapter() {
344 @Override
345 public void widgetSelected(SelectionEvent e) {
346 addAction.run();
351 MenuItem refreshItem = new MenuItem(men, SWT.PUSH);
352 refreshItem.setText(refreshAction.getText());
353 refreshItem.addSelectionListener(new SelectionAdapter() {
355 @Override
356 public void widgetSelected(SelectionEvent e) {
357 refreshAction.run();
364 @SuppressWarnings("unchecked")
365 private void addMenuItemsForTreeSelection(Menu men) {
367 final IStructuredSelection sel = (IStructuredSelection) tv
368 .getSelection();
370 boolean importableProjectsOnly = true;
372 for (Object node : sel.toArray()) {
373 RepositoryTreeNode tnode = (RepositoryTreeNode) node;
374 importableProjectsOnly = tnode.getType() == RepositoryTreeNodeType.PROJ;
375 if (!importableProjectsOnly)
376 break;
379 if (importableProjectsOnly) {
380 MenuItem sync = new MenuItem(men, SWT.PUSH);
381 sync.setText(UIText.RepositoriesView_ImportProject_MenuItem);
383 sync.addSelectionListener(new SelectionAdapter() {
385 @Override
386 public void widgetSelected(SelectionEvent e) {
388 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
390 public void run(IProgressMonitor monitor)
391 throws CoreException {
393 for (Object selected : sel.toArray()) {
394 RepositoryTreeNode<File> projectNode = (RepositoryTreeNode<File>) selected;
395 File file = projectNode.getObject();
397 IProjectDescription pd = ResourcesPlugin
398 .getWorkspace().newProjectDescription(
399 file.getName());
400 IPath locationPath = new Path(file
401 .getAbsolutePath());
403 pd.setLocation(locationPath);
405 ResourcesPlugin.getWorkspace().getRoot()
406 .getProject(pd.getName()).create(pd,
407 monitor);
408 IProject project = ResourcesPlugin
409 .getWorkspace().getRoot().getProject(
410 pd.getName());
411 project.open(monitor);
413 File gitDir = projectNode.getRepository()
414 .getDirectory();
416 ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation(
417 project, gitDir);
418 connectProviderOperation
419 .run(new SubProgressMonitor(monitor, 20));
426 try {
428 ResourcesPlugin.getWorkspace().run(wsr,
429 ResourcesPlugin.getWorkspace().getRoot(),
430 IWorkspace.AVOID_UPDATE,
431 new NullProgressMonitor());
433 scheduleRefresh();
434 } catch (CoreException e1) {
435 Activator.getDefault().getLog().log(e1.getStatus());
443 // from here on, we only deal with single selection
444 if (sel.size() > 1)
445 return;
447 final RepositoryTreeNode node = (RepositoryTreeNode) sel
448 .getFirstElement();
450 // for Refs (branches): checkout
451 if (node.getType() == RepositoryTreeNodeType.REF) {
453 final Ref ref = (Ref) node.getObject();
455 MenuItem checkout = new MenuItem(men, SWT.PUSH);
456 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
457 checkout.addSelectionListener(new SelectionAdapter() {
459 @Override
460 public void widgetSelected(SelectionEvent e) {
461 Repository repo = node.getRepository();
462 String refName = ref.getLeaf().getName();
463 final BranchOperation op = new BranchOperation(repo,
464 refName);
465 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
467 public void run(IProgressMonitor monitor)
468 throws CoreException {
469 op.run(monitor);
472 try {
473 ResourcesPlugin.getWorkspace().run(wsr,
474 ResourcesPlugin.getWorkspace().getRoot(),
475 IWorkspace.AVOID_UPDATE,
476 new NullProgressMonitor());
477 scheduleRefresh();
478 } catch (CoreException e1) {
479 MessageDialog.openError(getSite().getShell(),
480 UIText.RepositoriesView_Error_WindowTitle, e1
481 .getMessage());
489 // for Repository: import existing projects, remove, (delete), open
490 // properties
491 if (node.getType() == RepositoryTreeNodeType.REPO) {
493 final Repository repo = (Repository) node.getObject();
495 // TODO "import existing plug-in" menu item
497 MenuItem remove = new MenuItem(men, SWT.PUSH);
498 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
499 remove.addSelectionListener(new SelectionAdapter() {
501 @Override
502 public void widgetSelected(SelectionEvent e) {
504 List<IProject> projectsToDelete = new ArrayList<IProject>();
505 File workDir = repo.getWorkDir();
506 final IPath wdPath = new Path(workDir.getAbsolutePath());
507 for (IProject prj : ResourcesPlugin.getWorkspace()
508 .getRoot().getProjects()) {
509 if (wdPath.isPrefixOf(prj.getLocation())) {
510 projectsToDelete.add(prj);
514 if (!projectsToDelete.isEmpty()) {
515 boolean confirmed;
516 confirmed = confirmProjectDeletion(projectsToDelete);
517 if (!confirmed) {
518 return;
522 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
524 public void run(IProgressMonitor monitor)
525 throws CoreException {
527 for (IProject prj : ResourcesPlugin.getWorkspace()
528 .getRoot().getProjects()) {
529 if (wdPath.isPrefixOf(prj.getLocation())) {
530 prj.delete(false, false, monitor);
534 removeDir(repo.getDirectory());
535 scheduleRefresh();
539 try {
540 ResourcesPlugin.getWorkspace().run(wsr,
541 ResourcesPlugin.getWorkspace().getRoot(),
542 IWorkspace.AVOID_UPDATE,
543 new NullProgressMonitor());
544 } catch (CoreException e1) {
545 Activator.getDefault().getLog().log(e1.getStatus());
552 // TODO delete does not work because of file locks on .pack-files
553 // Shawn Pearce has added the following thoughts:
555 // Hmm. We probably can't active detect file locks on pack files on
556 // Windows, can we?
557 // It would be nice if we could support a delete, but only if the
558 // repository is
559 // reasonably believed to be not-in-use right now.
561 // Within EGit you might be able to check GitProjectData and its
562 // repositoryCache to
563 // see if the repository is open by this workspace. If it is, then
564 // we know we shouldn't
565 // try to delete it.
567 // Some coding might look like this:
569 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
570 // deleteRepo.setText("Delete");
571 // deleteRepo.addSelectionListener(new SelectionAdapter() {
573 // @Override
574 // public void widgetSelected(SelectionEvent e) {
576 // boolean confirmed = MessageDialog.openConfirm(getSite()
577 // .getShell(), "Confirm",
578 // "This will delete the repository, continue?");
580 // if (!confirmed)
581 // return;
583 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
585 // public void run(IProgressMonitor monitor)
586 // throws CoreException {
587 // File workDir = repos.get(0).getRepository()
588 // .getWorkDir();
590 // File gitDir = repos.get(0).getRepository()
591 // .getDirectory();
593 // IPath wdPath = new Path(workDir.getAbsolutePath());
594 // for (IProject prj : ResourcesPlugin.getWorkspace()
595 // .getRoot().getProjects()) {
596 // if (wdPath.isPrefixOf(prj.getLocation())) {
597 // prj.delete(false, false, monitor);
598 // }
599 // }
601 // repos.get(0).getRepository().close();
603 // boolean deleted = deleteRecursively(gitDir, monitor);
604 // if (!deleted) {
605 // MessageDialog.openError(getSite().getShell(),
606 // "Error",
607 // "Could not delete Git Repository");
608 // }
610 // deleted = deleteRecursively(workDir, monitor);
611 // if (!deleted) {
612 // MessageDialog
613 // .openError(getSite().getShell(),
614 // "Error",
615 // "Could not delete Git Working Directory");
616 // }
618 // scheduleRefresh();
619 // }
621 // private boolean deleteRecursively(File fileToDelete,
622 // IProgressMonitor monitor) {
623 // if (fileToDelete.isDirectory()) {
624 // for (File file : fileToDelete.listFiles()) {
625 // if (!deleteRecursively(file, monitor)) {
626 // return false;
627 // }
628 // }
629 // }
630 // monitor.setTaskName(fileToDelete.getAbsolutePath());
631 // boolean deleted = fileToDelete.delete();
632 // if (!deleted) {
633 // System.err.println("Could not delete "
634 // + fileToDelete.getAbsolutePath());
635 // }
636 // return deleted;
637 // }
638 // };
640 // try {
641 // ResourcesPlugin.getWorkspace().run(wsr,
642 // ResourcesPlugin.getWorkspace().getRoot(),
643 // IWorkspace.AVOID_UPDATE,
644 // new NullProgressMonitor());
645 // } catch (CoreException e1) {
646 // // TODO Exception handling
647 // e1.printStackTrace();
648 // }
650 // }
652 // });
654 new MenuItem(men, SWT.SEPARATOR);
656 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
657 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
658 openPropsView.addSelectionListener(new SelectionAdapter() {
660 @Override
661 public void widgetSelected(SelectionEvent e) {
662 try {
663 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
664 .getActivePage().showView(
665 IPageLayout.ID_PROP_SHEET);
666 } catch (PartInitException e1) {
667 // just ignore
674 if (node.getType() == RepositoryTreeNodeType.REMOTES) {
676 MenuItem remoteConfig = new MenuItem(men, SWT.PUSH);
677 remoteConfig.setText(UIText.RepositoriesView_NewRemoteMenu);
678 remoteConfig.addSelectionListener(new SelectionAdapter() {
680 @Override
681 public void widgetSelected(SelectionEvent e) {
682 WizardDialog dialog = new WizardDialog(
683 getSite().getShell(), new ConfigureRemoteWizard(
684 node.getRepository()));
685 if (dialog.open() == Window.OK) {
686 scheduleRefresh();
693 if (node.getType() == RepositoryTreeNodeType.REMOTE) {
695 final String name = (String) node.getObject();
697 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
698 configureUrlFetch
699 .setText(UIText.RepositoriesView_ConfigureFetchMenu);
700 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
702 @Override
703 public void widgetSelected(SelectionEvent e) {
704 WizardDialog dialog = new WizardDialog(
705 getSite().getShell(), new ConfigureRemoteWizard(
706 node.getRepository(), name, false));
707 if (dialog.open() == Window.OK) {
708 scheduleRefresh();
714 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
715 configureUrlPush.setText(UIText.RepositoriesView_ConfigurePushMenu);
716 configureUrlPush.addSelectionListener(new SelectionAdapter() {
718 @Override
719 public void widgetSelected(SelectionEvent e) {
720 WizardDialog dialog = new WizardDialog(
721 getSite().getShell(), new ConfigureRemoteWizard(
722 node.getRepository(), name, true));
723 if (dialog.open() == Window.OK) {
724 scheduleRefresh();
730 new MenuItem(men, SWT.SEPARATOR);
732 MenuItem removeRemote = new MenuItem(men, SWT.PUSH);
733 removeRemote.setText(UIText.RepositoriesView_RemoveRemoteMenu);
734 removeRemote.addSelectionListener(new SelectionAdapter() {
736 @Override
737 public void widgetSelected(SelectionEvent e) {
739 boolean ok = MessageDialog
740 .openConfirm(
741 getSite().getShell(),
742 UIText.RepositoriesView_ConfirmDeleteRemoteHeader,
744 .bind(
745 UIText.RepositoriesView_ConfirmDeleteRemoteMessage,
746 name));
747 if (ok) {
748 RepositoryConfig config = node.getRepository()
749 .getConfig();
750 config.unsetSection(REMOTE, name);
751 try {
752 config.save();
753 scheduleRefresh();
754 } catch (IOException e1) {
755 MessageDialog.openError(getSite().getShell(),
756 UIText.RepositoriesView_ErrorHeader, e1
757 .getMessage());
765 new MenuItem(men, SWT.SEPARATOR);
767 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
768 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
769 openPropsView.addSelectionListener(new SelectionAdapter() {
771 @Override
772 public void widgetSelected(SelectionEvent e) {
773 try {
774 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
775 .getActivePage().showView(
776 IPageLayout.ID_PROP_SHEET);
777 } catch (PartInitException e1) {
778 // just ignore
785 if (node.getType() == RepositoryTreeNodeType.FILE) {
787 final File file = (File) node.getObject();
789 MenuItem openInTextEditor = new MenuItem(men, SWT.PUSH);
790 openInTextEditor
791 .setText(UIText.RepositoriesView_OpenInTextEditor_menu);
792 openInTextEditor.addSelectionListener(new SelectionAdapter() {
794 @Override
795 public void widgetSelected(SelectionEvent e) {
796 IFileStore store = EFS.getLocalFileSystem().getStore(
797 new Path(file.getAbsolutePath()));
798 try {
799 // TODO do we need a read-only editor here?
800 IDE.openEditor(getSite().getPage(),
801 new FileStoreEditorInput(store),
802 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
804 } catch (PartInitException e1) {
805 MessageDialog.openError(getSite().getShell(),
806 UIText.RepositoriesView_Error_WindowTitle, e1
807 .getMessage());
815 if (node.getType() == RepositoryTreeNodeType.FOLDER) {
816 String path = ((File) node.getObject()).getAbsolutePath();
817 createImportProjectItem(men, node.getRepository(), path);
820 if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
821 String path = node.getRepository().getWorkDir().getAbsolutePath();
822 createImportProjectItem(men, node.getRepository(), path);
827 private void createImportProjectItem(Menu men, final Repository repo,
828 final String path) {
829 MenuItem importProjects;
830 importProjects = new MenuItem(men, SWT.PUSH);
831 importProjects
832 .setText(UIText.RepositoriesView_ImportExistingProjects_MenuItem);
833 importProjects.addSelectionListener(new SelectionAdapter() {
835 @Override
836 public void widgetSelected(SelectionEvent e) {
837 // Instead of the generic ExternalProjectImportWizard
838 // from the org.eclipse.ui.ide plug-in, we use our
839 // own wizard (the generic one does not allow to set the
840 // path in 3.4; in addition, we have added project filtering
841 // capabilities)
842 Wizard wiz = new GitImportProjectsWizard(repo, path);
844 WizardDialog dlg = new WizardDialog(getSite().getShell(), wiz);
845 if (dlg.open() == Window.OK)
846 // TODO if we drop the "existing projects" node, we can
847 // probably do without refresh
848 scheduleRefresh();
855 private void addActionsToToolbar() {
856 importAction = new Action(UIText.RepositoriesView_Import_Button) {
858 @Override
859 public void run() {
860 GitCloneWizard wiz = new GitCloneWizard();
861 wiz.init(null, null);
862 new WizardDialog(getSite().getShell(), wiz).open();
865 importAction.setToolTipText(UIText.RepositoriesView_Clone_Tooltip);
867 importAction.setImageDescriptor(UIIcons.IMPORT);
869 getViewSite().getActionBars().getToolBarManager().add(importAction);
871 addAction = new Action(UIText.RepositoriesView_Add_Button) {
873 @Override
874 public void run() {
875 RepositorySearchDialog sd = new RepositorySearchDialog(
876 getSite().getShell(), getDirs());
877 if (sd.open() == Window.OK) {
878 Set<String> dirs = new HashSet<String>();
879 dirs.addAll(getDirs());
880 if (dirs.addAll(sd.getDirectories()))
881 saveDirs(dirs);
882 scheduleRefresh();
887 addAction.setToolTipText(UIText.RepositoriesView_AddRepository_Tooltip);
889 addAction.setImageDescriptor(UIIcons.NEW_REPOSITORY);
891 getViewSite().getActionBars().getToolBarManager().add(addAction);
893 linkWithSelectionAction = new Action(
894 UIText.RepositoriesView_LinkWithSelection_action,
895 IAction.AS_CHECK_BOX) {
897 @Override
898 public void run() {
899 IEclipsePreferences prefs = getPrefs();
900 prefs.putBoolean(PREFS_SYNCED, isChecked());
901 try {
902 prefs.flush();
903 } catch (BackingStoreException e) {
904 // ignore here
906 if (isChecked()) {
907 ISelectionService srv = (ISelectionService) getSite()
908 .getService(ISelectionService.class);
909 reactOnSelection(srv.getSelection());
916 linkWithSelectionAction
917 .setToolTipText(UIText.RepositoriesView_LinkWithSelection_action);
919 linkWithSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
921 linkWithSelectionAction.setChecked(getPrefs().getBoolean(PREFS_SYNCED,
922 false));
924 getViewSite().getActionBars().getToolBarManager().add(
925 linkWithSelectionAction);
927 refreshAction = new Action(UIText.RepositoriesView_Refresh_Button) {
929 @Override
930 public void run() {
931 scheduleRefresh();
935 refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
937 getViewSite().getActionBars().getToolBarManager().add(refreshAction);
941 * @return the preferences
943 protected static IEclipsePreferences getPrefs() {
944 return new InstanceScope().getNode(Activator.getPluginId());
947 @Override
948 public void dispose() {
949 // make sure to cancel the refresh job
950 if (this.scheduledJob != null) {
951 this.scheduledJob.cancel();
952 this.scheduledJob = null;
954 super.dispose();
958 * Schedules a refreh
960 public void scheduleRefresh() {
962 Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
964 @Override
965 protected IStatus run(IProgressMonitor monitor) {
967 final List<Repository> input;
968 try {
969 input = getRepositoriesFromDirs(monitor);
970 } catch (InterruptedException e) {
971 return new Status(IStatus.ERROR, Activator.getPluginId(), e
972 .getMessage(), e);
975 Display.getDefault().syncExec(new Runnable() {
977 public void run() {
978 // keep expansion state and selection so that we can
979 // restore the tree
980 // after update
981 Object[] expanded = tv.getExpandedElements();
982 IStructuredSelection sel = (IStructuredSelection) tv
983 .getSelection();
984 tv.setInput(input);
985 tv.setExpandedElements(expanded);
987 Object selected = sel.getFirstElement();
988 if (selected != null)
989 tv.reveal(selected);
991 IViewPart part = PlatformUI.getWorkbench()
992 .getActiveWorkbenchWindow().getActivePage()
993 .findView(IPageLayout.ID_PROP_SHEET);
994 if (part != null) {
995 PropertySheet sheet = (PropertySheet) part;
996 PropertySheetPage page = (PropertySheetPage) sheet
997 .getCurrentPage();
998 page.refresh();
1003 return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$
1008 job.setSystem(true);
1010 IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite()
1011 .getService(IWorkbenchSiteProgressService.class);
1013 service.schedule(job);
1015 scheduledJob = job;
1019 private List<Repository> getRepositoriesFromDirs(IProgressMonitor monitor)
1020 throws InterruptedException {
1022 List<String> gitDirStrings = getDirs();
1023 List<Repository> input = new ArrayList<Repository>();
1024 for (String dirString : gitDirStrings) {
1025 if (monitor.isCanceled()) {
1026 throw new InterruptedException(
1027 UIText.RepositoriesView_ActionCanceled_Message);
1029 try {
1030 File dir = new File(dirString);
1031 if (dir.exists() && dir.isDirectory()) {
1032 input.add(new Repository(dir));
1034 } catch (IOException e) {
1035 IStatus error = new Status(IStatus.ERROR, Activator
1036 .getPluginId(), e.getMessage(), e);
1037 Activator.getDefault().getLog().log(error);
1040 return input;
1044 * Adds a directory to the list if it is not already there
1046 * @param file
1047 * @return see {@link Collection#add(Object)}
1049 public static boolean addDir(File file) {
1051 String dirString;
1052 try {
1053 dirString = file.getCanonicalPath();
1054 } catch (IOException e) {
1055 dirString = file.getAbsolutePath();
1058 List<String> dirStrings = getDirs();
1059 if (dirStrings.contains(dirString)) {
1060 return false;
1061 } else {
1062 Set<String> dirs = new HashSet<String>();
1063 dirs.addAll(dirStrings);
1064 dirs.add(dirString);
1065 saveDirs(dirs);
1066 return true;
1070 private static void saveDirs(Set<String> gitDirStrings) {
1071 StringBuilder sb = new StringBuilder();
1072 for (String gitDirString : gitDirStrings) {
1073 sb.append(gitDirString);
1074 sb.append(File.pathSeparatorChar);
1077 IEclipsePreferences prefs = getPrefs();
1078 prefs.put(PREFS_DIRECTORIES, sb.toString());
1079 try {
1080 prefs.flush();
1081 } catch (BackingStoreException e) {
1082 IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(),
1083 e.getMessage(), e);
1084 Activator.getDefault().getLog().log(error);
1088 @Override
1089 public void setFocus() {
1090 tv.getTree().setFocus();
1093 @SuppressWarnings("boxing")
1094 private boolean confirmProjectDeletion(List<IProject> projectsToDelete) {
1095 boolean confirmed;
1096 confirmed = MessageDialog
1097 .openConfirm(
1098 getSite().getShell(),
1099 UIText.RepositoriesView_ConfirmProjectDeletion_WindowTitle,
1101 .bind(
1102 UIText.RepositoriesView_ConfirmProjectDeletion_Question,
1103 projectsToDelete.size()));
1104 return confirmed;
1107 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1108 selectionListeners.add(listener);
1111 public ISelection getSelection() {
1112 return currentSelection;
1115 public void removeSelectionChangedListener(
1116 ISelectionChangedListener listener) {
1117 selectionListeners.remove(listener);
1121 public void setSelection(ISelection selection) {
1122 currentSelection = selection;
1123 for (ISelectionChangedListener listener : selectionListeners) {
1124 listener.selectionChanged(new SelectionChangedEvent(
1125 RepositoriesView.this, selection));
1130 * Opens the tree and marks the folder to which a project is pointing
1132 * @param resource
1133 * TODO exceptions?
1135 @SuppressWarnings("unchecked")
1136 public void showResource(final IResource resource) {
1137 IProject project = resource.getProject();
1138 RepositoryMapping mapping = RepositoryMapping.getMapping(project);
1139 if (mapping == null)
1140 return;
1142 if (addDir(mapping.getRepository().getDirectory())) {
1143 scheduleRefresh();
1146 boolean doSetSelection = false;
1148 if (this.scheduledJob != null) {
1149 int state = this.scheduledJob.getState();
1150 if (state == Job.WAITING || state == Job.RUNNING) {
1151 this.scheduledJob.addJobChangeListener(new JobChangeAdapter() {
1153 @Override
1154 public void done(IJobChangeEvent event) {
1155 showResource(resource);
1158 } else {
1159 doSetSelection = true;
1163 if (doSetSelection) {
1164 RepositoriesViewContentProvider cp = (RepositoriesViewContentProvider) tv
1165 .getContentProvider();
1166 RepositoryTreeNode currentNode = null;
1167 Object[] repos = cp.getElements(tv.getInput());
1168 for (Object repo : repos) {
1169 RepositoryTreeNode node = (RepositoryTreeNode) repo;
1170 // TODO equals implementation of Repository?
1171 if (mapping.getRepository().getDirectory().equals(
1172 ((Repository) node.getObject()).getDirectory())) {
1173 for (Object child : cp.getChildren(node)) {
1174 RepositoryTreeNode childNode = (RepositoryTreeNode) child;
1175 if (childNode.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1176 currentNode = childNode;
1177 break;
1180 break;
1184 IPath relPath = new Path(mapping.getRepoRelativePath(resource));
1186 for (String segment : relPath.segments()) {
1187 for (Object child : cp.getChildren(currentNode)) {
1188 RepositoryTreeNode<File> childNode = (RepositoryTreeNode<File>) child;
1189 if (childNode.getObject().getName().equals(segment)) {
1190 currentNode = childNode;
1191 break;
1196 final RepositoryTreeNode selNode = currentNode;
1198 Display.getDefault().asyncExec(new Runnable() {
1200 public void run() {
1201 tv.setSelection(new StructuredSelection(selNode), true);