Git Repositories View: Bare Repositoy Support
[egit/spearce.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / repository / RepositoriesView.java
blobcfea62bfd14c2d5cfac9dd6c4848b6bcb31303b9
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 (!isBare(node.getRepository())
328 && (ref.getName().startsWith(
329 Constants.R_HEADS) || ref.getName()
330 .startsWith(Constants.R_REMOTES)))
331 checkoutBranch(node, ref.getName());
338 addContextMenu();
340 addActionsToToolbar();
342 scheduleRefresh();
344 ISelectionService srv = (ISelectionService) getSite().getService(
345 ISelectionService.class);
346 srv.addPostSelectionListener(new ISelectionListener() {
348 public void selectionChanged(IWorkbenchPart part,
349 ISelection selection) {
351 // if the "link with selection" toggle is off, we're done
352 if (linkWithSelectionAction == null
353 || !linkWithSelectionAction.isChecked())
354 return;
356 // this may happen if we switch between editors
357 if (part instanceof IEditorPart) {
358 IEditorInput input = ((IEditorPart) part).getEditorInput();
359 if (input instanceof IFileEditorInput)
360 reactOnSelection(new StructuredSelection(
361 ((IFileEditorInput) input).getFile()));
363 } else {
364 reactOnSelection(selection);
371 private void reactOnSelection(ISelection selection) {
372 if (selection instanceof StructuredSelection) {
373 StructuredSelection ssel = (StructuredSelection) selection;
374 if (ssel.size() != 1)
375 return;
376 if (ssel.getFirstElement() instanceof IResource) {
377 showResource((IResource) ssel.getFirstElement());
379 if (ssel.getFirstElement() instanceof IAdaptable) {
380 IResource adapted = (IResource) ((IAdaptable) ssel
381 .getFirstElement()).getAdapter(IResource.class);
382 if (adapted != null)
383 showResource(adapted);
388 private void addContextMenu() {
389 tv.getTree().addMenuDetectListener(new MenuDetectListener() {
391 public void menuDetected(MenuDetectEvent e) {
392 Menu men = tv.getTree().getMenu();
393 if (men != null) {
394 men.dispose();
396 men = new Menu(tv.getTree());
398 TreeItem testItem = tv.getTree().getItem(
399 tv.getTree().toControl(new Point(e.x, e.y)));
400 if (testItem == null) {
401 addMenuItemsForPanel(men);
402 } else {
403 addMenuItemsForTreeSelection(men);
406 tv.getTree().setMenu(men);
411 private void addMenuItemsForPanel(Menu men) {
413 MenuItem importItem = new MenuItem(men, SWT.PUSH);
414 importItem.setText(UIText.RepositoriesView_ImportRepository_MenuItem);
415 importItem.addSelectionListener(new SelectionAdapter() {
417 @Override
418 public void widgetSelected(SelectionEvent e) {
419 importAction.run();
424 MenuItem addItem = new MenuItem(men, SWT.PUSH);
425 addItem.setText(UIText.RepositoriesView_AddRepository_MenuItem);
426 addItem.addSelectionListener(new SelectionAdapter() {
428 @Override
429 public void widgetSelected(SelectionEvent e) {
430 addAction.run();
435 MenuItem pasteItem = new MenuItem(men, SWT.PUSH);
436 pasteItem.setText(UIText.RepositoriesView_PasteMenu);
437 pasteItem.addSelectionListener(new SelectionAdapter() {
439 @Override
440 public void widgetSelected(SelectionEvent e) {
441 pasteAction.run();
446 MenuItem refreshItem = new MenuItem(men, SWT.PUSH);
447 refreshItem.setText(refreshAction.getText());
448 refreshItem.addSelectionListener(new SelectionAdapter() {
450 @Override
451 public void widgetSelected(SelectionEvent e) {
452 refreshAction.run();
459 private void addMenuItemsForTreeSelection(Menu men) {
461 final IStructuredSelection sel = (IStructuredSelection) tv
462 .getSelection();
464 boolean repoOnly = true;
465 for (Object selected : sel.toArray()) {
467 if (((RepositoryTreeNode) selected).getType() != RepositoryTreeNodeType.REPO) {
468 repoOnly = false;
469 break;
473 if (sel.size() > 1 && repoOnly) {
474 List nodes = sel.toList();
475 final Repository[] repos = new Repository[nodes.size()];
476 for (int i = 0; i < sel.size(); i++)
477 repos[i] = ((RepositoryTreeNode) nodes.get(i)).getRepository();
479 MenuItem remove = new MenuItem(men, SWT.PUSH);
480 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
481 remove.addSelectionListener(new SelectionAdapter() {
483 @Override
484 public void widgetSelected(SelectionEvent e) {
485 // TODO progress monitoring/cancellation
486 removeRepository(new NullProgressMonitor(), repos);
492 // from here on, we only deal with single selection
493 if (sel.size() > 1)
494 return;
496 final RepositoryTreeNode node = (RepositoryTreeNode) sel
497 .getFirstElement();
499 final boolean isBare = isBare(node.getRepository());
501 if (node.getType() == RepositoryTreeNodeType.REF) {
503 final Ref ref = (Ref) node.getObject();
505 // we don't check out symbolic references
506 if (!ref.isSymbolic()) {
508 if (!isBare) {
509 MenuItem checkout = new MenuItem(men, SWT.PUSH);
510 checkout.setText(UIText.RepositoriesView_CheckOut_MenuItem);
512 try {
513 if (node.getRepository().getFullBranch().equals(
514 ref.getName())) {
515 // no checkout on current branch
516 checkout.setEnabled(false);
518 } catch (IOException e2) {
519 // ignore
522 checkout.addSelectionListener(new SelectionAdapter() {
524 @Override
525 public void widgetSelected(SelectionEvent e) {
526 checkoutBranch(node, ref.getLeaf().getName());
530 new MenuItem(men, SWT.SEPARATOR);
533 createCreateBranchItem(men, node);
534 createDeleteBranchItem(men, node);
539 if (node.getType() == RepositoryTreeNodeType.LOCALBRANCHES
540 || node.getType() == RepositoryTreeNodeType.REMOTEBRANCHES)
541 createCreateBranchItem(men, node);
543 // for Repository: import existing projects, remove, (delete), open
544 // properties
545 if (node.getType() == RepositoryTreeNodeType.REPO) {
547 final Repository repo = (Repository) node.getObject();
549 // TODO "import existing plug-in" menu item
551 MenuItem remove = new MenuItem(men, SWT.PUSH);
552 remove.setText(UIText.RepositoriesView_Remove_MenuItem);
553 remove.addSelectionListener(new SelectionAdapter() {
555 @Override
556 public void widgetSelected(SelectionEvent e) {
557 // TODO progress monitoring/cancellation
558 removeRepository(new NullProgressMonitor(), repo);
562 // TODO delete does not work because of file locks on .pack-files
563 // Shawn Pearce has added the following thoughts:
565 // Hmm. We probably can't active detect file locks on pack files on
566 // Windows, can we?
567 // It would be nice if we could support a delete, but only if the
568 // repository is
569 // reasonably believed to be not-in-use right now.
571 // Within EGit you might be able to check GitProjectData and its
572 // repositoryCache to
573 // see if the repository is open by this workspace. If it is, then
574 // we know we shouldn't
575 // try to delete it.
577 // Some coding might look like this:
579 // MenuItem deleteRepo = new MenuItem(men, SWT.PUSH);
580 // deleteRepo.setText("Delete");
581 // deleteRepo.addSelectionListener(new SelectionAdapter() {
583 // @Override
584 // public void widgetSelected(SelectionEvent e) {
586 // boolean confirmed = MessageDialog.openConfirm(getSite()
587 // .getShell(), "Confirm",
588 // "This will delete the repository, continue?");
590 // if (!confirmed)
591 // return;
593 // IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
595 // public void run(IProgressMonitor monitor)
596 // throws CoreException {
597 // File workDir = repos.get(0).getRepository()
598 // .getWorkDir();
600 // File gitDir = repos.get(0).getRepository()
601 // .getDirectory();
603 // IPath wdPath = new Path(workDir.getAbsolutePath());
604 // for (IProject prj : ResourcesPlugin.getWorkspace()
605 // .getRoot().getProjects()) {
606 // if (wdPath.isPrefixOf(prj.getLocation())) {
607 // prj.delete(false, false, monitor);
608 // }
609 // }
611 // repos.get(0).getRepository().close();
613 // boolean deleted = deleteRecursively(gitDir, monitor);
614 // if (!deleted) {
615 // MessageDialog.openError(getSite().getShell(),
616 // "Error",
617 // "Could not delete Git Repository");
618 // }
620 // deleted = deleteRecursively(workDir, monitor);
621 // if (!deleted) {
622 // MessageDialog
623 // .openError(getSite().getShell(),
624 // "Error",
625 // "Could not delete Git Working Directory");
626 // }
628 // scheduleRefresh();
629 // }
631 // private boolean deleteRecursively(File fileToDelete,
632 // IProgressMonitor monitor) {
633 // if (fileToDelete.isDirectory()) {
634 // for (File file : fileToDelete.listFiles()) {
635 // if (!deleteRecursively(file, monitor)) {
636 // return false;
637 // }
638 // }
639 // }
640 // monitor.setTaskName(fileToDelete.getAbsolutePath());
641 // boolean deleted = fileToDelete.delete();
642 // if (!deleted) {
643 // System.err.println("Could not delete "
644 // + fileToDelete.getAbsolutePath());
645 // }
646 // return deleted;
647 // }
648 // };
650 // try {
651 // ResourcesPlugin.getWorkspace().run(wsr,
652 // ResourcesPlugin.getWorkspace().getRoot(),
653 // IWorkspace.AVOID_UPDATE,
654 // new NullProgressMonitor());
655 // } catch (CoreException e1) {
656 // // TODO Exception handling
657 // e1.printStackTrace();
658 // }
660 // }
662 // });
664 new MenuItem(men, SWT.SEPARATOR);
666 if (!isBare) {
667 createImportProjectItem(men, repo, repo.getWorkDir().getPath());
669 new MenuItem(men, SWT.SEPARATOR);
672 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
673 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
674 openPropsView.addSelectionListener(new SelectionAdapter() {
676 @Override
677 public void widgetSelected(SelectionEvent e) {
678 try {
679 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
680 .getActivePage().showView(
681 IPageLayout.ID_PROP_SHEET);
682 } catch (PartInitException e1) {
683 // just ignore
689 new MenuItem(men, SWT.SEPARATOR);
691 createCopyPathItem(men, repo.getDirectory().getPath());
694 if (node.getType() == RepositoryTreeNodeType.REMOTES) {
696 MenuItem remoteConfig = new MenuItem(men, SWT.PUSH);
697 remoteConfig.setText(UIText.RepositoriesView_NewRemoteMenu);
698 remoteConfig.addSelectionListener(new SelectionAdapter() {
700 @Override
701 public void widgetSelected(SelectionEvent e) {
703 WizardDialog dlg = new WizardDialog(getSite().getShell(),
704 new NewRemoteWizard(node.getRepository()));
705 if (dlg.open() == Window.OK)
706 scheduleRefresh();
713 if (node.getType() == RepositoryTreeNodeType.REMOTE) {
715 final String configName = (String) node.getObject();
717 RemoteConfig rconfig;
718 try {
719 rconfig = new RemoteConfig(node.getRepository().getConfig(),
720 configName);
721 } catch (URISyntaxException e2) {
722 // TODO Exception handling
723 rconfig = null;
726 boolean fetchExists = rconfig != null
727 && !rconfig.getURIs().isEmpty();
728 boolean pushExists = rconfig != null
729 && !rconfig.getPushURIs().isEmpty();
731 if (!fetchExists) {
732 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
733 configureUrlFetch
734 .setText(UIText.RepositoriesView_CreateFetch_menu);
736 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
738 @Override
739 public void widgetSelected(SelectionEvent e) {
741 WizardDialog dlg = new WizardDialog(getSite()
742 .getShell(), new ConfigureRemoteWizard(node
743 .getRepository(), configName, false));
744 if (dlg.open() == Window.OK)
745 scheduleRefresh();
752 if (!pushExists) {
753 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
755 configureUrlPush
756 .setText(UIText.RepositoriesView_CreatePush_menu);
758 configureUrlPush.addSelectionListener(new SelectionAdapter() {
760 @Override
761 public void widgetSelected(SelectionEvent e) {
763 WizardDialog dlg = new WizardDialog(getSite()
764 .getShell(), new ConfigureRemoteWizard(node
765 .getRepository(), configName, true));
766 if (dlg.open() == Window.OK)
767 scheduleRefresh();
774 if (!fetchExists || !pushExists)
775 // add a separator dynamically
776 new MenuItem(men, SWT.SEPARATOR);
778 MenuItem removeRemote = new MenuItem(men, SWT.PUSH);
779 removeRemote.setText(UIText.RepositoriesView_RemoveRemoteMenu);
780 removeRemote.addSelectionListener(new SelectionAdapter() {
782 @Override
783 public void widgetSelected(SelectionEvent e) {
785 boolean ok = MessageDialog
786 .openConfirm(
787 getSite().getShell(),
788 UIText.RepositoriesView_ConfirmDeleteRemoteHeader,
790 .bind(
791 UIText.RepositoriesView_ConfirmDeleteRemoteMessage,
792 configName));
793 if (ok) {
794 RepositoryConfig config = node.getRepository()
795 .getConfig();
796 config.unsetSection(REMOTE, configName);
797 try {
798 config.save();
799 scheduleRefresh();
800 } catch (IOException e1) {
801 Activator.handleError(
802 UIText.RepositoriesView_ErrorHeader, e1,
803 true);
811 new MenuItem(men, SWT.SEPARATOR);
813 MenuItem openPropsView = new MenuItem(men, SWT.PUSH);
814 openPropsView.setText(UIText.RepositoriesView_OpenPropertiesMenu);
815 openPropsView.addSelectionListener(new SelectionAdapter() {
817 @Override
818 public void widgetSelected(SelectionEvent e) {
819 try {
820 PlatformUI.getWorkbench().getActiveWorkbenchWindow()
821 .getActivePage().showView(
822 IPageLayout.ID_PROP_SHEET);
823 } catch (PartInitException e1) {
824 // just ignore
831 if (node.getType() == RepositoryTreeNodeType.FETCH) {
833 final String configName = (String) node.getParent().getObject();
835 MenuItem configureUrlFetch = new MenuItem(men, SWT.PUSH);
836 configureUrlFetch
837 .setText(UIText.RepositoriesView_ConfigureFetchMenu);
839 configureUrlFetch.addSelectionListener(new SelectionAdapter() {
841 @Override
842 public void widgetSelected(SelectionEvent e) {
844 WizardDialog dlg = new WizardDialog(getSite().getShell(),
845 new ConfigureRemoteWizard(node.getRepository(),
846 configName, false));
847 if (dlg.open() == Window.OK)
848 scheduleRefresh();
854 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
855 deleteFetch.setText(UIText.RepositoriesView_RemoveFetch_menu);
856 deleteFetch.addSelectionListener(new SelectionAdapter() {
858 @Override
859 public void widgetSelected(SelectionEvent e) {
860 RepositoryConfig config = node.getRepository().getConfig();
861 config.unset("remote", configName, "url"); //$NON-NLS-1$ //$NON-NLS-2$
862 config.unset("remote", configName, "fetch"); //$NON-NLS-1$//$NON-NLS-2$
863 try {
864 config.save();
865 scheduleRefresh();
866 } catch (IOException e1) {
867 MessageDialog.openError(getSite().getShell(),
868 UIText.RepositoriesView_ErrorHeader, e1
869 .getMessage());
877 if (node.getType() == RepositoryTreeNodeType.PUSH) {
879 final String configName = (String) node.getParent().getObject();
881 MenuItem configureUrlPush = new MenuItem(men, SWT.PUSH);
883 configureUrlPush.setText(UIText.RepositoriesView_ConfigurePushMenu);
885 configureUrlPush.addSelectionListener(new SelectionAdapter() {
887 @Override
888 public void widgetSelected(SelectionEvent e) {
890 WizardDialog dlg = new WizardDialog(getSite().getShell(),
891 new ConfigureRemoteWizard(node.getRepository(),
892 configName, true));
893 if (dlg.open() == Window.OK)
894 scheduleRefresh();
899 MenuItem deleteFetch = new MenuItem(men, SWT.PUSH);
900 deleteFetch.setText(UIText.RepositoriesView_RemovePush_menu);
901 deleteFetch.addSelectionListener(new SelectionAdapter() {
903 @Override
904 public void widgetSelected(SelectionEvent e) {
905 RepositoryConfig config = node.getRepository().getConfig();
906 config.unset("remote", configName, "pushurl"); //$NON-NLS-1$ //$NON-NLS-2$
907 config.unset("remote", configName, "push"); //$NON-NLS-1$ //$NON-NLS-2$
908 try {
909 config.save();
910 scheduleRefresh();
911 } catch (IOException e1) {
912 MessageDialog.openError(getSite().getShell(),
913 UIText.RepositoriesView_ErrorHeader, e1
914 .getMessage());
921 if (node.getType() == RepositoryTreeNodeType.FILE) {
923 final File file = (File) node.getObject();
925 MenuItem openInTextEditor = new MenuItem(men, SWT.PUSH);
926 openInTextEditor
927 .setText(UIText.RepositoriesView_OpenInTextEditor_menu);
928 openInTextEditor.addSelectionListener(new SelectionAdapter() {
930 @Override
931 public void widgetSelected(SelectionEvent e) {
932 openFile(file);
937 new MenuItem(men, SWT.SEPARATOR);
938 createCopyPathItem(men, file.getPath());
941 if (!isBare && node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
942 String path = node.getRepository().getWorkDir().getAbsolutePath();
943 createImportProjectItem(men, node.getRepository(), path);
944 new MenuItem(men, SWT.SEPARATOR);
945 createCopyPathItem(men, path);
948 if (node.getType() == RepositoryTreeNodeType.FOLDER) {
949 String path = ((File) node.getObject()).getPath();
950 createImportProjectItem(men, node.getRepository(), path);
951 new MenuItem(men, SWT.SEPARATOR);
952 createCopyPathItem(men, path);
957 private boolean isBare(Repository repository) {
958 return repository.getConfig().getBoolean("core", "bare", false); //$NON-NLS-1$ //$NON-NLS-2$
961 private void createCopyPathItem(Menu men, final String path) {
963 MenuItem copyPath;
964 copyPath = new MenuItem(men, SWT.PUSH);
965 copyPath.setText(UIText.RepositoriesView_CopyPathToClipboardMenu);
966 copyPath.addSelectionListener(new SelectionAdapter() {
968 @Override
969 public void widgetSelected(SelectionEvent e) {
970 Clipboard clipboard = new Clipboard(null);
971 TextTransfer textTransfer = TextTransfer.getInstance();
972 Transfer[] transfers = new Transfer[] { textTransfer };
973 Object[] data = new Object[] { path };
974 clipboard.setContents(data, transfers);
975 clipboard.dispose();
982 private void createCreateBranchItem(Menu men, final RepositoryTreeNode node) {
984 final boolean remoteMode;
985 final Ref ref;
986 if (node.getType() == RepositoryTreeNodeType.REF) {
987 remoteMode = node.getParent().getType() == RepositoryTreeNodeType.REMOTEBRANCHES;
988 ref = (Ref) node.getObject();
989 } else if (node.getType() == RepositoryTreeNodeType.LOCALBRANCHES) {
990 remoteMode = false;
991 ref = null;
992 } else if (node.getType() == RepositoryTreeNodeType.REMOTEBRANCHES) {
993 remoteMode = true;
994 ref = null;
995 } else
996 return;
998 MenuItem createLocal = new MenuItem(men, SWT.PUSH);
999 if (remoteMode)
1000 createLocal.setText(UIText.RepositoriesView_NewRemoteBranchMenu);
1001 else
1002 createLocal.setText(UIText.RepositoriesView_NewLocalBranchMenu);
1004 createLocal.addSelectionListener(new SelectionAdapter() {
1006 @Override
1007 public void widgetSelected(SelectionEvent e) {
1009 Wizard wiz = new Wizard() {
1011 @Override
1012 public void addPages() {
1013 addPage(new CreateBranchPage(node.getRepository(), ref,
1014 remoteMode));
1015 setWindowTitle(UIText.RepositoriesView_NewBranchTitle);
1018 @Override
1019 public boolean performFinish() {
1021 try {
1022 getContainer().run(false, true,
1023 new IRunnableWithProgress() {
1025 public void run(IProgressMonitor monitor)
1026 throws InvocationTargetException,
1027 InterruptedException {
1028 CreateBranchPage cp = (CreateBranchPage) getPages()[0];
1029 try {
1030 cp.createBranch(monitor);
1031 } catch (CoreException ce) {
1032 throw new InvocationTargetException(
1033 ce);
1034 } catch (IOException ioe) {
1035 throw new InvocationTargetException(
1036 ioe);
1041 } catch (InvocationTargetException ite) {
1042 Activator
1043 .handleError(
1044 UIText.RepositoriesView_BranchCreationFailureMessage,
1045 ite.getCause(), true);
1046 return false;
1047 } catch (InterruptedException ie) {
1048 // ignore here
1050 return true;
1053 if (new WizardDialog(getSite().getShell(), wiz).open() == Window.OK)
1054 scheduleRefresh();
1061 private void createDeleteBranchItem(Menu men, final RepositoryTreeNode node) {
1063 final Ref ref = (Ref) node.getObject();
1065 MenuItem deleteBranch = new MenuItem(men, SWT.PUSH);
1066 deleteBranch.setText(UIText.RepositoriesView_DeleteBranchMenu);
1068 try {
1069 if (node.getRepository().getFullBranch().equals(ref.getName())) {
1070 deleteBranch.setEnabled(false);
1072 } catch (IOException e2) {
1073 // ignore
1076 deleteBranch.addSelectionListener(new SelectionAdapter() {
1078 @Override
1079 public void widgetSelected(SelectionEvent e) {
1081 if (!MessageDialog
1082 .openConfirm(
1083 getSite().getShell(),
1084 UIText.RepositoriesView_ConfirmDeleteTitle,
1086 .bind(
1087 UIText.RepositoriesView_ConfirmBranchDeletionMessage,
1088 ref.getName())))
1089 return;
1091 try {
1092 new ProgressMonitorDialog(getSite().getShell()).run(false,
1093 false, new IRunnableWithProgress() {
1095 public void run(IProgressMonitor monitor)
1096 throws InvocationTargetException,
1097 InterruptedException {
1099 try {
1100 RefUpdate op = node.getRepository()
1101 .updateRef(ref.getName());
1102 op.setRefLogMessage("branch deleted", //$NON-NLS-1$
1103 false);
1104 // we set the force update in order
1105 // to avoid having this rejected
1106 // due to minor issues
1107 op.setForceUpdate(true);
1108 op.delete();
1109 scheduleRefresh();
1110 } catch (IOException ioe) {
1111 throw new InvocationTargetException(ioe);
1116 } catch (InvocationTargetException e1) {
1117 Activator
1118 .handleError(
1119 UIText.RepositoriesView_BranchDeletionFailureMessage,
1120 e1.getCause(), true);
1121 e1.printStackTrace();
1122 } catch (InterruptedException e1) {
1123 // ignore
1131 private void openFile(File file) {
1132 IFileStore store = EFS.getLocalFileSystem().getStore(
1133 new Path(file.getAbsolutePath()));
1134 try {
1135 // TODO do we need a read-only editor here?
1136 IDE.openEditor(getSite().getPage(),
1137 new FileStoreEditorInput(store),
1138 EditorsUI.DEFAULT_TEXT_EDITOR_ID);
1139 } catch (PartInitException e) {
1140 Activator.handleError(UIText.RepositoriesView_Error_WindowTitle, e,
1141 true);
1145 private void checkoutBranch(final RepositoryTreeNode node,
1146 final String refName) {
1147 // for the sake of UI responsiveness, let's start a job
1148 Job job = new Job(NLS.bind(UIText.RepositoriesView_CheckingOutMessage,
1149 refName)) {
1151 @Override
1152 protected IStatus run(IProgressMonitor monitor) {
1154 Repository repo = node.getRepository();
1156 final BranchOperation op = new BranchOperation(repo, refName);
1157 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1159 public void run(IProgressMonitor myMonitor)
1160 throws CoreException {
1161 op.execute(myMonitor);
1165 try {
1166 ResourcesPlugin.getWorkspace().run(wsr,
1167 ResourcesPlugin.getWorkspace().getRoot(),
1168 IWorkspace.AVOID_UPDATE, monitor);
1169 scheduleRefresh();
1170 } catch (CoreException e1) {
1171 return new Status(IStatus.ERROR, Activator.getPluginId(),
1172 e1.getMessage(), e1);
1175 return Status.OK_STATUS;
1179 job.setUser(true);
1180 job.schedule();
1183 private void createImportProjectItem(Menu men, final Repository repo,
1184 final String path) {
1186 MenuItem startWizard;
1187 startWizard = new MenuItem(men, SWT.PUSH);
1188 startWizard.setText(UIText.RepositoriesView_ImportProjectsMenu);
1189 startWizard.addSelectionListener(new SelectionAdapter() {
1191 @Override
1192 public void widgetSelected(SelectionEvent e) {
1193 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1194 new GitCreateProjectViaWizardWizard(repo, path));
1195 if (dlg.open() == Window.OK)
1196 scheduleRefresh();
1202 // we could start the ImportWizard here,
1203 // unfortunately, this fails within a wizard
1204 // startWizard = new MenuItem(men, SWT.PUSH);
1205 // startWizard.setText("Start the Import wizard...");
1206 // startWizard.addSelectionListener(new SelectionAdapter() {
1208 // @Override
1209 // public void widgetSelected(SelectionEvent e) {
1211 // IHandlerService handlerService = (IHandlerService) getSite()
1212 // .getWorkbenchWindow().getWorkbench().getService(
1213 // IHandlerService.class);
1215 // try {
1216 // handlerService.executeCommand("org.eclipse.ui.file.import", //$NON-NLS-1$
1217 // null);
1218 // } catch (ExecutionException e1) {
1219 // Activator.handleError(e1.getMessage(), e1, true);
1220 // } catch (NotDefinedException e1) {
1221 // Activator.handleError(e1.getMessage(), e1, true);
1222 // } catch (NotEnabledException e1) {
1223 // Activator.handleError(e1.getMessage(), e1, true);
1224 // } catch (NotHandledException e1) {
1225 // Activator.handleError(e1.getMessage(), e1, true);
1226 // }
1227 // }
1229 // });
1232 private void addActionsToToolbar() {
1234 IToolBarManager manager = getViewSite().getActionBars()
1235 .getToolBarManager();
1237 refreshAction = new Action(UIText.RepositoriesView_Refresh_Button) {
1239 @Override
1240 public void run() {
1241 scheduleRefresh();
1244 refreshAction.setImageDescriptor(UIIcons.ELCL16_REFRESH);
1245 manager.add(refreshAction);
1247 linkWithSelectionAction = new Action(
1248 UIText.RepositoriesView_LinkWithSelection_action,
1249 IAction.AS_CHECK_BOX) {
1251 @Override
1252 public void run() {
1253 IEclipsePreferences prefs = getPrefs();
1254 prefs.putBoolean(PREFS_SYNCED, isChecked());
1255 try {
1256 prefs.flush();
1257 } catch (BackingStoreException e) {
1258 // ignore here
1260 if (isChecked()) {
1261 ISelectionService srv = (ISelectionService) getSite()
1262 .getService(ISelectionService.class);
1263 reactOnSelection(srv.getSelection());
1270 linkWithSelectionAction
1271 .setToolTipText(UIText.RepositoriesView_LinkWithSelection_action);
1272 linkWithSelectionAction.setImageDescriptor(UIIcons.ELCL16_SYNCED);
1273 linkWithSelectionAction.setChecked(getPrefs().getBoolean(PREFS_SYNCED,
1274 false));
1276 manager.add(linkWithSelectionAction);
1278 manager.add(new Separator());
1280 IAction collapseAllAction = new Action(
1281 UIText.RepositoriesView_CollapseAllMenu) {
1283 @Override
1284 public void run() {
1285 tv.collapseAll();
1288 collapseAllAction.setImageDescriptor(UIIcons.COLLAPSEALL);
1289 manager.add(collapseAllAction);
1291 manager.add(new Separator());
1293 importAction = new Action(UIText.RepositoriesView_Import_Button) {
1295 @Override
1296 public void run() {
1297 WizardDialog dlg = new WizardDialog(getSite().getShell(),
1298 new GitCloneWizard());
1299 if (dlg.open() == Window.OK)
1300 scheduleRefresh();
1303 importAction.setToolTipText(UIText.RepositoriesView_Clone_Tooltip);
1304 importAction.setImageDescriptor(UIIcons.CLONEGIT);
1306 manager.add(importAction);
1308 addAction = new Action(UIText.RepositoriesView_Add_Button) {
1310 @Override
1311 public void run() {
1312 RepositorySearchDialog sd = new RepositorySearchDialog(
1313 getSite().getShell(), getDirs());
1314 if (sd.open() == Window.OK) {
1315 Set<String> dirs = new HashSet<String>();
1316 dirs.addAll(getDirs());
1317 if (dirs.addAll(sd.getDirectories()))
1318 saveDirs(dirs);
1319 scheduleRefresh();
1324 addAction.setToolTipText(UIText.RepositoriesView_AddRepository_Tooltip);
1325 addAction.setImageDescriptor(UIIcons.NEW_REPOSITORY);
1327 manager.add(addAction);
1329 // copy and paste are global actions; we just implement them
1330 // and register them with the global action handler
1331 // we enable/disable them upon tree selection changes
1333 copyAction = new Action("") { //$NON-NLS-1$
1335 @Override
1336 public void run() {
1337 // for REPO, WORKINGDIR, FILE, FOLDER: copy directory
1338 IStructuredSelection sel = (IStructuredSelection) tv
1339 .getSelection();
1340 if (sel.size() == 1) {
1341 RepositoryTreeNode node = (RepositoryTreeNode) sel
1342 .getFirstElement();
1343 String dir = null;
1344 if (node.getType() == RepositoryTreeNodeType.REPO) {
1345 dir = node.getRepository().getDirectory().getPath();
1346 } else if (node.getType() == RepositoryTreeNodeType.FILE
1347 || node.getType() == RepositoryTreeNodeType.FOLDER) {
1348 dir = ((File) node.getObject()).getPath();
1349 } else if (node.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1350 if (!isBare(node.getRepository()))
1351 dir = node.getRepository().getWorkDir().getPath();
1353 if (dir != null) {
1354 Clipboard clip = null;
1355 try {
1356 clip = new Clipboard(getSite().getShell()
1357 .getDisplay());
1358 clip
1359 .setContents(new Object[] { dir },
1360 new Transfer[] { TextTransfer
1361 .getInstance() });
1362 } finally {
1363 if (clip != null)
1364 // we must dispose ourselves
1365 clip.dispose();
1372 copyAction.setEnabled(false);
1374 getViewSite().getActionBars().setGlobalActionHandler(
1375 ActionFactory.COPY.getId(), copyAction);
1377 pasteAction = new Action("") { //$NON-NLS-1$
1379 @Override
1380 public void run() {
1381 // we check if the pasted content is a directory
1382 // repository location and try to add this
1383 String errorMessage = null;
1385 Clipboard clip = null;
1386 try {
1387 clip = new Clipboard(getSite().getShell().getDisplay());
1388 String content = (String) clip.getContents(TextTransfer
1389 .getInstance());
1390 if (content == null) {
1391 errorMessage = UIText.RepositoriesView_NothingToPasteMessage;
1392 return;
1395 File file = new File(content);
1396 if (!file.exists() || !file.isDirectory()) {
1397 errorMessage = UIText.RepositoriesView_ClipboardContentNotDirectoryMessage;
1398 return;
1401 if (!RepositoryCache.FileKey.isGitRepository(file)) {
1402 errorMessage = NLS
1403 .bind(
1404 UIText.RepositoriesView_ClipboardContentNoGitRepoMessage,
1405 content);
1406 return;
1409 if (addDir(file))
1410 scheduleRefresh();
1411 else
1412 errorMessage = NLS.bind(
1413 UIText.RepositoriesView_PasteRepoAlreadyThere,
1414 content);
1415 } finally {
1416 if (clip != null)
1417 // we must dispose ourselves
1418 clip.dispose();
1419 if (errorMessage != null)
1420 // TODO String ext
1421 MessageDialog.openWarning(getSite().getShell(),
1422 UIText.RepositoriesView_PasteFailureTitle,
1423 errorMessage);
1429 getViewSite().getActionBars().setGlobalActionHandler(
1430 ActionFactory.PASTE.getId(), pasteAction);
1435 * @return the preferences
1437 protected static IEclipsePreferences getPrefs() {
1438 return new InstanceScope().getNode(Activator.getPluginId());
1441 @Override
1442 public void dispose() {
1443 // make sure to cancel the refresh job
1444 if (this.scheduledJob != null) {
1445 this.scheduledJob.cancel();
1446 this.scheduledJob = null;
1448 super.dispose();
1452 * Schedules a refreh
1454 public void scheduleRefresh() {
1456 Job job = new Job("Refreshing Git Repositories view") { //$NON-NLS-1$
1458 @Override
1459 protected IStatus run(IProgressMonitor monitor) {
1461 final List<RepositoryTreeNode<Repository>> input;
1462 try {
1463 input = getRepositoriesFromDirs(monitor);
1464 } catch (InterruptedException e) {
1465 return new Status(IStatus.ERROR, Activator.getPluginId(), e
1466 .getMessage(), e);
1469 boolean needsNewInput = tv.getInput() == null;
1470 List oldInput = (List) tv.getInput();
1471 if (!needsNewInput)
1472 needsNewInput = oldInput.size() != input.size();
1474 if (!needsNewInput) {
1475 for (int i = 0; i < input.size(); i++) {
1476 needsNewInput = !input.get(i).equals(oldInput.get(i));
1477 if (needsNewInput)
1478 break;
1482 final boolean updateInput = needsNewInput;
1484 Display.getDefault().syncExec(new Runnable() {
1486 public void run() {
1487 // keep expansion state and selection so that we can
1488 // restore the tree
1489 // after update
1490 Object[] expanded = tv.getExpandedElements();
1491 IStructuredSelection sel = (IStructuredSelection) tv
1492 .getSelection();
1493 if (updateInput)
1494 tv.setInput(input);
1495 else
1496 tv.refresh();
1497 tv.setExpandedElements(expanded);
1499 Object selected = sel.getFirstElement();
1500 if (selected != null)
1501 tv.reveal(selected);
1503 IViewPart part = PlatformUI.getWorkbench()
1504 .getActiveWorkbenchWindow().getActivePage()
1505 .findView(IPageLayout.ID_PROP_SHEET);
1506 if (part != null) {
1507 PropertySheet sheet = (PropertySheet) part;
1508 PropertySheetPage page = (PropertySheetPage) sheet
1509 .getCurrentPage();
1510 page.refresh();
1515 return new Status(IStatus.OK, Activator.getPluginId(), ""); //$NON-NLS-1$
1520 job.setSystem(true);
1522 IWorkbenchSiteProgressService service = (IWorkbenchSiteProgressService) getSite()
1523 .getService(IWorkbenchSiteProgressService.class);
1525 service.schedule(job);
1527 scheduledJob = job;
1532 * Adds a directory to the list if it is not already there
1534 * @param file
1535 * @return see {@link Collection#add(Object)}
1537 public static boolean addDir(File file) {
1539 String dirString;
1540 try {
1541 dirString = file.getCanonicalPath();
1542 } catch (IOException e) {
1543 dirString = file.getAbsolutePath();
1546 List<String> dirStrings = getDirs();
1547 if (dirStrings.contains(dirString)) {
1548 return false;
1549 } else {
1550 Set<String> dirs = new HashSet<String>();
1551 dirs.addAll(dirStrings);
1552 dirs.add(dirString);
1553 saveDirs(dirs);
1554 return true;
1559 * Converts the directories as configured for this view into a list of
1560 * {@link Repository} objects suitable for the tree content provider
1561 * <p>
1562 * TODO move to some utility class
1564 * @param monitor
1565 * @return a list of nodes
1566 * @throws InterruptedException
1568 public static List<RepositoryTreeNode<Repository>> getRepositoriesFromDirs(
1569 IProgressMonitor monitor) throws InterruptedException {
1571 List<String> gitDirStrings = getDirs();
1572 List<RepositoryTreeNode<Repository>> input = new ArrayList<RepositoryTreeNode<Repository>>();
1574 for (String dirString : gitDirStrings) {
1575 if (monitor != null && monitor.isCanceled()) {
1576 throw new InterruptedException(
1577 UIText.RepositoriesView_ActionCanceled_Message);
1579 try {
1580 File dir = new File(dirString);
1581 if (dir.exists() && dir.isDirectory()) {
1582 Repository repo = new Repository(dir);
1583 RepositoryTreeNode<Repository> node = new RepositoryTreeNode<Repository>(
1584 null, RepositoryTreeNodeType.REPO, repo, repo);
1585 input.add(node);
1587 } catch (IOException e) {
1588 IStatus error = new Status(IStatus.ERROR, Activator
1589 .getPluginId(), e.getMessage(), e);
1590 Activator.getDefault().getLog().log(error);
1593 Collections.sort(input);
1594 return input;
1597 private static void saveDirs(Set<String> gitDirStrings) {
1598 StringBuilder sb = new StringBuilder();
1599 for (String gitDirString : gitDirStrings) {
1600 sb.append(gitDirString);
1601 sb.append(File.pathSeparatorChar);
1604 IEclipsePreferences prefs = getPrefs();
1605 prefs.put(PREFS_DIRECTORIES, sb.toString());
1606 try {
1607 prefs.flush();
1608 } catch (BackingStoreException e) {
1609 IStatus error = new Status(IStatus.ERROR, Activator.getPluginId(),
1610 e.getMessage(), e);
1611 Activator.getDefault().getLog().log(error);
1615 @Override
1616 public void setFocus() {
1617 tv.getTree().setFocus();
1620 @SuppressWarnings("boxing")
1621 private boolean confirmProjectDeletion(List<IProject> projectsToDelete) {
1622 boolean confirmed;
1623 confirmed = MessageDialog
1624 .openConfirm(
1625 getSite().getShell(),
1626 UIText.RepositoriesView_ConfirmProjectDeletion_WindowTitle,
1628 .bind(
1629 UIText.RepositoriesView_ConfirmProjectDeletion_Question,
1630 projectsToDelete.size()));
1631 return confirmed;
1634 public void addSelectionChangedListener(ISelectionChangedListener listener) {
1635 selectionListeners.add(listener);
1638 public ISelection getSelection() {
1639 return currentSelection;
1642 public void removeSelectionChangedListener(
1643 ISelectionChangedListener listener) {
1644 selectionListeners.remove(listener);
1648 public void setSelection(ISelection selection) {
1649 currentSelection = selection;
1650 for (ISelectionChangedListener listener : selectionListeners) {
1651 listener.selectionChanged(new SelectionChangedEvent(
1652 RepositoriesView.this, selection));
1657 * Opens the tree and marks the folder to which a project is pointing
1659 * @param resource
1660 * TODO exceptions?
1662 @SuppressWarnings("unchecked")
1663 public void showResource(final IResource resource) {
1664 IProject project = resource.getProject();
1665 RepositoryMapping mapping = RepositoryMapping.getMapping(project);
1666 if (mapping == null)
1667 return;
1669 if (addDir(mapping.getRepository().getDirectory())) {
1670 scheduleRefresh();
1673 boolean doSetSelection = false;
1675 if (this.scheduledJob != null) {
1676 int state = this.scheduledJob.getState();
1677 if (state == Job.WAITING || state == Job.RUNNING) {
1678 this.scheduledJob.addJobChangeListener(new JobChangeAdapter() {
1680 @Override
1681 public void done(IJobChangeEvent event) {
1682 showResource(resource);
1685 } else {
1686 doSetSelection = true;
1690 if (doSetSelection) {
1691 RepositoriesViewContentProvider cp = (RepositoriesViewContentProvider) tv
1692 .getContentProvider();
1693 RepositoryTreeNode currentNode = null;
1694 Object[] repos = cp.getElements(tv.getInput());
1695 for (Object repo : repos) {
1696 RepositoryTreeNode node = (RepositoryTreeNode) repo;
1697 // TODO equals implementation of Repository?
1698 if (mapping.getRepository().getDirectory().equals(
1699 ((Repository) node.getObject()).getDirectory())) {
1700 for (Object child : cp.getChildren(node)) {
1701 RepositoryTreeNode childNode = (RepositoryTreeNode) child;
1702 if (childNode.getType() == RepositoryTreeNodeType.WORKINGDIR) {
1703 currentNode = childNode;
1704 break;
1707 break;
1711 IPath relPath = new Path(mapping.getRepoRelativePath(resource));
1713 for (String segment : relPath.segments()) {
1714 for (Object child : cp.getChildren(currentNode)) {
1715 RepositoryTreeNode<File> childNode = (RepositoryTreeNode<File>) child;
1716 if (childNode.getObject().getName().equals(segment)) {
1717 currentNode = childNode;
1718 break;
1723 final RepositoryTreeNode selNode = currentNode;
1725 Display.getDefault().asyncExec(new Runnable() {
1727 public void run() {
1728 tv.setSelection(new StructuredSelection(selNode), true);
1736 public boolean show(ShowInContext context) {
1737 ISelection selection = context.getSelection();
1738 if (selection instanceof IStructuredSelection) {
1739 IStructuredSelection ss = (IStructuredSelection) selection;
1740 if (ss.size() == 1) {
1741 Object element = ss.getFirstElement();
1742 if (element instanceof IAdaptable) {
1743 IResource resource = (IResource) ((IAdaptable) element)
1744 .getAdapter(IResource.class);
1745 if (resource != null) {
1746 showResource(resource);
1747 return true;
1752 return false;
1755 private void removeRepository(final IProgressMonitor monitor,
1756 final Repository... repository) {
1757 final List<IProject> projectsToDelete = new ArrayList<IProject>();
1759 monitor
1760 .setTaskName(UIText.RepositoriesView_DeleteRepoDeterminProjectsMessage);
1762 for (Repository repo : repository) {
1763 File workDir = repo.getWorkDir();
1764 final IPath wdPath = new Path(workDir.getAbsolutePath());
1765 for (IProject prj : ResourcesPlugin.getWorkspace().getRoot()
1766 .getProjects()) {
1767 if (monitor.isCanceled())
1768 return;
1769 if (wdPath.isPrefixOf(prj.getLocation())) {
1770 projectsToDelete.add(prj);
1775 if (!projectsToDelete.isEmpty()) {
1776 boolean confirmed;
1777 confirmed = confirmProjectDeletion(projectsToDelete);
1778 if (!confirmed) {
1779 return;
1783 if (monitor.isCanceled())
1784 return;
1786 IWorkspaceRunnable wsr = new IWorkspaceRunnable() {
1788 public void run(IProgressMonitor actMonitor) throws CoreException {
1790 for (IProject prj : projectsToDelete) {
1791 prj.delete(false, false, actMonitor);
1793 for (Repository repo : repository)
1794 removeDir(repo.getDirectory());
1795 scheduleRefresh();
1799 try {
1800 ResourcesPlugin.getWorkspace().run(wsr,
1801 ResourcesPlugin.getWorkspace().getRoot(),
1802 IWorkspace.AVOID_UPDATE, monitor);
1803 } catch (CoreException e1) {
1804 Activator.logError(e1.getMessage(), e1);