Git Repositories View: Allow to "remove" multiple Repositories
[egit.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositoriesView.java
blob65f8d8bb8ecd34cea78d8646bddc3aac3b6d8f71
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.net.URISyntaxException;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Set;
23 import java.util.StringTokenizer;
24 import java.util.TreeSet;
26 import org.eclipse.core.filesystem.EFS;
27 import org.eclipse.core.filesystem.IFileStore;
28 import org.eclipse.core.resources.IProject;
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.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.project.RepositoryMapping;
48 import org.eclipse.egit.ui.Activator;
49 import org.eclipse.egit.ui.UIIcons;
50 import org.eclipse.egit.ui.UIText;
51 import org.eclipse.egit.ui.internal.clone.GitCloneWizard;
52 import org.eclipse.egit.ui.internal.clone.GitCreateProjectViaWizardWizard;
53 import org.eclipse.egit.ui.internal.repository.RepositoryTreeNode.RepositoryTreeNodeType;
54 import org.eclipse.jface.action.Action;
55 import org.eclipse.jface.action.IAction;
56 import org.eclipse.jface.action.IToolBarManager;
57 import org.eclipse.jface.action.Separator;
58 import org.eclipse.jface.dialogs.MessageDialog;
59 import org.eclipse.jface.dialogs.ProgressMonitorDialog;
60 import org.eclipse.jface.operation.IRunnableWithProgress;
61 import org.eclipse.jface.viewers.IOpenListener;
62 import org.eclipse.jface.viewers.ISelection;
63 import org.eclipse.jface.viewers.ISelectionChangedListener;
64 import org.eclipse.jface.viewers.ISelectionProvider;
65 import org.eclipse.jface.viewers.IStructuredSelection;
66 import org.eclipse.jface.viewers.ITreeContentProvider;
67 import org.eclipse.jface.viewers.OpenEvent;
68 import org.eclipse.jface.viewers.SelectionChangedEvent;
69 import org.eclipse.jface.viewers.StructuredSelection;
70 import org.eclipse.jface.viewers.TreeViewer;
71 import org.eclipse.jface.window.Window;
72 import org.eclipse.jface.wizard.Wizard;
73 import org.eclipse.jface.wizard.WizardDialog;
74 import org.eclipse.jgit.lib.Constants;
75 import org.eclipse.jgit.lib.Ref;
76 import org.eclipse.jgit.lib.RefUpdate;
77 import org.eclipse.jgit.lib.Repository;
78 import org.eclipse.jgit.lib.RepositoryCache;
79 import org.eclipse.jgit.lib.RepositoryConfig;
80 import org.eclipse.jgit.transport.RemoteConfig;
81 import org.eclipse.osgi.util.NLS;
82 import org.eclipse.swt.SWT;
83 import org.eclipse.swt.dnd.Clipboard;
84 import org.eclipse.swt.dnd.TextTransfer;
85 import org.eclipse.swt.dnd.Transfer;
86 import org.eclipse.swt.events.MenuDetectEvent;
87 import org.eclipse.swt.events.MenuDetectListener;
88 import org.eclipse.swt.events.SelectionAdapter;
89 import org.eclipse.swt.events.SelectionEvent;
90 import org.eclipse.swt.graphics.Point;
91 import org.eclipse.swt.widgets.Composite;
92 import org.eclipse.swt.widgets.Display;
93 import org.eclipse.swt.widgets.Menu;
94 import org.eclipse.swt.widgets.MenuItem;
95 import org.eclipse.swt.widgets.TreeItem;
96 import org.eclipse.ui.IEditorInput;
97 import org.eclipse.ui.IEditorPart;
98 import org.eclipse.ui.IFileEditorInput;
99 import org.eclipse.ui.IPageLayout;
100 import org.eclipse.ui.ISelectionListener;
101 import org.eclipse.ui.ISelectionService;
102 import org.eclipse.ui.IViewPart;
103 import org.eclipse.ui.IWorkbenchPart;
104 import org.eclipse.ui.PartInitException;
105 import org.eclipse.ui.PlatformUI;
106 import org.eclipse.ui.actions.ActionFactory;
107 import org.eclipse.ui.editors.text.EditorsUI;
108 import org.eclipse.ui.ide.FileStoreEditorInput;
109 import org.eclipse.ui.ide.IDE;
110 import org.eclipse.ui.part.IShowInTarget;
111 import org.eclipse.ui.part.ShowInContext;
112 import org.eclipse.ui.part.ViewPart;
113 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
114 import org.eclipse.ui.views.properties.IPropertySheetPage;
115 import org.eclipse.ui.views.properties.PropertySheet;
116 import org.eclipse.ui.views.properties.PropertySheetPage;
117 import org.osgi.service.prefs.BackingStoreException;
121 * The Git Repositories view.
122 * <p>
123 * This keeps track of a bunch of local directory names each of which represent
124 * a Git Repository. This list is stored in some Preferences object and used to
125 * build the tree in the view.
126 * <p>
127 * Implements {@link ISelectionProvider} in order to integrate with the
128 * Properties view.
129 * <p>
130 * TODO
131 * <li>Clarification whether to show projects, perhaps configurable switch</li>
134 public class RepositoriesView extends ViewPart implements ISelectionProvider,
135 IShowInTarget {
137 /** The view ID */
138 public static final String VIEW_ID = "org.eclipse.egit.ui.RepositoriesView"; //$NON-NLS-1$
140 // TODO central constants? RemoteConfig ones are private
141 static final String REMOTE = "remote"; //$NON-NLS-1$
143 static final String URL = "url"; //$NON-NLS-1$
145 static final String PUSHURL = "pushurl"; //$NON-NLS-1$
147 static final String FETCH = "fetch"; //$NON-NLS-1$
149 static final String PUSH = "push"; //$NON-NLS-1$
151 private static final String PREFS_DIRECTORIES = "GitRepositoriesView.GitDirectories"; //$NON-NLS-1$
153 private static final String PREFS_SYNCED = "GitRepositoriesView.SyncWithSelection"; //$NON-NLS-1$
155 private final List<ISelectionChangedListener> selectionListeners = new ArrayList<ISelectionChangedListener>();
157 private ISelection currentSelection = new StructuredSelection();
159 private Job scheduledJob;
161 private TreeViewer tv;
163 private IAction importAction;
165 private IAction addAction;
167 private IAction refreshAction;
169 private IAction linkWithSelectionAction;
171 private IAction copyAction;
173 private IAction pasteAction;
176 * TODO move to utility class
178 * @return the directories as configured for this view
180 public static List<String> getDirs() {
181 List<String> resultStrings = new ArrayList<String>();
182 String dirs = getPrefs().get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
183 if (dirs != null && dirs.length() > 0) {
184 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
185 while (tok.hasMoreTokens()) {
186 String dirName = tok.nextToken();
187 File testFile = new File(dirName);
188 if (testFile.exists()) {
189 resultStrings.add(dirName);
193 Collections.sort(resultStrings);
194 return resultStrings;
197 private static void removeDir(File file) {
199 String dir;
200 try {
201 dir = file.getCanonicalPath();
202 } catch (IOException e1) {
203 dir = file.getAbsolutePath();
206 IEclipsePreferences prefs = getPrefs();
208 TreeSet<String> resultStrings = new TreeSet<String>();
209 String dirs = prefs.get(PREFS_DIRECTORIES, ""); //$NON-NLS-1$
210 if (dirs != null && dirs.length() > 0) {
211 StringTokenizer tok = new StringTokenizer(dirs, File.pathSeparator);
212 while (tok.hasMoreTokens()) {
213 String dirName = tok.nextToken();
214 File testFile = new File(dirName);
215 if (testFile.exists()) {
216 try {
217 resultStrings.add(testFile.getCanonicalPath());
218 } catch (IOException e) {
219 resultStrings.add(testFile.getAbsolutePath());
225 if (resultStrings.remove(dir)) {
226 StringBuilder sb = new StringBuilder();
227 for (String gitDirString : resultStrings) {
228 sb.append(gitDirString);
229 sb.append(File.pathSeparatorChar);
232 prefs.put(PREFS_DIRECTORIES, sb.toString());
233 try {
234 prefs.flush();
235 } catch (BackingStoreException e) {
236 IStatus error = new Status(IStatus.ERROR, Activator
237 .getPluginId(), e.getMessage(), e);
238 Activator.getDefault().getLog().log(error);
244 @Override
245 public Object getAdapter(Class adapter) {
246 // integrate with Properties view
247 if (adapter == IPropertySheetPage.class) {
248 PropertySheetPage page = new PropertySheetPage();
249 page
250 .setPropertySourceProvider(new RepositoryPropertySourceProvider(
251 page));
252 return page;
255 return super.getAdapter(adapter);
258 @Override
259 public void createPartControl(Composite parent) {
260 tv = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
261 tv.setContentProvider(new RepositoriesViewContentProvider());
262 // the label provider registers itself
263 new RepositoriesViewLabelProvider(tv);
265 getSite().setSelectionProvider(this);
267 tv.addSelectionChangedListener(new ISelectionChangedListener() {
269 public void selectionChanged(SelectionChangedEvent event) {
271 copyAction.setEnabled(false);
273 IStructuredSelection ssel = (IStructuredSelection) event
274 .getSelection();
275 if (ssel.size() == 1) {
276 RepositoryTreeNode node = (RepositoryTreeNode) ssel
277 .getFirstElement();
278 // allow copy on repository, file, or folder (copying the
279 // directory)
280 if (node.getType() == RepositoryTreeNodeType.REPO
281 || node.getType() == RepositoryTreeNodeType.WORKINGDIR
282 || node.getType() == RepositoryTreeNodeType.FOLDER
283 || node.getType() == RepositoryTreeNodeType.FILE) {
284 copyAction.setEnabled(true);
286 setSelection(new StructuredSelection(ssel.getFirstElement()));
287 } else {
288 setSelection(new StructuredSelection());
293 tv.addOpenListener(new IOpenListener() {
294 public void open(OpenEvent event) {
295 IStructuredSelection selection = (IStructuredSelection) event
296 .getSelection();
297 if (selection.isEmpty()) {
298 // nothing selected, ignore
299 return;
302 Object element = selection.getFirstElement();
303 ITreeContentProvider contentProvider = (ITreeContentProvider) tv
304 .getContentProvider();
305 if (contentProvider.hasChildren(element)) {
306 // this element has children, expand/collapse it
307 tv.setExpandedState(element, !tv.getExpandedState(element));
308 } else {
309 Object[] selectionArray = selection.toArray();
310 for (Object selectedElement : selectionArray) {
311 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
312 // if any of the selected elements are not files, ignore
313 // the open request
314 if (node.getType() != RepositoryTreeNodeType.FILE
315 && node.getType() != RepositoryTreeNodeType.REF) {
316 return;
320 // open the files the user has selected
321 for (Object selectedElement : selectionArray) {
322 RepositoryTreeNode node = (RepositoryTreeNode) selectedElement;
323 if (node.getType() == RepositoryTreeNodeType.FILE)
324 openFile((File) node.getObject());
325 else if (node.getType() == RepositoryTreeNodeType.REF) {
326 Ref ref = (Ref) node.getObject();
327 if (ref.getName().startsWith(Constants.R_HEADS)
328 || ref.getName().startsWith(
329 Constants.R_REMOTES))
330 checkoutBranch(node, ref.getName());
337 addContextMenu();
339 addActionsToToolbar();
341 scheduleRefresh();
343 ISelectionService srv = (ISelectionService) getSite().getService(
344 ISelectionService.class);
345 srv.addPostSelectionListener(new ISelectionListener() {
347 public void selectionChanged(IWorkbenchPart part,
348 ISelection selection) {
350 // if the "link with selection" toggle is off, we're done
351 if (linkWithSelectionAction == null
352 || !linkWithSelectionAction.isChecked())
353 return;
355 // this may happen if we switch between editors
356 if (part instanceof IEditorPart) {
357 IEditorInput input = ((IEditorPart) part).getEditorInput();
358 if (input instanceof IFileEditorInput)
359 reactOnSelection(new StructuredSelection(
360 ((IFileEditorInput) input).getFile()));
362 } else {
363 reactOnSelection(selection);
370 private void reactOnSelection(ISelection selection) {
371 if (selection instanceof StructuredSelection) {
372 StructuredSelection ssel = (StructuredSelection) selection;
373 if (ssel.size() != 1)
374 return;
375 if (ssel.getFirstElement() instanceof IResource) {
376 showResource((IResource) ssel.getFirstElement());
378 if (ssel.getFirstElement() instanceof IAdaptable) {
379 IResource adapted = (IResource) ((IAdaptable) ssel
380 .getFirstElement()).getAdapter(IResource.class);
381 if (adapted != null)
382 showResource(adapted);
387 private void addContextMenu() {
388 tv.getTree().addMenuDetectListener(new MenuDetectListener() {
390 public void menuDetected(MenuDetectEvent e) {
392 tv.getTree().setMenu(null);
393 Menu men = new Menu(tv.getTree());
395 TreeItem testItem = tv.getTree().getItem(
396 tv.getTree().toControl(new Point(e.x, e.y)));
397 if (testItem == null) {
398 addMenuItemsForPanel(men);
399 } else {
400 addMenuItemsForTreeSelection(men);
403 tv.getTree().setMenu(men);
408 private void addMenuItemsForPanel(Menu men) {
410 MenuItem importItem = new MenuItem(men, SWT.PUSH);
411 importItem.setText(UIText.RepositoriesView_ImportRepository_MenuItem);
412 importItem.addSelectionListener(new SelectionAdapter() {
414 @Override
415 public void widgetSelected(SelectionEvent e) {
416 importAction.run();
421 MenuItem addItem = new MenuItem(men, SWT.PUSH);
422 addItem.setText(UIText.RepositoriesView_AddRepository_MenuItem);
423 addItem.addSelectionListener(new SelectionAdapter() {
425 @Override
426 public void widgetSelected(SelectionEvent e) {
427 addAction.run();
432 MenuItem pasteItem = new MenuItem(men, SWT.PUSH);
433 pasteItem.setText(UIText.RepositoriesView_PasteMenu);
434 pasteItem.addSelectionListener(new SelectionAdapter() {
436 @Override
437 public void widgetSelected(SelectionEvent e) {
438 pasteAction.run();
443 MenuItem refreshItem = new MenuItem(men, SWT.PUSH);
444 refreshItem.setText(refreshAction.getText());
445 refreshItem.addSelectionListener(new SelectionAdapter() {
447 @Override
448 public void widgetSelected(SelectionEvent e) {
449 refreshAction.run();
456 private void addMenuItemsForTreeSelection(Menu men) {
458 final IStructuredSelection sel = (IStructuredSelection) tv
459 .getSelection();
461 boolean repoOnly = true;
462 for (Object selected : sel.toArray()) {
464 if (((RepositoryTreeNode) selected).getType() != RepositoryTreeNodeType.REPO) {
465 repoOnly = false;
466 break;
470 if (sel.size() > 1 && repoOnly) {
471 List nodes = sel.toList();
472 final Repository[] repos = new Repository[nodes.size()];
473 for (int i = 0; i < sel.size(); i++)
474 repos[i] = ((RepositoryTreeNode) nodes.get(i)).getRepository();
476 MenuItem remove = new MenuItem(men, SWT.PUSH);
477 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
478 remove.addSelectionListener(new SelectionAdapter() {
480 @Override
481 public void widgetSelected(SelectionEvent e) {
482 // TODO progress monitoring/cancellation
483 removeRepository(new NullProgressMonitor(), repos);
489 // from here on, we only deal with single selection
490 if (sel.size() > 1)
491 return;
493 final RepositoryTreeNode node = (RepositoryTreeNode) sel
494 .getFirstElement();
496 if (node.getType() == RepositoryTreeNodeType.REF) {
498 final Ref ref = (Ref) node.getObject();
500 // we don't check out symbolic references
501 if (!ref.isSymbolic()) {
503 MenuItem checkout = new MenuItem(men, SWT.PUSH);
504 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
506 try {
507 if (node.getRepository().getFullBranch().equals(
508 ref.getName())) {
509 // no checkout on current branch
510 checkout.setEnabled(false);
512 } catch (IOException e2) {
513 // ignore
516 checkout.addSelectionListener(new SelectionAdapter() {
518 @Override
519 public void widgetSelected(SelectionEvent e) {
520 checkoutBranch(node, ref.getLeaf().getName());
524 new MenuItem(men, SWT.SEPARATOR);
526 createCreateBranchItem(men, node);
527 createDeleteBranchItem(men, node);
532 if (node.getType() == RepositoryTreeNodeType.LOCALBRANCHES
533 || node.getType() == RepositoryTreeNodeType.REMOTEBRANCHES)
534 createCreateBranchItem(men, node);
536 // for Repository: import existing projects, remove, (delete), open
537 // properties
538 if (node.getType() == RepositoryTreeNodeType.REPO) {
540 final Repository repo = (Repository) node.getObject();
542 // TODO "import existing plug-in" menu item
544 MenuItem remove = new MenuItem(men, SWT.PUSH);
545 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
546 remove.addSelectionListener(new SelectionAdapter() {
548 @Override
549 public void widgetSelected(SelectionEvent e) {
550 // TODO progress monitoring/cancellation
551 removeRepository(new NullProgressMonitor(), repo);
555 // TODO delete does not work because of file locks on .pack-files
556 // Shawn Pearce has added the following thoughts:
558 // Hmm. We probably can't active detect file locks on pack files on
559 // Windows, can we?
560 // It would be nice if we could support a delete, but only if the
561 // repository is
562 // reasonably believed to be not-in-use right now.
564 // Within EGit you might be able to check GitProjectData and its
565 // repositoryCache to
566 // see if the repository is open by this workspace. If it is, then
567 // we know we shouldn't
568 // try to delete it.
570 // Some coding might look like this:
572 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
573 // deleteRepo.setText("Delete");
574 // deleteRepo.addSelectionListener(new SelectionAdapter() {
576 // @Override
577 // public void widgetSelected(SelectionEvent e) {
579 // boolean confirmed = MessageDialog.openConfirm(getSite()
580 // .getShell(), "Confirm",
581 // "This will delete the repository, continue?");
583 // if (!confirmed)
584 // return;
586 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
588 // public void run(IProgressMonitor monitor)
589 // throws CoreException {
590 // File workDir = repos.get(0).getRepository()
591 // .getWorkDir();
593 // File gitDir = repos.get(0).getRepository()
594 // .getDirectory();
596 // IPath wdPath = new Path(workDir.getAbsolutePath());
597 // for (IProject prj : ResourcesPlugin.getWorkspace()
598 // .getRoot().getProjects()) {
599 // if (wdPath.isPrefixOf(prj.getLocation())) {
600 // prj.delete(false, false, monitor);
601 // }
602 // }
604 // repos.get(0).getRepository().close();
606 // boolean deleted = deleteRecursively(gitDir, monitor);
607 // if (!deleted) {
608 // MessageDialog.openError(getSite().getShell(),
609 // "Error",
610 // "Could not delete Git Repository");
611 // }
613 // deleted = deleteRecursively(workDir, monitor);
614 // if (!deleted) {
615 // MessageDialog
616 // .openError(getSite().getShell(),
617 // "Error",
618 // "Could not delete Git Working Directory");
619 // }
621 // scheduleRefresh();
622 // }
624 // private boolean deleteRecursively(File fileToDelete,
625 // IProgressMonitor monitor) {
626 // if (fileToDelete.isDirectory()) {
627 // for (File file : fileToDelete.listFiles()) {
628 // if (!deleteRecursively(file, monitor)) {
629 // return false;
630 // }
631 // }
632 // }
633 // monitor.setTaskName(fileToDelete.getAbsolutePath());
634 // boolean deleted = fileToDelete.delete();
635 // if (!deleted) {
636 // System.err.println("Could not delete "
637 // + fileToDelete.getAbsolutePath());
638 // }
639 // return deleted;
640 // }
641 // };
643 // try {
644 // ResourcesPlugin.getWorkspace().run(wsr,
645 // ResourcesPlugin.getWorkspace().getRoot(),
646 // IWorkspace.AVOID_UPDATE,
647 // new NullProgressMonitor());
648 // } catch (CoreException e1) {
649 // // TODO Exception handling
650 // e1.printStackTrace();
651 // }
653 // }
655 // });
657 new MenuItem(men, SWT.SEPARATOR);
659 createImportProjectItem(men, repo, repo.getWorkDir().getPath());
661 new MenuItem(men, SWT.SEPARATOR);
663 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
664 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
665 openPropsView.addSelectionListener(new SelectionAdapter() {
667 @Override
668 public void widgetSelected(SelectionEvent e) {
669 try {
670 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
671 .getActivePage().showView(
672 IPageLayout.ID_PROP_SHEET);
673 } catch (PartInitException e1) {
674 // just ignore
680 new MenuItem(men, SWT.SEPARATOR);
682 createCopyPathItem(men, repo.getDirectory().getPath());
685 if (node.getType() == RepositoryTreeNodeType.REMOTES) {
687 MenuItem remoteConfig = new MenuItem(men, SWT.PUSH);
688 remoteConfig.setText(UIText.RepositoriesView_NewRemoteMenu);
689 remoteConfig.addSelectionListener(new SelectionAdapter() {
691 @Override
692 public void widgetSelected(SelectionEvent e) {
694 WizardDialog dlg = new WizardDialog(getSite().getShell(),
695 new NewRemoteWizard(node.getRepository()));
696 if (dlg.open() == Window.OK)
697 scheduleRefresh();
704 if (node.getType() == RepositoryTreeNodeType.REMOTE) {
706 final String configName = (String) node.getObject();
708 RemoteConfig rconfig;
709 try {
710 rconfig = new RemoteConfig(node.getRepository().getConfig(),
711 configName);
712 } catch (URISyntaxException e2) {
713 // TODO Exception handling
714 rconfig = null;
717 boolean fetchExists = rconfig != null
718 && !rconfig.getURIs().isEmpty();
719 boolean pushExists = rconfig != null
720 && !rconfig.getPushURIs().isEmpty();
722 if (!fetchExists) {
723 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
724 configureUrlFetch
725 .setText(UIText.RepositoriesView_CreateFetch_menu);
727 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
729 @Override
730 public void widgetSelected(SelectionEvent e) {
732 WizardDialog dlg = new WizardDialog(getSite()
733 .getShell(), new ConfigureRemoteWizard(node
734 .getRepository(), configName, false));
735 if (dlg.open() == Window.OK)
736 scheduleRefresh();
743 if (!pushExists) {
744 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
746 configureUrlPush
747 .setText(UIText.RepositoriesView_CreatePush_menu);
749 configureUrlPush.addSelectionListener(new SelectionAdapter() {
751 @Override
752 public void widgetSelected(SelectionEvent e) {
754 WizardDialog dlg = new WizardDialog(getSite()
755 .getShell(), new ConfigureRemoteWizard(node
756 .getRepository(), configName, true));
757 if (dlg.open() == Window.OK)
758 scheduleRefresh();
765 if (!fetchExists || !pushExists)
766 // add a separator dynamically
767 new MenuItem(men, SWT.SEPARATOR);
769 MenuItem removeRemote = new MenuItem(men, SWT.PUSH);
770 removeRemote.setText(UIText.RepositoriesView_RemoveRemoteMenu);
771 removeRemote.addSelectionListener(new SelectionAdapter() {
773 @Override
774 public void widgetSelected(SelectionEvent e) {
776 boolean ok = MessageDialog
777 .openConfirm(
778 getSite().getShell(),
779 UIText.RepositoriesView_ConfirmDeleteRemoteHeader,
781 .bind(
782 UIText.RepositoriesView_ConfirmDeleteRemoteMessage,
783 configName));
784 if (ok) {
785 RepositoryConfig config = node.getRepository()
786 .getConfig();
787 config.unsetSection(REMOTE, configName);
788 try {
789 config.save();
790 scheduleRefresh();
791 } catch (IOException e1) {
792 Activator.handleError(
793 UIText.RepositoriesView_ErrorHeader, e1,
794 true);
802 new MenuItem(men, SWT.SEPARATOR);
804 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
805 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
806 openPropsView.addSelectionListener(new SelectionAdapter() {
808 @Override
809 public void widgetSelected(SelectionEvent e) {
810 try {
811 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
812 .getActivePage().showView(
813 IPageLayout.ID_PROP_SHEET);
814 } catch (PartInitException e1) {
815 // just ignore
822 if (node.getType() == RepositoryTreeNodeType.FETCH) {
824 final String configName = (String) node.getParent().getObject();
826 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
827 configureUrlFetch
828 .setText(UIText.RepositoriesView_ConfigureFetchMenu);
830 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
832 @Override
833 public void widgetSelected(SelectionEvent e) {
835 WizardDialog dlg = new WizardDialog(getSite().getShell(),
836 new ConfigureRemoteWizard(node.getRepository(),
837 configName, false));
838 if (dlg.open() == Window.OK)
839 scheduleRefresh();
845 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
846 deleteFetch.setText(UIText.RepositoriesView_RemoveFetch_menu);
847 deleteFetch.addSelectionListener(new SelectionAdapter() {
849 @Override
850 public void widgetSelected(SelectionEvent e) {
851 RepositoryConfig config = node.getRepository().getConfig();
852 config.unset("remote", configName, "url"); //$NON-NLS-1$ //$NON-NLS-2$
853 config.unset("remote", configName, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
854 try {
855 config.save();
856 scheduleRefresh();
857 } catch (IOException e1) {
858 MessageDialog.openError(getSite().getShell(),
859 UIText.RepositoriesView_ErrorHeader, e1
860 .getMessage());
868 if (node.getType() == RepositoryTreeNodeType.PUSH) {
870 final String configName = (String) node.getParent().getObject();
872 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
874 configureUrlPush.setText(UIText.RepositoriesView_ConfigurePushMenu);
876 configureUrlPush.addSelectionListener(new SelectionAdapter() {
878 @Override
879 public void widgetSelected(SelectionEvent e) {
881 WizardDialog dlg = new WizardDialog(getSite().getShell(),
882 new ConfigureRemoteWizard(node.getRepository(),
883 configName, true));
884 if (dlg.open() == Window.OK)
885 scheduleRefresh();
890 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
891 deleteFetch.setText(UIText.RepositoriesView_RemovePush_menu);
892 deleteFetch.addSelectionListener(new SelectionAdapter() {
894 @Override
895 public void widgetSelected(SelectionEvent e) {
896 RepositoryConfig config = node.getRepository().getConfig();
897 config.unset("remote", configName, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
898 config.unset("remote", configName, "push"); //$NON-NLS-1$ //$NON-NLS-2$
899 try {
900 config.save();
901 scheduleRefresh();
902 } catch (IOException e1) {
903 MessageDialog.openError(getSite().getShell(),
904 UIText.RepositoriesView_ErrorHeader, e1
905 .getMessage());
912 if (node.getType() == RepositoryTreeNodeType.FILE) {
914 final File file = (File) node.getObject();
916 MenuItem openInTextEditor = new MenuItem(men, SWT.PUSH);
917 openInTextEditor
918 .setText(UIText.RepositoriesView_OpenInTextEditor_menu);
919 openInTextEditor.addSelectionListener(new SelectionAdapter() {
921 @Override
922 public void widgetSelected(SelectionEvent e) {
923 openFile(file);
928 new MenuItem(men, SWT.SEPARATOR);
929 createCopyPathItem(men, file.getPath());
932 if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
933 String path = node.getRepository().getWorkDir().getAbsolutePath();
934 createImportProjectItem(men, node.getRepository(), path);
935 new MenuItem(men, SWT.SEPARATOR);
936 createCopyPathItem(men, path);
939 if (node.getType() == RepositoryTreeNodeType.FOLDER) {
940 String path = ((File) node.getObject()).getPath();
941 createImportProjectItem(men, node.getRepository(), path);
942 new MenuItem(men, SWT.SEPARATOR);
943 createCopyPathItem(men, path);
948 private void createCopyPathItem(Menu men, final String path) {
950 MenuItem copyPath;
951 copyPath = new MenuItem(men, SWT.PUSH);
952 copyPath.setText(UIText.RepositoriesView_CopyPathToClipboardMenu);
953 copyPath.addSelectionListener(new SelectionAdapter() {
955 @Override
956 public void widgetSelected(SelectionEvent e) {
957 Clipboard clipboard = new Clipboard(null);
958 TextTransfer textTransfer = TextTransfer.getInstance();
959 Transfer[] transfers = new Transfer[] { textTransfer };
960 Object[] data = new Object[] { path };
961 clipboard.setContents(data, transfers);
962 clipboard.dispose();
969 private void createCreateBranchItem(Menu men, final RepositoryTreeNode node) {
971 final boolean remoteMode;
972 final Ref ref;
973 if (node.getType() == RepositoryTreeNodeType.REF) {
974 remoteMode = node.getParent().getType() == RepositoryTreeNodeType.REMOTEBRANCHES;
975 ref = (Ref) node.getObject();
976 } else if (node.getType() == RepositoryTreeNodeType.LOCALBRANCHES) {
977 remoteMode = false;
978 ref = null;
979 } else if (node.getType() == RepositoryTreeNodeType.REMOTEBRANCHES) {
980 remoteMode = true;
981 ref = null;
982 } else
983 return;
985 MenuItem createLocal = new MenuItem(men, SWT.PUSH);
986 if (remoteMode)
987 createLocal.setText(UIText.RepositoriesView_NewRemoteBranchMenu);
988 else
989 createLocal.setText(UIText.RepositoriesView_NewLocalBranchMenu);
991 createLocal.addSelectionListener(new SelectionAdapter() {
993 @Override
994 public void widgetSelected(SelectionEvent e) {
996 Wizard wiz = new Wizard() {
998 @Override
999 public void addPages() {
1000 addPage(new CreateBranchPage(node.getRepository(), ref,
1001 remoteMode));
1002 setWindowTitle(UIText.RepositoriesView_NewBranchTitle);
1005 @Override
1006 public boolean performFinish() {
1008 try {
1009 getContainer().run(false, true,
1010 new IRunnableWithProgress() {
1012 public void run(IProgressMonitor monitor)
1013 throws InvocationTargetException,
1014 InterruptedException {
1015 CreateBranchPage cp = (CreateBranchPage) getPages()[0];
1016 try {
1017 cp.createBranch(monitor);
1018 } catch (CoreException ce) {
1019 throw new InvocationTargetException(
1020 ce);
1021 } catch (IOException ioe) {
1022 throw new InvocationTargetException(
1023 ioe);
1028 } catch (InvocationTargetException ite) {
1029 Activator
1030 .handleError(
1031 UIText.RepositoriesView_BranchCreationFailureMessage,
1032 ite.getCause(), true);
1033 return false;
1034 } catch (InterruptedException ie) {
1035 // ignore here
1037 return true;
1040 if (new WizardDialog(getSite().getShell(), wiz).open() == Window.OK)
1041 scheduleRefresh();
1048 private void createDeleteBranchItem(Menu men, final RepositoryTreeNode node) {
1050 final Ref ref = (Ref) node.getObject();
1052 MenuItem deleteBranch = new MenuItem(men, SWT.PUSH);
1053 deleteBranch.setText(UIText.RepositoriesView_DeleteBranchMenu);
1055 try {
1056 if (node.getRepository().getFullBranch().equals(ref.getName())) {
1057 deleteBranch.setEnabled(false);
1059 } catch (IOException e2) {
1060 // ignore
1063 deleteBranch.addSelectionListener(new SelectionAdapter() {
1065 @Override
1066 public void widgetSelected(SelectionEvent e) {
1068 if (!MessageDialog
1069 .openConfirm(
1070 getSite().getShell(),
1071 UIText.RepositoriesView_ConfirmDeleteTitle,
1073 .bind(
1074 UIText.RepositoriesView_ConfirmBranchDeletionMessage,
1075 ref.getName())))
1076 return;
1078 try {
1079 new ProgressMonitorDialog(getSite().getShell()).run(false,
1080 false, new IRunnableWithProgress() {
1082 public void run(IProgressMonitor monitor)
1083 throws InvocationTargetException,
1084 InterruptedException {
1086 try {
1087 RefUpdate op = node.getRepository()
1088 .updateRef(ref.getName());
1089 op.setRefLogMessage("branch deleted", //$NON-NLS-1$
1090 false);
1091 // we set the force update in order
1092 // to avoid having this rejected
1093 // due to minor issues
1094 op.setForceUpdate(true);
1095 op.delete();
1096 scheduleRefresh();
1097 } catch (IOException ioe) {
1098 throw new InvocationTargetException(ioe);
1103 } catch (InvocationTargetException e1) {
1104 Activator
1105 .handleError(
1106 UIText.RepositoriesView_BranchDeletionFailureMessage,
1107 e1.getCause(), true);
1108 e1.printStackTrace();
1109 } catch (InterruptedException e1) {
1110 // ignore
1118 private void openFile(File file) {
1119 IFileStore store = EFS.getLocalFileSystem().getStore(
1120 new Path(file.getAbsolutePath()));
1121 try {
1122 // TODO do we need a read-only editor here?
1123 IDE.openEditor(getSite().getPage(),
1124 new FileStoreEditorInput(store),
1125 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
1126 } catch (PartInitException e) {
1127 Activator.handleError(UIText.RepositoriesView_Error_WindowTitle, e,
1128 true);
1132 private void checkoutBranch(final RepositoryTreeNode node,
1133 final String refName) {
1134 // for the sake of UI responsiveness, let's start a job
1135 Job job = new Job(NLS.bind(UIText.RepositoriesView_CheckingOutMessage,
1136 refName)) {
1138 @Override
1139 protected IStatus run(IProgressMonitor monitor) {
1141 Repository repo = node.getRepository();
1143 final BranchOperation op = new BranchOperation(repo, refName);
1144 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1146 public void run(IProgressMonitor myMonitor)
1147 throws CoreException {
1148 op.execute(myMonitor);
1152 try {
1153 ResourcesPlugin.getWorkspace().run(wsr,
1154 ResourcesPlugin.getWorkspace().getRoot(),
1155 IWorkspace.AVOID_UPDATE, monitor);
1156 scheduleRefresh();
1157 } catch (CoreException e1) {
1158 return new Status(IStatus.ERROR, Activator.getPluginId(),
1159 e1.getMessage(), e1);
1162 return Status.OK_STATUS;
1166 job.setUser(true);
1167 job.schedule();
1170 private void createImportProjectItem(Menu men, final Repository repo,
1171 final String path) {
1173 MenuItem startWizard;
1174 startWizard = new MenuItem(men, SWT.PUSH);
1175 startWizard.setText(UIText.RepositoriesView_ImportProjectsMenu);
1176 startWizard.addSelectionListener(new SelectionAdapter() {
1178 @Override
1179 public void widgetSelected(SelectionEvent e) {
1180 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1181 new GitCreateProjectViaWizardWizard(repo, path));
1182 if (dlg.open() == Window.OK)
1183 scheduleRefresh();
1189 // we could start the ImportWizard here,
1190 // unfortunately, this fails within a wizard
1191 // startWizard = new MenuItem(men, SWT.PUSH);
1192 // startWizard.setText("Start the Import wizard...");
1193 // startWizard.addSelectionListener(new SelectionAdapter() {
1195 // @Override
1196 // public void widgetSelected(SelectionEvent e) {
1198 // IHandlerService handlerService = (IHandlerService) getSite()
1199 // .getWorkbenchWindow().getWorkbench().getService(
1200 // IHandlerService.class);
1202 // try {
1203 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1204 // null);
1205 // } catch (ExecutionException e1) {
1206 // Activator.handleError(e1.getMessage(), e1, true);
1207 // } catch (NotDefinedException e1) {
1208 // Activator.handleError(e1.getMessage(), e1, true);
1209 // } catch (NotEnabledException e1) {
1210 // Activator.handleError(e1.getMessage(), e1, true);
1211 // } catch (NotHandledException e1) {
1212 // Activator.handleError(e1.getMessage(), e1, true);
1213 // }
1214 // }
1216 // });
1219 private void addActionsToToolbar() {
1221 IToolBarManager manager = getViewSite().getActionBars()
1222 .getToolBarManager();
1224 refreshAction = new Action(UIText.RepositoriesView_Refresh_Button) {
1226 @Override
1227 public void run() {
1228 scheduleRefresh();
1231 refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
1232 manager.add(refreshAction);
1234 linkWithSelectionAction = new Action(
1235 UIText.RepositoriesView_LinkWithSelection_action,
1236 IAction.AS_CHECK_BOX) {
1238 @Override
1239 public void run() {
1240 IEclipsePreferences prefs = getPrefs();
1241 prefs.putBoolean(PREFS_SYNCED, isChecked());
1242 try {
1243 prefs.flush();
1244 } catch (BackingStoreException e) {
1245 // ignore here
1247 if (isChecked()) {
1248 ISelectionService srv = (ISelectionService) getSite()
1249 .getService(ISelectionService.class);
1250 reactOnSelection(srv.getSelection());
1257 linkWithSelectionAction
1258 .setToolTipText(UIText.RepositoriesView_LinkWithSelection_action);
1259 linkWithSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
1260 linkWithSelectionAction.setChecked(getPrefs().getBoolean(PREFS_SYNCED,
1261 false));
1263 manager.add(linkWithSelectionAction);
1265 manager.add(new Separator());
1267 IAction collapseAllAction = new Action(
1268 UIText.RepositoriesView_CollapseAllMenu) {
1270 @Override
1271 public void run() {
1272 tv.collapseAll();
1275 collapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL);
1276 manager.add(collapseAllAction);
1278 manager.add(new Separator());
1280 importAction = new Action(UIText.RepositoriesView_Import_Button) {
1282 @Override
1283 public void run() {
1284 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1285 new GitCloneWizard());
1286 if (dlg.open() == Window.OK)
1287 scheduleRefresh();
1290 importAction.setToolTipText(UIText.RepositoriesView_Clone_Tooltip);
1291 importAction.setImageDescriptor(UIIcons.CLONEGIT);
1293 manager.add(importAction);
1295 addAction = new Action(UIText.RepositoriesView_Add_Button) {
1297 @Override
1298 public void run() {
1299 RepositorySearchDialog sd = new RepositorySearchDialog(
1300 getSite().getShell(), getDirs());
1301 if (sd.open() == Window.OK) {
1302 Set<String> dirs = new HashSet<String>();
1303 dirs.addAll(getDirs());
1304 if (dirs.addAll(sd.getDirectories()))
1305 saveDirs(dirs);
1306 scheduleRefresh();
1311 addAction.setToolTipText(UIText.RepositoriesView_AddRepository_Tooltip);
1312 addAction.setImageDescriptor(UIIcons.NEW_REPOSITORY);
1314 manager.add(addAction);
1316 // copy and paste are global actions; we just implement them
1317 // and register them with the global action handler
1318 // we enable/disable them upon tree selection changes
1320 copyAction = new Action("") { //$NON-NLS-1$
1322 @Override
1323 public void run() {
1324 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1325 IStructuredSelection sel = (IStructuredSelection) tv
1326 .getSelection();
1327 if (sel.size() == 1) {
1328 RepositoryTreeNode node = (RepositoryTreeNode) sel
1329 .getFirstElement();
1330 String dir = null;
1331 if (node.getType() == RepositoryTreeNodeType.REPO) {
1332 dir = node.getRepository().getDirectory().getPath();
1333 } else if (node.getType() == RepositoryTreeNodeType.FILE
1334 || node.getType() == RepositoryTreeNodeType.FOLDER) {
1335 dir = ((File) node.getObject()).getPath();
1336 } else if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1337 dir = node.getRepository().getWorkDir().getPath();
1339 if (dir != null) {
1340 Clipboard clip = null;
1341 try {
1342 clip = new Clipboard(getSite().getShell()
1343 .getDisplay());
1344 clip
1345 .setContents(new Object[] { dir },
1346 new Transfer[] { TextTransfer
1347 .getInstance() });
1348 } finally {
1349 if (clip != null)
1350 // we must dispose ourselves
1351 clip.dispose();
1358 copyAction.setEnabled(false);
1360 getViewSite().getActionBars().setGlobalActionHandler(
1361 ActionFactory.COPY.getId(), copyAction);
1363 pasteAction = new Action("") { //$NON-NLS-1$
1365 @Override
1366 public void run() {
1367 // we check if the pasted content is a directory
1368 // repository location and try to add this
1369 String errorMessage = null;
1371 Clipboard clip = null;
1372 try {
1373 clip = new Clipboard(getSite().getShell().getDisplay());
1374 String content = (String) clip.getContents(TextTransfer
1375 .getInstance());
1376 if (content == null) {
1377 errorMessage = UIText.RepositoriesView_NothingToPasteMessage;
1378 return;
1381 File file = new File(content);
1382 if (!file.exists() || !file.isDirectory()) {
1383 errorMessage = UIText.RepositoriesView_ClipboardContentNotDirectoryMessage;
1384 return;
1387 if (!RepositoryCache.FileKey.isGitRepository(file)) {
1388 errorMessage = NLS
1389 .bind(
1390 UIText.RepositoriesView_ClipboardContentNoGitRepoMessage,
1391 content);
1392 return;
1395 if (addDir(file))
1396 scheduleRefresh();
1397 else
1398 errorMessage = NLS.bind(
1399 UIText.RepositoriesView_PasteRepoAlreadyThere,
1400 content);
1401 } finally {
1402 if (clip != null)
1403 // we must dispose ourselves
1404 clip.dispose();
1405 if (errorMessage != null)
1406 // TODO String ext
1407 MessageDialog.openWarning(getSite().getShell(),
1408 UIText.RepositoriesView_PasteFailureTitle,
1409 errorMessage);
1415 getViewSite().getActionBars().setGlobalActionHandler(
1416 ActionFactory.PASTE.getId(), pasteAction);
1421 * @return the preferences
1423 protected static IEclipsePreferences getPrefs() {
1424 return new InstanceScope().getNode(Activator.getPluginId());
1427 @Override
1428 public void dispose() {
1429 // make sure to cancel the refresh job
1430 if (this.scheduledJob != null) {
1431 this.scheduledJob.cancel();
1432 this.scheduledJob = null;
1434 super.dispose();
1438 * Schedules a refreh
1440 public void scheduleRefresh() {
1442 Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1444 @Override
1445 protected IStatus run(IProgressMonitor monitor) {
1447 final List<RepositoryTreeNode<Repository>> input;
1448 try {
1449 input = getRepositoriesFromDirs(monitor);
1450 } catch (InterruptedException e) {
1451 return new Status(IStatus.ERROR, Activator.getPluginId(), e
1452 .getMessage(), e);
1455 boolean needsNewInput = tv.getInput() == null;
1456 List oldInput = (List) tv.getInput();
1457 if (!needsNewInput)
1458 needsNewInput = oldInput.size() != input.size();
1460 if (!needsNewInput) {
1461 for (int i = 0; i < input.size(); i++) {
1462 needsNewInput = !input.get(i).equals(oldInput.get(i));
1463 if (needsNewInput)
1464 break;
1468 final boolean updateInput = needsNewInput;
1470 Display.getDefault().syncExec(new Runnable() {
1472 public void run() {
1473 // keep expansion state and selection so that we can
1474 // restore the tree
1475 // after update
1476 Object[] expanded = tv.getExpandedElements();
1477 IStructuredSelection sel = (IStructuredSelection) tv
1478 .getSelection();
1479 if (updateInput)
1480 tv.setInput(input);
1481 else
1482 tv.refresh();
1483 tv.setExpandedElements(expanded);
1485 Object selected = sel.getFirstElement();
1486 if (selected != null)
1487 tv.reveal(selected);
1489 IViewPart part = PlatformUI.getWorkbench()
1490 .getActiveWorkbenchWindow().getActivePage()
1491 .findView(IPageLayout.ID_PROP_SHEET);
1492 if (part != null) {
1493 PropertySheet sheet = (PropertySheet) part;
1494 PropertySheetPage page = (PropertySheetPage) sheet
1495 .getCurrentPage();
1496 page.refresh();
1501 return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$
1506 job.setSystem(true);
1508 IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite()
1509 .getService(IWorkbenchSiteProgressService.class);
1511 service.schedule(job);
1513 scheduledJob = job;
1518 * Adds a directory to the list if it is not already there
1520 * @param file
1521 * @return see {@link Collection#add(Object)}
1523 public static boolean addDir(File file) {
1525 String dirString;
1526 try {
1527 dirString = file.getCanonicalPath();
1528 } catch (IOException e) {
1529 dirString = file.getAbsolutePath();
1532 List<String> dirStrings = getDirs();
1533 if (dirStrings.contains(dirString)) {
1534 return false;
1535 } else {
1536 Set<String> dirs = new HashSet<String>();
1537 dirs.addAll(dirStrings);
1538 dirs.add(dirString);
1539 saveDirs(dirs);
1540 return true;
1545 * Converts the directories as configured for this view into a list of
1546 * {@link Repository} objects suitable for the tree content provider
1547 * <p>
1548 * TODO move to some utility class
1550 * @param monitor
1551 * @return a list of nodes
1552 * @throws InterruptedException
1554 public static List<RepositoryTreeNode<Repository>> getRepositoriesFromDirs(
1555 IProgressMonitor monitor) throws InterruptedException {
1557 List<String> gitDirStrings = getDirs();
1558 List<RepositoryTreeNode<Repository>> input = new ArrayList<RepositoryTreeNode<Repository>>();
1560 for (String dirString : gitDirStrings) {
1561 if (monitor != null && monitor.isCanceled()) {
1562 throw new InterruptedException(
1563 UIText.RepositoriesView_ActionCanceled_Message);
1565 try {
1566 File dir = new File(dirString);
1567 if (dir.exists() && dir.isDirectory()) {
1568 Repository repo = new Repository(dir);
1569 RepositoryTreeNode<Repository> node = new RepositoryTreeNode<Repository>(
1570 null, RepositoryTreeNodeType.REPO, repo, repo);
1571 input.add(node);
1573 } catch (IOException e) {
1574 IStatus error = new Status(IStatus.ERROR, Activator
1575 .getPluginId(), e.getMessage(), e);
1576 Activator.getDefault().getLog().log(error);
1579 Collections.sort(input);
1580 return input;
1583 private static void saveDirs(Set<String> gitDirStrings) {
1584 StringBuilder sb = new StringBuilder();
1585 for (String gitDirString : gitDirStrings) {
1586 sb.append(gitDirString);
1587 sb.append(File.pathSeparatorChar);
1590 IEclipsePreferences prefs = getPrefs();
1591 prefs.put(PREFS_DIRECTORIES, sb.toString());
1592 try {
1593 prefs.flush();
1594 } catch (BackingStoreException e) {
1595 IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(),
1596 e.getMessage(), e);
1597 Activator.getDefault().getLog().log(error);
1601 @Override
1602 public void setFocus() {
1603 tv.getTree().setFocus();
1606 @SuppressWarnings("boxing")
1607 private boolean confirmProjectDeletion(List<IProject> projectsToDelete) {
1608 boolean confirmed;
1609 confirmed = MessageDialog
1610 .openConfirm(
1611 getSite().getShell(),
1612 UIText.RepositoriesView_ConfirmProjectDeletion_WindowTitle,
1614 .bind(
1615 UIText.RepositoriesView_ConfirmProjectDeletion_Question,
1616 projectsToDelete.size()));
1617 return confirmed;
1620 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1621 selectionListeners.add(listener);
1624 public ISelection getSelection() {
1625 return currentSelection;
1628 public void removeSelectionChangedListener(
1629 ISelectionChangedListener listener) {
1630 selectionListeners.remove(listener);
1634 public void setSelection(ISelection selection) {
1635 currentSelection = selection;
1636 for (ISelectionChangedListener listener : selectionListeners) {
1637 listener.selectionChanged(new SelectionChangedEvent(
1638 RepositoriesView.this, selection));
1643 * Opens the tree and marks the folder to which a project is pointing
1645 * @param resource
1646 * TODO exceptions?
1648 @SuppressWarnings("unchecked")
1649 public void showResource(final IResource resource) {
1650 IProject project = resource.getProject();
1651 RepositoryMapping mapping = RepositoryMapping.getMapping(project);
1652 if (mapping == null)
1653 return;
1655 if (addDir(mapping.getRepository().getDirectory())) {
1656 scheduleRefresh();
1659 boolean doSetSelection = false;
1661 if (this.scheduledJob != null) {
1662 int state = this.scheduledJob.getState();
1663 if (state == Job.WAITING || state == Job.RUNNING) {
1664 this.scheduledJob.addJobChangeListener(new JobChangeAdapter() {
1666 @Override
1667 public void done(IJobChangeEvent event) {
1668 showResource(resource);
1671 } else {
1672 doSetSelection = true;
1676 if (doSetSelection) {
1677 RepositoriesViewContentProvider cp = (RepositoriesViewContentProvider) tv
1678 .getContentProvider();
1679 RepositoryTreeNode currentNode = null;
1680 Object[] repos = cp.getElements(tv.getInput());
1681 for (Object repo : repos) {
1682 RepositoryTreeNode node = (RepositoryTreeNode) repo;
1683 // TODO equals implementation of Repository?
1684 if (mapping.getRepository().getDirectory().equals(
1685 ((Repository) node.getObject()).getDirectory())) {
1686 for (Object child : cp.getChildren(node)) {
1687 RepositoryTreeNode childNode = (RepositoryTreeNode) child;
1688 if (childNode.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1689 currentNode = childNode;
1690 break;
1693 break;
1697 IPath relPath = new Path(mapping.getRepoRelativePath(resource));
1699 for (String segment : relPath.segments()) {
1700 for (Object child : cp.getChildren(currentNode)) {
1701 RepositoryTreeNode<File> childNode = (RepositoryTreeNode<File>) child;
1702 if (childNode.getObject().getName().equals(segment)) {
1703 currentNode = childNode;
1704 break;
1709 final RepositoryTreeNode selNode = currentNode;
1711 Display.getDefault().asyncExec(new Runnable() {
1713 public void run() {
1714 tv.setSelection(new StructuredSelection(selNode), true);
1722 public boolean show(ShowInContext context) {
1723 ISelection selection = context.getSelection();
1724 if (selection instanceof IStructuredSelection) {
1725 IStructuredSelection ss = (IStructuredSelection) selection;
1726 if (ss.size() == 1) {
1727 Object element = ss.getFirstElement();
1728 if (element instanceof IAdaptable) {
1729 IResource resource = (IResource) ((IAdaptable) element)
1730 .getAdapter(IResource.class);
1731 if (resource != null) {
1732 showResource(resource);
1733 return true;
1738 return false;
1741 private void removeRepository(final IProgressMonitor monitor,
1742 final Repository... repository) {
1743 final List<IProject> projectsToDelete = new ArrayList<IProject>();
1745 monitor
1746 .setTaskName(UIText.RepositoriesView_DeleteRepoDeterminProjectsMessage);
1748 for (Repository repo : repository) {
1749 File workDir = repo.getWorkDir();
1750 final IPath wdPath = new Path(workDir.getAbsolutePath());
1751 for (IProject prj : ResourcesPlugin.getWorkspace().getRoot()
1752 .getProjects()) {
1753 if (monitor.isCanceled())
1754 return;
1755 if (wdPath.isPrefixOf(prj.getLocation())) {
1756 projectsToDelete.add(prj);
1761 if (!projectsToDelete.isEmpty()) {
1762 boolean confirmed;
1763 confirmed = confirmProjectDeletion(projectsToDelete);
1764 if (!confirmed) {
1765 return;
1769 if (monitor.isCanceled())
1770 return;
1772 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1774 public void run(IProgressMonitor actMonitor) throws CoreException {
1776 for (IProject prj : projectsToDelete) {
1777 prj.delete(false, false, actMonitor);
1779 for (Repository repo : repository)
1780 removeDir(repo.getDirectory());
1781 scheduleRefresh();
1785 try {
1786 ResourcesPlugin.getWorkspace().run(wsr,
1787 ResourcesPlugin.getWorkspace().getRoot(),
1788 IWorkspace.AVOID_UPDATE, monitor);
1789 } catch (CoreException e1) {
1790 Activator.logError(e1.getMessage(), e1);