Git Repositories View: Import Projects
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositoriesView.java
blob4edffc1a812f3a728a8776bfb24ea0bd452b1608
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.net.URISyntaxException;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23 import java.util.TreeSet;
25 import org.eclipse.core.filesystem.EFS;
26 import org.eclipse.core.filesystem.IFileStore;
27 import org.eclipse.core.resources.IProject;
28 import org.eclipse.core.resources.IProjectDescription;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.resources.IWorkspace;
31 import org.eclipse.core.resources.IWorkspaceRunnable;
32 import org.eclipse.core.resources.ResourcesPlugin;
33 import org.eclipse.core.runtime.CoreException;
34 import org.eclipse.core.runtime.IAdaptable;
35 import org.eclipse.core.runtime.IPath;
36 import org.eclipse.core.runtime.IProgressMonitor;
37 import org.eclipse.core.runtime.IStatus;
38 import org.eclipse.core.runtime.NullProgressMonitor;
39 import org.eclipse.core.runtime.Path;
40 import org.eclipse.core.runtime.Status;
41 import org.eclipse.core.runtime.SubProgressMonitor;
42 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
43 import org.eclipse.core.runtime.jobs.Job;
44 import org.eclipse.core.runtime.jobs.JobChangeAdapter;
45 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
46 import org.eclipse.core.runtime.preferences.InstanceScope;
47 import org.eclipse.egit.core.op.BranchOperation;
48 import org.eclipse.egit.core.op.ConnectProviderOperation;
49 import org.eclipse.egit.core.project.RepositoryMapping;
50 import org.eclipse.egit.ui.Activator;
51 import org.eclipse.egit.ui.UIIcons;
52 import org.eclipse.egit.ui.UIText;
53 import org.eclipse.egit.ui.internal.clone.GitCloneWizard;
54 import org.eclipse.egit.ui.internal.clone.GitCreateProjectViaWizardWizard;
55 import org.eclipse.egit.ui.internal.repository.RepositoryTreeNode.RepositoryTreeNodeType;
56 import org.eclipse.jface.action.Action;
57 import org.eclipse.jface.action.IAction;
58 import org.eclipse.jface.dialogs.MessageDialog;
59 import org.eclipse.jface.viewers.IOpenListener;
60 import org.eclipse.jface.viewers.ISelection;
61 import org.eclipse.jface.viewers.ISelectionChangedListener;
62 import org.eclipse.jface.viewers.ISelectionProvider;
63 import org.eclipse.jface.viewers.IStructuredSelection;
64 import org.eclipse.jface.viewers.ITreeContentProvider;
65 import org.eclipse.jface.viewers.OpenEvent;
66 import org.eclipse.jface.viewers.SelectionChangedEvent;
67 import org.eclipse.jface.viewers.StructuredSelection;
68 import org.eclipse.jface.viewers.TreeViewer;
69 import org.eclipse.jface.window.Window;
70 import org.eclipse.jface.wizard.WizardDialog;
71 import org.eclipse.jgit.lib.Ref;
72 import org.eclipse.jgit.lib.Repository;
73 import org.eclipse.jgit.lib.RepositoryConfig;
74 import org.eclipse.jgit.transport.RemoteConfig;
75 import org.eclipse.osgi.util.NLS;
76 import org.eclipse.swt.SWT;
77 import org.eclipse.swt.dnd.Clipboard;
78 import org.eclipse.swt.dnd.TextTransfer;
79 import org.eclipse.swt.dnd.Transfer;
80 import org.eclipse.swt.events.MenuDetectEvent;
81 import org.eclipse.swt.events.MenuDetectListener;
82 import org.eclipse.swt.events.SelectionAdapter;
83 import org.eclipse.swt.events.SelectionEvent;
84 import org.eclipse.swt.graphics.Point;
85 import org.eclipse.swt.widgets.Composite;
86 import org.eclipse.swt.widgets.Display;
87 import org.eclipse.swt.widgets.Menu;
88 import org.eclipse.swt.widgets.MenuItem;
89 import org.eclipse.swt.widgets.TreeItem;
90 import org.eclipse.ui.IEditorInput;
91 import org.eclipse.ui.IEditorPart;
92 import org.eclipse.ui.IFileEditorInput;
93 import org.eclipse.ui.IPageLayout;
94 import org.eclipse.ui.ISelectionListener;
95 import org.eclipse.ui.ISelectionService;
96 import org.eclipse.ui.IViewPart;
97 import org.eclipse.ui.IWorkbenchPart;
98 import org.eclipse.ui.PartInitException;
99 import org.eclipse.ui.PlatformUI;
100 import org.eclipse.ui.editors.text.EditorsUI;
101 import org.eclipse.ui.ide.FileStoreEditorInput;
102 import org.eclipse.ui.ide.IDE;
103 import org.eclipse.ui.part.IShowInTarget;
104 import org.eclipse.ui.part.ShowInContext;
105 import org.eclipse.ui.part.ViewPart;
106 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
107 import org.eclipse.ui.views.properties.IPropertySheetPage;
108 import org.eclipse.ui.views.properties.PropertySheet;
109 import org.eclipse.ui.views.properties.PropertySheetPage;
110 import org.osgi.service.prefs.BackingStoreException;
114 * The Git Repositories view.
115 * <p>
116 * This keeps track of a bunch of local directory names each of which represent
117 * a Git Repository. This list is stored in some Preferences object and used to
118 * build the tree in the view.
119 * <p>
120 * Implements {@link ISelectionProvider} in order to integrate with the
121 * Properties view.
122 * <p>
123 * TODO
124 * <li>Clarification whether to show projects, perhaps configurable switch</li>
127 public class RepositoriesView extends ViewPart implements ISelectionProvider,
128 IShowInTarget {
130 /** The view ID */
131 public static final String VIEW_ID = "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
133 // TODO central constants? RemoteConfig ones are private
134 static final String REMOTE = "remote"; //$NON-NLS-1$
136 static final String URL = "url"; //$NON-NLS-1$
138 static final String PUSHURL = "pushurl"; //$NON-NLS-1$
140 static final String FETCH = "fetch"; //$NON-NLS-1$
142 static final String PUSH = "push"; //$NON-NLS-1$
144 private static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
146 private static final String PREFS_SYNCED = "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
148 private final List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
150 private ISelection currentSelection = new StructuredSelection();
152 private Job scheduledJob;
154 private TreeViewer tv;
156 private IAction importAction;
158 private IAction addAction;
160 private IAction refreshAction;
162 private IAction linkWithSelectionAction;
164 private static List<String> getDirs() {
165 List<String> resultStrings = new ArrayList<String>();
166 String dirs = getPrefs().get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
167 if (dirs != null && dirs.length() > 0) {
168 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
169 while (tok.hasMoreTokens()) {
170 String dirName = tok.nextToken();
171 File testFile = new File(dirName);
172 if (testFile.exists()) {
173 resultStrings.add(dirName);
177 Collections.sort(resultStrings);
178 return resultStrings;
181 private static void removeDir(File file) {
183 String dir;
184 try {
185 dir = file.getCanonicalPath();
186 } catch (IOException e1) {
187 dir = file.getAbsolutePath();
190 IEclipsePreferences prefs = getPrefs();
192 TreeSet<String> resultStrings = new TreeSet<String>();
193 String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
194 if (dirs != null && dirs.length() > 0) {
195 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
196 while (tok.hasMoreTokens()) {
197 String dirName = tok.nextToken();
198 File testFile = new File(dirName);
199 if (testFile.exists()) {
200 try {
201 resultStrings.add(testFile.getCanonicalPath());
202 } catch (IOException e) {
203 resultStrings.add(testFile.getAbsolutePath());
209 if (resultStrings.remove(dir)) {
210 StringBuilder sb = new StringBuilder();
211 for (String gitDirString : resultStrings) {
212 sb.append(gitDirString);
213 sb.append(File.pathSeparatorChar);
216 prefs.put(PREFS_DIRECTORIES, sb.toString());
217 try {
218 prefs.flush();
219 } catch (BackingStoreException e) {
220 IStatus error = new Status(IStatus.ERROR, Activator
221 .getPluginId(), e.getMessage(), e);
222 Activator.getDefault().getLog().log(error);
228 @Override
229 public Object getAdapter(Class adapter) {
230 // integrate with Properties view
231 if (adapter == IPropertySheetPage.class) {
232 PropertySheetPage page = new PropertySheetPage();
233 page
234 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
235 page));
236 return page;
239 return super.getAdapter(adapter);
242 @Override
243 public void createPartControl(Composite parent) {
244 tv = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
245 tv.setContentProvider(new RepositoriesViewContentProvider());
246 // the label provider registers itself
247 new RepositoriesViewLabelProvider(tv);
249 getSite().setSelectionProvider(this);
251 tv.addSelectionChangedListener(new ISelectionChangedListener() {
253 public void selectionChanged(SelectionChangedEvent event) {
255 IStructuredSelection ssel = (IStructuredSelection) event
256 .getSelection();
257 if (ssel.size() == 1) {
258 setSelection(new StructuredSelection(ssel.getFirstElement()));
259 } else {
260 setSelection(new StructuredSelection());
265 tv.addOpenListener(new IOpenListener() {
266 public void open(OpenEvent event) {
267 IStructuredSelection selection = (IStructuredSelection) event
268 .getSelection();
269 if (selection.isEmpty()) {
270 // nothing selected, ignore
271 return;
274 Object element = selection.getFirstElement();
275 ITreeContentProvider contentProvider = (ITreeContentProvider) tv
276 .getContentProvider();
277 if (contentProvider.hasChildren(element)) {
278 // this element has children, expand/collapse it
279 tv.setExpandedState(element, !tv.getExpandedState(element));
280 } else {
281 Object[] selectionArray = selection.toArray();
282 for (Object selectedElement : selectionArray) {
283 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
284 // if any of the selected elements are not files, ignore
285 // the open request
286 if (node.getType() != RepositoryTreeNodeType.FILE) {
287 return;
291 // open the files the user has selected
292 for (Object selectedElement : selectionArray) {
293 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
294 openFile((File) node.getObject());
300 addContextMenu();
302 addActionsToToolbar();
304 scheduleRefresh();
306 ISelectionService srv = (ISelectionService) getSite().getService(
307 ISelectionService.class);
308 srv.addPostSelectionListener(new ISelectionListener() {
310 public void selectionChanged(IWorkbenchPart part,
311 ISelection selection) {
313 // if the "link with selection" toggle is off, we're done
314 if (linkWithSelectionAction == null
315 || !linkWithSelectionAction.isChecked())
316 return;
318 // this may happen if we switch between editors
319 if (part instanceof IEditorPart) {
320 IEditorInput input = ((IEditorPart) part).getEditorInput();
321 if (input instanceof IFileEditorInput)
322 reactOnSelection(new StructuredSelection(
323 ((IFileEditorInput) input).getFile()));
325 } else {
326 reactOnSelection(selection);
333 private void reactOnSelection(ISelection selection) {
334 if (selection instanceof StructuredSelection) {
335 StructuredSelection ssel = (StructuredSelection) selection;
336 if (ssel.size() != 1)
337 return;
338 if (ssel.getFirstElement() instanceof IResource) {
339 showResource((IResource) ssel.getFirstElement());
341 if (ssel.getFirstElement() instanceof IAdaptable) {
342 IResource adapted = (IResource) ((IAdaptable) ssel
343 .getFirstElement()).getAdapter(IResource.class);
344 if (adapted != null)
345 showResource(adapted);
350 private void addContextMenu() {
351 tv.getTree().addMenuDetectListener(new MenuDetectListener() {
353 public void menuDetected(MenuDetectEvent e) {
355 tv.getTree().setMenu(null);
356 Menu men = new Menu(tv.getTree());
358 TreeItem testItem = tv.getTree().getItem(
359 tv.getTree().toControl(new Point(e.x, e.y)));
360 if (testItem == null) {
361 addMenuItemsForPanel(men);
362 } else {
363 addMenuItemsForTreeSelection(men);
366 tv.getTree().setMenu(men);
371 private void addMenuItemsForPanel(Menu men) {
373 MenuItem importItem = new MenuItem(men, SWT.PUSH);
374 importItem.setText(UIText.RepositoriesView_ImportRepository_MenuItem);
375 importItem.addSelectionListener(new SelectionAdapter() {
377 @Override
378 public void widgetSelected(SelectionEvent e) {
379 importAction.run();
384 MenuItem addItem = new MenuItem(men, SWT.PUSH);
385 addItem.setText(UIText.RepositoriesView_AddRepository_MenuItem);
386 addItem.addSelectionListener(new SelectionAdapter() {
388 @Override
389 public void widgetSelected(SelectionEvent e) {
390 addAction.run();
395 MenuItem refreshItem = new MenuItem(men, SWT.PUSH);
396 refreshItem.setText(refreshAction.getText());
397 refreshItem.addSelectionListener(new SelectionAdapter() {
399 @Override
400 public void widgetSelected(SelectionEvent e) {
401 refreshAction.run();
408 @SuppressWarnings("unchecked")
409 private void addMenuItemsForTreeSelection(Menu men) {
411 final IStructuredSelection sel = (IStructuredSelection) tv
412 .getSelection();
414 boolean importableProjectsOnly = true;
416 for (Object node : sel.toArray()) {
417 RepositoryTreeNode tnode = (RepositoryTreeNode) node;
418 importableProjectsOnly = tnode.getType() == RepositoryTreeNodeType.PROJ;
419 if (!importableProjectsOnly)
420 break;
423 if (importableProjectsOnly) {
424 MenuItem sync = new MenuItem(men, SWT.PUSH);
425 sync.setText(UIText.RepositoriesView_ImportProject_MenuItem);
427 sync.addSelectionListener(new SelectionAdapter() {
429 @Override
430 public void widgetSelected(SelectionEvent e) {
432 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
434 public void run(IProgressMonitor monitor)
435 throws CoreException {
437 for (Object selected : sel.toArray()) {
438 RepositoryTreeNode<File> projectNode = (RepositoryTreeNode<File>) selected;
439 File file = projectNode.getObject();
441 IProjectDescription pd = ResourcesPlugin
442 .getWorkspace().newProjectDescription(
443 file.getName());
444 IPath locationPath = new Path(file
445 .getAbsolutePath());
447 pd.setLocation(locationPath);
449 ResourcesPlugin.getWorkspace().getRoot()
450 .getProject(pd.getName()).create(pd,
451 monitor);
452 IProject project = ResourcesPlugin
453 .getWorkspace().getRoot().getProject(
454 pd.getName());
455 project.open(monitor);
457 File gitDir = projectNode.getRepository()
458 .getDirectory();
460 ConnectProviderOperation connectProviderOperation = new ConnectProviderOperation(
461 project, gitDir);
462 connectProviderOperation
463 .run(new SubProgressMonitor(monitor, 20));
470 try {
472 ResourcesPlugin.getWorkspace().run(wsr,
473 ResourcesPlugin.getWorkspace().getRoot(),
474 IWorkspace.AVOID_UPDATE,
475 new NullProgressMonitor());
477 scheduleRefresh();
478 } catch (CoreException e1) {
479 Activator.logError(e1.getMessage(), e1);
487 // from here on, we only deal with single selection
488 if (sel.size() > 1)
489 return;
491 final RepositoryTreeNode node = (RepositoryTreeNode) sel
492 .getFirstElement();
494 if (node.getType() == RepositoryTreeNodeType.REF) {
496 final Ref ref = (Ref) node.getObject();
498 // we don't check out symbolic references (most notably HEAD)
499 if (!ref.isSymbolic()) {
501 MenuItem checkout = new MenuItem(men, SWT.PUSH);
502 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
503 checkout.addSelectionListener(new SelectionAdapter() {
505 @Override
506 public void widgetSelected(SelectionEvent e) {
508 final String refName = ref.getLeaf().getName();
509 // for the sake of UI responsiveness, let's start a job
510 Job job = new Job(NLS.bind(
511 UIText.RepositoriesView_CheckingOutMessage,
512 refName)) {
514 @Override
515 protected IStatus run(IProgressMonitor monitor) {
517 Repository repo = node.getRepository();
519 final BranchOperation op = new BranchOperation(
520 repo, refName);
521 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
523 public void run(IProgressMonitor myMonitor)
524 throws CoreException {
525 op.run(myMonitor);
529 try {
530 ResourcesPlugin.getWorkspace().run(
531 wsr,
532 ResourcesPlugin.getWorkspace()
533 .getRoot(),
534 IWorkspace.AVOID_UPDATE, monitor);
535 scheduleRefresh();
536 } catch (CoreException e1) {
537 return new Status(IStatus.ERROR, Activator
538 .getPluginId(), e1.getMessage(), e1);
541 return Status.OK_STATUS;
545 job.setUser(true);
546 job.schedule();
553 // for Repository: import existing projects, remove, (delete), open
554 // properties
555 if (node.getType() == RepositoryTreeNodeType.REPO) {
557 final Repository repo = (Repository) node.getObject();
559 // TODO "import existing plug-in" menu item
561 MenuItem remove = new MenuItem(men, SWT.PUSH);
562 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
563 remove.addSelectionListener(new SelectionAdapter() {
565 @Override
566 public void widgetSelected(SelectionEvent e) {
568 List<IProject> projectsToDelete = new ArrayList<IProject>();
569 File workDir = repo.getWorkDir();
570 final IPath wdPath = new Path(workDir.getAbsolutePath());
571 for (IProject prj : ResourcesPlugin.getWorkspace()
572 .getRoot().getProjects()) {
573 if (wdPath.isPrefixOf(prj.getLocation())) {
574 projectsToDelete.add(prj);
578 if (!projectsToDelete.isEmpty()) {
579 boolean confirmed;
580 confirmed = confirmProjectDeletion(projectsToDelete);
581 if (!confirmed) {
582 return;
586 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
588 public void run(IProgressMonitor monitor)
589 throws CoreException {
591 for (IProject prj : ResourcesPlugin.getWorkspace()
592 .getRoot().getProjects()) {
593 if (wdPath.isPrefixOf(prj.getLocation())) {
594 prj.delete(false, false, monitor);
598 removeDir(repo.getDirectory());
599 scheduleRefresh();
603 try {
604 ResourcesPlugin.getWorkspace().run(wsr,
605 ResourcesPlugin.getWorkspace().getRoot(),
606 IWorkspace.AVOID_UPDATE,
607 new NullProgressMonitor());
608 } catch (CoreException e1) {
609 Activator.logError(e1.getMessage(), e1);
616 // TODO delete does not work because of file locks on .pack-files
617 // Shawn Pearce has added the following thoughts:
619 // Hmm. We probably can't active detect file locks on pack files on
620 // Windows, can we?
621 // It would be nice if we could support a delete, but only if the
622 // repository is
623 // reasonably believed to be not-in-use right now.
625 // Within EGit you might be able to check GitProjectData and its
626 // repositoryCache to
627 // see if the repository is open by this workspace. If it is, then
628 // we know we shouldn't
629 // try to delete it.
631 // Some coding might look like this:
633 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
634 // deleteRepo.setText("Delete");
635 // deleteRepo.addSelectionListener(new SelectionAdapter() {
637 // @Override
638 // public void widgetSelected(SelectionEvent e) {
640 // boolean confirmed = MessageDialog.openConfirm(getSite()
641 // .getShell(), "Confirm",
642 // "This will delete the repository, continue?");
644 // if (!confirmed)
645 // return;
647 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
649 // public void run(IProgressMonitor monitor)
650 // throws CoreException {
651 // File workDir = repos.get(0).getRepository()
652 // .getWorkDir();
654 // File gitDir = repos.get(0).getRepository()
655 // .getDirectory();
657 // IPath wdPath = new Path(workDir.getAbsolutePath());
658 // for (IProject prj : ResourcesPlugin.getWorkspace()
659 // .getRoot().getProjects()) {
660 // if (wdPath.isPrefixOf(prj.getLocation())) {
661 // prj.delete(false, false, monitor);
662 // }
663 // }
665 // repos.get(0).getRepository().close();
667 // boolean deleted = deleteRecursively(gitDir, monitor);
668 // if (!deleted) {
669 // MessageDialog.openError(getSite().getShell(),
670 // "Error",
671 // "Could not delete Git Repository");
672 // }
674 // deleted = deleteRecursively(workDir, monitor);
675 // if (!deleted) {
676 // MessageDialog
677 // .openError(getSite().getShell(),
678 // "Error",
679 // "Could not delete Git Working Directory");
680 // }
682 // scheduleRefresh();
683 // }
685 // private boolean deleteRecursively(File fileToDelete,
686 // IProgressMonitor monitor) {
687 // if (fileToDelete.isDirectory()) {
688 // for (File file : fileToDelete.listFiles()) {
689 // if (!deleteRecursively(file, monitor)) {
690 // return false;
691 // }
692 // }
693 // }
694 // monitor.setTaskName(fileToDelete.getAbsolutePath());
695 // boolean deleted = fileToDelete.delete();
696 // if (!deleted) {
697 // System.err.println("Could not delete "
698 // + fileToDelete.getAbsolutePath());
699 // }
700 // return deleted;
701 // }
702 // };
704 // try {
705 // ResourcesPlugin.getWorkspace().run(wsr,
706 // ResourcesPlugin.getWorkspace().getRoot(),
707 // IWorkspace.AVOID_UPDATE,
708 // new NullProgressMonitor());
709 // } catch (CoreException e1) {
710 // // TODO Exception handling
711 // e1.printStackTrace();
712 // }
714 // }
716 // });
718 new MenuItem(men, SWT.SEPARATOR);
720 createImportProjectItem(men, repo, repo.getWorkDir().getPath());
722 new MenuItem(men, SWT.SEPARATOR);
724 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
725 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
726 openPropsView.addSelectionListener(new SelectionAdapter() {
728 @Override
729 public void widgetSelected(SelectionEvent e) {
730 try {
731 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
732 .getActivePage().showView(
733 IPageLayout.ID_PROP_SHEET);
734 } catch (PartInitException e1) {
735 // just ignore
741 new MenuItem(men, SWT.SEPARATOR);
743 createCopyPathItem(men, repo.getDirectory().getPath());
746 if (node.getType() == RepositoryTreeNodeType.REMOTES) {
748 MenuItem remoteConfig = new MenuItem(men, SWT.PUSH);
749 remoteConfig.setText(UIText.RepositoriesView_NewRemoteMenu);
750 remoteConfig.addSelectionListener(new SelectionAdapter() {
752 @Override
753 public void widgetSelected(SelectionEvent e) {
755 WizardDialog dlg = new WizardDialog(getSite().getShell(),
756 new NewRemoteWizard(node.getRepository()));
757 if (dlg.open() == Window.OK)
758 scheduleRefresh();
765 if (node.getType() == RepositoryTreeNodeType.REMOTE) {
767 final String configName = (String) node.getObject();
769 RemoteConfig rconfig;
770 try {
771 rconfig = new RemoteConfig(node.getRepository().getConfig(),
772 configName);
773 } catch (URISyntaxException e2) {
774 // TODO Exception handling
775 rconfig = null;
778 boolean fetchExists = rconfig != null
779 && !rconfig.getURIs().isEmpty();
780 boolean pushExists = rconfig != null
781 && !rconfig.getPushURIs().isEmpty();
783 if (!fetchExists) {
784 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
785 configureUrlFetch
786 .setText(UIText.RepositoriesView_CreateFetch_menu);
788 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
790 @Override
791 public void widgetSelected(SelectionEvent e) {
793 WizardDialog dlg = new WizardDialog(getSite()
794 .getShell(), new ConfigureRemoteWizard(node
795 .getRepository(), configName, false));
796 if (dlg.open() == Window.OK)
797 scheduleRefresh();
804 if (!pushExists) {
805 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
807 configureUrlPush
808 .setText(UIText.RepositoriesView_CreatePush_menu);
810 configureUrlPush.addSelectionListener(new SelectionAdapter() {
812 @Override
813 public void widgetSelected(SelectionEvent e) {
815 WizardDialog dlg = new WizardDialog(getSite()
816 .getShell(), new ConfigureRemoteWizard(node
817 .getRepository(), configName, true));
818 if (dlg.open() == Window.OK)
819 scheduleRefresh();
826 if (!fetchExists || !pushExists)
827 // add a separator dynamically
828 new MenuItem(men, SWT.SEPARATOR);
830 MenuItem removeRemote = new MenuItem(men, SWT.PUSH);
831 removeRemote.setText(UIText.RepositoriesView_RemoveRemoteMenu);
832 removeRemote.addSelectionListener(new SelectionAdapter() {
834 @Override
835 public void widgetSelected(SelectionEvent e) {
837 boolean ok = MessageDialog
838 .openConfirm(
839 getSite().getShell(),
840 UIText.RepositoriesView_ConfirmDeleteRemoteHeader,
842 .bind(
843 UIText.RepositoriesView_ConfirmDeleteRemoteMessage,
844 configName));
845 if (ok) {
846 RepositoryConfig config = node.getRepository()
847 .getConfig();
848 config.unsetSection(REMOTE, configName);
849 try {
850 config.save();
851 scheduleRefresh();
852 } catch (IOException e1) {
853 Activator.handleError(
854 UIText.RepositoriesView_ErrorHeader, e1,
855 true);
863 new MenuItem(men, SWT.SEPARATOR);
865 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
866 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
867 openPropsView.addSelectionListener(new SelectionAdapter() {
869 @Override
870 public void widgetSelected(SelectionEvent e) {
871 try {
872 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
873 .getActivePage().showView(
874 IPageLayout.ID_PROP_SHEET);
875 } catch (PartInitException e1) {
876 // just ignore
883 if (node.getType() == RepositoryTreeNodeType.FETCH) {
885 final String configName = (String) node.getParent().getObject();
887 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
888 configureUrlFetch
889 .setText(UIText.RepositoriesView_ConfigureFetchMenu);
891 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
893 @Override
894 public void widgetSelected(SelectionEvent e) {
896 WizardDialog dlg = new WizardDialog(getSite().getShell(),
897 new ConfigureRemoteWizard(node.getRepository(),
898 configName, false));
899 if (dlg.open() == Window.OK)
900 scheduleRefresh();
906 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
907 deleteFetch.setText(UIText.RepositoriesView_RemoveFetch_menu);
908 deleteFetch.addSelectionListener(new SelectionAdapter() {
910 @Override
911 public void widgetSelected(SelectionEvent e) {
912 RepositoryConfig config = node.getRepository().getConfig();
913 config.unset("remote", configName, "url"); //$NON-NLS-1$ //$NON-NLS-2$
914 config.unset("remote", configName, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
915 try {
916 config.save();
917 scheduleRefresh();
918 } catch (IOException e1) {
919 MessageDialog.openError(getSite().getShell(),
920 UIText.RepositoriesView_ErrorHeader, e1
921 .getMessage());
929 if (node.getType() == RepositoryTreeNodeType.PUSH) {
931 final String configName = (String) node.getParent().getObject();
933 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
935 configureUrlPush.setText(UIText.RepositoriesView_ConfigurePushMenu);
937 configureUrlPush.addSelectionListener(new SelectionAdapter() {
939 @Override
940 public void widgetSelected(SelectionEvent e) {
942 WizardDialog dlg = new WizardDialog(getSite().getShell(),
943 new ConfigureRemoteWizard(node.getRepository(),
944 configName, true));
945 if (dlg.open() == Window.OK)
946 scheduleRefresh();
951 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
952 deleteFetch.setText(UIText.RepositoriesView_RemovePush_menu);
953 deleteFetch.addSelectionListener(new SelectionAdapter() {
955 @Override
956 public void widgetSelected(SelectionEvent e) {
957 RepositoryConfig config = node.getRepository().getConfig();
958 config.unset("remote", configName, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
959 config.unset("remote", configName, "push"); //$NON-NLS-1$ //$NON-NLS-2$
960 try {
961 config.save();
962 scheduleRefresh();
963 } catch (IOException e1) {
964 MessageDialog.openError(getSite().getShell(),
965 UIText.RepositoriesView_ErrorHeader, e1
966 .getMessage());
973 if (node.getType() == RepositoryTreeNodeType.FILE) {
975 final File file = (File) node.getObject();
977 MenuItem openInTextEditor = new MenuItem(men, SWT.PUSH);
978 openInTextEditor
979 .setText(UIText.RepositoriesView_OpenInTextEditor_menu);
980 openInTextEditor.addSelectionListener(new SelectionAdapter() {
982 @Override
983 public void widgetSelected(SelectionEvent e) {
984 openFile(file);
989 new MenuItem(men, SWT.SEPARATOR);
990 createCopyPathItem(men, file.getPath());
993 if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
994 String path = node.getRepository().getWorkDir().getAbsolutePath();
995 createImportProjectItem(men, node.getRepository(), path);
996 new MenuItem(men, SWT.SEPARATOR);
997 createCopyPathItem(men, path);
1000 if (node.getType() == RepositoryTreeNodeType.FOLDER) {
1001 String path = ((File) node.getObject()).getPath();
1002 createImportProjectItem(men, node.getRepository(), path);
1003 new MenuItem(men, SWT.SEPARATOR);
1004 createCopyPathItem(men, path);
1009 private void createCopyPathItem(Menu men, final String path) {
1011 MenuItem copyPath;
1012 copyPath = new MenuItem(men, SWT.PUSH);
1013 copyPath.setText(UIText.RepositoriesView_CopyPathToClipboardMenu);
1014 copyPath.addSelectionListener(new SelectionAdapter() {
1016 @Override
1017 public void widgetSelected(SelectionEvent e) {
1018 Clipboard clipboard = new Clipboard(null);
1019 TextTransfer textTransfer = TextTransfer.getInstance();
1020 Transfer[] transfers = new Transfer[] { textTransfer };
1021 Object[] data = new Object[] { path };
1022 clipboard.setContents(data, transfers);
1023 clipboard.dispose();
1030 private void openFile(File file) {
1031 IFileStore store = EFS.getLocalFileSystem().getStore(
1032 new Path(file.getAbsolutePath()));
1033 try {
1034 // TODO do we need a read-only editor here?
1035 IDE.openEditor(getSite().getPage(),
1036 new FileStoreEditorInput(store),
1037 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
1038 } catch (PartInitException e) {
1039 Activator.handleError(UIText.RepositoriesView_Error_WindowTitle, e,
1040 true);
1044 private void createImportProjectItem(Menu men, final Repository repo,
1045 final String path) {
1047 MenuItem startWizard;
1048 startWizard = new MenuItem(men, SWT.PUSH);
1049 startWizard.setText(UIText.RepositoriesView_ImportProjectsMenu);
1050 startWizard.addSelectionListener(new SelectionAdapter() {
1052 @Override
1053 public void widgetSelected(SelectionEvent e) {
1054 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1055 new GitCreateProjectViaWizardWizard(repo, path));
1056 if (dlg.open() == Window.OK)
1057 scheduleRefresh();
1063 // we could start the ImportWizard here,
1064 // unfortunately, this fails within a wizard
1065 // startWizard = new MenuItem(men, SWT.PUSH);
1066 // startWizard.setText("Start the Import wizard...");
1067 // startWizard.addSelectionListener(new SelectionAdapter() {
1069 // @Override
1070 // public void widgetSelected(SelectionEvent e) {
1072 // IHandlerService handlerService = (IHandlerService) getSite()
1073 // .getWorkbenchWindow().getWorkbench().getService(
1074 // IHandlerService.class);
1076 // try {
1077 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1078 // null);
1079 // } catch (ExecutionException e1) {
1080 // Activator.handleError(e1.getMessage(), e1, true);
1081 // } catch (NotDefinedException e1) {
1082 // Activator.handleError(e1.getMessage(), e1, true);
1083 // } catch (NotEnabledException e1) {
1084 // Activator.handleError(e1.getMessage(), e1, true);
1085 // } catch (NotHandledException e1) {
1086 // Activator.handleError(e1.getMessage(), e1, true);
1087 // }
1088 // }
1090 // });
1093 private void addActionsToToolbar() {
1094 importAction = new Action(UIText.RepositoriesView_Import_Button) {
1096 @Override
1097 public void run() {
1098 GitCloneWizard wiz = new GitCloneWizard();
1099 wiz.init(null, null);
1100 new WizardDialog(getSite().getShell(), wiz).open();
1103 importAction.setToolTipText(UIText.RepositoriesView_Clone_Tooltip);
1105 importAction.setImageDescriptor(UIIcons.IMPORT);
1107 getViewSite().getActionBars().getToolBarManager().add(importAction);
1109 addAction = new Action(UIText.RepositoriesView_Add_Button) {
1111 @Override
1112 public void run() {
1113 RepositorySearchDialog sd = new RepositorySearchDialog(
1114 getSite().getShell(), getDirs());
1115 if (sd.open() == Window.OK) {
1116 Set<String> dirs = new HashSet<String>();
1117 dirs.addAll(getDirs());
1118 if (dirs.addAll(sd.getDirectories()))
1119 saveDirs(dirs);
1120 scheduleRefresh();
1125 addAction.setToolTipText(UIText.RepositoriesView_AddRepository_Tooltip);
1127 addAction.setImageDescriptor(UIIcons.NEW_REPOSITORY);
1129 getViewSite().getActionBars().getToolBarManager().add(addAction);
1131 linkWithSelectionAction = new Action(
1132 UIText.RepositoriesView_LinkWithSelection_action,
1133 IAction.AS_CHECK_BOX) {
1135 @Override
1136 public void run() {
1137 IEclipsePreferences prefs = getPrefs();
1138 prefs.putBoolean(PREFS_SYNCED, isChecked());
1139 try {
1140 prefs.flush();
1141 } catch (BackingStoreException e) {
1142 // ignore here
1144 if (isChecked()) {
1145 ISelectionService srv = (ISelectionService) getSite()
1146 .getService(ISelectionService.class);
1147 reactOnSelection(srv.getSelection());
1154 linkWithSelectionAction
1155 .setToolTipText(UIText.RepositoriesView_LinkWithSelection_action);
1157 linkWithSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
1159 linkWithSelectionAction.setChecked(getPrefs().getBoolean(PREFS_SYNCED,
1160 false));
1162 getViewSite().getActionBars().getToolBarManager().add(
1163 linkWithSelectionAction);
1165 refreshAction = new Action(UIText.RepositoriesView_Refresh_Button) {
1167 @Override
1168 public void run() {
1169 scheduleRefresh();
1173 refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
1175 getViewSite().getActionBars().getToolBarManager().add(refreshAction);
1179 * @return the preferences
1181 protected static IEclipsePreferences getPrefs() {
1182 return new InstanceScope().getNode(Activator.getPluginId());
1185 @Override
1186 public void dispose() {
1187 // make sure to cancel the refresh job
1188 if (this.scheduledJob != null) {
1189 this.scheduledJob.cancel();
1190 this.scheduledJob = null;
1192 super.dispose();
1196 * Schedules a refreh
1198 public void scheduleRefresh() {
1200 Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1202 @Override
1203 protected IStatus run(IProgressMonitor monitor) {
1205 final List<Repository> input;
1206 try {
1207 input = getRepositoriesFromDirs(monitor);
1208 } catch (InterruptedException e) {
1209 return new Status(IStatus.ERROR, Activator.getPluginId(), e
1210 .getMessage(), e);
1213 Display.getDefault().syncExec(new Runnable() {
1215 public void run() {
1216 // keep expansion state and selection so that we can
1217 // restore the tree
1218 // after update
1219 Object[] expanded = tv.getExpandedElements();
1220 IStructuredSelection sel = (IStructuredSelection) tv
1221 .getSelection();
1222 tv.setInput(input);
1223 tv.setExpandedElements(expanded);
1225 Object selected = sel.getFirstElement();
1226 if (selected != null)
1227 tv.reveal(selected);
1229 IViewPart part = PlatformUI.getWorkbench()
1230 .getActiveWorkbenchWindow().getActivePage()
1231 .findView(IPageLayout.ID_PROP_SHEET);
1232 if (part != null) {
1233 PropertySheet sheet = (PropertySheet) part;
1234 PropertySheetPage page = (PropertySheetPage) sheet
1235 .getCurrentPage();
1236 page.refresh();
1241 return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$
1246 job.setSystem(true);
1248 IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite()
1249 .getService(IWorkbenchSiteProgressService.class);
1251 service.schedule(job);
1253 scheduledJob = job;
1257 private List<Repository> getRepositoriesFromDirs(IProgressMonitor monitor)
1258 throws InterruptedException {
1260 List<String> gitDirStrings = getDirs();
1261 List<Repository> input = new ArrayList<Repository>();
1262 for (String dirString : gitDirStrings) {
1263 if (monitor.isCanceled()) {
1264 throw new InterruptedException(
1265 UIText.RepositoriesView_ActionCanceled_Message);
1267 try {
1268 File dir = new File(dirString);
1269 if (dir.exists() && dir.isDirectory()) {
1270 input.add(new Repository(dir));
1272 } catch (IOException e) {
1273 IStatus error = new Status(IStatus.ERROR, Activator
1274 .getPluginId(), e.getMessage(), e);
1275 Activator.getDefault().getLog().log(error);
1278 return input;
1282 * Adds a directory to the list if it is not already there
1284 * @param file
1285 * @return see {@link Collection#add(Object)}
1287 public static boolean addDir(File file) {
1289 String dirString;
1290 try {
1291 dirString = file.getCanonicalPath();
1292 } catch (IOException e) {
1293 dirString = file.getAbsolutePath();
1296 List<String> dirStrings = getDirs();
1297 if (dirStrings.contains(dirString)) {
1298 return false;
1299 } else {
1300 Set<String> dirs = new HashSet<String>();
1301 dirs.addAll(dirStrings);
1302 dirs.add(dirString);
1303 saveDirs(dirs);
1304 return true;
1308 private static void saveDirs(Set<String> gitDirStrings) {
1309 StringBuilder sb = new StringBuilder();
1310 for (String gitDirString : gitDirStrings) {
1311 sb.append(gitDirString);
1312 sb.append(File.pathSeparatorChar);
1315 IEclipsePreferences prefs = getPrefs();
1316 prefs.put(PREFS_DIRECTORIES, sb.toString());
1317 try {
1318 prefs.flush();
1319 } catch (BackingStoreException e) {
1320 IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(),
1321 e.getMessage(), e);
1322 Activator.getDefault().getLog().log(error);
1326 @Override
1327 public void setFocus() {
1328 tv.getTree().setFocus();
1331 @SuppressWarnings("boxing")
1332 private boolean confirmProjectDeletion(List<IProject> projectsToDelete) {
1333 boolean confirmed;
1334 confirmed = MessageDialog
1335 .openConfirm(
1336 getSite().getShell(),
1337 UIText.RepositoriesView_ConfirmProjectDeletion_WindowTitle,
1339 .bind(
1340 UIText.RepositoriesView_ConfirmProjectDeletion_Question,
1341 projectsToDelete.size()));
1342 return confirmed;
1345 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1346 selectionListeners.add(listener);
1349 public ISelection getSelection() {
1350 return currentSelection;
1353 public void removeSelectionChangedListener(
1354 ISelectionChangedListener listener) {
1355 selectionListeners.remove(listener);
1359 public void setSelection(ISelection selection) {
1360 currentSelection = selection;
1361 for (ISelectionChangedListener listener : selectionListeners) {
1362 listener.selectionChanged(new SelectionChangedEvent(
1363 RepositoriesView.this, selection));
1368 * Opens the tree and marks the folder to which a project is pointing
1370 * @param resource
1371 * TODO exceptions?
1373 @SuppressWarnings("unchecked")
1374 public void showResource(final IResource resource) {
1375 IProject project = resource.getProject();
1376 RepositoryMapping mapping = RepositoryMapping.getMapping(project);
1377 if (mapping == null)
1378 return;
1380 if (addDir(mapping.getRepository().getDirectory())) {
1381 scheduleRefresh();
1384 boolean doSetSelection = false;
1386 if (this.scheduledJob != null) {
1387 int state = this.scheduledJob.getState();
1388 if (state == Job.WAITING || state == Job.RUNNING) {
1389 this.scheduledJob.addJobChangeListener(new JobChangeAdapter() {
1391 @Override
1392 public void done(IJobChangeEvent event) {
1393 showResource(resource);
1396 } else {
1397 doSetSelection = true;
1401 if (doSetSelection) {
1402 RepositoriesViewContentProvider cp = (RepositoriesViewContentProvider) tv
1403 .getContentProvider();
1404 RepositoryTreeNode currentNode = null;
1405 Object[] repos = cp.getElements(tv.getInput());
1406 for (Object repo : repos) {
1407 RepositoryTreeNode node = (RepositoryTreeNode) repo;
1408 // TODO equals implementation of Repository?
1409 if (mapping.getRepository().getDirectory().equals(
1410 ((Repository) node.getObject()).getDirectory())) {
1411 for (Object child : cp.getChildren(node)) {
1412 RepositoryTreeNode childNode = (RepositoryTreeNode) child;
1413 if (childNode.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1414 currentNode = childNode;
1415 break;
1418 break;
1422 IPath relPath = new Path(mapping.getRepoRelativePath(resource));
1424 for (String segment : relPath.segments()) {
1425 for (Object child : cp.getChildren(currentNode)) {
1426 RepositoryTreeNode<File> childNode = (RepositoryTreeNode<File>) child;
1427 if (childNode.getObject().getName().equals(segment)) {
1428 currentNode = childNode;
1429 break;
1434 final RepositoryTreeNode selNode = currentNode;
1436 Display.getDefault().asyncExec(new Runnable() {
1438 public void run() {
1439 tv.setSelection(new StructuredSelection(selNode), true);
1447 public boolean show(ShowInContext context) {
1448 ISelection selection = context.getSelection();
1449 if (selection instanceof IStructuredSelection) {
1450 IStructuredSelection ss = (IStructuredSelection) selection;
1451 if (ss.size() == 1) {
1452 Object element = ss.getFirstElement();
1453 if (element instanceof IAdaptable) {
1454 IResource resource = (IResource) ((IAdaptable) element)
1455 .getAdapter(IResource.class);
1456 if (resource != null) {
1457 showResource(resource);
1458 return true;
1463 return false;